import Foundation public struct Artist: Identifiable, Hashable, Codable, Sendable { public let id: EntityID public var name: String public var sortName: String public var artwork: EntityID? public var albumCount: Int public init(id: EntityID, name: String, sortName: String? = nil, artwork: EntityID? = nil, albumCount: Int = 0) { self.id = id self.name = name self.sortName = sortName ?? name self.artwork = artwork self.albumCount = albumCount } } public struct Album: Identifiable, Hashable, Codable, Sendable { public let id: EntityID public var title: String public var artistID: EntityID? public var artistName: String public var year: Int? public var genre: String? public var artwork: EntityID? public var trackCount: Int public var duration: TimeInterval public var addedAt: Date public var bestFormat: AudioFormat? public init( id: EntityID, title: String, artistID: EntityID?, artistName: String, year: Int? = nil, genre: String? = nil, artwork: EntityID? = nil, trackCount: Int = 0, duration: TimeInterval = 0, addedAt: Date = .distantPast, bestFormat: AudioFormat? = nil ) { self.id = id self.title = title self.artistID = artistID self.artistName = artistName self.year = year self.genre = genre self.artwork = artwork self.trackCount = trackCount self.duration = duration self.addedAt = addedAt self.bestFormat = bestFormat } } public struct Track: Identifiable, Hashable, Codable, Sendable { public let id: EntityID public var title: String public var albumID: EntityID? public var albumTitle: String public var artistID: EntityID? public var artistName: String public var discNumber: Int public var trackNumber: Int public var duration: TimeInterval public var year: Int? public var genre: String? public var artwork: EntityID? public var isExplicit: Bool public var contentHash: Data? public var formats: [AudioFormat] public init( id: EntityID, title: String, albumID: EntityID?, albumTitle: String, artistID: EntityID?, artistName: String, discNumber: Int = 1, trackNumber: Int = 0, duration: TimeInterval, year: Int? = nil, genre: String? = nil, artwork: EntityID? = nil, isExplicit: Bool = false, contentHash: Data? = nil, formats: [AudioFormat] = [] ) { self.id = id self.title = title self.albumID = albumID self.albumTitle = albumTitle self.artistID = artistID self.artistName = artistName self.discNumber = discNumber self.trackNumber = trackNumber self.duration = duration self.year = year self.genre = genre self.artwork = artwork self.isExplicit = isExplicit self.contentHash = contentHash self.formats = formats } } public struct Playlist: Identifiable, Hashable, Codable, Sendable { public let id: EntityID public var name: String public var summary: String public var artwork: EntityID? public var trackCount: Int public var duration: TimeInterval public var updatedAt: Date public init( id: EntityID, name: String, summary: String = "", artwork: EntityID? = nil, trackCount: Int = 0, duration: TimeInterval = 0, updatedAt: Date = .distantPast ) { self.id = id self.name = name self.summary = summary self.artwork = artwork self.trackCount = trackCount self.duration = duration self.updatedAt = updatedAt } } public struct PlaylistEntry: Identifiable, Hashable, Codable, Sendable { public let id: String public let track: EntityID public init(id: String, track: EntityID) { self.id = id self.track = track } } public struct RadioStation: Identifiable, Hashable, Codable, Sendable { public let id: EntityID public var name: String public var tagline: String public var streamURL: URL public var artwork: EntityID? public init(id: EntityID, name: String, tagline: String = "", streamURL: URL, artwork: EntityID? = nil) { self.id = id self.name = name self.tagline = tagline self.streamURL = streamURL self.artwork = artwork } } public struct Genre: Identifiable, Hashable, Sendable { public var id: String { name } public let name: String public let albumCount: Int public init(name: String, albumCount: Int) { self.name = name self.albumCount = albumCount } }