import Foundation import SwiftData /// Represents a single audio track in the library. @Model final class Track { var id: UUID var title: String var artist: String var album: String var genre: String /// Path relative to the app's documents directory (e.g. "Music/Artist - Title.mp3"). var filePath: String /// Original filename without directory (used for cross-device matching). var fileName: String var duration: TimeInterval var bpm: Double? var musicalKey: String? var sampleRate: Double var bitDepth: Int var channels: Int var fileFormat: String var fileSizeBytes: Int64 var year: Int? // release year from metadata var dateAdded: Date var lastPlayed: Date? var playCount: Int var rating: Int // 0-5 stars var color: String? // user-assigned color tag var notes: String // MARK: - Cloud (Chad Music) fields /// If true, this track is from Chad Music cloud — streamed via AVPlayer, not local file. var isCloud: Bool = false /// Chad Music server URL for streaming (e.g., "/music/Artist/Album/track.mp3"). var cloudStreamPath: String? /// Chad Music track ID (hex string from server). var cloudTrackId: String? /// Cached waveform samples (downsampled min/max pairs), stored as Data for efficiency. var waveformData: Data? /// Whether BPM/key analysis has been performed. var isAnalyzed: Bool @Relationship(deleteRule: .cascade, inverse: \CuePoint.track) var cuePoints: [CuePoint] /// Resolve the full file URL from the app's documents directory. var fileURL: URL { let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! return docs.appendingPathComponent(filePath).standardizedFileURL } /// True if this track has a valid local file (not a cloud-only track). var hasLocalFile: Bool { !filePath.isEmpty && !isCloud } var formattedDuration: String { let minutes = Int(duration) / 60 let seconds = Int(duration) % 60 return String(format: "%d:%02d", minutes, seconds) } var formattedBPM: String { guard let bpm else { return "—" } return String(format: "%.1f", bpm) } var formattedFileSize: String { ByteCountFormatter.string(fromByteCount: fileSizeBytes, countStyle: .file) } init( title: String, artist: String = "", album: String = "", genre: String = "", filePath: String, fileName: String = "", duration: TimeInterval = 0, sampleRate: Double = 44100, bitDepth: Int = 16, channels: Int = 2, fileFormat: String = "", fileSizeBytes: Int64 = 0 ) { self.id = UUID() self.title = title self.artist = artist self.album = album self.genre = genre self.filePath = filePath self.fileName = fileName.isEmpty ? (filePath as NSString).lastPathComponent : fileName self.duration = duration self.bpm = nil self.musicalKey = nil self.sampleRate = sampleRate self.bitDepth = bitDepth self.channels = channels self.fileFormat = fileFormat self.fileSizeBytes = fileSizeBytes self.year = nil self.dateAdded = Date() self.lastPlayed = nil self.playCount = 0 self.rating = 0 self.color = nil self.notes = "" self.waveformData = nil self.isAnalyzed = false self.isCloud = false self.cloudStreamPath = nil self.cloudTrackId = nil self.cuePoints = [] } /// Create a Track from a Chad Music cloud track. static func fromCloud(_ chadTrack: ChadTrack) -> Track { let track = Track( title: chadTrack.title, artist: chadTrack.artist ?? "", album: chadTrack.album ?? "", filePath: "", duration: chadTrack.duration ?? 0 ) track.isCloud = true track.cloudStreamPath = chadTrack.url track.cloudTrackId = chadTrack.id track.year = chadTrack.year return track } }