import Foundation import SwiftData /// Represents a single audio track in the library. @Model final class Track { var id: UUID = UUID() var title: String = "" var artist: String = "" var album: String = "" var genre: String = "" var filePath: String = "" var duration: TimeInterval = 0 var bpm: Double? var musicalKey: String? var sampleRate: Double = 44100 var bitDepth: Int = 16 var channels: Int = 2 var fileFormat: String = "" var fileSizeBytes: Int64 = 0 var dateAdded: Date = Date() var lastPlayed: Date? var playCount: Int = 0 var rating: Int = 0 // 0-5 stars var color: String? // user-assigned color tag var year: Int? // release year from metadata 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 = false @Relationship(deleteRule: .cascade, inverse: \CuePoint.track) var cuePoints: [CuePoint] var fileURL: URL { URL(fileURLWithPath: filePath.isEmpty ? "/dev/null" : filePath) } /// 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, 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.duration = duration self.bpm = nil self.musicalKey = nil self.sampleRate = sampleRate self.bitDepth = bitDepth self.channels = channels self.fileFormat = fileFormat self.fileSizeBytes = fileSizeBytes self.dateAdded = Date() self.lastPlayed = nil self.playCount = 0 self.rating = 0 self.color = nil self.year = nil self.notes = "" self.waveformData = nil self.isAnalyzed = false 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 } }