diff --git a/Sources/MusadoraKit/Continuous/ContinuousSongs.swift b/Sources/MusadoraKit/Continuous/ContinuousSongs.swift deleted file mode 100644 index a02e1dff..00000000 --- a/Sources/MusadoraKit/Continuous/ContinuousSongs.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// ContinuousSongs.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -public extension MRecommendation { - - /// Retrieves recommended songs from the Apple Music catalog for a given song, with the option to limit the number of results. - /// - /// Example usage: - /// - /// do { - /// let song: Song = ... - /// let recommendedSongs = try await MRecommendation.continuousSongs(for: song, limit: 10) - /// - /// for recommendedSong in recommendedSongs { - /// print("Recommended song name: \(recommendedSong.title)") - /// print("Recommended song artist: \(recommendedSong.artistName)") - /// // access other recommended song properties as needed - /// } - /// } catch { - /// print("Error retrieving recommended songs: \(error.localizedDescription)") - /// } - /// - /// - Parameters: - /// - song: The song for which you want to retrieve recommendations. - /// - limit: Determines the number of recommendations you want to retrieve. The default value is 20. - /// - /// - Returns: An array of `Song` objects containing the recommended songs. - /// - /// - Note: This method uses a private endpoint and should be used at your own risk. - @available(*, message: "THIS IS A PRIVATE ENDPOINT. USE IT AT YOUR OWN RISK.") - static func continuousSongs(for song: Song, limit: Int = 20) async throws -> Songs { - var request = MContinuousTrackRequest(for: song) - request.limit = limit - let response = try await request.response() - return response - } -} diff --git a/Sources/MusadoraKit/Continuous/MContinuousTrackData.swift b/Sources/MusadoraKit/Continuous/MContinuousTrackData.swift deleted file mode 100644 index 2ec1d4f5..00000000 --- a/Sources/MusadoraKit/Continuous/MContinuousTrackData.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// MContinuousTrackData.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -/// Represents data for a continuous track request. -struct MContinuousTrackData: Codable { - - /// The data for the continuous track request. - let data: [MContinuousTrackDatum] - - /// Initializes a `MContinuousTrackData` object with the given song and album IDs. - /// - /// - Parameters: - /// - songID: The ID of the song for which the continuous track request is being made. - /// - albumID: The ID of the album to which the song belongs. - init(songID: MusicItemID, albumID: MusicItemID) { - self.data = [MContinuousTrackDatum(songID: songID, albumID: albumID)] - } -} - -/// Represents a datum for a continuous track request. -struct MContinuousTrackDatum: Codable { - - /// The ID of the song for the continuous track request. - let id: MusicItemID - - /// The type of the item being requested. - var type = "songs" - - /// The metadata for the continuous track request. - let meta: MContinuousTrackMetadata - - /// Initializes a `MContinuousTrackDatum` object with the given song and album IDs. - /// - /// - Parameters: - /// - songID: The ID of the song for which the continuous track request is being made. - /// - albumID: The ID of the album to which the song belongs. - init(songID: MusicItemID, albumID: MusicItemID) { - id = songID - meta = MContinuousTrackMetadata(container: MContinuousTrackContainer(id: albumID)) - } -} - -/// Represents metadata for a continuous track request. -struct MContinuousTrackMetadata: Codable { - - /// The container for the continuous track request. - let container: MContinuousTrackContainer -} - -/// Represents a container for a continuous track request. -struct MContinuousTrackContainer: Codable { - - /// The ID of the album for the continuous track request. - let id: MusicItemID - - /// The type of the container for the continuous track request. - var type = "albums" -} - -extension MContinuousTrackData: Equatable { - static func ==(lhs: MContinuousTrackData, rhs: MContinuousTrackData) -> Bool { - return lhs.data == rhs.data - } -} - -extension MContinuousTrackDatum: Equatable { - static func ==(lhs: MContinuousTrackDatum, rhs: MContinuousTrackDatum) -> Bool { - return lhs.id == rhs.id && lhs.type == rhs.type && lhs.meta == rhs.meta - } -} - -extension MContinuousTrackMetadata: Equatable { - static func ==(lhs: MContinuousTrackMetadata, rhs: MContinuousTrackMetadata) -> Bool { - return lhs.container == rhs.container - } -} - -extension MContinuousTrackContainer: Equatable { - static func ==(lhs: MContinuousTrackContainer, rhs: MContinuousTrackContainer) -> Bool { - return lhs.id == rhs.id && lhs.type == rhs.type - } -} - diff --git a/Sources/MusadoraKit/Continuous/MContinuousTrackRequest.swift b/Sources/MusadoraKit/Continuous/MContinuousTrackRequest.swift deleted file mode 100644 index c31881e7..00000000 --- a/Sources/MusadoraKit/Continuous/MContinuousTrackRequest.swift +++ /dev/null @@ -1,123 +0,0 @@ -// -// MContinuousTrackRequest.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -import Foundation - -/// Represents a continuous track request for a given `Song`. -struct MContinuousTrackRequest { - - /// The limit of songs to be returned in the response. Default value is `20`. - var limit: Int = 20 - - /// The song for which the continuous track request is to be made. - private let song: Song - - /// Initializes a `MContinuousTrackRequest` object with the given `Song`. - /// - /// - Parameter song: The song for which the continuous track request is to be made. - init(for song: Song) { - self.song = song - } - - /// Creates a `MDataPostRequest` object for the given `Song` and `Album`. - /// - /// - Parameters: - /// - song: The song for which the `MDataPostRequest` object is to be created. - /// - album: The album to which the song belongs. - /// - /// - Returns: A `MDataPostRequest` object for the given song and album. - internal func createPostRequest(for song: Song, album: Album?) throws -> MDataPostRequest { - guard let album = album else { - throw NSError(domain: "No album exists for this song.", code: 0) - } - - let trackData = MContinuousTrackData(songID: song.id, albumID: album.id) - let url = try continuousTracksEndpointURL - let data = try JSONEncoder().encode(trackData) - - let postRequest = MDataPostRequest(url: url, data: data) - return postRequest - } - - /// Sends a continuous track request and returns a list of songs. - /// - /// - Throws: An error if the request fails or no songs are returned. - /// - /// - Returns: A list of songs returned in the response. - func response() async throws -> Songs { - let playParametersData = try JSONEncoder().encode(song.playParameters) - let playParameters = try JSONDecoder().decode(MusicPlayParameters.self, from: playParametersData) - var postRequest: MDataPostRequest - - if let isLibrary = playParameters.isLibrary, isLibrary { - let globalID = playParameters.globalId - let catalogID = playParameters.catalogId - - let detailedSong = try await MCatalog.song(id: globalID ?? catalogID ?? song.id).with(.albums) - let album = detailedSong.albums?.first - postRequest = try createPostRequest(for: detailedSong, album: album) - } else { - let detailedSong = try await song.with(.albums) - let album = detailedSong.albums?.first - postRequest = try createPostRequest(for: detailedSong, album: album) - } - - return try await continuousSongs(for: postRequest) - } - - /// Sends multiple requests to get a list of continuous songs using a `TaskGroup`. - /// - /// - Parameter postRequest: The `MDataPostRequest` object to be used for sending the request. - /// - /// - Returns: A list of songs returned in the response. - private func continuousSongs(for postRequest: MDataPostRequest) async throws -> Songs { - try await withThrowingTaskGroup(of: Songs.self) { group in - let iteratorLimit = limit / 10 - - for _ in stride(from: 0, to: iteratorLimit, by: 1) { - group.addTask { - let response = try await postRequest.response() - return try JSONDecoder().decode(MContinuousTrackResponse.self, from: response.data).results.songs - } - } - - var stationSongs: Songs = [] - - for try await songs in group { - for song in songs { - if stationSongs.contains(where: { $0.id == song.id }) { - // DUPLICATE - } else { - stationSongs += MusicItemCollection(arrayLiteral: song) - } - } - } - - return stationSongs - } - } -} - -extension MContinuousTrackRequest { - internal var continuousTracksEndpointURL: URL { - get throws { - var components = AppleMusicURLComponents() - var queryItems: [URLQueryItem] = [] - components.path = "me/stations/continuous" - - queryItems.append(URLQueryItem(name: "limit[results:tracks]", value: "10")) - queryItems.append(URLQueryItem(name: "with", value: "tracks")) - - components.queryItems = queryItems - - guard let url = components.url else { - throw URLError(.badURL) - } - return url - } - } -} diff --git a/Sources/MusadoraKit/Continuous/MContinuousTrackResponse.swift b/Sources/MusadoraKit/Continuous/MContinuousTrackResponse.swift deleted file mode 100644 index 9ff7f7db..00000000 --- a/Sources/MusadoraKit/Continuous/MContinuousTrackResponse.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// MContinuousTrackResponse.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -import Foundation - -/// This struct represents the response for a continuous track request. -struct MContinuousTrackResponse: Codable { - - /// The results of the request. - public let results: Results - - /// The nested struct for the results of the request. - struct Results: Codable { - - /// The station associated with the request. - public let station: Station - - /// The songs returned in the response. - public let songs: Songs - - /// Coding keys to map the response fields to struct properties. - enum CodingKeys: String, CodingKey { - case station - case songs = "tracks" - } - - /// Initializes a Results instance by decoding data from a decoder. - /// - Parameter decoder: The decoder to use for decoding the data. - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.station = try container.decode(Station.self, forKey: .station) - let tracks = try container.decode([Track].self, forKey: .songs).compactMap { track in - if case let .song(song) = track { - return song - } else { - return nil - } - } - - self.songs = MusicItemCollection(tracks) - } - } -} diff --git a/Sources/MusadoraKit/Station Tracks/MStationTrackRequest.swift b/Sources/MusadoraKit/Station Tracks/MStationTrackRequest.swift deleted file mode 100644 index 074869f1..00000000 --- a/Sources/MusadoraKit/Station Tracks/MStationTrackRequest.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// MStationTrackRequest.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -import Foundation - -/// A request that your app uses to fetch tracks for a specific station. -struct MStationTrackRequest { - - /// A limit for the number of items to return - /// in the station tracks response. Default value is 20. - var limit: Int = 20 - - private let station: Station - - /// Creates a request to fetch tracks for a given station. - /// - Parameter station: The station for which to fetch tracks. - init(for station: Station) { - self.station = station - } - - /// Fetches tracks for the station associated with this request. - func response() async throws -> Songs { - let url = try stationTracksEndpointURL - return try await stationSongs(for: url) - } - - /// Fetches and processes station songs in parallel tasks. - /// - Parameter postRequest: The request to fetch station songs. - /// - Returns: An array of unique songs for the station. - private func stationSongs(for url: URL) async throws -> Songs { - try await withThrowingTaskGroup(of: Songs.self) { group in - let iteratorLimit = limit / 10 - - for _ in stride(from: 0, to: iteratorLimit, by: 1) { - group.addTask { - if let userToken = MusadoraKit.userToken { - var postRequest = URLRequest(url: url) - postRequest.httpMethod = "POST" - let request = MUserRequest(urlRequest: postRequest, userToken: userToken) - let data = try await request.response() - return try JSONDecoder().decode(Songs.self, from: data) - } else { - let postRequest = MDataPostRequest(url: url) - let response = try await postRequest.response() - return try JSONDecoder().decode(Songs.self, from: response.data) - } - } - } - - var stationSongs: Songs = [] - - for try await songs in group { - for song in songs { - if stationSongs.contains(where: { $0.id == song.id }) { - // DUPLICATE - } else { - stationSongs += MusicItemCollection(arrayLiteral: song) - } - } - } - - return stationSongs - } - } -} - -extension MStationTrackRequest { - internal var stationTracksEndpointURL: URL { - get throws { - var components = AppleMusicURLComponents() - components.path = "me/stations/next-tracks/\(station.id.rawValue)" - - components.queryItems = [URLQueryItem(name: "limit", value: "10")] - - guard let url = components.url else { - throw URLError(.badURL) - } - return url - } - } -} diff --git a/Sources/MusadoraKit/Station Tracks/StationSongs.swift b/Sources/MusadoraKit/Station Tracks/StationSongs.swift deleted file mode 100644 index 809e02ba..00000000 --- a/Sources/MusadoraKit/Station Tracks/StationSongs.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// StationSongs.swift -// MusadoraKit+ -// -// Created by Rudrank Riyam on 25/01/23. -// - -public extension MRecommendation { - - /// Retrieves station songs from the Apple Music catalog for a given station, with the option to limit the number of results. - /// - /// Example usage: - /// - /// do { - /// let station: Station = ... - /// let stationSongs = try await MRecommendation.stationSongs(for: station, limit: 10) - /// - /// for stationSong in stationSongs { - /// print("Station song name: \(stationSong.title)") - /// print("Station song artist: \(stationSong.artistName)") - /// // access other station song properties as needed - /// } - /// } catch { - /// print("Error retrieving station songs: \(error.localizedDescription)") - /// } - /// - /// - Parameters: - /// - station: The station for which you want to retrieve the songs. This parameter is of type `Station`, which is a custom type defined in the MusadoraKit library. - /// - limit: Determines the number of songs you want to retrieve. The default value is 20. - /// - /// - Returns: An array of `Song` objects containing the station songs. - /// - /// - Note: This method uses a private endpoint and should be used at your own risk. This functionality is not available in the official Apple Music app. - @available(*, message: "THIS IS A PRIVATE ENDPOINT. USE IT AT YOUR OWN RISK.") - static func stationSongs(for station: Station, limit: Int = 20) async throws -> Songs { - var request = MStationTrackRequest(for: station) - request.limit = limit - let response = try await request.response() - return response - } -}