diff --git a/Sources/MusicBrainz/Enums.swift b/Sources/MusicBrainz/Enums.swift new file mode 100644 index 0000000..05811bf --- /dev/null +++ b/Sources/MusicBrainz/Enums.swift @@ -0,0 +1,51 @@ +import Foundation + +public enum Gender: String, Sendable { + case male, female, other + case notApplicable = "not applicable" +} + +public enum ArtistType: String, Sendable { + case person, group, orchestra, choir, character, other +} + +public enum Country: String, Sendable { + case af = "AF" // Afghanistan + case al = "AL" // Albania + case dz = "DZ" // Algeria + case ad = "AD" // Andorra + case ao = "AO" // Angola + case ar = "AR" // Argentina + case am = "AM" // Armenia + case au = "AU" // Australia + case at = "AT" // Austria + case az = "AZ" // Azerbaijan + case be = "BE" // Belgium + case br = "BR" // Brazil + case ca = "CA" // Canada + case cn = "CN" // China + case dk = "DK" // Denmark + case fi = "FI" // Finland + case fr = "FR" // France + case de = "DE" // Germany + case gr = "GR" // Greece + case `in` = "IN" // India + case id = "ID" // Indonesia + case ie = "IE" // Ireland + case it = "IT" // Italy + case jp = "JP" // Japan + case mx = "MX" // Mexico + case nl = "NL" // Netherlands + case nz = "NZ" // New Zealand + case no = "NO" // Norway + case pl = "PL" // Poland + case pt = "PT" // Portugal + case ru = "RU" // Russia + case kr = "KR" // South Korea + case es = "ES" // Spain + case se = "SE" // Sweden + case ch = "CH" // Switzerland + case tr = "TR" // Turkey + case gb = "GB" // United Kingdom + case us = "US" // United States +} diff --git a/Sources/MusicBrainz/Internal/DynamicCodingKey.swift b/Sources/MusicBrainz/Internal/DynamicCodingKey.swift new file mode 100644 index 0000000..a042f10 --- /dev/null +++ b/Sources/MusicBrainz/Internal/DynamicCodingKey.swift @@ -0,0 +1,8 @@ +import Foundation + +internal struct DynamicCodingKey: CodingKey { + var stringValue: String + var intValue: Int? + init?(stringValue: String) { self.stringValue = stringValue } + init?(intValue: Int) { return nil } +} diff --git a/Sources/MusicBrainz/Models/Area.swift b/Sources/MusicBrainz/Models/Area.swift new file mode 100644 index 0000000..ddecadc --- /dev/null +++ b/Sources/MusicBrainz/Models/Area.swift @@ -0,0 +1,20 @@ +import Foundation + +public struct Area: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .area + + public let id: String + public let type: String? + public let name: String + public let sortName: String? + public let disambiguation: String? + public let lifeSpan: LifeSpan? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, type, name, disambiguation, score, relations + case sortName = "sort-name" + case lifeSpan = "life-span" + } +} diff --git a/Sources/MusicBrainz/Models/Artist.swift b/Sources/MusicBrainz/Models/Artist.swift new file mode 100644 index 0000000..43ea402 --- /dev/null +++ b/Sources/MusicBrainz/Models/Artist.swift @@ -0,0 +1,40 @@ +import Foundation + +public struct Artist: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .artist + + public let id: String + public let name: String + public let sortName: String? + public let type: String? + public let country: String? + public let disambiguation: String? + public let lifeSpan: LifeSpan? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id + case name + case sortName = "sort-name" + case type + case country + case disambiguation + case lifeSpan = "life-span" + case relations + case score + } + + public var imageURL: URL? { + guard let relations else { return nil } + // Look for 'image' or 'wikimedia commons' + for rel in relations { + if rel.type == "image" || rel.type == "wikimedia commons" { + if let resource = rel.url?.resource { + return URL(string: resource) + } + } + } + return nil + } +} diff --git a/Sources/MusicBrainz/Models/Event.swift b/Sources/MusicBrainz/Models/Event.swift new file mode 100644 index 0000000..c50e580 --- /dev/null +++ b/Sources/MusicBrainz/Models/Event.swift @@ -0,0 +1,18 @@ +import Foundation + +public struct Event: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .event + + public let id: String + public let name: String + public let time: String? + public let disambiguation: String? + public let lifeSpan: LifeSpan? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, name, time, disambiguation, score, relations + case lifeSpan = "life-span" + } +} diff --git a/Sources/MusicBrainz/Models/Instrument.swift b/Sources/MusicBrainz/Models/Instrument.swift new file mode 100644 index 0000000..b28c8f2 --- /dev/null +++ b/Sources/MusicBrainz/Models/Instrument.swift @@ -0,0 +1,17 @@ +import Foundation + +public struct Instrument: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .instrument + + public let id: String + public let name: String + public let type: String? + public let description: String? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, name, type, description, disambiguation, score, relations + } +} diff --git a/Sources/MusicBrainz/Models/Label.swift b/Sources/MusicBrainz/Models/Label.swift new file mode 100644 index 0000000..fb4084e --- /dev/null +++ b/Sources/MusicBrainz/Models/Label.swift @@ -0,0 +1,21 @@ +import Foundation + +public struct Label: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .label + + public let id: String + public let name: String + public let type: String? + public let labelCode: Int? + public let country: String? + public let disambiguation: String? + public let lifeSpan: LifeSpan? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, name, type, country, disambiguation, score, relations + case labelCode = "label-code" + case lifeSpan = "life-span" + } +} diff --git a/Sources/MusicBrainz/Models/Place.swift b/Sources/MusicBrainz/Models/Place.swift new file mode 100644 index 0000000..33b21e1 --- /dev/null +++ b/Sources/MusicBrainz/Models/Place.swift @@ -0,0 +1,18 @@ +import Foundation + +public struct Place: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .place + + public let id: String + public let name: String + public let type: String? + public let address: String? + public let area: Area? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, name, type, address, area, disambiguation, score, relations + } +} diff --git a/Sources/MusicBrainz/Models/Recording.swift b/Sources/MusicBrainz/Models/Recording.swift new file mode 100644 index 0000000..a9f0082 --- /dev/null +++ b/Sources/MusicBrainz/Models/Recording.swift @@ -0,0 +1,25 @@ +import Foundation + +public struct Recording: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .recording + + public let id: String + public let title: String + public let length: Int? + public let video: Bool? + public let disambiguation: String? + public let firstReleaseDate: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id + case title + case length + case video + case disambiguation + case firstReleaseDate = "first-release-date" + case relations + case score + } +} diff --git a/Sources/MusicBrainz/Models/Release.swift b/Sources/MusicBrainz/Models/Release.swift new file mode 100644 index 0000000..befb188 --- /dev/null +++ b/Sources/MusicBrainz/Models/Release.swift @@ -0,0 +1,27 @@ +import Foundation + +public struct Release: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .release + + public let id: String + public let title: String + public let status: String? + public let date: String? + public let country: String? + public let barcode: String? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id + case title + case status + case date + case country + case barcode + case disambiguation + case relations + case score + } +} diff --git a/Sources/MusicBrainz/Models/ReleaseGroup.swift b/Sources/MusicBrainz/Models/ReleaseGroup.swift new file mode 100644 index 0000000..f939839 --- /dev/null +++ b/Sources/MusicBrainz/Models/ReleaseGroup.swift @@ -0,0 +1,19 @@ +import Foundation + +public struct ReleaseGroup: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .releaseGroup + + public let id: String + public let title: String + public let primaryType: String? + public let artistCredit: [ArtistCredit]? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, title, disambiguation, score, relations + case primaryType = "primary-type" + case artistCredit = "artist-credit" + } +} diff --git a/Sources/MusicBrainz/Models/Series.swift b/Sources/MusicBrainz/Models/Series.swift new file mode 100644 index 0000000..928700b --- /dev/null +++ b/Sources/MusicBrainz/Models/Series.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct Series: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .series + + public let id: String + public let name: String + public let type: String? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, name, type, disambiguation, score, relations + } +} diff --git a/Sources/MusicBrainz/Models/Shared/ArtistCredit.swift b/Sources/MusicBrainz/Models/Shared/ArtistCredit.swift new file mode 100644 index 0000000..2c278eb --- /dev/null +++ b/Sources/MusicBrainz/Models/Shared/ArtistCredit.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct ArtistCredit: Codable, Sendable { + public let name: String + public let artist: Artist? + public let joinphrase: String? +} diff --git a/Sources/MusicBrainz/Models/Shared/CoverArt.swift b/Sources/MusicBrainz/Models/Shared/CoverArt.swift new file mode 100644 index 0000000..d21d84f --- /dev/null +++ b/Sources/MusicBrainz/Models/Shared/CoverArt.swift @@ -0,0 +1,21 @@ +import Foundation + +public struct CoverArtImage: Codable, Sendable { + public let image: String + public let thumbnails: [String: String] + public let types: [String] + public let front: Bool + public let back: Bool + public let edit: Int + public let comment: String + public let id: String +} + +public struct CoverArtResponse: Codable, Sendable { + public let images: [CoverArtImage] + public let release: String + + public var frontImage: CoverArtImage? { + images.first { $0.front } + } +} diff --git a/Sources/MusicBrainz/Models/Shared/LifeSpan.swift b/Sources/MusicBrainz/Models/Shared/LifeSpan.swift new file mode 100644 index 0000000..bd4a5d5 --- /dev/null +++ b/Sources/MusicBrainz/Models/Shared/LifeSpan.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct LifeSpan: Codable, Sendable { + public let begin: String? + public let end: String? + public let ended: Bool? +} diff --git a/Sources/MusicBrainz/Models/Shared/Relation.swift b/Sources/MusicBrainz/Models/Shared/Relation.swift new file mode 100644 index 0000000..40b1443 --- /dev/null +++ b/Sources/MusicBrainz/Models/Shared/Relation.swift @@ -0,0 +1,14 @@ +import Foundation + +public struct Relation: Codable, Sendable { + public let type: String + public let direction: String + public let url: URLResource? + public let artist: Artist? + public let release: Release? + // Add other entities as needed + + enum CodingKeys: String, CodingKey { + case type, direction, url, artist, release + } +} diff --git a/Sources/MusicBrainz/Models/Shared/URLResource.swift b/Sources/MusicBrainz/Models/Shared/URLResource.swift new file mode 100644 index 0000000..b6eea92 --- /dev/null +++ b/Sources/MusicBrainz/Models/Shared/URLResource.swift @@ -0,0 +1,6 @@ +import Foundation + +public struct URLResource: Codable, Sendable { + public let id: String + public let resource: String +} diff --git a/Sources/MusicBrainz/Models/URLReference.swift b/Sources/MusicBrainz/Models/URLReference.swift new file mode 100644 index 0000000..16a8040 --- /dev/null +++ b/Sources/MusicBrainz/Models/URLReference.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct URLReference: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .url + + public let id: String + public let resource: String + public let score: Int? +} diff --git a/Sources/MusicBrainz/Models/Work.swift b/Sources/MusicBrainz/Models/Work.swift new file mode 100644 index 0000000..d4f115e --- /dev/null +++ b/Sources/MusicBrainz/Models/Work.swift @@ -0,0 +1,17 @@ +import Foundation + +public struct Work: MusicBrainzSearchable { + public static let entityType: MusicBrainzEntity = .work + + public let id: String + public let title: String + public let type: String? + public let language: String? + public let disambiguation: String? + public let relations: [Relation]? + public let score: Int? + + enum CodingKeys: String, CodingKey { + case id, title, type, language, disambiguation, score, relations + } +} diff --git a/Sources/MusicBrainz/MusicBrainz.swift b/Sources/MusicBrainz/MusicBrainz.swift deleted file mode 100644 index 9754f2a..0000000 --- a/Sources/MusicBrainz/MusicBrainz.swift +++ /dev/null @@ -1,863 +0,0 @@ -import Foundation - -#if canImport(FoundationNetworking) - import FoundationNetworking -#endif - -public enum MusicBrainzError: Error { - case badURL - case badServerResponse(Int) - case decodingError(Error) - case coordinator(Error) -} - -// MARK: - Generic Support - -internal struct DynamicCodingKey: CodingKey { - var stringValue: String - var intValue: Int? - init?(stringValue: String) { self.stringValue = stringValue } - init?(intValue: Int) { return nil } -} - -public enum MusicBrainzEntity: String, Sendable { - case area - case artist - case event - case instrument - case label - case place - case recording - case release - case releaseGroup = "release-group" - case series - case work - case url - - public var responseKey: String { - switch self { - case .series: return "series" - default: return "\(self.rawValue)s" - } - } -} - -public struct MusicBrainzEntityType: Sendable { - public let entity: MusicBrainzEntity - - public static var area: MusicBrainzEntityType { .init(entity: .area) } - public static var artist: MusicBrainzEntityType { .init(entity: .artist) } - public static var event: MusicBrainzEntityType { .init(entity: .event) } - public static var instrument: MusicBrainzEntityType { .init(entity: .instrument) } - public static var label: MusicBrainzEntityType