@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.
Files changed (452) hide show
  1. package/ATTRIBUTION.md +24 -0
  2. package/LICENSE +190 -0
  3. package/NOTICE +7 -0
  4. package/README.md +81 -0
  5. package/android/build.gradle +26 -0
  6. package/android/libs/mediaplayback-release-1.1.1.aar +0 -0
  7. package/android/libs/musickitauth-release-1.1.2.aar +0 -0
  8. package/android/src/main/AndroidManifest.xml +16 -0
  9. package/android/src/main/java/expo/modules/applemusic/AndroidCatalogService.kt +86 -0
  10. package/android/src/main/java/expo/modules/applemusic/AndroidDeveloperToken.kt +39 -0
  11. package/android/src/main/java/expo/modules/applemusic/AndroidHistoryService.kt +24 -0
  12. package/android/src/main/java/expo/modules/applemusic/AndroidLibraryMutationsService.kt +30 -0
  13. package/android/src/main/java/expo/modules/applemusic/AndroidLibraryService.kt +61 -0
  14. package/android/src/main/java/expo/modules/applemusic/AndroidPlaybackController.kt +484 -0
  15. package/android/src/main/java/expo/modules/applemusic/AndroidPlaybackObserver.kt +173 -0
  16. package/android/src/main/java/expo/modules/applemusic/AndroidQueueService.kt +78 -0
  17. package/android/src/main/java/expo/modules/applemusic/AndroidRatingsService.kt +27 -0
  18. package/android/src/main/java/expo/modules/applemusic/AndroidRecommendationsService.kt +15 -0
  19. package/android/src/main/java/expo/modules/applemusic/AndroidSubscriptionService.kt +24 -0
  20. package/android/src/main/java/expo/modules/applemusic/AppleMusicErrorCodes.kt +13 -0
  21. package/android/src/main/java/expo/modules/applemusic/AppleMusicErrors.kt +46 -0
  22. package/android/src/main/java/expo/modules/applemusic/AppleMusicHttpMethod.kt +8 -0
  23. package/android/src/main/java/expo/modules/applemusic/AppleMusicJsonMapper.kt +258 -0
  24. package/android/src/main/java/expo/modules/applemusic/AppleMusicNativeLoader.kt +32 -0
  25. package/android/src/main/java/expo/modules/applemusic/AppleMusicRestJson.kt +40 -0
  26. package/android/src/main/java/expo/modules/applemusic/AppleMusicRestQuery.kt +12 -0
  27. package/android/src/main/java/expo/modules/applemusic/AppleMusicRestStack.kt +19 -0
  28. package/android/src/main/java/expo/modules/applemusic/AppleMusicRestTransport.kt +118 -0
  29. package/android/src/main/java/expo/modules/applemusic/AuthenticatedSession.kt +57 -0
  30. package/android/src/main/java/expo/modules/applemusic/BridgeResponses.kt +55 -0
  31. package/android/src/main/java/expo/modules/applemusic/CatalogRestClient.kt +306 -0
  32. package/android/src/main/java/expo/modules/applemusic/ExpoAppleMusicModule.kt +152 -0
  33. package/android/src/main/java/expo/modules/applemusic/HistoryRestClient.kt +60 -0
  34. package/android/src/main/java/expo/modules/applemusic/LibraryIds.kt +6 -0
  35. package/android/src/main/java/expo/modules/applemusic/LibraryMutationsRestClient.kt +95 -0
  36. package/android/src/main/java/expo/modules/applemusic/LibraryRestClient.kt +195 -0
  37. package/android/src/main/java/expo/modules/applemusic/MusicKitAuthContract.kt +78 -0
  38. package/android/src/main/java/expo/modules/applemusic/MusicKitAuthStorage.kt +76 -0
  39. package/android/src/main/java/expo/modules/applemusic/MusicKitTokenProvider.kt +13 -0
  40. package/android/src/main/java/expo/modules/applemusic/PaginationOptions.kt +14 -0
  41. package/android/src/main/java/expo/modules/applemusic/RatingsRestClient.kt +72 -0
  42. package/android/src/main/java/expo/modules/applemusic/RecommendationsRestClient.kt +37 -0
  43. package/android/src/main/java/expo/modules/applemusic/StorefrontRestClient.kt +44 -0
  44. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeAuth.kt +69 -0
  45. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeCatalog.kt +76 -0
  46. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeHistory.kt +35 -0
  47. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeLibrary.kt +54 -0
  48. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeLibraryMutations.kt +30 -0
  49. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgePlayer.kt +89 -0
  50. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeRatings.kt +29 -0
  51. package/android/src/main/java/expo/modules/applemusic/bridge/ExpoBridgeRecommendations.kt +18 -0
  52. package/app.plugin.js +1 -0
  53. package/build/ExpoAppleMusicModule.web.d.ts +15 -0
  54. package/build/ExpoAppleMusicModule.web.d.ts.map +1 -0
  55. package/build/ExpoAppleMusicModule.web.js +33 -0
  56. package/build/ExpoAppleMusicModule.web.js.map +1 -0
  57. package/build/api/call-native.d.ts +6 -0
  58. package/build/api/call-native.d.ts.map +1 -0
  59. package/build/api/call-native.js +35 -0
  60. package/build/api/call-native.js.map +1 -0
  61. package/build/api/decode-jwt-exp.d.ts +5 -0
  62. package/build/api/decode-jwt-exp.d.ts.map +1 -0
  63. package/build/api/decode-jwt-exp.js +28 -0
  64. package/build/api/decode-jwt-exp.js.map +1 -0
  65. package/build/api/library-ids.d.ts +4 -0
  66. package/build/api/library-ids.d.ts.map +1 -0
  67. package/build/api/library-ids.js +11 -0
  68. package/build/api/library-ids.js.map +1 -0
  69. package/build/api/pagination.d.ts +12 -0
  70. package/build/api/pagination.d.ts.map +1 -0
  71. package/build/api/pagination.js +13 -0
  72. package/build/api/pagination.js.map +1 -0
  73. package/build/api/parse-authorize-result.d.ts +3 -0
  74. package/build/api/parse-authorize-result.d.ts.map +1 -0
  75. package/build/api/parse-authorize-result.js +27 -0
  76. package/build/api/parse-authorize-result.js.map +1 -0
  77. package/build/api/require-music-user-token.d.ts +2 -0
  78. package/build/api/require-music-user-token.d.ts.map +1 -0
  79. package/build/api/require-music-user-token.js +14 -0
  80. package/build/api/require-music-user-token.js.map +1 -0
  81. package/build/api/sync-developer-token.d.ts +4 -0
  82. package/build/api/sync-developer-token.d.ts.map +1 -0
  83. package/build/api/sync-developer-token.js +27 -0
  84. package/build/api/sync-developer-token.js.map +1 -0
  85. package/build/bridge/bridge-methods.d.ts +17 -0
  86. package/build/bridge/bridge-methods.d.ts.map +1 -0
  87. package/build/bridge/bridge-methods.js +71 -0
  88. package/build/bridge/bridge-methods.js.map +1 -0
  89. package/build/bridge/bridge-responses.d.ts +64 -0
  90. package/build/bridge/bridge-responses.d.ts.map +1 -0
  91. package/build/bridge/bridge-responses.js +67 -0
  92. package/build/bridge/bridge-responses.js.map +1 -0
  93. package/build/bridge/handlers/auth-bridge.d.ts +11 -0
  94. package/build/bridge/handlers/auth-bridge.d.ts.map +1 -0
  95. package/build/bridge/handlers/auth-bridge.js +49 -0
  96. package/build/bridge/handlers/auth-bridge.js.map +1 -0
  97. package/build/bridge/handlers/catalog-bridge.d.ts +34 -0
  98. package/build/bridge/handlers/catalog-bridge.d.ts.map +1 -0
  99. package/build/bridge/handlers/catalog-bridge.js +58 -0
  100. package/build/bridge/handlers/catalog-bridge.js.map +1 -0
  101. package/build/bridge/handlers/history-bridge.d.ts +19 -0
  102. package/build/bridge/handlers/history-bridge.d.ts.map +1 -0
  103. package/build/bridge/handlers/history-bridge.js +31 -0
  104. package/build/bridge/handlers/history-bridge.js.map +1 -0
  105. package/build/bridge/handlers/index.d.ts +124 -0
  106. package/build/bridge/handlers/index.d.ts.map +1 -0
  107. package/build/bridge/handlers/index.js +21 -0
  108. package/build/bridge/handlers/index.js.map +1 -0
  109. package/build/bridge/handlers/library-bridge.d.ts +23 -0
  110. package/build/bridge/handlers/library-bridge.d.ts.map +1 -0
  111. package/build/bridge/handlers/library-bridge.js +41 -0
  112. package/build/bridge/handlers/library-bridge.js.map +1 -0
  113. package/build/bridge/handlers/library-mutations-bridge.d.ts +16 -0
  114. package/build/bridge/handlers/library-mutations-bridge.d.ts.map +1 -0
  115. package/build/bridge/handlers/library-mutations-bridge.js +20 -0
  116. package/build/bridge/handlers/library-mutations-bridge.js.map +1 -0
  117. package/build/bridge/handlers/player-bridge.d.ts +18 -0
  118. package/build/bridge/handlers/player-bridge.d.ts.map +1 -0
  119. package/build/bridge/handlers/player-bridge.js +42 -0
  120. package/build/bridge/handlers/player-bridge.js.map +1 -0
  121. package/build/bridge/handlers/ratings-bridge.d.ts +15 -0
  122. package/build/bridge/handlers/ratings-bridge.d.ts.map +1 -0
  123. package/build/bridge/handlers/ratings-bridge.js +16 -0
  124. package/build/bridge/handlers/ratings-bridge.js.map +1 -0
  125. package/build/bridge/handlers/recommendations-bridge.d.ts +10 -0
  126. package/build/bridge/handlers/recommendations-bridge.d.ts.map +1 -0
  127. package/build/bridge/handlers/recommendations-bridge.js +14 -0
  128. package/build/bridge/handlers/recommendations-bridge.js.map +1 -0
  129. package/build/constants/apple-music-error-codes.d.ts +22 -0
  130. package/build/constants/apple-music-error-codes.d.ts.map +1 -0
  131. package/build/constants/apple-music-error-codes.js +21 -0
  132. package/build/constants/apple-music-error-codes.js.map +1 -0
  133. package/build/hooks/use-current-song.d.ts +10 -0
  134. package/build/hooks/use-current-song.d.ts.map +1 -0
  135. package/build/hooks/use-current-song.js +35 -0
  136. package/build/hooks/use-current-song.js.map +1 -0
  137. package/build/hooks/use-is-playing.d.ts +6 -0
  138. package/build/hooks/use-is-playing.d.ts.map +1 -0
  139. package/build/hooks/use-is-playing.js +21 -0
  140. package/build/hooks/use-is-playing.js.map +1 -0
  141. package/build/hooks/use-playback-state.d.ts +12 -0
  142. package/build/hooks/use-playback-state.d.ts.map +1 -0
  143. package/build/hooks/use-playback-state.js +41 -0
  144. package/build/hooks/use-playback-state.js.map +1 -0
  145. package/build/index.d.ts +52 -0
  146. package/build/index.d.ts.map +1 -0
  147. package/build/index.js +44 -0
  148. package/build/index.js.map +1 -0
  149. package/build/mappers/apple-music-json-mapper.d.ts +94 -0
  150. package/build/mappers/apple-music-json-mapper.d.ts.map +1 -0
  151. package/build/mappers/apple-music-json-mapper.js +212 -0
  152. package/build/mappers/apple-music-json-mapper.js.map +1 -0
  153. package/build/modules/auth.d.ts +32 -0
  154. package/build/modules/auth.d.ts.map +1 -0
  155. package/build/modules/auth.js +60 -0
  156. package/build/modules/auth.js.map +1 -0
  157. package/build/modules/catalog.d.ts +46 -0
  158. package/build/modules/catalog.d.ts.map +1 -0
  159. package/build/modules/catalog.js +47 -0
  160. package/build/modules/catalog.js.map +1 -0
  161. package/build/modules/history.d.ts +17 -0
  162. package/build/modules/history.d.ts.map +1 -0
  163. package/build/modules/history.js +28 -0
  164. package/build/modules/history.js.map +1 -0
  165. package/build/modules/library-mutations.d.ts +9 -0
  166. package/build/modules/library-mutations.d.ts.map +1 -0
  167. package/build/modules/library-mutations.js +41 -0
  168. package/build/modules/library-mutations.js.map +1 -0
  169. package/build/modules/library.d.ts +21 -0
  170. package/build/modules/library.d.ts.map +1 -0
  171. package/build/modules/library.js +38 -0
  172. package/build/modules/library.js.map +1 -0
  173. package/build/modules/player.d.ts +53 -0
  174. package/build/modules/player.d.ts.map +1 -0
  175. package/build/modules/player.js +68 -0
  176. package/build/modules/player.js.map +1 -0
  177. package/build/modules/ratings.d.ts +10 -0
  178. package/build/modules/ratings.d.ts.map +1 -0
  179. package/build/modules/ratings.js +37 -0
  180. package/build/modules/ratings.js.map +1 -0
  181. package/build/modules/recommendations.d.ts +7 -0
  182. package/build/modules/recommendations.d.ts.map +1 -0
  183. package/build/modules/recommendations.js +15 -0
  184. package/build/modules/recommendations.js.map +1 -0
  185. package/build/native-module.d.ts +5 -0
  186. package/build/native-module.d.ts.map +1 -0
  187. package/build/native-module.js +5 -0
  188. package/build/native-module.js.map +1 -0
  189. package/build/native-module.web.d.ts +11 -0
  190. package/build/native-module.web.d.ts.map +1 -0
  191. package/build/native-module.web.js +11 -0
  192. package/build/native-module.web.js.map +1 -0
  193. package/build/rest/apple-music-rest-stack.d.ts +21 -0
  194. package/build/rest/apple-music-rest-stack.d.ts.map +1 -0
  195. package/build/rest/apple-music-rest-stack.js +21 -0
  196. package/build/rest/apple-music-rest-stack.js.map +1 -0
  197. package/build/rest/apple-music-rest-transport.d.ts +8 -0
  198. package/build/rest/apple-music-rest-transport.d.ts.map +1 -0
  199. package/build/rest/apple-music-rest-transport.js +2 -0
  200. package/build/rest/apple-music-rest-transport.js.map +1 -0
  201. package/build/rest/catalog-rest-client.d.ts +38 -0
  202. package/build/rest/catalog-rest-client.d.ts.map +1 -0
  203. package/build/rest/catalog-rest-client.js +189 -0
  204. package/build/rest/catalog-rest-client.js.map +1 -0
  205. package/build/rest/history-rest-client.d.ts +37 -0
  206. package/build/rest/history-rest-client.d.ts.map +1 -0
  207. package/build/rest/history-rest-client.js +30 -0
  208. package/build/rest/history-rest-client.js.map +1 -0
  209. package/build/rest/library-ids.d.ts +2 -0
  210. package/build/rest/library-ids.d.ts.map +1 -0
  211. package/build/rest/library-ids.js +2 -0
  212. package/build/rest/library-ids.js.map +1 -0
  213. package/build/rest/library-mutations-rest-client.d.ts +22 -0
  214. package/build/rest/library-mutations-rest-client.d.ts.map +1 -0
  215. package/build/rest/library-mutations-rest-client.js +37 -0
  216. package/build/rest/library-mutations-rest-client.js.map +1 -0
  217. package/build/rest/library-rest-client.d.ts +59 -0
  218. package/build/rest/library-rest-client.d.ts.map +1 -0
  219. package/build/rest/library-rest-client.js +141 -0
  220. package/build/rest/library-rest-client.js.map +1 -0
  221. package/build/rest/ratings-rest-client.d.ts +18 -0
  222. package/build/rest/ratings-rest-client.d.ts.map +1 -0
  223. package/build/rest/ratings-rest-client.js +37 -0
  224. package/build/rest/ratings-rest-client.js.map +1 -0
  225. package/build/rest/recommendations-rest-client.d.ts +32 -0
  226. package/build/rest/recommendations-rest-client.d.ts.map +1 -0
  227. package/build/rest/recommendations-rest-client.js +26 -0
  228. package/build/rest/recommendations-rest-client.js.map +1 -0
  229. package/build/rest/resource-ids-query.d.ts +2 -0
  230. package/build/rest/resource-ids-query.d.ts.map +1 -0
  231. package/build/rest/resource-ids-query.js +10 -0
  232. package/build/rest/resource-ids-query.js.map +1 -0
  233. package/build/rest/rest-json.d.ts +11 -0
  234. package/build/rest/rest-json.d.ts.map +1 -0
  235. package/build/rest/rest-json.js +29 -0
  236. package/build/rest/rest-json.js.map +1 -0
  237. package/build/rest/storefront-rest-client.d.ts +14 -0
  238. package/build/rest/storefront-rest-client.d.ts.map +1 -0
  239. package/build/rest/storefront-rest-client.js +36 -0
  240. package/build/rest/storefront-rest-client.js.map +1 -0
  241. package/build/types/album.d.ts +8 -0
  242. package/build/types/album.d.ts.map +1 -0
  243. package/build/types/album.js +2 -0
  244. package/build/types/album.js.map +1 -0
  245. package/build/types/albums-response.d.ts +5 -0
  246. package/build/types/albums-response.d.ts.map +1 -0
  247. package/build/types/albums-response.js +2 -0
  248. package/build/types/albums-response.js.map +1 -0
  249. package/build/types/android-authorize-options.d.ts +18 -0
  250. package/build/types/android-authorize-options.d.ts.map +1 -0
  251. package/build/types/android-authorize-options.js +2 -0
  252. package/build/types/android-authorize-options.js.map +1 -0
  253. package/build/types/artist.d.ts +9 -0
  254. package/build/types/artist.d.ts.map +1 -0
  255. package/build/types/artist.js +2 -0
  256. package/build/types/artist.js.map +1 -0
  257. package/build/types/auth-status.d.ts +19 -0
  258. package/build/types/auth-status.d.ts.map +1 -0
  259. package/build/types/auth-status.js +18 -0
  260. package/build/types/auth-status.js.map +1 -0
  261. package/build/types/authorize-result.d.ts +8 -0
  262. package/build/types/authorize-result.d.ts.map +1 -0
  263. package/build/types/authorize-result.js +2 -0
  264. package/build/types/authorize-result.js.map +1 -0
  265. package/build/types/catalog-album-tracks.d.ts +5 -0
  266. package/build/types/catalog-album-tracks.d.ts.map +1 -0
  267. package/build/types/catalog-album-tracks.js +2 -0
  268. package/build/types/catalog-album-tracks.js.map +1 -0
  269. package/build/types/catalog-charts.d.ts +25 -0
  270. package/build/types/catalog-charts.d.ts.map +1 -0
  271. package/build/types/catalog-charts.js +7 -0
  272. package/build/types/catalog-charts.js.map +1 -0
  273. package/build/types/catalog-resource-type.d.ts +11 -0
  274. package/build/types/catalog-resource-type.d.ts.map +1 -0
  275. package/build/types/catalog-resource-type.js +10 -0
  276. package/build/types/catalog-resource-type.js.map +1 -0
  277. package/build/types/catalog-search.d.ts +24 -0
  278. package/build/types/catalog-search.d.ts.map +1 -0
  279. package/build/types/catalog-search.js +9 -0
  280. package/build/types/catalog-search.js.map +1 -0
  281. package/build/types/check-subscription.d.ts +31 -0
  282. package/build/types/check-subscription.d.ts.map +1 -0
  283. package/build/types/check-subscription.js +5 -0
  284. package/build/types/check-subscription.js.map +1 -0
  285. package/build/types/library-music-videos.d.ts +5 -0
  286. package/build/types/library-music-videos.d.ts.map +1 -0
  287. package/build/types/library-music-videos.js +2 -0
  288. package/build/types/library-music-videos.js.map +1 -0
  289. package/build/types/library-mutations.d.ts +21 -0
  290. package/build/types/library-mutations.d.ts.map +1 -0
  291. package/build/types/library-mutations.js +8 -0
  292. package/build/types/library-mutations.js.map +1 -0
  293. package/build/types/library-search.d.ts +22 -0
  294. package/build/types/library-search.d.ts.map +1 -0
  295. package/build/types/library-search.js +9 -0
  296. package/build/types/library-search.js.map +1 -0
  297. package/build/types/music-item.d.ts +8 -0
  298. package/build/types/music-item.d.ts.map +1 -0
  299. package/build/types/music-item.js +7 -0
  300. package/build/types/music-item.js.map +1 -0
  301. package/build/types/music-video.d.ts +8 -0
  302. package/build/types/music-video.d.ts.map +1 -0
  303. package/build/types/music-video.js +2 -0
  304. package/build/types/music-video.js.map +1 -0
  305. package/build/types/pagination.d.ts +11 -0
  306. package/build/types/pagination.d.ts.map +1 -0
  307. package/build/types/pagination.js +2 -0
  308. package/build/types/pagination.js.map +1 -0
  309. package/build/types/playback-state.d.ts +9 -0
  310. package/build/types/playback-state.d.ts.map +1 -0
  311. package/build/types/playback-state.js +2 -0
  312. package/build/types/playback-state.js.map +1 -0
  313. package/build/types/playback-status.d.ts +10 -0
  314. package/build/types/playback-status.d.ts.map +1 -0
  315. package/build/types/playback-status.js +9 -0
  316. package/build/types/playback-status.js.map +1 -0
  317. package/build/types/playlist.d.ts +15 -0
  318. package/build/types/playlist.d.ts.map +1 -0
  319. package/build/types/playlist.js +2 -0
  320. package/build/types/playlist.js.map +1 -0
  321. package/build/types/rating.d.ts +34 -0
  322. package/build/types/rating.d.ts.map +1 -0
  323. package/build/types/rating.js +26 -0
  324. package/build/types/rating.js.map +1 -0
  325. package/build/types/recent-resource.d.ts +11 -0
  326. package/build/types/recent-resource.d.ts.map +1 -0
  327. package/build/types/recent-resource.js +2 -0
  328. package/build/types/recent-resource.js.map +1 -0
  329. package/build/types/recommendation.d.ts +37 -0
  330. package/build/types/recommendation.d.ts.map +1 -0
  331. package/build/types/recommendation.js +2 -0
  332. package/build/types/recommendation.js.map +1 -0
  333. package/build/types/song.d.ts +8 -0
  334. package/build/types/song.d.ts.map +1 -0
  335. package/build/types/song.js +2 -0
  336. package/build/types/song.js.map +1 -0
  337. package/build/types/station.d.ts +9 -0
  338. package/build/types/station.d.ts.map +1 -0
  339. package/build/types/station.js +2 -0
  340. package/build/types/station.js.map +1 -0
  341. package/build/types/storefront.d.ts +4 -0
  342. package/build/types/storefront.d.ts.map +1 -0
  343. package/build/types/storefront.js +2 -0
  344. package/build/types/storefront.js.map +1 -0
  345. package/build/types/tracks-from-library.d.ts +11 -0
  346. package/build/types/tracks-from-library.d.ts.map +1 -0
  347. package/build/types/tracks-from-library.js +2 -0
  348. package/build/types/tracks-from-library.js.map +1 -0
  349. package/build/utils/apple-music-error.d.ts +10 -0
  350. package/build/utils/apple-music-error.d.ts.map +1 -0
  351. package/build/utils/apple-music-error.js +13 -0
  352. package/build/utils/apple-music-error.js.map +1 -0
  353. package/build/utils/get-error-message.d.ts +2 -0
  354. package/build/utils/get-error-message.d.ts.map +1 -0
  355. package/build/utils/get-error-message.js +21 -0
  356. package/build/utils/get-error-message.js.map +1 -0
  357. package/build/utils/is-library-item.d.ts +2 -0
  358. package/build/utils/is-library-item.d.ts.map +1 -0
  359. package/build/utils/is-library-item.js +4 -0
  360. package/build/utils/is-library-item.js.map +1 -0
  361. package/build/utils/normalize-resource-ids.d.ts +4 -0
  362. package/build/utils/normalize-resource-ids.d.ts.map +1 -0
  363. package/build/utils/normalize-resource-ids.js +12 -0
  364. package/build/utils/normalize-resource-ids.js.map +1 -0
  365. package/build/web/MusicKitLoader.d.ts +11 -0
  366. package/build/web/MusicKitLoader.d.ts.map +1 -0
  367. package/build/web/MusicKitLoader.js +135 -0
  368. package/build/web/MusicKitLoader.js.map +1 -0
  369. package/build/web/WebAppleMusicApiClient.d.ts +151 -0
  370. package/build/web/WebAppleMusicApiClient.d.ts.map +1 -0
  371. package/build/web/WebAppleMusicApiClient.js +139 -0
  372. package/build/web/WebAppleMusicApiClient.js.map +1 -0
  373. package/build/web/WebAppleMusicRestTransport.d.ts +9 -0
  374. package/build/web/WebAppleMusicRestTransport.d.ts.map +1 -0
  375. package/build/web/WebAppleMusicRestTransport.js +31 -0
  376. package/build/web/WebAppleMusicRestTransport.js.map +1 -0
  377. package/build/web/WebPlaybackController.d.ts +12 -0
  378. package/build/web/WebPlaybackController.d.ts.map +1 -0
  379. package/build/web/WebPlaybackController.js +90 -0
  380. package/build/web/WebPlaybackController.js.map +1 -0
  381. package/build/web/WebPlaybackObserver.d.ts +22 -0
  382. package/build/web/WebPlaybackObserver.d.ts.map +1 -0
  383. package/build/web/WebPlaybackObserver.js +106 -0
  384. package/build/web/WebPlaybackObserver.js.map +1 -0
  385. package/build/web/WebQueueService.d.ts +10 -0
  386. package/build/web/WebQueueService.d.ts.map +1 -0
  387. package/build/web/WebQueueService.js +53 -0
  388. package/build/web/WebQueueService.js.map +1 -0
  389. package/build/web/WebSubscriptionService.d.ts +7 -0
  390. package/build/web/WebSubscriptionService.d.ts.map +1 -0
  391. package/build/web/WebSubscriptionService.js +24 -0
  392. package/build/web/WebSubscriptionService.js.map +1 -0
  393. package/build/web/apple-music-errors.d.ts +11 -0
  394. package/build/web/apple-music-errors.d.ts.map +1 -0
  395. package/build/web/apple-music-errors.js +31 -0
  396. package/build/web/apple-music-errors.js.map +1 -0
  397. package/build/web/extract-music-user-token.d.ts +4 -0
  398. package/build/web/extract-music-user-token.d.ts.map +1 -0
  399. package/build/web/extract-music-user-token.js +15 -0
  400. package/build/web/extract-music-user-token.js.map +1 -0
  401. package/build/web/map-auth-status.d.ts +17 -0
  402. package/build/web/map-auth-status.d.ts.map +1 -0
  403. package/build/web/map-auth-status.js +85 -0
  404. package/build/web/map-auth-status.js.map +1 -0
  405. package/build/web/music-kit-api.d.ts +18 -0
  406. package/build/web/music-kit-api.d.ts.map +1 -0
  407. package/build/web/music-kit-api.js +120 -0
  408. package/build/web/music-kit-api.js.map +1 -0
  409. package/build/web/musickit-types.d.ts +70 -0
  410. package/build/web/musickit-types.d.ts.map +1 -0
  411. package/build/web/musickit-types.js +3 -0
  412. package/build/web/musickit-types.js.map +1 -0
  413. package/build/web/pagination.d.ts +2 -0
  414. package/build/web/pagination.d.ts.map +1 -0
  415. package/build/web/pagination.js +2 -0
  416. package/build/web/pagination.js.map +1 -0
  417. package/expo-module.config.json +19 -0
  418. package/ios/AppleMusicBridgeError.swift +60 -0
  419. package/ios/AppleMusicErrorCodes.swift +11 -0
  420. package/ios/AppleMusicRestClient.swift +213 -0
  421. package/ios/AuthenticatedSession.swift +64 -0
  422. package/ios/BridgePagination.swift +17 -0
  423. package/ios/BridgeResponses.swift +82 -0
  424. package/ios/CatalogSearchStore.swift +13 -0
  425. package/ios/CatalogSearchStoreFactory.swift +31 -0
  426. package/ios/CatalogService.swift +307 -0
  427. package/ios/ExpoAppleMusic.podspec +29 -0
  428. package/ios/ExpoAppleMusicModule.swift +505 -0
  429. package/ios/HistoryService.swift +53 -0
  430. package/ios/LibraryMutationsService.swift +63 -0
  431. package/ios/LibraryService.swift +313 -0
  432. package/ios/MusicItemMapper.swift +171 -0
  433. package/ios/MusicKitAuthStorage.swift +38 -0
  434. package/ios/MusicKitCatalogSearchStore.swift +62 -0
  435. package/ios/PlaybackController.swift +201 -0
  436. package/ios/PlaybackObserver.swift +225 -0
  437. package/ios/QueueService.swift +166 -0
  438. package/ios/RatingsService.swift +66 -0
  439. package/ios/RecommendationsService.swift +34 -0
  440. package/ios/RestCatalogSearchStore.swift +62 -0
  441. package/ios/RestJsonMapper.swift +268 -0
  442. package/ios/StorefrontService.swift +55 -0
  443. package/ios/SubscriptionService.swift +119 -0
  444. package/ios/bridge/ExpoBridgeCatalog.swift +98 -0
  445. package/ios/bridge/ExpoBridgeHistory.swift +71 -0
  446. package/ios/bridge/ExpoBridgeLibrary.swift +93 -0
  447. package/ios/bridge/ExpoBridgeRecommendations.swift +28 -0
  448. package/package.json +89 -0
  449. package/plugin/build/index.d.ts +5 -0
  450. package/plugin/build/index.js +10 -0
  451. package/plugin/build/with-expo-apple-music.d.ts +10 -0
  452. package/plugin/build/with-expo-apple-music.js +50 -0
