| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- import CoreTransferable
- import Foundation
- import UniformTypeIdentifiers
- // MARK: - Custom UTTypes for drag-and-drop
- extension UTType {
- static let chadTrack = UTType(exportedAs: "com.mixboard.chad-track")
- static let chadAlbum = UTType(exportedAs: "com.mixboard.chad-album")
- }
- // MARK: - Chad Music API Response Models
- /// Navigation value for drilling into a category (e.g., artist "Pink Floyd" → show their albums).
- struct CategoryFilter: Hashable {
- let category: ChadCategoryType
- let value: String
- }
- /// A category entry from GET /api/cat/:category (e.g., album, artist, genre).
- /// Server returns: {"item": "Rock", "count": 42}
- struct ChadCategory: Codable, Identifiable, Hashable {
- let item: String
- let count: Int?
- // No server-side ID — use item name as identity
- var id: String { item }
- var name: String { item }
- }
- /// An album from the Chad Music API.
- /// Server keys: id, artist, year, album (title), publisher, country, genre, type, status, mb_id, track_count, total_duration, cover
- struct ChadAlbum: Codable, Identifiable, Hashable {
- let id: String
- let album: String? // server can return null for some releases
- let artist: String?
- let year: Int?
- let genre: String?
- let trackCount: Int?
- let cover: String?
- let publisher: String?
- let country: String?
- let type: String?
- let status: String?
- let totalDuration: Double?
- let originalDate: String?
- let mbId: String?
- /// Display-friendly title.
- var title: String { album ?? "Untitled" }
- enum CodingKeys: String, CodingKey {
- case id, album, artist, year, genre, cover, publisher, country
- case trackCount = "track_count"
- case totalDuration = "total_duration"
- case originalDate = "original_date"
- case mbId = "mb_id"
- case type, status
- }
- }
- /// A track from GET /api/album/:id/tracks.
- /// Server keys: id, artist, album_artist, album, year, no (track#), title, bit_rate, duration, url, cover
- struct ChadTrack: Codable, Identifiable, Hashable {
- let id: String
- let title: String
- let artist: String?
- let albumArtist: String?
- let album: String?
- let duration: Double?
- let no: Int? // server calls track number "no"
- let url: String // relative path for streaming
- let bitRate: Int?
- let year: Int?
- let cover: String?
- /// Track number for display.
- var trackNumber: Int? { no }
- enum CodingKeys: String, CodingKey {
- case id, title, artist, album, duration, no, url, year, cover
- case albumArtist = "album_artist"
- case bitRate = "bit_rate"
- }
- /// Human-readable duration string.
- var formattedDuration: String {
- guard let duration else { return "—" }
- let total = Int(duration)
- let minutes = total / 60
- let seconds = total % 60
- return String(format: "%d:%02d", minutes, seconds)
- }
- }
- extension ChadTrack: Transferable {
- static var transferRepresentation: some TransferRepresentation {
- CodableRepresentation(contentType: .chadTrack)
- }
- }
- extension ChadAlbum: Transferable {
- static var transferRepresentation: some TransferRepresentation {
- CodableRepresentation(contentType: .chadAlbum)
- }
- }
- /// The album-tracks endpoint returns a plain array of tracks (not a wrapper object).
- /// We decode as [ChadTrack] directly in the API client.
- /// Library statistics from GET /api/stats.
- /// Server keys: tracks, albums, artists, duration (formatted string), rescans
- struct ChadStats: Codable {
- let tracks: Int?
- let albums: Int?
- let artists: Int?
- let duration: String? // server returns pre-formatted string like "3d 14h 22m"
- }
- /// The browsable category types exposed by the API.
- enum ChadCategoryType: String, CaseIterable, Identifiable {
- case album
- case artist
- case genre
- case year
- case publisher
- case country
- case type
- case status
- var id: String { rawValue }
- var displayName: String {
- switch self {
- case .album: "Albums"
- case .artist: "Artists"
- case .genre: "Genres"
- case .year: "Years"
- case .publisher: "Publishers"
- case .country: "Countries"
- case .type: "Types"
- case .status: "Status"
- }
- }
- var icon: String {
- switch self {
- case .album: "square.stack"
- case .artist: "person.2"
- case .genre: "guitars"
- case .year: "calendar"
- case .publisher: "building.2"
- case .country: "globe"
- case .type: "tag"
- case .status: "checkmark.circle"
- }
- }
- }
|