M3UExporter.swift 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import Foundation
  2. /// Exports a playlist as an M3U/M3U8 playlist file.
  3. /// M3U is a simple, widely-supported playlist format.
  4. struct M3UExporter: DAWExporter {
  5. static let formatID = "m3u"
  6. static let formatName = "M3U Playlist"
  7. static let fileExtension = "m3u"
  8. static func export(playlist: Playlist, to url: URL, options: ExportOptions) throws {
  9. let entries = playlist.sortedEntries
  10. var lines: [String] = []
  11. // Extended M3U header
  12. lines.append("#EXTM3U")
  13. lines.append("#PLAYLIST:\(playlist.name)")
  14. lines.append("")
  15. for entry in entries {
  16. guard let track = entry.track,
  17. let fileURL = options.effectiveFileURL(for: track) else { continue }
  18. let duration = Int(track.duration)
  19. let displayTitle: String
  20. if track.artist.isEmpty {
  21. displayTitle = track.title
  22. } else {
  23. displayTitle = "\(track.artist) - \(track.title)"
  24. }
  25. // EXTINF line: duration, artist - title
  26. lines.append("#EXTINF:\(duration),\(displayTitle)")
  27. // Additional metadata as comments
  28. if let bpm = track.bpm {
  29. lines.append("#EXTBPM:\(String(format: "%.1f", bpm))")
  30. }
  31. if let key = track.musicalKey {
  32. lines.append("#EXTKEY:\(key)")
  33. }
  34. // File path (relative if copying, absolute otherwise)
  35. if options.copyAudioFiles {
  36. lines.append("\(options.audioFilesRelativePath)/\(fileURL.lastPathComponent)")
  37. } else {
  38. lines.append(fileURL.path)
  39. }
  40. lines.append("")
  41. }
  42. let content = lines.joined(separator: "\n")
  43. try content.write(to: url, atomically: true, encoding: .utf8)
  44. }
  45. }