@@ -0,0 +1,505 @@
1
+ import ExpoModulesCore
2
+
3
+ @available(iOS 16.0, *)
4
+ public class ExpoAppleMusicModule: Module {
5
+ private let playbackController = PlaybackController.shared
6
+ private let subscriptionService = SubscriptionService()
7
+ private let catalogService = CatalogService()
8
+ private lazy var queueService = QueueService(
9
+ playbackController: playbackController,
10
+ catalogService: catalogService
11
+ )
12
+ private lazy var libraryService = LibraryService()
13
+ private lazy var historyService = HistoryService()
14
+ private lazy var ratingsService = RatingsService()
15
+ private lazy var libraryMutationsService = LibraryMutationsService()
16
+ private lazy var recommendationsService = RecommendationsService()
17
+
18
+ private var playbackObserver: PlaybackObserver?
19
+
20
+ public func definition() -> ModuleDefinition {
21
+ Name("ExpoAppleMusic")
22
+
23
+ Events(
24
+ "onPlaybackStateChange",
25
+ "onCurrentSongChange",
26
+ "onPlaybackTimeUpdate",
27
+ "onPlaybackError"
28
+ )
29
+
30
+ OnStartObserving {
31
+ let observer = PlaybackObserver(playbackController: self.playbackController)
32
+ observer.delegate = self
33
+ observer.startObserving()
34
+ self.playbackObserver = observer
35
+ }
36
+
37
+ OnStopObserving {
38
+ self.playbackObserver?.stopObserving()
39
+ self.playbackObserver = nil
40
+ }
41
+
42
+ // MARK: - Auth
43
+
44
+ AsyncFunction("setDeveloperToken") { (token: String) in
45
+ MusicKitAuthStorage.saveDeveloperToken(token)
46
+ }
47
+
48
+ AsyncFunction("authorization") { (developerToken: String?, _ startScreenMessage: String?, _ hideStartScreen: Bool?) -> [String: Any?] in
49
+ if let token = developerToken, !token.isEmpty {
50
+ MusicKitAuthStorage.saveDeveloperToken(token)
51
+ }
52
+ let status = await self.subscriptionService.requestAuthorization()
53
+ var musicUserToken: String? = nil
54
+ if status == .authorized, let token = developerToken, !token.isEmpty {
55
+ musicUserToken = await self.subscriptionService.fetchMusicUserToken(developerToken: token)
56
+ }
57
+ return ["status": status.rawValue, "musicUserToken": musicUserToken]
58
+ }
59
+
60
+ AsyncFunction("checkSubscription") { (musicUserToken: String) -> [String: Any] in
61
+ do {
62
+ let details = try await self.subscriptionService.checkSubscription()
63
+ return details.toDictionary()
64
+ } catch {
65
+ if let subError = SubscriptionService.wrapSubscriptionError(error) {
66
+ throw Exception(
67
+ name: subError.code,
68
+ description: subError.message,
69
+ code: subError.code
70
+ )
71
+ }
72
+ throw AppleMusicBridgeError.exception(from: error)
73
+ }
74
+ }
75
+
76
+ AsyncFunction("getStorefront") { (musicUserToken: String) -> [String: Any] in
77
+ try await AppleMusicBridgeError.rethrow {
78
+ let id = try await StorefrontService.getStorefrontId(musicUserToken: musicUserToken)
79
+ return BridgeResponses.storefront(id: id)
80
+ }
81
+ }
82
+
83
+ // MARK: - Catalog
84
+
85
+ AsyncFunction("catalogSearch") { (term: String, types: [String], options: [String: Any]) -> [String: Any] in
86
+ try await ExpoBridgeCatalog.catalogSearch(
87
+ service: self.catalogService,
88
+ term: term,
89
+ types: types,
90
+ options: options as NSDictionary
91
+ )
92
+ }
93
+
94
+ AsyncFunction("getCatalogSong") { (id: String) -> [String: Any] in
95
+ try await AppleMusicBridgeError.rethrow {
96
+ try await self.catalogService.getSong(id: id)
97
+ }
98
+ }
99
+
100
+ AsyncFunction("getCatalogAlbum") { (id: String) -> [String: Any] in
101
+ try await AppleMusicBridgeError.rethrow {
102
+ try await self.catalogService.getAlbum(id: id)
103
+ }
104
+ }
105
+
106
+ AsyncFunction("getCatalogArtist") { (id: String) -> [String: Any] in
107
+ try await AppleMusicBridgeError.rethrow {
108
+ try await self.catalogService.getArtist(id: id)
109
+ }
110
+ }
111
+
112
+ AsyncFunction("getCatalogPlaylist") { (id: String) -> [String: Any] in
113
+ try await AppleMusicBridgeError.rethrow {
114
+ try await self.catalogService.getPlaylist(id: id)
115
+ }
116
+ }
117
+
118
+ AsyncFunction("getCatalogStation") { (id: String) -> [String: Any] in
119
+ try await AppleMusicBridgeError.rethrow {
120
+ try await self.catalogService.getStation(id: id)
121
+ }
122
+ }
123
+
124
+ AsyncFunction("getCatalogMusicVideo") { (id: String) -> [String: Any] in
125
+ try await AppleMusicBridgeError.rethrow {
126
+ try await self.catalogService.getMusicVideo(id: id)
127
+ }
128
+ }
129
+
130
+ AsyncFunction("getCatalogAlbumTracks") { (albumId: String, options: [String: Any]) -> [String: Any] in
131
+ try await ExpoBridgeCatalog.getCatalogAlbumTracks(
132
+ service: self.catalogService,
133
+ albumId: albumId,
134
+ options: options as NSDictionary
135
+ )
136
+ }
137
+
138
+ AsyncFunction("getCatalogArtistAlbums") { (artistId: String, options: [String: Any]) -> [String: Any] in
139
+ try await ExpoBridgeCatalog.getCatalogArtistAlbums(
140
+ service: self.catalogService,
141
+ artistId: artistId,
142
+ options: options as NSDictionary
143
+ )
144
+ }
145
+
146
+ AsyncFunction("getCatalogPlaylistTracks") { (playlistId: String, options: [String: Any]) -> [String: Any] in
147
+ try await ExpoBridgeCatalog.getCatalogPlaylistTracks(
148
+ service: self.catalogService,
149
+ playlistId: playlistId,
150
+ options: options as NSDictionary
151
+ )
152
+ }
153
+
154
+ AsyncFunction("getCatalogCharts") { (types: [String], options: [String: Any]) -> [String: Any] in
155
+ try await ExpoBridgeCatalog.getCatalogCharts(
156
+ service: self.catalogService,
157
+ types: types,
158
+ options: options
159
+ )
160
+ }
161
+
162
+ AsyncFunction("getCatalogResources") { (type: String, ids: [String]) -> [String: Any] in
163
+ try await ExpoBridgeCatalog.getCatalogResources(
164
+ service: self.catalogService,
165
+ type: type,
166
+ ids: ids
167
+ )
168
+ }
169
+
170
+ // MARK: - Library
171
+
172
+ AsyncFunction("getUserPlaylists") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
173
+ try await ExpoBridgeLibrary.getUserPlaylists(
174
+ service: self.libraryService,
175
+ musicUserToken: musicUserToken,
176
+ options: options as NSDictionary
177
+ )
178
+ }
179
+
180
+ AsyncFunction("getLibrarySongs") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
181
+ try await ExpoBridgeLibrary.getLibrarySongs(
182
+ service: self.libraryService,
183
+ musicUserToken: musicUserToken,
184
+ options: options as NSDictionary
185
+ )
186
+ }
187
+
188
+ AsyncFunction("getPlaylistSongs") { (musicUserToken: String, playlistId: String, options: [String: Any]) -> [String: Any] in
189
+ try await ExpoBridgeLibrary.getPlaylistSongs(
190
+ service: self.libraryService,
191
+ musicUserToken: musicUserToken,
192
+ playlistId: playlistId
193
+ )
194
+ }
195
+
196
+ AsyncFunction("getLibraryArtists") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
197
+ try await ExpoBridgeLibrary.getLibraryArtists(
198
+ service: self.libraryService,
199
+ musicUserToken: musicUserToken,
200
+ options: options as NSDictionary
201
+ )
202
+ }
203
+
204
+ AsyncFunction("getLibraryAlbums") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
205
+ try await ExpoBridgeLibrary.getLibraryAlbums(
206
+ service: self.libraryService,
207
+ musicUserToken: musicUserToken,
208
+ options: options as NSDictionary
209
+ )
210
+ }
211
+
212
+ AsyncFunction("getLibraryMusicVideos") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
213
+ try await ExpoBridgeLibrary.getLibraryMusicVideos(
214
+ service: self.libraryService,
215
+ musicUserToken: musicUserToken,
216
+ options: options as NSDictionary
217
+ )
218
+ }
219
+
220
+ AsyncFunction("librarySearch") { (musicUserToken: String, term: String, types: [String], options: [String: Any]) -> [String: Any] in
221
+ try await ExpoBridgeLibrary.librarySearch(
222
+ service: self.libraryService,
223
+ musicUserToken: musicUserToken,
224
+ term: term,
225
+ types: types,
226
+ options: options as NSDictionary
227
+ )
228
+ }
229
+
230
+ // MARK: - History
231
+
232
+ AsyncFunction("getRecentlyPlayedResources") { (musicUserToken: String) -> [String: Any] in
233
+ try await ExpoBridgeHistory.getRecentlyPlayedResources(
234
+ service: self.historyService,
235
+ musicUserToken: musicUserToken)
236
+ }
237
+
238
+ AsyncFunction("getRecentlyPlayedTracks") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
239
+ try await ExpoBridgeHistory.getRecentlyPlayedTracks(
240
+ service: self.historyService,
241
+ musicUserToken: musicUserToken,
242
+ options: options as NSDictionary
243
+ )
244
+ }
245
+
246
+ AsyncFunction("getHeavyRotation") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
247
+ try await ExpoBridgeHistory.getHeavyRotation(
248
+ service: self.historyService,
249
+ musicUserToken: musicUserToken,
250
+ options: options as NSDictionary
251
+ )
252
+ }
253
+
254
+ AsyncFunction("getRecentlyPlayedStations") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
255
+ try await ExpoBridgeHistory.getRecentlyPlayedStations(
256
+ service: self.historyService,
257
+ musicUserToken: musicUserToken,
258
+ options: options as NSDictionary
259
+ )
260
+ }
261
+
262
+ AsyncFunction("getRecentlyAdded") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
263
+ try await ExpoBridgeHistory.getRecentlyAdded(
264
+ service: self.historyService,
265
+ musicUserToken: musicUserToken,
266
+ options: options as NSDictionary
267
+ )
268
+ }
269
+
270
+ // MARK: - Player
271
+
272
+ AsyncFunction("setPlaybackQueue") { (itemId: String, type: String) -> String in
273
+ try await AppleMusicBridgeError.rethrow {
274
+ try await self.queueService.setQueue(itemId: itemId, type: type)
275
+ return "Track(s) added to queue"
276
+ }
277
+ }
278
+
279
+ AsyncFunction("configurePlayer") { (mixWithOthers: Bool) -> [String: Any] in
280
+ try self.playbackController.configureAudioSession(mixWithOthers: mixWithOthers)
281
+ return BridgeResponses.configurePlayer(mixWithOthers: mixWithOthers)
282
+ }
283
+
284
+ Function("play") {
285
+ Task {
286
+ do {
287
+ try await self.playbackController.play()
288
+ } catch {
289
+ self.emitPlaybackError(error, operation: "play")
290
+ }
291
+ }
292
+ }
293
+
294
+ Function("pause") {
295
+ self.playbackController.pause()
296
+ }
297
+
298
+ Function("skipToNextEntry") {
299
+ Task {
300
+ do {
301
+ try await self.playbackController.skipToNext()
302
+ } catch {
303
+ self.emitPlaybackError(error, operation: "skipToNext")
304
+ }
305
+ }
306
+ }
307
+
308
+ Function("skipToPreviousEntry") {
309
+ Task {
310
+ do {
311
+ try await self.playbackController.skipToPrevious()
312
+ } catch {
313
+ self.emitPlaybackError(error, operation: "skipToPrevious")
314
+ }
315
+ }
316
+ }
317
+
318
+ Function("restartCurrentEntry") {
319
+ Task { @MainActor in
320
+ self.playbackController.restartCurrentEntry()
321
+ self.playbackTimeDidUpdate(0)
322
+ }
323
+ }
324
+
325
+ Function("seekToTime") { (time: Double) in
326
+ Task { @MainActor in
327
+ self.playbackController.seek(to: time)
328
+ self.playbackTimeDidUpdate(time)
329
+ }
330
+ }
331
+
332
+ Function("togglePlayerState") {
333
+ Task {
334
+ do {
335
+ try await self.playbackController.togglePlayback()
336
+ } catch {
337
+ self.emitPlaybackError(error, operation: "togglePlayback")
338
+ }
339
+ }
340
+ }
341
+
342
+ AsyncFunction("getCurrentState") { () -> [String: Any] in
343
+ let state = self.playbackController.state
344
+ let songInfo = await self.playbackController.fetchCurrentSongInfo()
345
+
346
+ var result: [String: Any] = [
347
+ "playbackRate": state.playbackRate,
348
+ "playbackStatus": MusicItemMapper.describePlaybackStatus(state.playbackStatus),
349
+ "playbackTime": self.playbackController.playbackTime,
350
+ ]
351
+ if let songInfo = songInfo {
352
+ result["currentSong"] = songInfo
353
+ }
354
+ return result
355
+ }
356
+
357
+ AsyncFunction("playLibrarySong") { (musicUserToken: String, songId: String) -> String in
358
+ try await AppleMusicBridgeError.rethrow {
359
+ try await self.queueService.playLibrarySong(musicUserToken: musicUserToken, songId: songId)
360
+ return "Library song added to queue"
361
+ }
362
+ }
363
+
364
+ AsyncFunction("playLibraryPlaylist") { (musicUserToken: String, playlistId: String, startingAt: Int) -> String in
365
+ try await AppleMusicBridgeError.rethrow {
366
+ try await self.queueService.playLibraryPlaylist(
367
+ musicUserToken: musicUserToken,
368
+ playlistId: playlistId,
369
+ startingAt: startingAt
370
+ )
371
+ return "Library playlist added to queue"
372
+ }
373
+ }
374
+
375
+ // MARK: - Ratings
376
+
377
+ AsyncFunction("getRating") { (musicUserToken: String, resourceType: String, id: String) -> [String: Any]? in
378
+ try await AppleMusicBridgeError.rethrow {
379
+ try await self.ratingsService.getRating(
380
+ musicUserToken: musicUserToken, resourceType: resourceType, id: id)
381
+ }
382
+ }
383
+
384
+ AsyncFunction("setRating") { (musicUserToken: String, resourceType: String, id: String, value: Int) -> [String: Any] in
385
+ try await AppleMusicBridgeError.rethrow {
386
+ try await self.ratingsService.setRating(
387
+ musicUserToken: musicUserToken, resourceType: resourceType, id: id, value: value)
388
+ }
389
+ }
390
+
391
+ AsyncFunction("clearRating") { (musicUserToken: String, resourceType: String, id: String) -> Void in
392
+ try await AppleMusicBridgeError.rethrow {
393
+ try await self.ratingsService.clearRating(
394
+ musicUserToken: musicUserToken, resourceType: resourceType, id: id)
395
+ }
396
+ }
397
+
398
+ AsyncFunction("addToFavorites") { (musicUserToken: String, resourceIds: [String: [String]]) -> Void in
399
+ try await AppleMusicBridgeError.rethrow {
400
+ try await self.ratingsService.addToFavorites(
401
+ musicUserToken: musicUserToken, resourceIds: resourceIds)
402
+ }
403
+ }
404
+
405
+ AsyncFunction("removeFromFavorites") { (musicUserToken: String, resourceIds: [String: [String]]) -> Void in
406
+ try await AppleMusicBridgeError.rethrow {
407
+ try await self.ratingsService.removeFromFavorites(
408
+ musicUserToken: musicUserToken, resourceIds: resourceIds)
409
+ }
410
+ }
411
+
412
+ // MARK: - Library mutations
413
+
414
+ AsyncFunction("addToLibrary") { (musicUserToken: String, resourceIds: [String: [String]]) -> Void in
415
+ try await AppleMusicBridgeError.rethrow {
416
+ try await self.libraryMutationsService.addToLibrary(
417
+ musicUserToken: musicUserToken, resourceIds: resourceIds)
418
+ }
419
+ }
420
+
421
+ AsyncFunction("createLibraryPlaylist") { (musicUserToken: String, options: [String: Any]) -> [String: Any] in
422
+ try await AppleMusicBridgeError.rethrow {
423
+ let name = options["name"] as? String ?? ""
424
+ let description = options["description"] as? String
425
+ let isPublic = options["isPublic"] as? Bool ?? false
426
+ let tracks = options["tracks"] as? [[String: String]]
427
+ return try await self.libraryMutationsService.createPlaylist(
428
+ musicUserToken: musicUserToken,
429
+ name: name,
430
+ description: description,
431
+ isPublic: isPublic,
432
+ tracks: tracks
433
+ )
434
+ }
435
+ }
436
+
437
+ AsyncFunction("addTracksToLibraryPlaylist") { (musicUserToken: String, playlistId: String, tracks: [[String: String]]) -> Void in
438
+ try await AppleMusicBridgeError.rethrow {
439
+ try await self.libraryMutationsService.addTracksToPlaylist(
440
+ musicUserToken: musicUserToken,
441
+ playlistId: playlistId,
442
+ tracks: tracks
443
+ )
444
+ }
445
+ }
446
+
447
+ // MARK: - Recommendations
448
+
449
+ AsyncFunction("getRecommendations") { (musicUserToken: String, ids: [String]?) -> [String: Any] in
450
+ try await ExpoBridgeRecommendations.getRecommendations(
451
+ service: self.recommendationsService,
452
+ musicUserToken: musicUserToken,
453
+ ids: ids
454
+ )
455
+ }
456
+
457
+ AsyncFunction("getReplay") { (musicUserToken: String, year: Int?) -> [String: Any] in
458
+ try await ExpoBridgeRecommendations.getReplay(
459
+ service: self.recommendationsService,
460
+ musicUserToken: musicUserToken,
461
+ year: year
462
+ )
463
+ }
464
+ }
465
+
466
+ private func emitPlaybackError(_ error: Error, operation: String) {
467
+ let nsError = error as NSError
468
+ sendEvent(
469
+ "onPlaybackError",
470
+ [
471
+ "message": error.localizedDescription,
472
+ "code": nsError.code,
473
+ "domain": nsError.domain,
474
+ "operation": operation,
475
+ ]
476
+ )
477
+ }
478
+ }
479
+
480
+ @available(iOS 16.0, *)
481
+ extension ExpoAppleMusicModule: PlaybackObserverDelegate {
482
+ @MainActor
483
+ func playbackStateDidChange(_ state: PlaybackObserver.PlaybackInfo) {
484
+ var body: [String: Any] = [
485
+ "playbackRate": state.playbackRate,
486
+ "playbackStatus": state.playbackStatus,
487
+ "playbackTime": state.playbackTime,
488
+ ]
489
+ if let song = state.currentSong {
490
+ body["currentSong"] = song
491
+ }
492
+ sendEvent("onPlaybackStateChange", body)
493
+ }
494
+
495
+ @MainActor
496
+ func currentSongDidChange(_ songInfo: [String: Any]?) {
497
+ guard let songInfo = songInfo else { return }
498
+ sendEvent("onCurrentSongChange", ["currentSong": songInfo])
499
+ }
500
+
501
+ @MainActor
502
+ func playbackTimeDidUpdate(_ time: TimeInterval) {
503
+ sendEvent("onPlaybackTimeUpdate", ["playbackTime": time])
504
+ }
505
+ }
@@ -0,0 +1,53 @@
1
+ // HistoryService.swift
2
+ // Listening history via Apple Music REST (app-supplied music user token).
3
+
4
+ import Foundation
5
+
6
+ @available(iOS 16.0, *)
7
+ final class HistoryService {
8
+
9
+ func getRecentlyPlayedResources(musicUserToken: String) async throws -> [[String: Any]] {
10
+ let data = try await AppleMusicRestClient.getDataArray(
11
+ path: "/v1/me/recent/played",
12
+ musicUserToken: musicUserToken,
13
+ query: ["limit": "10"]
14
+ )
15
+ return data.map(RestJsonMapper.mapRecentlyPlayed)
16
+ }
17
+
18
+ func getRecentlyPlayedTracks(musicUserToken: String, options: BridgePagination) async throws -> [[String: Any]] {
19
+ let data = try await AppleMusicRestClient.getDataArray(
20
+ path: "/v1/me/recent/played/tracks",
21
+ musicUserToken: musicUserToken,
22
+ query: ["limit": "\(options.limit)"]
23
+ )
24
+ return data.map(RestJsonMapper.mapSong)
25
+ }
26
+
27
+ func getHeavyRotation(musicUserToken: String, limit: Int) async throws -> [[String: Any]] {
28
+ let data = try await AppleMusicRestClient.getDataArray(
29
+ path: "/v1/me/history/heavy-rotation",
30
+ musicUserToken: musicUserToken,
31
+ query: ["limit": "\(limit)"]
32
+ )
33
+ return data.map(RestJsonMapper.mapRecentResource)
34
+ }
35
+
36
+ func getRecentlyPlayedStations(musicUserToken: String, limit: Int) async throws -> [[String: Any]] {
37
+ let data = try await AppleMusicRestClient.getDataArray(
38
+ path: "/v1/me/recent/radio-stations",
39
+ musicUserToken: musicUserToken,
40
+ query: ["limit": "\(limit)"]
41
+ )
42
+ return data.map(RestJsonMapper.mapStation)
43
+ }
44
+
45
+ func getRecentlyAdded(musicUserToken: String, limit: Int, offset: Int) async throws -> [[String: Any]] {
46
+ let data = try await AppleMusicRestClient.getDataArray(
47
+ path: "/v1/me/library/recently-added",
48
+ musicUserToken: musicUserToken,
49
+ query: ["limit": "\(limit)", "offset": "\(offset)"]
50
+ )
51
+ return data.map(RestJsonMapper.mapRecentResource)
52
+ }
53
+ }
@@ -0,0 +1,63 @@
1
+ // LibraryMutationsService.swift
2
+ // Library write operations via Apple Music REST API.
3
+
4
+ import Foundation
5
+
6
+ @available(iOS 16.0, *)
7
+ final class LibraryMutationsService {
8
+
9
+ func addToLibrary(musicUserToken: String, resourceIds: [String: [String]]) async throws {
10
+ _ = try await AppleMusicRestClient.request(
11
+ method: .post,
12
+ path: "/v1/me/library",
13
+ musicUserToken: musicUserToken,
14
+ query: RestJsonMapper.buildIdsQuery(resourceIds)
15
+ )
16
+ }
17
+
18
+ func createPlaylist(
19
+ musicUserToken: String,
20
+ name: String,
21
+ description: String?,
22
+ isPublic: Bool,
23
+ tracks: [[String: String]]?
24
+ ) async throws -> [String: Any] {
25
+ var attributes: [String: Any] = [
26
+ "name": name,
27
+ "isPublic": isPublic,
28
+ ]
29
+ if let description, !description.isEmpty {
30
+ attributes["description"] = ["standard": description]
31
+ }
32
+
33
+ var payload: [String: Any] = ["attributes": attributes]
34
+ if let tracks, !tracks.isEmpty {
35
+ payload["relationships"] = [
36
+ "tracks": [
37
+ "data": tracks.map { ["id": $0["id"] ?? "", "type": $0["type"] ?? ""] }
38
+ ]
39
+ ]
40
+ }
41
+
42
+ let json = try await AppleMusicRestClient.request(
43
+ method: .post,
44
+ path: "/v1/me/library/playlists",
45
+ musicUserToken: musicUserToken,
46
+ body: payload
47
+ )
48
+ guard let data = json["data"] as? [[String: Any]], let first = data.first else {
49
+ throw AppleMusicRestClient.RestError.invalidResponse
50
+ }
51
+ return RestJsonMapper.mapPlaylist(first)
52
+ }
53
+
54
+ func addTracksToPlaylist(musicUserToken: String, playlistId: String, tracks: [[String: String]]) async throws {
55
+ let data = tracks.map { ["id": $0["id"] ?? "", "type": $0["type"] ?? ""] }
56
+ _ = try await AppleMusicRestClient.request(
57
+ method: .post,
58
+ path: "/v1/me/library/playlists/\(playlistId)/tracks",
59
+ musicUserToken: musicUserToken,
60
+ body: ["data": data]
61
+ )
62
+ }
63
+ }