@wwdrew/expo-apple-music 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +24 -0
- package/LICENSE +190 -0
- package/NOTICE +7 -0
- package/README.md +81 -0
- package/android/build.gradle +26 -0
- package/android/libs/mediaplayback-release-1.1.1.aar +0 -0
- package/android/libs/musickitauth-release-1.1.2.aar +0 -0
- package/android/src/main/AndroidManifest.xml +16 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidCatalogService.kt +86 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidDeveloperToken.kt +39 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidHistoryService.kt +24 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidLibraryMutationsService.kt +30 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidLibraryService.kt +61 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidPlaybackController.kt +484 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidPlaybackObserver.kt +173 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidQueueService.kt +78 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidRatingsService.kt +27 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidRecommendationsService.kt +15 -0
- package/android/src/main/java/expo/modules/applemusic/AndroidSubscriptionService.kt +24 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicErrorCodes.kt +13 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicErrors.kt +46 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicHttpMethod.kt +8 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicJsonMapper.kt +258 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicNativeLoader.kt +32 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicRestJson.kt +40 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicRestQuery.kt +12 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicRestStack.kt +19 -0
- package/android/src/main/java/expo/modules/applemusic/AppleMusicRestTransport.kt +118 -0
- package/android/src/main/java/expo/modules/applemusic/AuthenticatedSession.kt +57 -0
- package/android/src/main/java/expo/modules/applemusic/BridgeResponses.kt +55 -0
- package/android/src/main/java/expo/modules/applemusic/CatalogRestClient.kt +306 -0
- package/android/src/main/java/expo/modules/applemusic/ExpoAppleMusicModule.kt +152 -0
- package/android/src/main/java/expo/modules/applemusic/HistoryRestClient.kt +60 -0
- package/android/src/main/java/expo/modules/applemusic/LibraryIds.kt +6 -0
- package/android/src/main/java/expo/modules/applemusic/LibraryMutationsRestClient.kt +95 -0
- package/android/src/main/java/expo/modules/applemusic/LibraryRestClient.kt +195 -0
- package/android/src/main/java/expo/modules/applemusic/MusicKitAuthContract.kt +78 -0
- package/android/src/main/java/expo/modules/applemusic/MusicKitAuthStorage.kt +76 -0
- package/android/src/main/java/expo/modules/applemusic/MusicKitTokenProvider.kt +13 -0
- package/android/src/main/java/expo/modules/applemusic/PaginationOptions.kt +14 -0
- package/android/src/main/java/expo/modules/applemusic/RatingsRestClient.kt +72 -0
- package/android/src/main/java/expo/modules/applemusic/RecommendationsRestClient.kt +37 -0
- package/android/src/main/java/expo/modules/applemusic/StorefrontRestClient.kt +44 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeAuth.kt +69 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeCatalog.kt +76 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeHistory.kt +35 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeLibrary.kt +54 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeLibraryMutations.kt +30 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgePlayer.kt +89 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeRatings.kt +29 -0
- package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeRecommendations.kt +18 -0
- package/app.plugin.js +1 -0
- package/build/ExpoAppleMusicModule.web.d.ts +15 -0
- package/build/ExpoAppleMusicModule.web.d.ts.map +1 -0
- package/build/ExpoAppleMusicModule.web.js +33 -0
- package/build/ExpoAppleMusicModule.web.js.map +1 -0
- package/build/api/call-native.d.ts +6 -0
- package/build/api/call-native.d.ts.map +1 -0
- package/build/api/call-native.js +35 -0
- package/build/api/call-native.js.map +1 -0
- package/build/api/decode-jwt-exp.d.ts +5 -0
- package/build/api/decode-jwt-exp.d.ts.map +1 -0
- package/build/api/decode-jwt-exp.js +28 -0
- package/build/api/decode-jwt-exp.js.map +1 -0
- package/build/api/library-ids.d.ts +4 -0
- package/build/api/library-ids.d.ts.map +1 -0
- package/build/api/library-ids.js +11 -0
- package/build/api/library-ids.js.map +1 -0
- package/build/api/pagination.d.ts +12 -0
- package/build/api/pagination.d.ts.map +1 -0
- package/build/api/pagination.js +13 -0
- package/build/api/pagination.js.map +1 -0
- package/build/api/parse-authorize-result.d.ts +3 -0
- package/build/api/parse-authorize-result.d.ts.map +1 -0
- package/build/api/parse-authorize-result.js +27 -0
- package/build/api/parse-authorize-result.js.map +1 -0
- package/build/api/require-music-user-token.d.ts +2 -0
- package/build/api/require-music-user-token.d.ts.map +1 -0
- package/build/api/require-music-user-token.js +14 -0
- package/build/api/require-music-user-token.js.map +1 -0
- package/build/api/sync-developer-token.d.ts +4 -0
- package/build/api/sync-developer-token.d.ts.map +1 -0
- package/build/api/sync-developer-token.js +27 -0
- package/build/api/sync-developer-token.js.map +1 -0
- package/build/bridge/bridge-methods.d.ts +17 -0
- package/build/bridge/bridge-methods.d.ts.map +1 -0
- package/build/bridge/bridge-methods.js +71 -0
- package/build/bridge/bridge-methods.js.map +1 -0
- package/build/bridge/bridge-responses.d.ts +64 -0
- package/build/bridge/bridge-responses.d.ts.map +1 -0
- package/build/bridge/bridge-responses.js +67 -0
- package/build/bridge/bridge-responses.js.map +1 -0
- package/build/bridge/handlers/auth-bridge.d.ts +11 -0
- package/build/bridge/handlers/auth-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/auth-bridge.js +49 -0
- package/build/bridge/handlers/auth-bridge.js.map +1 -0
- package/build/bridge/handlers/catalog-bridge.d.ts +34 -0
- package/build/bridge/handlers/catalog-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/catalog-bridge.js +58 -0
- package/build/bridge/handlers/catalog-bridge.js.map +1 -0
- package/build/bridge/handlers/history-bridge.d.ts +19 -0
- package/build/bridge/handlers/history-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/history-bridge.js +31 -0
- package/build/bridge/handlers/history-bridge.js.map +1 -0
- package/build/bridge/handlers/index.d.ts +124 -0
- package/build/bridge/handlers/index.d.ts.map +1 -0
- package/build/bridge/handlers/index.js +21 -0
- package/build/bridge/handlers/index.js.map +1 -0
- package/build/bridge/handlers/library-bridge.d.ts +23 -0
- package/build/bridge/handlers/library-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/library-bridge.js +41 -0
- package/build/bridge/handlers/library-bridge.js.map +1 -0
- package/build/bridge/handlers/library-mutations-bridge.d.ts +16 -0
- package/build/bridge/handlers/library-mutations-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/library-mutations-bridge.js +20 -0
- package/build/bridge/handlers/library-mutations-bridge.js.map +1 -0
- package/build/bridge/handlers/player-bridge.d.ts +18 -0
- package/build/bridge/handlers/player-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/player-bridge.js +42 -0
- package/build/bridge/handlers/player-bridge.js.map +1 -0
- package/build/bridge/handlers/ratings-bridge.d.ts +15 -0
- package/build/bridge/handlers/ratings-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/ratings-bridge.js +16 -0
- package/build/bridge/handlers/ratings-bridge.js.map +1 -0
- package/build/bridge/handlers/recommendations-bridge.d.ts +10 -0
- package/build/bridge/handlers/recommendations-bridge.d.ts.map +1 -0
- package/build/bridge/handlers/recommendations-bridge.js +14 -0
- package/build/bridge/handlers/recommendations-bridge.js.map +1 -0
- package/build/constants/apple-music-error-codes.d.ts +22 -0
- package/build/constants/apple-music-error-codes.d.ts.map +1 -0
- package/build/constants/apple-music-error-codes.js +21 -0
- package/build/constants/apple-music-error-codes.js.map +1 -0
- package/build/hooks/use-current-song.d.ts +10 -0
- package/build/hooks/use-current-song.d.ts.map +1 -0
- package/build/hooks/use-current-song.js +35 -0
- package/build/hooks/use-current-song.js.map +1 -0
- package/build/hooks/use-is-playing.d.ts +6 -0
- package/build/hooks/use-is-playing.d.ts.map +1 -0
- package/build/hooks/use-is-playing.js +21 -0
- package/build/hooks/use-is-playing.js.map +1 -0
- package/build/hooks/use-playback-state.d.ts +12 -0
- package/build/hooks/use-playback-state.d.ts.map +1 -0
- package/build/hooks/use-playback-state.js +41 -0
- package/build/hooks/use-playback-state.js.map +1 -0
- package/build/index.d.ts +52 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +44 -0
- package/build/index.js.map +1 -0
- package/build/mappers/apple-music-json-mapper.d.ts +94 -0
- package/build/mappers/apple-music-json-mapper.d.ts.map +1 -0
- package/build/mappers/apple-music-json-mapper.js +212 -0
- package/build/mappers/apple-music-json-mapper.js.map +1 -0
- package/build/modules/auth.d.ts +32 -0
- package/build/modules/auth.d.ts.map +1 -0
- package/build/modules/auth.js +60 -0
- package/build/modules/auth.js.map +1 -0
- package/build/modules/catalog.d.ts +46 -0
- package/build/modules/catalog.d.ts.map +1 -0
- package/build/modules/catalog.js +47 -0
- package/build/modules/catalog.js.map +1 -0
- package/build/modules/history.d.ts +17 -0
- package/build/modules/history.d.ts.map +1 -0
- package/build/modules/history.js +28 -0
- package/build/modules/history.js.map +1 -0
- package/build/modules/library-mutations.d.ts +9 -0
- package/build/modules/library-mutations.d.ts.map +1 -0
- package/build/modules/library-mutations.js +41 -0
- package/build/modules/library-mutations.js.map +1 -0
- package/build/modules/library.d.ts +21 -0
- package/build/modules/library.d.ts.map +1 -0
- package/build/modules/library.js +38 -0
- package/build/modules/library.js.map +1 -0
- package/build/modules/player.d.ts +53 -0
- package/build/modules/player.d.ts.map +1 -0
- package/build/modules/player.js +68 -0
- package/build/modules/player.js.map +1 -0
- package/build/modules/ratings.d.ts +10 -0
- package/build/modules/ratings.d.ts.map +1 -0
- package/build/modules/ratings.js +37 -0
- package/build/modules/ratings.js.map +1 -0
- package/build/modules/recommendations.d.ts +7 -0
- package/build/modules/recommendations.d.ts.map +1 -0
- package/build/modules/recommendations.js +15 -0
- package/build/modules/recommendations.js.map +1 -0
- package/build/native-module.d.ts +5 -0
- package/build/native-module.d.ts.map +1 -0
- package/build/native-module.js +5 -0
- package/build/native-module.js.map +1 -0
- package/build/native-module.web.d.ts +11 -0
- package/build/native-module.web.d.ts.map +1 -0
- package/build/native-module.web.js +11 -0
- package/build/native-module.web.js.map +1 -0
- package/build/rest/apple-music-rest-stack.d.ts +21 -0
- package/build/rest/apple-music-rest-stack.d.ts.map +1 -0
- package/build/rest/apple-music-rest-stack.js +21 -0
- package/build/rest/apple-music-rest-stack.js.map +1 -0
- package/build/rest/apple-music-rest-transport.d.ts +8 -0
- package/build/rest/apple-music-rest-transport.d.ts.map +1 -0
- package/build/rest/apple-music-rest-transport.js +2 -0
- package/build/rest/apple-music-rest-transport.js.map +1 -0
- package/build/rest/catalog-rest-client.d.ts +38 -0
- package/build/rest/catalog-rest-client.d.ts.map +1 -0
- package/build/rest/catalog-rest-client.js +189 -0
- package/build/rest/catalog-rest-client.js.map +1 -0
- package/build/rest/history-rest-client.d.ts +37 -0
- package/build/rest/history-rest-client.d.ts.map +1 -0
- package/build/rest/history-rest-client.js +30 -0
- package/build/rest/history-rest-client.js.map +1 -0
- package/build/rest/library-ids.d.ts +2 -0
- package/build/rest/library-ids.d.ts.map +1 -0
- package/build/rest/library-ids.js +2 -0
- package/build/rest/library-ids.js.map +1 -0
- package/build/rest/library-mutations-rest-client.d.ts +22 -0
- package/build/rest/library-mutations-rest-client.d.ts.map +1 -0
- package/build/rest/library-mutations-rest-client.js +37 -0
- package/build/rest/library-mutations-rest-client.js.map +1 -0
- package/build/rest/library-rest-client.d.ts +59 -0
- package/build/rest/library-rest-client.d.ts.map +1 -0
- package/build/rest/library-rest-client.js +141 -0
- package/build/rest/library-rest-client.js.map +1 -0
- package/build/rest/ratings-rest-client.d.ts +18 -0
- package/build/rest/ratings-rest-client.d.ts.map +1 -0
- package/build/rest/ratings-rest-client.js +37 -0
- package/build/rest/ratings-rest-client.js.map +1 -0
- package/build/rest/recommendations-rest-client.d.ts +32 -0
- package/build/rest/recommendations-rest-client.d.ts.map +1 -0
- package/build/rest/recommendations-rest-client.js +26 -0
- package/build/rest/recommendations-rest-client.js.map +1 -0
- package/build/rest/resource-ids-query.d.ts +2 -0
- package/build/rest/resource-ids-query.d.ts.map +1 -0
- package/build/rest/resource-ids-query.js +10 -0
- package/build/rest/resource-ids-query.js.map +1 -0
- package/build/rest/rest-json.d.ts +11 -0
- package/build/rest/rest-json.d.ts.map +1 -0
- package/build/rest/rest-json.js +29 -0
- package/build/rest/rest-json.js.map +1 -0
- package/build/rest/storefront-rest-client.d.ts +14 -0
- package/build/rest/storefront-rest-client.d.ts.map +1 -0
- package/build/rest/storefront-rest-client.js +36 -0
- package/build/rest/storefront-rest-client.js.map +1 -0
- package/build/types/album.d.ts +8 -0
- package/build/types/album.d.ts.map +1 -0
- package/build/types/album.js +2 -0
- package/build/types/album.js.map +1 -0
- package/build/types/albums-response.d.ts +5 -0
- package/build/types/albums-response.d.ts.map +1 -0
- package/build/types/albums-response.js +2 -0
- package/build/types/albums-response.js.map +1 -0
- package/build/types/android-authorize-options.d.ts +18 -0
- package/build/types/android-authorize-options.d.ts.map +1 -0
- package/build/types/android-authorize-options.js +2 -0
- package/build/types/android-authorize-options.js.map +1 -0
- package/build/types/artist.d.ts +9 -0
- package/build/types/artist.d.ts.map +1 -0
- package/build/types/artist.js +2 -0
- package/build/types/artist.js.map +1 -0
- package/build/types/auth-status.d.ts +19 -0
- package/build/types/auth-status.d.ts.map +1 -0
- package/build/types/auth-status.js +18 -0
- package/build/types/auth-status.js.map +1 -0
- package/build/types/authorize-result.d.ts +8 -0
- package/build/types/authorize-result.d.ts.map +1 -0
- package/build/types/authorize-result.js +2 -0
- package/build/types/authorize-result.js.map +1 -0
- package/build/types/catalog-album-tracks.d.ts +5 -0
- package/build/types/catalog-album-tracks.d.ts.map +1 -0
- package/build/types/catalog-album-tracks.js +2 -0
- package/build/types/catalog-album-tracks.js.map +1 -0
- package/build/types/catalog-charts.d.ts +25 -0
- package/build/types/catalog-charts.d.ts.map +1 -0
- package/build/types/catalog-charts.js +7 -0
- package/build/types/catalog-charts.js.map +1 -0
- package/build/types/catalog-resource-type.d.ts +11 -0
- package/build/types/catalog-resource-type.d.ts.map +1 -0
- package/build/types/catalog-resource-type.js +10 -0
- package/build/types/catalog-resource-type.js.map +1 -0
- package/build/types/catalog-search.d.ts +24 -0
- package/build/types/catalog-search.d.ts.map +1 -0
- package/build/types/catalog-search.js +9 -0
- package/build/types/catalog-search.js.map +1 -0
- package/build/types/check-subscription.d.ts +31 -0
- package/build/types/check-subscription.d.ts.map +1 -0
- package/build/types/check-subscription.js +5 -0
- package/build/types/check-subscription.js.map +1 -0
- package/build/types/library-music-videos.d.ts +5 -0
- package/build/types/library-music-videos.d.ts.map +1 -0
- package/build/types/library-music-videos.js +2 -0
- package/build/types/library-music-videos.js.map +1 -0
- package/build/types/library-mutations.d.ts +21 -0
- package/build/types/library-mutations.d.ts.map +1 -0
- package/build/types/library-mutations.js +8 -0
- package/build/types/library-mutations.js.map +1 -0
- package/build/types/library-search.d.ts +22 -0
- package/build/types/library-search.d.ts.map +1 -0
- package/build/types/library-search.js +9 -0
- package/build/types/library-search.js.map +1 -0
- package/build/types/music-item.d.ts +8 -0
- package/build/types/music-item.d.ts.map +1 -0
- package/build/types/music-item.js +7 -0
- package/build/types/music-item.js.map +1 -0
- package/build/types/music-video.d.ts +8 -0
- package/build/types/music-video.d.ts.map +1 -0
- package/build/types/music-video.js +2 -0
- package/build/types/music-video.js.map +1 -0
- package/build/types/pagination.d.ts +11 -0
- package/build/types/pagination.d.ts.map +1 -0
- package/build/types/pagination.js +2 -0
- package/build/types/pagination.js.map +1 -0
- package/build/types/playback-state.d.ts +9 -0
- package/build/types/playback-state.d.ts.map +1 -0
- package/build/types/playback-state.js +2 -0
- package/build/types/playback-state.js.map +1 -0
- package/build/types/playback-status.d.ts +10 -0
- package/build/types/playback-status.d.ts.map +1 -0
- package/build/types/playback-status.js +9 -0
- package/build/types/playback-status.js.map +1 -0
- package/build/types/playlist.d.ts +15 -0
- package/build/types/playlist.d.ts.map +1 -0
- package/build/types/playlist.js +2 -0
- package/build/types/playlist.js.map +1 -0
- package/build/types/rating.d.ts +34 -0
- package/build/types/rating.d.ts.map +1 -0
- package/build/types/rating.js +26 -0
- package/build/types/rating.js.map +1 -0
- package/build/types/recent-resource.d.ts +11 -0
- package/build/types/recent-resource.d.ts.map +1 -0
- package/build/types/recent-resource.js +2 -0
- package/build/types/recent-resource.js.map +1 -0
- package/build/types/recommendation.d.ts +37 -0
- package/build/types/recommendation.d.ts.map +1 -0
- package/build/types/recommendation.js +2 -0
- package/build/types/recommendation.js.map +1 -0
- package/build/types/song.d.ts +8 -0
- package/build/types/song.d.ts.map +1 -0
- package/build/types/song.js +2 -0
- package/build/types/song.js.map +1 -0
- package/build/types/station.d.ts +9 -0
- package/build/types/station.d.ts.map +1 -0
- package/build/types/station.js +2 -0
- package/build/types/station.js.map +1 -0
- package/build/types/storefront.d.ts +4 -0
- package/build/types/storefront.d.ts.map +1 -0
- package/build/types/storefront.js +2 -0
- package/build/types/storefront.js.map +1 -0
- package/build/types/tracks-from-library.d.ts +11 -0
- package/build/types/tracks-from-library.d.ts.map +1 -0
- package/build/types/tracks-from-library.js +2 -0
- package/build/types/tracks-from-library.js.map +1 -0
- package/build/utils/apple-music-error.d.ts +10 -0
- package/build/utils/apple-music-error.d.ts.map +1 -0
- package/build/utils/apple-music-error.js +13 -0
- package/build/utils/apple-music-error.js.map +1 -0
- package/build/utils/get-error-message.d.ts +2 -0
- package/build/utils/get-error-message.d.ts.map +1 -0
- package/build/utils/get-error-message.js +21 -0
- package/build/utils/get-error-message.js.map +1 -0
- package/build/utils/is-library-item.d.ts +2 -0
- package/build/utils/is-library-item.d.ts.map +1 -0
- package/build/utils/is-library-item.js +4 -0
- package/build/utils/is-library-item.js.map +1 -0
- package/build/utils/normalize-resource-ids.d.ts +4 -0
- package/build/utils/normalize-resource-ids.d.ts.map +1 -0
- package/build/utils/normalize-resource-ids.js +12 -0
- package/build/utils/normalize-resource-ids.js.map +1 -0
- package/build/web/MusicKitLoader.d.ts +11 -0
- package/build/web/MusicKitLoader.d.ts.map +1 -0
- package/build/web/MusicKitLoader.js +135 -0
- package/build/web/MusicKitLoader.js.map +1 -0
- package/build/web/WebAppleMusicApiClient.d.ts +151 -0
- package/build/web/WebAppleMusicApiClient.d.ts.map +1 -0
- package/build/web/WebAppleMusicApiClient.js +139 -0
- package/build/web/WebAppleMusicApiClient.js.map +1 -0
- package/build/web/WebAppleMusicRestTransport.d.ts +9 -0
- package/build/web/WebAppleMusicRestTransport.d.ts.map +1 -0
- package/build/web/WebAppleMusicRestTransport.js +31 -0
- package/build/web/WebAppleMusicRestTransport.js.map +1 -0
- package/build/web/WebPlaybackController.d.ts +12 -0
- package/build/web/WebPlaybackController.d.ts.map +1 -0
- package/build/web/WebPlaybackController.js +90 -0
- package/build/web/WebPlaybackController.js.map +1 -0
- package/build/web/WebPlaybackObserver.d.ts +22 -0
- package/build/web/WebPlaybackObserver.d.ts.map +1 -0
- package/build/web/WebPlaybackObserver.js +106 -0
- package/build/web/WebPlaybackObserver.js.map +1 -0
- package/build/web/WebQueueService.d.ts +10 -0
- package/build/web/WebQueueService.d.ts.map +1 -0
- package/build/web/WebQueueService.js +53 -0
- package/build/web/WebQueueService.js.map +1 -0
- package/build/web/WebSubscriptionService.d.ts +7 -0
- package/build/web/WebSubscriptionService.d.ts.map +1 -0
- package/build/web/WebSubscriptionService.js +24 -0
- package/build/web/WebSubscriptionService.js.map +1 -0
- package/build/web/apple-music-errors.d.ts +11 -0
- package/build/web/apple-music-errors.d.ts.map +1 -0
- package/build/web/apple-music-errors.js +31 -0
- package/build/web/apple-music-errors.js.map +1 -0
- package/build/web/extract-music-user-token.d.ts +4 -0
- package/build/web/extract-music-user-token.d.ts.map +1 -0
- package/build/web/extract-music-user-token.js +15 -0
- package/build/web/extract-music-user-token.js.map +1 -0
- package/build/web/map-auth-status.d.ts +17 -0
- package/build/web/map-auth-status.d.ts.map +1 -0
- package/build/web/map-auth-status.js +85 -0
- package/build/web/map-auth-status.js.map +1 -0
- package/build/web/music-kit-api.d.ts +18 -0
- package/build/web/music-kit-api.d.ts.map +1 -0
- package/build/web/music-kit-api.js +120 -0
- package/build/web/music-kit-api.js.map +1 -0
- package/build/web/musickit-types.d.ts +70 -0
- package/build/web/musickit-types.d.ts.map +1 -0
- package/build/web/musickit-types.js +3 -0
- package/build/web/musickit-types.js.map +1 -0
- package/build/web/pagination.d.ts +2 -0
- package/build/web/pagination.d.ts.map +1 -0
- package/build/web/pagination.js +2 -0
- package/build/web/pagination.js.map +1 -0
- package/expo-module.config.json +19 -0
- package/ios/AppleMusicBridgeError.swift +60 -0
- package/ios/AppleMusicErrorCodes.swift +11 -0
- package/ios/AppleMusicRestClient.swift +213 -0
- package/ios/AuthenticatedSession.swift +64 -0
- package/ios/BridgePagination.swift +17 -0
- package/ios/BridgeResponses.swift +82 -0
- package/ios/CatalogSearchStore.swift +13 -0
- package/ios/CatalogSearchStoreFactory.swift +31 -0
- package/ios/CatalogService.swift +307 -0
- package/ios/ExpoAppleMusic.podspec +29 -0
- package/ios/ExpoAppleMusicModule.swift +505 -0
- package/ios/HistoryService.swift +53 -0
- package/ios/LibraryMutationsService.swift +63 -0
- package/ios/LibraryService.swift +313 -0
- package/ios/MusicItemMapper.swift +171 -0
- package/ios/MusicKitAuthStorage.swift +38 -0
- package/ios/MusicKitCatalogSearchStore.swift +62 -0
- package/ios/PlaybackController.swift +201 -0
- package/ios/PlaybackObserver.swift +225 -0
- package/ios/QueueService.swift +166 -0
- package/ios/RatingsService.swift +66 -0
- package/ios/RecommendationsService.swift +34 -0
- package/ios/RestCatalogSearchStore.swift +62 -0
- package/ios/RestJsonMapper.swift +268 -0
- package/ios/StorefrontService.swift +55 -0
- package/ios/SubscriptionService.swift +119 -0
- package/ios/bridge/ExpoBridgeCatalog.swift +98 -0
- package/ios/bridge/ExpoBridgeHistory.swift +71 -0
- package/ios/bridge/ExpoBridgeLibrary.swift +93 -0
- package/ios/bridge/ExpoBridgeRecommendations.swift +28 -0
- package/package.json +89 -0
- package/plugin/build/index.d.ts +5 -0
- package/plugin/build/index.js +10 -0
- package/plugin/build/with-expo-apple-music.d.ts +10 -0
- package/plugin/build/with-expo-apple-music.js +50 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platforms": ["apple", "android", "web"],
|
|
3
|
+
"apple": {
|
|
4
|
+
"modules": ["ExpoAppleMusicModule"]
|
|
5
|
+
},
|
|
6
|
+
"android": {
|
|
7
|
+
"modules": ["expo.modules.applemusic.ExpoAppleMusicModule"],
|
|
8
|
+
"gradleAarProjects": [
|
|
9
|
+
{
|
|
10
|
+
"name": "musickit-auth",
|
|
11
|
+
"aarFilePath": "android/libs/musickitauth-release-1.1.2.aar"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "musickit-playback",
|
|
15
|
+
"aarFilePath": "android/libs/mediaplayback-release-1.1.1.aar"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// AppleMusicBridgeError.swift
|
|
2
|
+
// Map native errors to Expo bridge Exception codes.
|
|
3
|
+
|
|
4
|
+
import ExpoModulesCore
|
|
5
|
+
|
|
6
|
+
@available(iOS 16.0, *)
|
|
7
|
+
enum AppleMusicBridgeError {
|
|
8
|
+
/// Maps thrown errors to Expo `Exception` codes for async bridge methods.
|
|
9
|
+
static func rethrow<T>(_ operation: () async throws -> T) async throws -> T {
|
|
10
|
+
do {
|
|
11
|
+
return try await operation()
|
|
12
|
+
} catch let exception as Exception {
|
|
13
|
+
throw exception
|
|
14
|
+
} catch {
|
|
15
|
+
throw exception(from: error)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static func exception(from error: Error) -> Exception {
|
|
20
|
+
if let rest = error as? AppleMusicRestClient.RestError {
|
|
21
|
+
let code = rest.bridgeCode
|
|
22
|
+
return Exception(
|
|
23
|
+
name: code,
|
|
24
|
+
description: rest.errorDescription ?? "Apple Music error",
|
|
25
|
+
code: code
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if let catalog = error as? CatalogService.CatalogServiceError {
|
|
30
|
+
switch catalog {
|
|
31
|
+
case .configurationRequired(let message):
|
|
32
|
+
return Exception(
|
|
33
|
+
name: AppleMusicErrorCodes.permissionDenied,
|
|
34
|
+
description: message,
|
|
35
|
+
code: AppleMusicErrorCodes.permissionDenied
|
|
36
|
+
)
|
|
37
|
+
case .notFound, .unknownResourceType:
|
|
38
|
+
let message = catalog.errorDescription ?? "Apple Music error"
|
|
39
|
+
return Exception(name: AppleMusicErrorCodes.error, description: message, code: AppleMusicErrorCodes.error)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if let subscription = error as? SubscriptionService.SubscriptionError {
|
|
44
|
+
return Exception(
|
|
45
|
+
name: subscription.code,
|
|
46
|
+
description: subscription.errorDescription ?? "Apple Music error",
|
|
47
|
+
code: subscription.code
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let message = error.localizedDescription
|
|
52
|
+
let code =
|
|
53
|
+
message.localizedCaseInsensitiveContains("403")
|
|
54
|
+
|| message.localizedCaseInsensitiveContains("authorization required")
|
|
55
|
+
|| message.localizedCaseInsensitiveContains("subscription needed")
|
|
56
|
+
? AppleMusicErrorCodes.permissionDenied
|
|
57
|
+
: AppleMusicErrorCodes.error
|
|
58
|
+
return Exception(name: code, description: message, code: code)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// AppleMusicErrorCodes.swift
|
|
2
|
+
// Bridge rejection codes — keep in sync with src/constants/apple-music-error-codes.ts and docs/ERROR_CODES.md.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
@available(iOS 16.0, *)
|
|
7
|
+
enum AppleMusicErrorCodes {
|
|
8
|
+
static let error = "ERROR"
|
|
9
|
+
static let permissionDenied = "permissionDenied"
|
|
10
|
+
static let playbackError = "PLAYBACK_ERROR"
|
|
11
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// AppleMusicRestClient.swift
|
|
2
|
+
// Authenticated requests to api.music.apple.com (URLSession + stored tokens, or MusicDataRequest).
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
import MusicKit
|
|
6
|
+
|
|
7
|
+
@available(iOS 16.0, *)
|
|
8
|
+
enum AppleMusicHttpMethod: String {
|
|
9
|
+
case get = "GET"
|
|
10
|
+
case post = "POST"
|
|
11
|
+
case put = "PUT"
|
|
12
|
+
case delete = "DELETE"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@available(iOS 16.0, *)
|
|
16
|
+
enum AppleMusicRestClient {
|
|
17
|
+
|
|
18
|
+
enum RestError: LocalizedError {
|
|
19
|
+
case invalidURL
|
|
20
|
+
case invalidResponse
|
|
21
|
+
case permissionDenied(String)
|
|
22
|
+
case apiError(String)
|
|
23
|
+
|
|
24
|
+
var errorDescription: String? {
|
|
25
|
+
switch self {
|
|
26
|
+
case .invalidURL: return "Invalid Apple Music API URL"
|
|
27
|
+
case .invalidResponse: return "Invalid Apple Music API response"
|
|
28
|
+
case .permissionDenied(let message): return message
|
|
29
|
+
case .apiError(let message): return message
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var bridgeCode: String {
|
|
34
|
+
switch self {
|
|
35
|
+
case .permissionDenied:
|
|
36
|
+
return AppleMusicErrorCodes.permissionDenied
|
|
37
|
+
case .invalidURL, .invalidResponse, .apiError:
|
|
38
|
+
return AppleMusicErrorCodes.error
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static func request(
|
|
44
|
+
method: AppleMusicHttpMethod,
|
|
45
|
+
path: String,
|
|
46
|
+
musicUserToken: String? = nil,
|
|
47
|
+
query: [String: String] = [:],
|
|
48
|
+
body: [String: Any]? = nil
|
|
49
|
+
) async throws -> [String: Any] {
|
|
50
|
+
let session = AuthenticatedSession.current
|
|
51
|
+
if path.hasPrefix("/v1/me/") {
|
|
52
|
+
guard let musicUserToken, !musicUserToken.isEmpty else {
|
|
53
|
+
throw RestError.apiError("Apple Music REST requires a music user token")
|
|
54
|
+
}
|
|
55
|
+
return try await requestViaUrlSession(
|
|
56
|
+
session: session,
|
|
57
|
+
method: method,
|
|
58
|
+
path: path,
|
|
59
|
+
musicUserToken: musicUserToken,
|
|
60
|
+
query: query,
|
|
61
|
+
body: body)
|
|
62
|
+
}
|
|
63
|
+
if method == .get, session.prefersStoredDeveloperTokenForGet {
|
|
64
|
+
return try await requestViaUrlSession(
|
|
65
|
+
session: session,
|
|
66
|
+
method: method,
|
|
67
|
+
path: path,
|
|
68
|
+
musicUserToken: nil,
|
|
69
|
+
query: query,
|
|
70
|
+
body: body)
|
|
71
|
+
}
|
|
72
|
+
if method == .get, session.canUseMusicKitAutoTokenForGet {
|
|
73
|
+
return try await getViaMusicDataRequest(path: path, query: query)
|
|
74
|
+
}
|
|
75
|
+
return try await requestViaUrlSession(
|
|
76
|
+
session: session,
|
|
77
|
+
method: method,
|
|
78
|
+
path: path,
|
|
79
|
+
musicUserToken: musicUserToken,
|
|
80
|
+
query: query,
|
|
81
|
+
body: body)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static func get(
|
|
85
|
+
path: String,
|
|
86
|
+
musicUserToken: String? = nil,
|
|
87
|
+
query: [String: String] = [:]
|
|
88
|
+
) async throws -> [String: Any] {
|
|
89
|
+
try await request(method: .get, path: path, musicUserToken: musicUserToken, query: query)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static func getDataArray(
|
|
93
|
+
path: String,
|
|
94
|
+
musicUserToken: String? = nil,
|
|
95
|
+
query: [String: String] = [:]
|
|
96
|
+
) async throws -> [[String: Any]] {
|
|
97
|
+
let json = try await get(path: path, musicUserToken: musicUserToken, query: query)
|
|
98
|
+
return try parseDataArray(from: json)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Top-level list responses must include a `data` array (may be empty).
|
|
102
|
+
static func parseDataArray(from json: [String: Any], key: String = "data") throws -> [[String: Any]] {
|
|
103
|
+
guard json.keys.contains(key) else {
|
|
104
|
+
throw RestError.apiError("Apple Music API response missing \"\(key)\"")
|
|
105
|
+
}
|
|
106
|
+
if json[key] is NSNull {
|
|
107
|
+
return []
|
|
108
|
+
}
|
|
109
|
+
guard let data = json[key] as? [[String: Any]] else {
|
|
110
|
+
throw RestError.apiError("Apple Music API response \"\(key)\" is not an array")
|
|
111
|
+
}
|
|
112
|
+
return data
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private static func getViaMusicDataRequest(
|
|
116
|
+
path: String,
|
|
117
|
+
query: [String: String]
|
|
118
|
+
) async throws -> [String: Any] {
|
|
119
|
+
guard var components = URLComponents(string: "https://api.music.apple.com\(path)") else {
|
|
120
|
+
throw RestError.invalidURL
|
|
121
|
+
}
|
|
122
|
+
if !query.isEmpty {
|
|
123
|
+
components.queryItems = query.map { URLQueryItem(name: $0.key, value: $0.value) }
|
|
124
|
+
}
|
|
125
|
+
guard let url = components.url else {
|
|
126
|
+
throw RestError.invalidURL
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let urlRequest = URLRequest(url: url)
|
|
130
|
+
let dataRequest = MusicDataRequest(urlRequest: urlRequest)
|
|
131
|
+
let response = try await dataRequest.response()
|
|
132
|
+
return try parseResponseData(response.data)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private static func requestViaUrlSession(
|
|
136
|
+
session: AuthenticatedSession,
|
|
137
|
+
method: AppleMusicHttpMethod,
|
|
138
|
+
path: String,
|
|
139
|
+
musicUserToken: String?,
|
|
140
|
+
query: [String: String],
|
|
141
|
+
body: [String: Any]?
|
|
142
|
+
) async throws -> [String: Any] {
|
|
143
|
+
guard let developerToken = session.developerToken,
|
|
144
|
+
!developerToken.isEmpty
|
|
145
|
+
else {
|
|
146
|
+
if method == .get {
|
|
147
|
+
return try await getViaMusicDataRequest(path: path, query: query)
|
|
148
|
+
}
|
|
149
|
+
throw RestError.apiError("Apple Music REST requires a stored developer token")
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let userToken = musicUserToken ?? session.musicUserToken
|
|
153
|
+
let requiresUserToken = session.pathRequiresMusicUserToken(path)
|
|
154
|
+
|
|
155
|
+
if requiresUserToken, userToken == nil || userToken?.isEmpty == true {
|
|
156
|
+
throw RestError.apiError("Apple Music REST requires a music user token")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
guard var components = URLComponents(string: "https://api.music.apple.com\(path)") else {
|
|
160
|
+
throw RestError.invalidURL
|
|
161
|
+
}
|
|
162
|
+
if !query.isEmpty {
|
|
163
|
+
components.queryItems = query.map { URLQueryItem(name: $0.key, value: $0.value) }
|
|
164
|
+
}
|
|
165
|
+
guard let url = components.url else {
|
|
166
|
+
throw RestError.invalidURL
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var urlRequest = URLRequest(url: url)
|
|
170
|
+
urlRequest.httpMethod = method.rawValue
|
|
171
|
+
urlRequest.setValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
|
|
172
|
+
if let userToken, !userToken.isEmpty {
|
|
173
|
+
urlRequest.setValue(userToken, forHTTPHeaderField: "Music-User-Token")
|
|
174
|
+
}
|
|
175
|
+
if let body {
|
|
176
|
+
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
177
|
+
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: body)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let (data, response) = try await URLSession.shared.data(for: urlRequest)
|
|
181
|
+
if let http = response as? HTTPURLResponse, !(200...299).contains(http.statusCode) {
|
|
182
|
+
if http.statusCode == 403 {
|
|
183
|
+
throw RestError.permissionDenied(
|
|
184
|
+
"Apple Music authorization required or subscription needed (403)"
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
188
|
+
let errors = json["errors"] as? [[String: Any]],
|
|
189
|
+
let detail = errors.first?["detail"] as? String
|
|
190
|
+
{
|
|
191
|
+
throw RestError.apiError(detail)
|
|
192
|
+
}
|
|
193
|
+
throw RestError.apiError("Apple Music API error (\(http.statusCode))")
|
|
194
|
+
}
|
|
195
|
+
return try parseResponseData(data)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private static func parseResponseData(_ data: Data) throws -> [String: Any] {
|
|
199
|
+
if data.isEmpty {
|
|
200
|
+
return [:]
|
|
201
|
+
}
|
|
202
|
+
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
203
|
+
throw RestError.invalidResponse
|
|
204
|
+
}
|
|
205
|
+
if let errors = json["errors"] as? [[String: Any]],
|
|
206
|
+
let first = errors.first,
|
|
207
|
+
let detail = first["detail"] as? String
|
|
208
|
+
{
|
|
209
|
+
throw RestError.apiError(detail)
|
|
210
|
+
}
|
|
211
|
+
return json
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// AuthenticatedSession.swift
|
|
2
|
+
// Single view of stored credentials and REST/catalog transport preferences (see docs/AUTH.md).
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
@available(iOS 16.0, *)
|
|
7
|
+
struct AuthenticatedSession {
|
|
8
|
+
let developerToken: String?
|
|
9
|
+
let musicUserToken: String?
|
|
10
|
+
|
|
11
|
+
static var current: AuthenticatedSession {
|
|
12
|
+
AuthenticatedSession(
|
|
13
|
+
developerToken: MusicKitAuthStorage.getDeveloperToken(),
|
|
14
|
+
musicUserToken: MusicKitAuthStorage.getMusicUserToken()
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
var hasDeveloperToken: Bool {
|
|
19
|
+
guard let developerToken, !developerToken.isEmpty else { return false }
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var hasMusicUserToken: Bool {
|
|
24
|
+
guard let musicUserToken, !musicUserToken.isEmpty else { return false }
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Developer JWT and music user token — required for `/v1/me/` REST and full URLSession auth.
|
|
29
|
+
var hasRestTokens: Bool {
|
|
30
|
+
hasDeveloperToken && hasMusicUserToken
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// GET may use MusicKit `MusicDataRequest` when stored REST tokens are incomplete.
|
|
34
|
+
var canUseMusicKitAutoTokenForGet: Bool {
|
|
35
|
+
!hasRestTokens
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// GET with a stored developer JWT uses URLSession + Bearer (see `AppleMusicRestClient`).
|
|
39
|
+
var prefersStoredDeveloperTokenForGet: Bool {
|
|
40
|
+
hasDeveloperToken
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func pathRequiresMusicUserToken(_ path: String) -> Bool {
|
|
44
|
+
path.hasPrefix("/v1/me/")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// In-memory storefront resolved after auth; cleared when tokens change.
|
|
49
|
+
@available(iOS 16.0, *)
|
|
50
|
+
enum AuthenticatedSessionCache {
|
|
51
|
+
private static var storefrontId: String?
|
|
52
|
+
|
|
53
|
+
static func cachedStorefrontId() -> String? {
|
|
54
|
+
storefrontId
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static func setStorefrontId(_ id: String) {
|
|
58
|
+
storefrontId = id
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static func invalidate() {
|
|
62
|
+
storefrontId = nil
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Shared bridge pagination defaults (limit 25, offset 0).
|
|
4
|
+
struct BridgePagination {
|
|
5
|
+
let limit: Int
|
|
6
|
+
let offset: Int
|
|
7
|
+
|
|
8
|
+
init(from dictionary: NSDictionary) {
|
|
9
|
+
limit = dictionary["limit"] as? Int ?? 25
|
|
10
|
+
offset = dictionary["offset"] as? Int ?? 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
init(limit: Int, offset: Int) {
|
|
14
|
+
self.limit = limit
|
|
15
|
+
self.offset = offset
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Standard bridge response envelopes (keys match Android and TypeScript).
|
|
4
|
+
enum BridgeResponses {
|
|
5
|
+
static func storefront(id: String) -> [String: Any] {
|
|
6
|
+
["id": id]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static func catalogSearch(_ result: CatalogService.SearchResult) -> [String: Any] {
|
|
10
|
+
[
|
|
11
|
+
"songs": result.songs,
|
|
12
|
+
"albums": result.albums,
|
|
13
|
+
"artists": result.artists,
|
|
14
|
+
"playlists": result.playlists,
|
|
15
|
+
"stations": result.stations,
|
|
16
|
+
"musicVideos": result.musicVideos,
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static func catalogCharts(_ result: CatalogService.ChartsResult) -> [String: Any] {
|
|
21
|
+
[
|
|
22
|
+
"songs": result.songs,
|
|
23
|
+
"albums": result.albums,
|
|
24
|
+
"playlists": result.playlists,
|
|
25
|
+
"musicVideos": result.musicVideos,
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static func songs(_ items: [[String: Any]]) -> [String: Any] {
|
|
30
|
+
["songs": items]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static func albums(_ items: [[String: Any]]) -> [String: Any] {
|
|
34
|
+
["albums": items]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static func artists(_ items: [[String: Any]]) -> [String: Any] {
|
|
38
|
+
["artists": items]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static func playlists(_ items: [[String: Any]]) -> [String: Any] {
|
|
42
|
+
["playlists": items]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static func musicVideos(_ items: [[String: Any]]) -> [String: Any] {
|
|
46
|
+
["musicVideos": items]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static func librarySearch(_ result: LibraryService.LibrarySearchResult) -> [String: Any] {
|
|
50
|
+
[
|
|
51
|
+
"songs": result.songs,
|
|
52
|
+
"albums": result.albums,
|
|
53
|
+
"artists": result.artists,
|
|
54
|
+
"playlists": result.playlists,
|
|
55
|
+
"musicVideos": result.musicVideos,
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static func stations(_ items: [[String: Any]]) -> [String: Any] {
|
|
60
|
+
["stations": items]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static func recentlyPlayedResources(_ items: [[String: Any]]) -> [String: Any] {
|
|
64
|
+
["recentlyPlayedItems": items]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static func recentItems(_ items: [[String: Any]]) -> [String: Any] {
|
|
68
|
+
["items": items]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static func recommendations(_ items: [[String: Any]]) -> [String: Any] {
|
|
72
|
+
["recommendations": items]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static func replaySummaries(_ items: [[String: Any]]) -> [String: Any] {
|
|
76
|
+
["summaries": items]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static func configurePlayer(mixWithOthers: Bool) -> [String: Any] {
|
|
80
|
+
["mixWithOthers": mixWithOthers]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// CatalogSearchStore.swift
|
|
2
|
+
// Catalog search transport seam — native MusicKit vs REST (developer JWT).
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
@available(iOS 16.0, *)
|
|
7
|
+
protocol CatalogSearchStore {
|
|
8
|
+
func search(
|
|
9
|
+
term: String,
|
|
10
|
+
types: [String],
|
|
11
|
+
options: CatalogService.SearchOptions
|
|
12
|
+
) async throws -> CatalogService.SearchResult
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// CatalogSearchStoreFactory.swift
|
|
2
|
+
// Native MusicKit search first; REST fallback only when auto-token fails and a developer JWT is stored.
|
|
3
|
+
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
@available(iOS 16.0, *)
|
|
7
|
+
enum CatalogSearchStoreFactory {
|
|
8
|
+
|
|
9
|
+
static func search(
|
|
10
|
+
term: String,
|
|
11
|
+
types: [String],
|
|
12
|
+
options: CatalogService.SearchOptions
|
|
13
|
+
) async throws -> CatalogService.SearchResult {
|
|
14
|
+
do {
|
|
15
|
+
return try await MusicKitCatalogSearchStore().search(
|
|
16
|
+
term: term, types: types, options: options)
|
|
17
|
+
} catch {
|
|
18
|
+
if MusicKitCatalogSearchStore.isClientNotRegistered(error),
|
|
19
|
+
AuthenticatedSession.current.hasDeveloperToken
|
|
20
|
+
{
|
|
21
|
+
return try await RestCatalogSearchStore().search(
|
|
22
|
+
term: term, types: types, options: options)
|
|
23
|
+
}
|
|
24
|
+
if MusicKitCatalogSearchStore.isClientNotRegistered(error) {
|
|
25
|
+
throw CatalogService.CatalogServiceError.configurationRequired(
|
|
26
|
+
MusicKitCatalogSearchStore.missingDeveloperTokenMessage)
|
|
27
|
+
}
|
|
28
|
+
throw error
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|