@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,44 @@
|
|
|
1
|
+
package expo.modules.applemusic
|
|
2
|
+
|
|
3
|
+
import java.util.Locale
|
|
4
|
+
import kotlinx.coroutines.Dispatchers
|
|
5
|
+
import kotlinx.coroutines.withContext
|
|
6
|
+
|
|
7
|
+
/** Resolves and caches the user's Apple Music storefront id for catalog paths. */
|
|
8
|
+
internal class StorefrontRestClient(
|
|
9
|
+
private val transport: AppleMusicRestTransport,
|
|
10
|
+
) {
|
|
11
|
+
suspend fun getStorefront(musicUserToken: String): String =
|
|
12
|
+
resolveStorefront(musicUserToken, allowLocaleFallback = true)
|
|
13
|
+
|
|
14
|
+
/** Catalog REST paths — cached storefront or device locale (no music user token). */
|
|
15
|
+
fun getCatalogStorefront(): String =
|
|
16
|
+
AuthenticatedSessionCache.storefrontId ?: localeStorefrontId()
|
|
17
|
+
|
|
18
|
+
/** Resolves storefront from `/v1/me/storefront` only — required before library playback. */
|
|
19
|
+
suspend fun requireUserStorefront(musicUserToken: String): String =
|
|
20
|
+
resolveStorefront(musicUserToken, allowLocaleFallback = false)
|
|
21
|
+
|
|
22
|
+
private suspend fun resolveStorefront(musicUserToken: String, allowLocaleFallback: Boolean): String =
|
|
23
|
+
withContext(Dispatchers.IO) {
|
|
24
|
+
AuthenticatedSessionCache.storefrontId?.let { return@withContext it }
|
|
25
|
+
try {
|
|
26
|
+
val json = transport.getJson(musicUserToken, "/v1/me/storefront")
|
|
27
|
+
val id = json.getJSONArray("data").getJSONObject(0).getString("id")
|
|
28
|
+
AuthenticatedSessionCache.storefrontId = id
|
|
29
|
+
id
|
|
30
|
+
} catch (error: Exception) {
|
|
31
|
+
if (allowLocaleFallback) {
|
|
32
|
+
localeStorefrontId()
|
|
33
|
+
} else {
|
|
34
|
+
throw error
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Fallback when `/v1/me/storefront` is unavailable (mirrors iOS [StorefrontService]). */
|
|
40
|
+
fun localeStorefrontId(): String {
|
|
41
|
+
val region = Locale.getDefault().country
|
|
42
|
+
return if (region.isNotEmpty()) region.lowercase(Locale.US) else "us"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import expo.modules.applemusic.AndroidDeveloperToken
|
|
5
|
+
import expo.modules.applemusic.AndroidPlaybackController
|
|
6
|
+
import expo.modules.applemusic.AndroidSubscriptionService
|
|
7
|
+
import expo.modules.applemusic.BridgeResponses
|
|
8
|
+
import expo.modules.applemusic.MusicKitAuthInput
|
|
9
|
+
import expo.modules.applemusic.MusicKitAuthOutput
|
|
10
|
+
import expo.modules.applemusic.MusicKitAuthStorage
|
|
11
|
+
import expo.modules.kotlin.activityresult.AppContextActivityResultLauncher
|
|
12
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
13
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
14
|
+
|
|
15
|
+
internal fun ModuleDefinitionBuilder.registerAuthBridge(
|
|
16
|
+
reactContext: () -> Context,
|
|
17
|
+
authLauncher: () -> AppContextActivityResultLauncher<MusicKitAuthInput, MusicKitAuthOutput>,
|
|
18
|
+
subscriptionService: () -> AndroidSubscriptionService,
|
|
19
|
+
libraryService: () -> expo.modules.applemusic.AndroidLibraryService,
|
|
20
|
+
) {
|
|
21
|
+
AsyncFunction("setDeveloperToken") { token: String ->
|
|
22
|
+
val trimmed = token.trim()
|
|
23
|
+
require(trimmed.isNotEmpty()) { "Developer token must not be empty" }
|
|
24
|
+
val context = reactContext()
|
|
25
|
+
MusicKitAuthStorage.saveDeveloperToken(context, trimmed)
|
|
26
|
+
AndroidPlaybackController.getInstance(context).attachPendingListenersAfterDeveloperTokenStored()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
AsyncFunction("authorization") Coroutine {
|
|
30
|
+
developerToken: String?,
|
|
31
|
+
startScreenMessage: String?,
|
|
32
|
+
hideStartScreen: Boolean?,
|
|
33
|
+
->
|
|
34
|
+
val context = reactContext()
|
|
35
|
+
val token = AndroidDeveloperToken.require(developerToken)
|
|
36
|
+
MusicKitAuthStorage.saveDeveloperToken(context, token)
|
|
37
|
+
AndroidPlaybackController.getInstance(context).attachPendingListenersAfterDeveloperTokenStored()
|
|
38
|
+
val message = startScreenMessage?.trim()?.takeIf { it.isNotEmpty() }
|
|
39
|
+
val result =
|
|
40
|
+
authLauncher().launch(
|
|
41
|
+
MusicKitAuthInput(
|
|
42
|
+
developerToken = token,
|
|
43
|
+
startScreenMessage = message,
|
|
44
|
+
hideStartScreen = hideStartScreen ?: false,
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if (result.status == "authorized") {
|
|
49
|
+
result.musicUserToken?.let { userToken ->
|
|
50
|
+
MusicKitAuthStorage.saveMusicUserToken(context, userToken)
|
|
51
|
+
AndroidPlaybackController.getInstance(context).applyMusicUserToken(userToken)
|
|
52
|
+
}
|
|
53
|
+
AndroidPlaybackController.warmUp(context)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
mapOf(
|
|
57
|
+
"status" to result.status,
|
|
58
|
+
"musicUserToken" to result.musicUserToken,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
AsyncFunction("checkSubscription") Coroutine { musicUserToken: String ->
|
|
63
|
+
subscriptionService().checkSubscription(musicUserToken)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
AsyncFunction("getStorefront") Coroutine { musicUserToken: String ->
|
|
67
|
+
BridgeResponses.storefront(libraryService().getStorefrontId(musicUserToken))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidCatalogService
|
|
4
|
+
import expo.modules.applemusic.AppleMusicErrors
|
|
5
|
+
import expo.modules.applemusic.BridgeResponses
|
|
6
|
+
import expo.modules.applemusic.PaginationOptions
|
|
7
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
9
|
+
|
|
10
|
+
internal fun ModuleDefinitionBuilder.registerCatalogBridge(
|
|
11
|
+
catalogService: () -> AndroidCatalogService,
|
|
12
|
+
) {
|
|
13
|
+
AsyncFunction("catalogSearch") Coroutine { term: String, types: List<String>, options: Map<String, Any?> ->
|
|
14
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
15
|
+
BridgeResponses.catalogSearch(catalogService().search(term, types, pagination))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
AsyncFunction("getCatalogSong") Coroutine { id: String ->
|
|
19
|
+
catalogService().getSong(id)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
AsyncFunction("getCatalogAlbum") Coroutine { id: String ->
|
|
23
|
+
catalogService().getAlbum(id)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
AsyncFunction("getCatalogArtist") Coroutine { id: String ->
|
|
27
|
+
catalogService().getArtist(id)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
AsyncFunction("getCatalogPlaylist") Coroutine { id: String ->
|
|
31
|
+
catalogService().getPlaylist(id)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
AsyncFunction("getCatalogStation") Coroutine { id: String ->
|
|
35
|
+
catalogService().getStation(id)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
AsyncFunction("getCatalogMusicVideo") Coroutine { id: String ->
|
|
39
|
+
catalogService().getMusicVideo(id)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
AsyncFunction("getCatalogAlbumTracks") Coroutine { albumId: String, options: Map<String, Any?> ->
|
|
43
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
44
|
+
BridgeResponses.songs(catalogService().getAlbumTracks(albumId, pagination))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
AsyncFunction("getCatalogArtistAlbums") Coroutine { artistId: String, options: Map<String, Any?> ->
|
|
48
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
49
|
+
BridgeResponses.albums(catalogService().getArtistAlbums(artistId, pagination))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
AsyncFunction("getCatalogPlaylistTracks") Coroutine { playlistId: String, options: Map<String, Any?> ->
|
|
53
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
54
|
+
BridgeResponses.songs(catalogService().getPlaylistTracks(playlistId, pagination))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
AsyncFunction("getCatalogCharts") Coroutine { types: List<String>, options: Map<String, Any?> ->
|
|
58
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
59
|
+
val genre = options["genre"] as? String
|
|
60
|
+
val chart = options["chart"] as? String
|
|
61
|
+
BridgeResponses.catalogCharts(catalogService().getCharts(types, pagination, genre, chart))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
AsyncFunction("getCatalogResources") Coroutine { type: String, ids: List<String> ->
|
|
65
|
+
val items = catalogService().getResources(type, ids)
|
|
66
|
+
when (type) {
|
|
67
|
+
"songs" -> BridgeResponses.songs(items)
|
|
68
|
+
"albums" -> BridgeResponses.albums(items)
|
|
69
|
+
"artists" -> BridgeResponses.artists(items)
|
|
70
|
+
"playlists" -> BridgeResponses.playlists(items)
|
|
71
|
+
"stations" -> BridgeResponses.stations(items)
|
|
72
|
+
"music-videos" -> BridgeResponses.musicVideos(items)
|
|
73
|
+
else -> throw AppleMusicErrors.unknownMediaType(type)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidHistoryService
|
|
4
|
+
import expo.modules.applemusic.BridgeResponses
|
|
5
|
+
import expo.modules.applemusic.PaginationOptions
|
|
6
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
7
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
8
|
+
|
|
9
|
+
internal fun ModuleDefinitionBuilder.registerHistoryBridge(
|
|
10
|
+
historyService: () -> AndroidHistoryService,
|
|
11
|
+
) {
|
|
12
|
+
AsyncFunction("getRecentlyPlayedResources") Coroutine { musicUserToken: String ->
|
|
13
|
+
BridgeResponses.recentlyPlayedResources(historyService().getRecentlyPlayedResources(musicUserToken))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
AsyncFunction("getRecentlyPlayedTracks") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
17
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
18
|
+
BridgeResponses.songs(historyService().getRecentlyPlayedTracks(musicUserToken, pagination))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
AsyncFunction("getHeavyRotation") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
22
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
23
|
+
BridgeResponses.recentItems(historyService().getHeavyRotation(musicUserToken, pagination))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
AsyncFunction("getRecentlyPlayedStations") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
27
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
28
|
+
BridgeResponses.stations(historyService().getRecentlyPlayedStations(musicUserToken, pagination))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
AsyncFunction("getRecentlyAdded") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
32
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
33
|
+
BridgeResponses.recentItems(historyService().getRecentlyAdded(musicUserToken, pagination))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidLibraryService
|
|
4
|
+
import expo.modules.applemusic.BridgeResponses
|
|
5
|
+
import expo.modules.applemusic.PaginationOptions
|
|
6
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
7
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
8
|
+
|
|
9
|
+
internal fun ModuleDefinitionBuilder.registerLibraryBridge(
|
|
10
|
+
libraryService: () -> AndroidLibraryService,
|
|
11
|
+
) {
|
|
12
|
+
AsyncFunction("getUserPlaylists") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
13
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
14
|
+
BridgeResponses.playlists(libraryService().getPlaylists(musicUserToken, pagination))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
AsyncFunction("getLibrarySongs") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
18
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
19
|
+
BridgeResponses.songs(libraryService().getSongs(musicUserToken, pagination))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
AsyncFunction("getPlaylistSongs") Coroutine {
|
|
23
|
+
musicUserToken: String,
|
|
24
|
+
playlistId: String,
|
|
25
|
+
options: Map<String, Any?>,
|
|
26
|
+
->
|
|
27
|
+
BridgeResponses.songs(libraryService().getPlaylistSongs(musicUserToken, playlistId))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
AsyncFunction("getLibraryArtists") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
31
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
32
|
+
BridgeResponses.artists(libraryService().getArtists(musicUserToken, pagination))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
AsyncFunction("getLibraryAlbums") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
36
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
37
|
+
BridgeResponses.albums(libraryService().getAlbums(musicUserToken, pagination))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
AsyncFunction("getLibraryMusicVideos") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
41
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
42
|
+
BridgeResponses.musicVideos(libraryService().getMusicVideos(musicUserToken, pagination))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
AsyncFunction("librarySearch") Coroutine {
|
|
46
|
+
musicUserToken: String,
|
|
47
|
+
term: String,
|
|
48
|
+
types: List<String>,
|
|
49
|
+
options: Map<String, Any?>,
|
|
50
|
+
->
|
|
51
|
+
val pagination = PaginationOptions.fromMap(options)
|
|
52
|
+
BridgeResponses.librarySearch(libraryService().search(musicUserToken, term, types, pagination))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidLibraryMutationsService
|
|
4
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
5
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
6
|
+
|
|
7
|
+
internal fun ModuleDefinitionBuilder.registerLibraryMutationsBridge(
|
|
8
|
+
libraryMutationsService: () -> AndroidLibraryMutationsService,
|
|
9
|
+
) {
|
|
10
|
+
AsyncFunction("addToLibrary") Coroutine { musicUserToken: String, resourceIds: Map<String, List<String>> ->
|
|
11
|
+
libraryMutationsService().addToLibrary(musicUserToken, resourceIds)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
AsyncFunction("createLibraryPlaylist") Coroutine { musicUserToken: String, options: Map<String, Any?> ->
|
|
15
|
+
val name = options["name"] as? String ?: ""
|
|
16
|
+
val description = options["description"] as? String
|
|
17
|
+
val isPublic = options["isPublic"] as? Boolean ?: false
|
|
18
|
+
@Suppress("UNCHECKED_CAST")
|
|
19
|
+
val tracks = options["tracks"] as? List<Map<String, String>>
|
|
20
|
+
libraryMutationsService().createPlaylist(musicUserToken, name, description, isPublic, tracks)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
AsyncFunction("addTracksToLibraryPlaylist") Coroutine {
|
|
24
|
+
musicUserToken: String,
|
|
25
|
+
playlistId: String,
|
|
26
|
+
tracks: List<Map<String, String>>,
|
|
27
|
+
->
|
|
28
|
+
libraryMutationsService().addTracksToPlaylist(musicUserToken, playlistId, tracks)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import com.apple.android.music.playback.model.MediaPlayerException
|
|
4
|
+
import expo.modules.applemusic.AndroidPlaybackController
|
|
5
|
+
import expo.modules.applemusic.AndroidQueueService
|
|
6
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
7
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
8
|
+
import kotlinx.coroutines.CoroutineScope
|
|
9
|
+
import kotlinx.coroutines.launch
|
|
10
|
+
|
|
11
|
+
internal fun ModuleDefinitionBuilder.registerPlayerBridge(
|
|
12
|
+
moduleScope: CoroutineScope,
|
|
13
|
+
playbackController: () -> AndroidPlaybackController,
|
|
14
|
+
queueService: () -> AndroidQueueService,
|
|
15
|
+
emitPlaybackError: (Exception, String) -> Unit,
|
|
16
|
+
emitPlaybackTimeUpdate: (Double) -> Unit,
|
|
17
|
+
) {
|
|
18
|
+
AsyncFunction("setPlaybackQueue") Coroutine { itemId: String, type: String ->
|
|
19
|
+
try {
|
|
20
|
+
queueService().setQueue(itemId, type)
|
|
21
|
+
"Track(s) added to queue"
|
|
22
|
+
} catch (error: Exception) {
|
|
23
|
+
throw AndroidPlaybackController.mapPlaybackException(error)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
AsyncFunction("playLibrarySong") Coroutine { musicUserToken: String, songId: String ->
|
|
28
|
+
queueService().playLibrarySong(musicUserToken, songId)
|
|
29
|
+
"Library song added to queue"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
AsyncFunction("playLibraryPlaylist") Coroutine { musicUserToken: String, playlistId: String, startingAt: Int ->
|
|
33
|
+
queueService().playLibraryPlaylist(musicUserToken, playlistId, startingAt)
|
|
34
|
+
"Library playlist added to queue"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
AsyncFunction("configurePlayer") { mixWithOthers: Boolean ->
|
|
38
|
+
playbackController().configurePlayer(mixWithOthers)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
AsyncFunction("getCurrentState") {
|
|
42
|
+
playbackController().currentState()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Function("play") {
|
|
46
|
+
runPlayback(moduleScope, emitPlaybackError, "play") { playbackController().play() }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Function("pause") {
|
|
50
|
+
playbackController().pause()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Function("skipToNextEntry") {
|
|
54
|
+
runPlayback(moduleScope, emitPlaybackError, "skipToNext") { playbackController().skipToNext() }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Function("skipToPreviousEntry") {
|
|
58
|
+
runPlayback(moduleScope, emitPlaybackError, "skipToPrevious") { playbackController().skipToPrevious() }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Function("restartCurrentEntry") {
|
|
62
|
+
playbackController().restartCurrentEntry { time -> emitPlaybackTimeUpdate(time) }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Function("seekToTime") { time: Double ->
|
|
66
|
+
playbackController().seekToTime(time) { actual -> emitPlaybackTimeUpdate(actual) }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Function("togglePlayerState") {
|
|
70
|
+
runPlayback(moduleScope, emitPlaybackError, "togglePlayback") { playbackController().togglePlayback() }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private fun runPlayback(
|
|
75
|
+
scope: CoroutineScope,
|
|
76
|
+
emitPlaybackError: (Exception, String) -> Unit,
|
|
77
|
+
operation: String,
|
|
78
|
+
block: () -> Unit,
|
|
79
|
+
) {
|
|
80
|
+
scope.launch {
|
|
81
|
+
try {
|
|
82
|
+
block()
|
|
83
|
+
} catch (error: MediaPlayerException) {
|
|
84
|
+
emitPlaybackError(error, operation)
|
|
85
|
+
} catch (error: Exception) {
|
|
86
|
+
emitPlaybackError(error, operation)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidRatingsService
|
|
4
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
5
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
6
|
+
|
|
7
|
+
internal fun ModuleDefinitionBuilder.registerRatingsBridge(
|
|
8
|
+
ratingsService: () -> AndroidRatingsService,
|
|
9
|
+
) {
|
|
10
|
+
AsyncFunction("getRating") Coroutine { musicUserToken: String, resourceType: String, id: String ->
|
|
11
|
+
ratingsService().getRating(musicUserToken, resourceType, id)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
AsyncFunction("setRating") Coroutine { musicUserToken: String, resourceType: String, id: String, value: Int ->
|
|
15
|
+
ratingsService().setRating(musicUserToken, resourceType, id, value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
AsyncFunction("clearRating") Coroutine { musicUserToken: String, resourceType: String, id: String ->
|
|
19
|
+
ratingsService().clearRating(musicUserToken, resourceType, id)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
AsyncFunction("addToFavorites") Coroutine { musicUserToken: String, resourceIds: Map<String, List<String>> ->
|
|
23
|
+
ratingsService().addToFavorites(musicUserToken, resourceIds)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
AsyncFunction("removeFromFavorites") Coroutine { musicUserToken: String, resourceIds: Map<String, List<String>> ->
|
|
27
|
+
ratingsService().removeFromFavorites(musicUserToken, resourceIds)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
package expo.modules.applemusic.bridge
|
|
2
|
+
|
|
3
|
+
import expo.modules.applemusic.AndroidRecommendationsService
|
|
4
|
+
import expo.modules.applemusic.BridgeResponses
|
|
5
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
6
|
+
import expo.modules.kotlin.modules.ModuleDefinitionBuilder
|
|
7
|
+
|
|
8
|
+
internal fun ModuleDefinitionBuilder.registerRecommendationsBridge(
|
|
9
|
+
recommendationsService: () -> AndroidRecommendationsService,
|
|
10
|
+
) {
|
|
11
|
+
AsyncFunction("getRecommendations") Coroutine { musicUserToken: String, ids: List<String>? ->
|
|
12
|
+
BridgeResponses.recommendations(recommendationsService().getRecommendations(musicUserToken, ids))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
AsyncFunction("getReplay") Coroutine { musicUserToken: String, year: Int? ->
|
|
16
|
+
BridgeResponses.replaySummaries(recommendationsService().getReplay(musicUserToken, year))
|
|
17
|
+
}
|
|
18
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/build/with-expo-apple-music');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NativeModule } from 'expo-modules-core';
|
|
2
|
+
export declare class ExpoAppleMusicModule extends NativeModule {
|
|
3
|
+
__expo_module_name__: string;
|
|
4
|
+
private readonly api;
|
|
5
|
+
private readonly subscription;
|
|
6
|
+
private readonly queue;
|
|
7
|
+
private readonly playback;
|
|
8
|
+
private readonly playbackObserver;
|
|
9
|
+
constructor();
|
|
10
|
+
startObserving(): void;
|
|
11
|
+
stopObserving(): void;
|
|
12
|
+
}
|
|
13
|
+
declare const moduleInstance: ExpoAppleMusicModule;
|
|
14
|
+
export default moduleInstance;
|
|
15
|
+
//# sourceMappingURL=ExpoAppleMusicModule.web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppleMusicModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoAppleMusicModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AASpE,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,oBAAoB,SAAoB;IAExC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgC;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IACxD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;;IAe9D,cAAc,IAAI,IAAI;IAItB,aAAa,IAAI,IAAI;CAGtB;AAED,QAAA,MAAM,cAAc,EAGJ,oBAAoB,CAAC;AAErC,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NativeModule, registerWebModule } from 'expo-modules-core';
|
|
2
|
+
import { createWebBridgeHandlers } from './bridge/handlers';
|
|
3
|
+
import { WebAppleMusicApiClient } from './web/WebAppleMusicApiClient';
|
|
4
|
+
import { WebPlaybackController } from './web/WebPlaybackController';
|
|
5
|
+
import { WebPlaybackObserver } from './web/WebPlaybackObserver';
|
|
6
|
+
import { WebQueueService } from './web/WebQueueService';
|
|
7
|
+
import { WebSubscriptionService } from './web/WebSubscriptionService';
|
|
8
|
+
export class ExpoAppleMusicModule extends NativeModule {
|
|
9
|
+
__expo_module_name__ = 'ExpoAppleMusic';
|
|
10
|
+
api = new WebAppleMusicApiClient();
|
|
11
|
+
subscription = new WebSubscriptionService(this.api);
|
|
12
|
+
queue = new WebQueueService(this.api);
|
|
13
|
+
playback = new WebPlaybackController();
|
|
14
|
+
playbackObserver = new WebPlaybackObserver();
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
Object.assign(this, createWebBridgeHandlers({
|
|
18
|
+
api: this.api,
|
|
19
|
+
subscription: this.subscription,
|
|
20
|
+
queue: this.queue,
|
|
21
|
+
playback: this.playback,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
startObserving() {
|
|
25
|
+
this.playbackObserver.start(this);
|
|
26
|
+
}
|
|
27
|
+
stopObserving() {
|
|
28
|
+
this.playbackObserver.stop();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const moduleInstance = registerWebModule(ExpoAppleMusicModule, 'ExpoAppleMusic');
|
|
32
|
+
export default moduleInstance;
|
|
33
|
+
//# sourceMappingURL=ExpoAppleMusicModule.web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppleMusicModule.web.js","sourceRoot":"","sources":["../src/ExpoAppleMusicModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAEtE,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACpD,oBAAoB,GAAG,gBAAgB,CAAC;IAEvB,GAAG,GAAG,IAAI,sBAAsB,EAAE,CAAC;IACnC,YAAY,GAAG,IAAI,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,KAAK,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,QAAQ,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACvC,gBAAgB,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAE9D;QACE,KAAK,EAAE,CAAC;QACR,MAAM,CAAC,MAAM,CACX,IAAI,EACJ,uBAAuB,CAAC;YACtB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CACH,CAAC;IACJ,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,aAAa;QACX,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,cAAc,GAAG,iBAAiB,CACtC,oBAAoB,EACpB,gBAAgB,CACkB,CAAC;AAErC,eAAe,cAAc,CAAC","sourcesContent":["import { NativeModule, registerWebModule } from 'expo-modules-core';\n\nimport { createWebBridgeHandlers } from './bridge/handlers';\nimport { WebAppleMusicApiClient } from './web/WebAppleMusicApiClient';\nimport { WebPlaybackController } from './web/WebPlaybackController';\nimport { WebPlaybackObserver } from './web/WebPlaybackObserver';\nimport { WebQueueService } from './web/WebQueueService';\nimport { WebSubscriptionService } from './web/WebSubscriptionService';\n\nexport class ExpoAppleMusicModule extends NativeModule {\n __expo_module_name__ = 'ExpoAppleMusic';\n\n private readonly api = new WebAppleMusicApiClient();\n private readonly subscription = new WebSubscriptionService(this.api);\n private readonly queue = new WebQueueService(this.api);\n private readonly playback = new WebPlaybackController();\n private readonly playbackObserver = new WebPlaybackObserver();\n\n constructor() {\n super();\n Object.assign(\n this,\n createWebBridgeHandlers({\n api: this.api,\n subscription: this.subscription,\n queue: this.queue,\n playback: this.playback,\n }),\n );\n }\n\n startObserving(): void {\n this.playbackObserver.start(this);\n }\n\n stopObserving(): void {\n this.playbackObserver.stop();\n }\n}\n\nconst moduleInstance = registerWebModule(\n ExpoAppleMusicModule,\n 'ExpoAppleMusic',\n) as unknown as ExpoAppleMusicModule;\n\nexport default moduleInstance;\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AppleMusicError } from '../utils/apple-music-error';
|
|
2
|
+
export declare function callNative<T>(operation: string, fn: () => Promise<T>): Promise<T>;
|
|
3
|
+
export declare function normalizeNativeError(operation: string, error: unknown): AppleMusicError;
|
|
4
|
+
export declare function invalidLibraryIdError(label: string, id: string): AppleMusicError;
|
|
5
|
+
export declare function throwInvalidLibraryIdError(label: string, id: string): never;
|
|
6
|
+
//# sourceMappingURL=call-native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-native.d.ts","sourceRoot":"","sources":["../../src/api/call-native.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlE,wBAAsB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMvF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,eAAe,CAkBvF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKhF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAE3E"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AppleMusicErrorCode } from '../constants/apple-music-error-codes';
|
|
2
|
+
import { asThrownAppleMusicError, isAppleMusicError } from '../utils/apple-music-error';
|
|
3
|
+
export async function callNative(operation, fn) {
|
|
4
|
+
try {
|
|
5
|
+
return await fn();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw asThrownAppleMusicError(normalizeNativeError(operation, error));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function normalizeNativeError(operation, error) {
|
|
12
|
+
if (isAppleMusicError(error)) {
|
|
13
|
+
return error.operation ? error : { ...error, operation };
|
|
14
|
+
}
|
|
15
|
+
const coded = error;
|
|
16
|
+
if (typeof coded.code === 'string' && typeof coded.message === 'string') {
|
|
17
|
+
return { code: coded.code, message: coded.message, operation };
|
|
18
|
+
}
|
|
19
|
+
const message = error instanceof Error
|
|
20
|
+
? error.message
|
|
21
|
+
: typeof error === 'string'
|
|
22
|
+
? error
|
|
23
|
+
: 'Unknown Apple Music error';
|
|
24
|
+
return { code: AppleMusicErrorCode.error, message, operation };
|
|
25
|
+
}
|
|
26
|
+
export function invalidLibraryIdError(label, id) {
|
|
27
|
+
return {
|
|
28
|
+
code: AppleMusicErrorCode.invalidLibraryId,
|
|
29
|
+
message: `Expected a library resource id (i., l., or p. prefix) for ${label}, got "${id}"`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function throwInvalidLibraryIdError(label, id) {
|
|
33
|
+
throw asThrownAppleMusicError(invalidLibraryIdError(label, id));
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=call-native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-native.js","sourceRoot":"","sources":["../../src/api/call-native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAExF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,SAAiB,EAAE,EAAoB;IACzE,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,uBAAuB,CAAC,oBAAoB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,KAAc;IACpE,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,KAAK,GAAG,KAA8C,CAAC;IAC7D,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;IACjE,CAAC;IAED,MAAM,OAAO,GACX,KAAK,YAAY,KAAK;QACpB,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,2BAA2B,CAAC;IAEpC,OAAO,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAa,EAAE,EAAU;IAC7D,OAAO;QACL,IAAI,EAAE,mBAAmB,CAAC,gBAAgB;QAC1C,OAAO,EAAE,6DAA6D,KAAK,UAAU,EAAE,GAAG;KAC3F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAa,EAAE,EAAU;IAClE,MAAM,uBAAuB,CAAC,qBAAqB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import { AppleMusicErrorCode } from '../constants/apple-music-error-codes';\nimport type { AppleMusicError } from '../utils/apple-music-error';\nimport { asThrownAppleMusicError, isAppleMusicError } from '../utils/apple-music-error';\n\nexport async function callNative<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n throw asThrownAppleMusicError(normalizeNativeError(operation, error));\n }\n}\n\nexport function normalizeNativeError(operation: string, error: unknown): AppleMusicError {\n if (isAppleMusicError(error)) {\n return error.operation ? error : { ...error, operation };\n }\n\n const coded = error as { code?: unknown; message?: unknown };\n if (typeof coded.code === 'string' && typeof coded.message === 'string') {\n return { code: coded.code, message: coded.message, operation };\n }\n\n const message =\n error instanceof Error\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Unknown Apple Music error';\n\n return { code: AppleMusicErrorCode.error, message, operation };\n}\n\nexport function invalidLibraryIdError(label: string, id: string): AppleMusicError {\n return {\n code: AppleMusicErrorCode.invalidLibraryId,\n message: `Expected a library resource id (i., l., or p. prefix) for ${label}, got \"${id}\"`,\n };\n}\n\nexport function throwInvalidLibraryIdError(label: string, id: string): never {\n throw asThrownAppleMusicError(invalidLibraryIdError(label, id));\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Decode JWT `exp` (seconds since epoch). Returns null if missing or malformed. */
|
|
2
|
+
export declare function decodeJwtExpSeconds(token: string): number | null;
|
|
3
|
+
/** True when `exp` is within `bufferSeconds` of now (or `exp` is missing). */
|
|
4
|
+
export declare function isJwtExpired(token: string, bufferSeconds?: number): boolean;
|
|
5
|
+
//# sourceMappingURL=decode-jwt-exp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-jwt-exp.d.ts","sourceRoot":"","sources":["../../src/api/decode-jwt-exp.ts"],"names":[],"mappings":"AAEA,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAchE;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,aAAa,GAAE,MAAuC,GACrD,OAAO,CAOT"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const DEFAULT_REFRESH_BUFFER_SECONDS = 300;
|
|
2
|
+
/** Decode JWT `exp` (seconds since epoch). Returns null if missing or malformed. */
|
|
3
|
+
export function decodeJwtExpSeconds(token) {
|
|
4
|
+
const parts = token.trim().split('.');
|
|
5
|
+
const encodedPayload = parts[1];
|
|
6
|
+
if (parts.length < 2 || encodedPayload == null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const payload = encodedPayload.replace(/-/g, '+').replace(/_/g, '/');
|
|
11
|
+
const padded = payload + '='.repeat((4 - (payload.length % 4)) % 4);
|
|
12
|
+
const json = JSON.parse(atob(padded));
|
|
13
|
+
return typeof json.exp === 'number' && Number.isFinite(json.exp) ? json.exp : null;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** True when `exp` is within `bufferSeconds` of now (or `exp` is missing). */
|
|
20
|
+
export function isJwtExpired(token, bufferSeconds = DEFAULT_REFRESH_BUFFER_SECONDS) {
|
|
21
|
+
const exp = decodeJwtExpSeconds(token);
|
|
22
|
+
if (exp == null) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const now = Math.floor(Date.now() / 1000);
|
|
26
|
+
return exp <= now + bufferSeconds;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=decode-jwt-exp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-jwt-exp.js","sourceRoot":"","sources":["../../src/api/decode-jwt-exp.ts"],"names":[],"mappings":"AAAA,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAE3C,oFAAoF;AACpF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAsB,CAAC;QAC3D,OAAO,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,gBAAwB,8BAA8B;IAEtD,MAAM,GAAG,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,OAAO,GAAG,IAAI,GAAG,GAAG,aAAa,CAAC;AACpC,CAAC","sourcesContent":["const DEFAULT_REFRESH_BUFFER_SECONDS = 300;\n\n/** Decode JWT `exp` (seconds since epoch). Returns null if missing or malformed. */\nexport function decodeJwtExpSeconds(token: string): number | null {\n const parts = token.trim().split('.');\n const encodedPayload = parts[1];\n if (parts.length < 2 || encodedPayload == null) {\n return null;\n }\n try {\n const payload = encodedPayload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = payload + '='.repeat((4 - (payload.length % 4)) % 4);\n const json = JSON.parse(atob(padded)) as { exp?: unknown };\n return typeof json.exp === 'number' && Number.isFinite(json.exp) ? json.exp : null;\n } catch {\n return null;\n }\n}\n\n/** True when `exp` is within `bufferSeconds` of now (or `exp` is missing). */\nexport function isJwtExpired(\n token: string,\n bufferSeconds: number = DEFAULT_REFRESH_BUFFER_SECONDS,\n): boolean {\n const exp = decodeJwtExpSeconds(token);\n if (exp == null) {\n return false;\n }\n const now = Math.floor(Date.now() / 1000);\n return exp <= now + bufferSeconds;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library-ids.d.ts","sourceRoot":"","sources":["../../src/api/library-ids.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI/D"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { throwInvalidLibraryIdError } from './call-native';
|
|
2
|
+
/** True when `id` uses an Apple Music library prefix (`i.`, `l.`, `p.`). */
|
|
3
|
+
export function isLibraryId(id) {
|
|
4
|
+
return id.startsWith('l.') || id.startsWith('i.') || id.startsWith('p.');
|
|
5
|
+
}
|
|
6
|
+
export function assertLibraryId(id, label) {
|
|
7
|
+
if (!isLibraryId(id)) {
|
|
8
|
+
throwInvalidLibraryIdError(label, id);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=library-ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library-ids.js","sourceRoot":"","sources":["../../src/api/library-ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAE3D,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,KAAa;IACvD,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;QACrB,0BAA0B,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;AACH,CAAC","sourcesContent":["import { throwInvalidLibraryIdError } from './call-native';\n\n/** True when `id` uses an Apple Music library prefix (`i.`, `l.`, `p.`). */\nexport function isLibraryId(id: string): boolean {\n return id.startsWith('l.') || id.startsWith('i.') || id.startsWith('p.');\n}\n\nexport function assertLibraryId(id: string, label: string): void {\n if (!isLibraryId(id)) {\n throwInvalidLibraryIdError(label, id);\n }\n}\n"]}
|