react-native-audio-browser 0.2.0-next.3 → 0.2.0-next.31

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 (989) hide show
  1. package/README.md +36 -22
  2. package/android/build.gradle +35 -12
  3. package/android/gradle.properties +2 -2
  4. package/android/src/main/AndroidManifest.xml +3 -1
  5. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +541 -278
  6. package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
  7. package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
  8. package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +52 -0
  9. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
  10. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +27 -9
  11. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +288 -0
  12. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
  13. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
  14. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  15. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -7
  16. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  17. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  18. package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
  19. package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
  20. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
  21. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
  22. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
  23. package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
  24. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
  25. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
  26. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
  27. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
  28. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
  29. package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
  30. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  31. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
  32. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
  33. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  34. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
  35. package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
  36. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
  37. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
  38. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
  39. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
  40. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +62 -40
  41. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  42. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +110 -0
  43. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
  44. package/android/src/main/res/values/strings.xml +25 -5
  45. package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +100 -0
  46. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
  47. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  48. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
  49. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
  50. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +40 -2
  51. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +301 -0
  52. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
  53. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +168 -0
  54. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
  55. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +160 -0
  56. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  57. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  58. package/android/src/test/java/com/audiobrowser/model/IcyTitleTest.kt +49 -0
  59. package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
  60. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
  61. package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
  62. package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
  63. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  64. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
  65. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  66. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
  67. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  68. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
  69. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
  70. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
  71. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
  72. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +55 -16
  73. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -0
  74. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  75. package/ios/AVPlayerEngine.swift +330 -0
  76. package/ios/AVPlayerEngineCallbacks.swift +15 -0
  77. package/ios/Browser/BrowseGate.swift +58 -0
  78. package/ios/Browser/BrowserConfig.swift +117 -78
  79. package/ios/Browser/BrowserManager.swift +209 -603
  80. package/ios/Browser/BrowserPathHelper.swift +50 -46
  81. package/ios/Browser/InertStyleDiagnostic.swift +296 -0
  82. package/ios/Browser/JsonModels.swift +264 -106
  83. package/ios/Browser/NavigationErrorState.swift +41 -0
  84. package/ios/Browser/PageResolver.swift +227 -0
  85. package/ios/Browser/RequestLayer.swift +90 -0
  86. package/ios/Browser/{BrowserManager+URLResolution.swift → RequestPipeline.swift} +193 -121
  87. package/ios/Browser/SearchDrillIn.swift +7 -7
  88. package/ios/Browser/SectionScope.swift +58 -24
  89. package/ios/Browser/StyleResolver.swift +58 -0
  90. package/ios/Browser/TabBarEntries.swift +9 -3
  91. package/ios/Browser/TrackIdTemplate.swift +37 -0
  92. package/ios/CarPlay/CPInterfaceController+Safe.swift +100 -43
  93. package/ios/CarPlay/CarPlayArtworkResolver.swift +36 -83
  94. package/ios/CarPlay/CarPlayController.swift +42 -1296
  95. package/ios/CarPlay/CarPlayImageLoader.swift +78 -97
  96. package/ios/CarPlay/CarPlayListItemFactory.swift +303 -92
  97. package/ios/CarPlay/CarPlayNowPlayingManager.swift +129 -136
  98. package/ios/CarPlay/CarPlaySession+Navigator.swift +114 -0
  99. package/ios/CarPlay/CarPlaySession+PageLoader.swift +92 -0
  100. package/ios/CarPlay/CarPlaySession+Refresher.swift +43 -0
  101. package/ios/CarPlay/CarPlaySession+TabBar.swift +193 -0
  102. package/ios/CarPlay/CarPlaySession.swift +209 -0
  103. package/ios/CarPlay/CarPlayTemplates.swift +63 -0
  104. package/ios/CarPlay/HybridAudioBrowser+IntentFavorite.swift +20 -0
  105. package/ios/{Player → CarPlay}/MediaIntentCriteria.swift +17 -9
  106. package/ios/CarPlay/MediaIntentPlayback.swift +74 -0
  107. package/ios/CarPlay/RNABMediaAddHandler.swift +8 -24
  108. package/ios/CarPlay/RNABMediaAffinityHandler.swift +7 -23
  109. package/ios/CarPlay/RNABMediaIntentHandler.swift +18 -19
  110. package/ios/CarPlay/SectionPresentation.swift +77 -0
  111. package/ios/CarPlay/TopTemplateReplacer.swift +96 -0
  112. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  113. package/ios/Extension/NavigationError+Conversion.swift +14 -4
  114. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  115. package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
  116. package/ios/Extension/Section+Copying.swift +29 -0
  117. package/ios/Extension/String+IcyCharset.swift +21 -0
  118. package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
  119. package/ios/Extension/Track+Copying.swift +8 -12
  120. package/ios/Extension/Track+Identity.swift +18 -0
  121. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  122. package/ios/Extension/UIImage+Adaptive.swift +12 -0
  123. package/ios/Extension/URLError+Connectivity.swift +15 -0
  124. package/ios/Http/HttpClient.swift +66 -142
  125. package/ios/HybridAudioBrowser+Browser.swift +153 -0
  126. package/ios/HybridAudioBrowser+Options.swift +144 -0
  127. package/ios/HybridAudioBrowser+Player.swift +397 -0
  128. package/ios/HybridAudioBrowser+PlayerCallbacks.swift +189 -0
  129. package/ios/HybridAudioBrowser+Stubs.swift +46 -0
  130. package/ios/HybridAudioBrowser.swift +153 -1610
  131. package/ios/Model/NitroTypeStubs.swift +146 -24
  132. package/ios/Model/PlayerUpdateOptions.swift +8 -1
  133. package/ios/Model/RemoteCommand.swift +18 -0
  134. package/ios/Model/TrackPlayerError.swift +2 -2
  135. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +18 -53
  136. package/ios/Observer/ItemObserver.swift +90 -0
  137. package/ios/Observer/PlayerObserver.swift +59 -0
  138. package/ios/Option/SessionCategories.swift +42 -92
  139. package/ios/Player/AudioSessionController.swift +189 -0
  140. package/ios/Player/DeferredSeek.swift +70 -0
  141. package/ios/Player/ErrorStatus.swift +27 -0
  142. package/ios/Player/MediaLoader.swift +62 -35
  143. package/ios/Player/NowPlayingUpdater.swift +197 -50
  144. package/ios/Player/PlaybackFailure.swift +125 -0
  145. package/ios/Player/PlaybackPersistence.swift +73 -0
  146. package/ios/Player/PlaybackTimer.swift +8 -2
  147. package/ios/Player/Player.swift +1078 -0
  148. package/ios/Player/{PlaybackCoordinatorCallbacks.swift → PlayerCallbacks.swift} +3 -4
  149. package/ios/Player/PlayerEngine.swift +87 -0
  150. package/ios/Player/PlayerStatusTypes.swift +2 -6
  151. package/ios/Player/QueueManager.swift +93 -67
  152. package/ios/Player/RetryLoop.swift +261 -0
  153. package/ios/Player/ShuffleOrder.swift +33 -0
  154. package/ios/Player/SleepTimerManager.swift +1 -1
  155. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +46 -0
  156. package/ios/RemoteCommand/RemoteCommandController.swift +87 -331
  157. package/ios/Tests/BrowserPathHelperTests.swift +56 -15
  158. package/ios/Tests/CarPlayArtworkResolverTests.swift +60 -373
  159. package/ios/Tests/DeferredSeekTests.swift +70 -0
  160. package/ios/Tests/EmitterTests.swift +29 -0
  161. package/ios/Tests/GenerationMemoTests.swift +121 -0
  162. package/ios/Tests/InertStyleDiagnosticTests.swift +209 -0
  163. package/ios/Tests/JsonModelsTests.swift +164 -1
  164. package/ios/Tests/LRUCacheTests.swift +110 -0
  165. package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
  166. package/ios/Tests/MediaLoaderTests.swift +12 -48
  167. package/ios/Tests/ObserverTests.swift +88 -0
  168. package/ios/Tests/PlaybackErrorTests.swift +76 -0
  169. package/ios/Tests/PlaybackFailureTests.swift +189 -0
  170. package/ios/Tests/PlaybackPersistenceTests.swift +93 -0
  171. package/ios/Tests/PlayerPolicyTests.swift +420 -0
  172. package/ios/Tests/{PlaybackCoordinatorTests.swift → PlayerTests.swift} +300 -248
  173. package/ios/Tests/PlayingStateTests.swift +42 -0
  174. package/ios/Tests/QueueManagerTests.swift +196 -115
  175. package/ios/Tests/ReadyValueTests.swift +73 -0
  176. package/ios/Tests/RemoteCommandControllerTests.swift +152 -0
  177. package/ios/Tests/RemoteCommandTests.swift +65 -0
  178. package/ios/Tests/RequestLayerTests.swift +112 -0
  179. package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
  180. package/ios/Tests/RetryLoopTests.swift +270 -0
  181. package/ios/Tests/SearchDrillInTests.swift +16 -16
  182. package/ios/Tests/SectionPresentationTests.swift +98 -0
  183. package/ios/Tests/SectionScopeTests.swift +87 -55
  184. package/ios/Tests/StyleResolverTests.swift +132 -0
  185. package/ios/Tests/Support/TestTypes.swift +108 -104
  186. package/ios/Tests/TabBarEntriesTests.swift +19 -19
  187. package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
  188. package/ios/Tests/TopTemplateReplacerTests.swift +172 -0
  189. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  190. package/ios/Tests/TrackIdentityTests.swift +20 -0
  191. package/ios/Tests/TrackSelectorTests.swift +168 -33
  192. package/ios/Tests/URLErrorConnectivityTests.swift +20 -0
  193. package/ios/TrackSelector.swift +46 -31
  194. package/ios/Util/ArtworkImageFetcher.swift +58 -1
  195. package/ios/Util/Emitter.swift +42 -0
  196. package/ios/Util/GenerationMemo.swift +36 -0
  197. package/ios/Util/LRUCache.swift +23 -159
  198. package/ios/Util/NetworkMonitor.swift +43 -26
  199. package/ios/Util/ReadyValue.swift +56 -0
  200. package/ios/Util/SFSymbolRenderer.swift +59 -31
  201. package/ios/Util/SVGProcessor.swift +9 -12
  202. package/lib/commonjs/AudioBrowser.js +8 -8
  203. package/lib/commonjs/AudioBrowser.js.map +1 -1
  204. package/lib/commonjs/CLAUDE.md +52 -0
  205. package/lib/commonjs/features/browser-config.js +12 -8
  206. package/lib/commonjs/features/browser-config.js.map +1 -1
  207. package/lib/commonjs/features/browser.js +11 -3
  208. package/lib/commonjs/features/browser.js.map +1 -1
  209. package/lib/commonjs/features/carConnection.js +1 -1
  210. package/lib/commonjs/features/equalizer.js +1 -1
  211. package/lib/commonjs/features/errors.js +3 -3
  212. package/lib/commonjs/features/favorites.js +14 -11
  213. package/lib/commonjs/features/favorites.js.map +1 -1
  214. package/lib/commonjs/features/gate.js +16 -1
  215. package/lib/commonjs/features/gate.js.map +1 -1
  216. package/lib/commonjs/features/metadata.js +20 -3
  217. package/lib/commonjs/features/metadata.js.map +1 -1
  218. package/lib/commonjs/features/network.js +1 -1
  219. package/lib/commonjs/features/nowPlaying.js +1 -1
  220. package/lib/commonjs/features/output.js +1 -1
  221. package/lib/commonjs/features/playback/controls.js +4 -2
  222. package/lib/commonjs/features/playback/controls.js.map +1 -1
  223. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  224. package/lib/commonjs/features/playback/playing.js +1 -1
  225. package/lib/commonjs/features/playback/progress.js +3 -2
  226. package/lib/commonjs/features/playback/progress.js.map +1 -1
  227. package/lib/commonjs/features/playback/rate.js +8 -1
  228. package/lib/commonjs/features/playback/rate.js.map +1 -1
  229. package/lib/commonjs/features/playback/state.js +3 -4
  230. package/lib/commonjs/features/playback/state.js.map +1 -1
  231. package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
  232. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
  233. package/lib/commonjs/features/playback/volume.js +1 -1
  234. package/lib/commonjs/features/player/options.js +48 -34
  235. package/lib/commonjs/features/player/options.js.map +1 -1
  236. package/lib/commonjs/features/player/setup.js +18 -9
  237. package/lib/commonjs/features/player/setup.js.map +1 -1
  238. package/lib/commonjs/features/player/validateOptions.js +23 -0
  239. package/lib/commonjs/features/player/validateOptions.js.map +1 -0
  240. package/lib/commonjs/features/queue/activeTrack.js +15 -4
  241. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  242. package/lib/commonjs/features/queue/queue.js +13 -6
  243. package/lib/commonjs/features/queue/queue.js.map +1 -1
  244. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  245. package/lib/commonjs/features/queue/shuffle.js +1 -1
  246. package/lib/commonjs/features/remoteControls.js +13 -92
  247. package/lib/commonjs/features/remoteControls.js.map +1 -1
  248. package/lib/commonjs/features/sleepTimer.js +13 -1
  249. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  250. package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
  251. package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
  252. package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
  253. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  254. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  255. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  256. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  257. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  258. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  259. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  260. package/lib/commonjs/web/NativeAudioBrowser.js +201 -76
  261. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  262. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
  263. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
  264. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  265. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  266. package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
  267. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  268. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +1 -1
  269. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  270. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  271. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
  272. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
  273. package/lib/commonjs/web/browser/BrowserManager.js +175 -100
  274. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  275. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  276. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  277. package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
  278. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
  279. package/lib/commonjs/web/browser/SearchManager.js +18 -23
  280. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  281. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  282. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  283. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  284. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  285. package/lib/commonjs/web/browser/sections.js +89 -0
  286. package/lib/commonjs/web/browser/sections.js.map +1 -0
  287. package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
  288. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  289. package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
  290. package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
  291. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  292. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  293. package/lib/commonjs/web/util/BrowserPathHelper.js +62 -11
  294. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  295. package/lib/module/AudioBrowser.js +2 -1
  296. package/lib/module/AudioBrowser.js.map +1 -1
  297. package/lib/module/CLAUDE.md +52 -0
  298. package/lib/module/features/browser-config.js +12 -8
  299. package/lib/module/features/browser-config.js.map +1 -1
  300. package/lib/module/features/browser.js +11 -3
  301. package/lib/module/features/browser.js.map +1 -1
  302. package/lib/module/features/carConnection.js +1 -1
  303. package/lib/module/features/equalizer.js +1 -1
  304. package/lib/module/features/errors.js +3 -3
  305. package/lib/module/features/favorites.js +14 -11
  306. package/lib/module/features/favorites.js.map +1 -1
  307. package/lib/module/features/gate.js +16 -1
  308. package/lib/module/features/gate.js.map +1 -1
  309. package/lib/module/features/metadata.js +20 -3
  310. package/lib/module/features/metadata.js.map +1 -1
  311. package/lib/module/features/network.js +1 -1
  312. package/lib/module/features/nowPlaying.js +1 -1
  313. package/lib/module/features/output.js +1 -1
  314. package/lib/module/features/playback/controls.js +4 -2
  315. package/lib/module/features/playback/controls.js.map +1 -1
  316. package/lib/module/features/playback/playWhenReady.js +1 -1
  317. package/lib/module/features/playback/playing.js +1 -1
  318. package/lib/module/features/playback/progress.js +3 -2
  319. package/lib/module/features/playback/progress.js.map +1 -1
  320. package/lib/module/features/playback/rate.js +8 -1
  321. package/lib/module/features/playback/rate.js.map +1 -1
  322. package/lib/module/features/playback/state.js +3 -4
  323. package/lib/module/features/playback/state.js.map +1 -1
  324. package/lib/module/features/playback/trackPlaybackTime.js +0 -5
  325. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
  326. package/lib/module/features/playback/volume.js +1 -1
  327. package/lib/module/features/player/options.js +47 -32
  328. package/lib/module/features/player/options.js.map +1 -1
  329. package/lib/module/features/player/setup.js +17 -8
  330. package/lib/module/features/player/setup.js.map +1 -1
  331. package/lib/module/features/player/validateOptions.js +19 -0
  332. package/lib/module/features/player/validateOptions.js.map +1 -0
  333. package/lib/module/features/queue/activeTrack.js +15 -4
  334. package/lib/module/features/queue/activeTrack.js.map +1 -1
  335. package/lib/module/features/queue/queue.js +13 -6
  336. package/lib/module/features/queue/queue.js.map +1 -1
  337. package/lib/module/features/queue/repeatMode.js +1 -1
  338. package/lib/module/features/queue/shuffle.js +1 -1
  339. package/lib/module/features/remoteControls.js +13 -92
  340. package/lib/module/features/remoteControls.js.map +1 -1
  341. package/lib/module/features/sleepTimer.js +13 -1
  342. package/lib/module/features/sleepTimer.js.map +1 -1
  343. package/lib/module/test-utils/reactNativeStub.js +58 -0
  344. package/lib/module/test-utils/reactNativeStub.js.map +1 -0
  345. package/lib/module/utils/LazyNativeEmitter.js +28 -14
  346. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  347. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  348. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  349. package/lib/module/utils/getTrackIdentity.js +20 -0
  350. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  351. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  352. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  353. package/lib/module/web/NativeAudioBrowser.js +201 -76
  354. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  355. package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
  356. package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
  357. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  358. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  359. package/lib/module/web/TrackPlayer/Player.js +254 -39
  360. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  361. package/lib/module/web/TrackPlayer/PlayingStateFactory.js +1 -1
  362. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  363. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  364. package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
  365. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
  366. package/lib/module/web/browser/BrowserManager.js +175 -100
  367. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  368. package/lib/module/web/browser/FavoriteManager.js +31 -21
  369. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  370. package/lib/module/web/browser/NavigationErrorManager.js +3 -1
  371. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
  372. package/lib/module/web/browser/SearchManager.js +18 -23
  373. package/lib/module/web/browser/SearchManager.js.map +1 -1
  374. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  375. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  376. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  377. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  378. package/lib/module/web/browser/sections.js +83 -0
  379. package/lib/module/web/browser/sections.js.map +1 -0
  380. package/lib/module/web/http/RequestConfigBuilder.js +110 -27
  381. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  382. package/lib/module/web/http/followMediaRedirect.js +215 -0
  383. package/lib/module/web/http/followMediaRedirect.js.map +1 -0
  384. package/lib/module/web/player/NowPlayingManager.js +4 -3
  385. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  386. package/lib/module/web/util/BrowserPathHelper.js +62 -11
  387. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  388. package/lib/typescript/package.json +1 -0
  389. package/lib/typescript/src/AudioBrowser.d.ts +1 -1
  390. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  391. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  392. package/lib/typescript/src/features/browser.d.ts +10 -2
  393. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  394. package/lib/typescript/src/features/carConnection.d.ts +1 -1
  395. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  396. package/lib/typescript/src/features/errors.d.ts +3 -3
  397. package/lib/typescript/src/features/favorites.d.ts +14 -11
  398. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  399. package/lib/typescript/src/features/gate.d.ts +18 -2
  400. package/lib/typescript/src/features/gate.d.ts.map +1 -1
  401. package/lib/typescript/src/features/metadata.d.ts +20 -3
  402. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  403. package/lib/typescript/src/features/network.d.ts +1 -1
  404. package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
  405. package/lib/typescript/src/features/output.d.ts +1 -1
  406. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  407. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  408. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  409. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  410. package/lib/typescript/src/features/playback/progress.d.ts +1 -1
  411. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  412. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  413. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  414. package/lib/typescript/src/features/playback/state.d.ts +3 -4
  415. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  416. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  417. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  418. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  419. package/lib/typescript/src/features/player/options.d.ts +108 -56
  420. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  421. package/lib/typescript/src/features/player/setup.d.ts +14 -4
  422. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  423. package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
  424. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
  425. package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
  426. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  427. package/lib/typescript/src/features/queue/queue.d.ts +7 -4
  428. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  429. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  430. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  431. package/lib/typescript/src/features/remoteControls.d.ts +23 -22
  432. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  433. package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
  434. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  435. package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
  436. package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
  437. package/lib/typescript/src/types/browser-native.d.ts +3 -2
  438. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  439. package/lib/typescript/src/types/browser-nodes.d.ts +387 -126
  440. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  441. package/lib/typescript/src/types/browser.d.ts +93 -61
  442. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  443. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
  444. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  445. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  446. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  447. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  448. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  449. package/lib/typescript/src/utils/resolveAssetSource.d.ts +1 -2
  450. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
  451. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  452. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  453. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
  454. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +4 -2
  455. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +2 -2
  456. package/nitrogen/generated/android/c++/JAndroidAudioOffloadSettings.hpp +2 -2
  457. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +12 -12
  458. package/nitrogen/generated/android/c++/JAndroidPlayerWakeMode.hpp +2 -2
  459. package/nitrogen/generated/android/c++/JAppKilledPlaybackBehavior.hpp +2 -2
  460. package/nitrogen/generated/android/c++/JArtworkRendering.hpp +58 -0
  461. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +10 -5
  462. package/nitrogen/generated/android/c++/JArtworkVariants.hpp +2 -2
  463. package/nitrogen/generated/android/c++/JBatteryOptimizationStatus.hpp +2 -2
  464. package/nitrogen/generated/android/c++/JBatteryOptimizationStatusChangedEvent.hpp +2 -2
  465. package/nitrogen/generated/android/c++/JBatteryWarningPendingChangedEvent.hpp +2 -2
  466. package/nitrogen/generated/android/c++/JBrowseError.hpp +2 -2
  467. package/nitrogen/generated/android/c++/JBrowseResult.hpp +19 -7
  468. package/nitrogen/generated/android/c++/JBrowserSourceCallbackParam.hpp +2 -2
  469. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +2 -2
  470. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  471. package/nitrogen/generated/android/c++/JCarPlaySiriListButtonPosition.hpp +2 -2
  472. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  473. package/nitrogen/generated/android/c++/JChapterMetadata.hpp +2 -2
  474. package/nitrogen/generated/android/c++/JEqualizerSettings.hpp +10 -10
  475. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +10 -5
  476. package/nitrogen/generated/android/c++/JFormatNavigationErrorParams.hpp +11 -5
  477. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +10 -5
  478. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +2 -2
  479. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +2 -2
  480. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +9 -9
  481. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +10 -5
  482. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.hpp +4 -2
  483. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +10 -5
  484. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +10 -5
  485. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +2 -2
  486. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.hpp +2 -2
  487. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +9 -9
  488. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +10 -5
  489. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +2 -2
  490. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +18 -12
  491. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +10 -4
  492. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
  493. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +18 -6
  494. package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
  495. package/nitrogen/generated/android/c++/JFunc_void_BatteryOptimizationStatusChangedEvent.hpp +2 -2
  496. package/nitrogen/generated/android/c++/JFunc_void_BatteryWarningPendingChangedEvent.hpp +2 -2
  497. package/nitrogen/generated/android/c++/JFunc_void_EqualizerSettings.hpp +2 -2
  498. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +10 -5
  499. package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +2 -2
  500. package/nitrogen/generated/android/c++/JFunc_void_NavigationErrorEvent.hpp +2 -2
  501. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +2 -2
  502. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +9 -12
  503. package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +2 -2
  504. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -2
  505. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +10 -5
  506. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -2
  507. package/nitrogen/generated/android/c++/JFunc_void_PlaybackPlayWhenReadyChangedEvent.hpp +2 -2
  508. package/nitrogen/generated/android/c++/JFunc_void_PlaybackProgressUpdatedEvent.hpp +2 -2
  509. package/nitrogen/generated/android/c++/JFunc_void_PlaybackQueueEndedEvent.hpp +2 -2
  510. package/nitrogen/generated/android/c++/JFunc_void_PlayingState.hpp +2 -2
  511. package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpBackwardEvent.hpp +2 -2
  512. package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpForwardEvent.hpp +2 -2
  513. package/nitrogen/generated/android/c++/JFunc_void_RemotePlayIdEvent.hpp +2 -2
  514. package/nitrogen/generated/android/c++/JFunc_void_RemotePlaySearchEvent.hpp +2 -2
  515. package/nitrogen/generated/android/c++/JFunc_void_RemoteSeekEvent.hpp +2 -2
  516. package/nitrogen/generated/android/c++/JFunc_void_RemoteSkipEvent.hpp +2 -2
  517. package/nitrogen/generated/android/c++/JFunc_void_RepeatModeChangedEvent.hpp +2 -2
  518. package/nitrogen/generated/android/c++/JFunc_void_TimedMetadata.hpp +2 -2
  519. package/nitrogen/generated/android/c++/JFunc_void_TrackMetadata.hpp +2 -2
  520. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +2 -2
  521. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +2 -2
  522. package/nitrogen/generated/android/c++/JFunc_void_std__optional_FormattedNavigationError_.hpp +2 -2
  523. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +17 -5
  524. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.hpp +2 -2
  525. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +2 -2
  526. package/nitrogen/generated/android/c++/JFunc_void_std__vector_ChapterMetadata_.hpp +16 -16
  527. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +24 -18
  528. package/nitrogen/generated/android/c++/JGate.hpp +2 -2
  529. package/nitrogen/generated/android/c++/JGateDecision.hpp +2 -2
  530. package/nitrogen/generated/android/c++/JGateEvent.hpp +2 -2
  531. package/nitrogen/generated/android/c++/JGateReason.hpp +2 -2
  532. package/nitrogen/generated/android/c++/JGridTile.hpp +64 -0
  533. package/nitrogen/generated/android/c++/JHttpMethod.hpp +2 -2
  534. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +77 -53
  535. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +3 -3
  536. package/nitrogen/generated/android/c++/JIOSCategory.hpp +2 -2
  537. package/nitrogen/generated/android/c++/JIOSCategoryMode.hpp +2 -2
  538. package/nitrogen/generated/android/c++/JIOSCategoryOptions.hpp +2 -2
  539. package/nitrogen/generated/android/c++/JIOSCategoryPolicy.hpp +2 -2
  540. package/nitrogen/generated/android/c++/JIOSOptions.hpp +17 -11
  541. package/nitrogen/generated/android/c++/JImageContext.hpp +2 -2
  542. package/nitrogen/generated/android/c++/JImageQueryParams.hpp +2 -2
  543. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  544. package/nitrogen/generated/android/c++/JImageSource.hpp +2 -2
  545. package/nitrogen/generated/android/c++/JMediaReference.hpp +2 -2
  546. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +10 -5
  547. package/nitrogen/generated/android/c++/JMediaTransformParams.hpp +2 -2
  548. package/nitrogen/generated/android/c++/JNativeAndroidSetupOptions.hpp +2 -2
  549. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +48 -21
  550. package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +2 -2
  551. package/nitrogen/generated/android/c++/JNativeIOSSetupOptions.hpp +10 -10
  552. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +16 -4
  553. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +17 -14
  554. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +9 -12
  555. package/nitrogen/generated/android/c++/JNavigationError.hpp +2 -2
  556. package/nitrogen/generated/android/c++/JNavigationErrorEvent.hpp +2 -2
  557. package/nitrogen/generated/android/c++/JNavigationErrorKind.hpp +58 -0
  558. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +2 -2
  559. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +12 -12
  560. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +17 -11
  561. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +2 -2
  562. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +2 -2
  563. package/nitrogen/generated/android/c++/JOptions.hpp +9 -12
  564. package/nitrogen/generated/android/c++/JOutput.hpp +2 -2
  565. package/nitrogen/generated/android/c++/JOutputType.hpp +2 -2
  566. package/nitrogen/generated/android/c++/JPlayback.hpp +2 -2
  567. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +10 -5
  568. package/nitrogen/generated/android/c++/JPlaybackError.hpp +2 -2
  569. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -2
  570. package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +2 -2
  571. package/nitrogen/generated/android/c++/JPlaybackPlayWhenReadyChangedEvent.hpp +2 -2
  572. package/nitrogen/generated/android/c++/JPlaybackProgressUpdatedEvent.hpp +2 -2
  573. package/nitrogen/generated/android/c++/JPlaybackQueueEndedEvent.hpp +2 -2
  574. package/nitrogen/generated/android/c++/JPlaybackState.hpp +2 -2
  575. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +7 -13
  576. package/nitrogen/generated/android/c++/JPlayingState.hpp +2 -2
  577. package/nitrogen/generated/android/c++/JProgress.hpp +2 -2
  578. package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
  579. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
  580. package/nitrogen/generated/android/c++/JRemoteJumpBackwardEvent.hpp +2 -2
  581. package/nitrogen/generated/android/c++/JRemoteJumpForwardEvent.hpp +2 -2
  582. package/nitrogen/generated/android/c++/JRemotePlayIdEvent.hpp +2 -2
  583. package/nitrogen/generated/android/c++/JRemotePlaySearchEvent.hpp +2 -2
  584. package/nitrogen/generated/android/c++/JRemoteSeekEvent.hpp +2 -2
  585. package/nitrogen/generated/android/c++/JRemoteSkipEvent.hpp +2 -2
  586. package/nitrogen/generated/android/c++/JRepeatMode.hpp +2 -2
  587. package/nitrogen/generated/android/c++/JRepeatModeChangedEvent.hpp +2 -2
  588. package/nitrogen/generated/android/c++/JRequestConfig.hpp +2 -2
  589. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +67 -59
  590. package/nitrogen/generated/android/c++/JRetryConfig.hpp +2 -2
  591. package/nitrogen/generated/android/c++/JSearchMode.hpp +2 -2
  592. package/nitrogen/generated/android/c++/JSearchParams.hpp +2 -2
  593. package/nitrogen/generated/android/c++/JSection.hpp +116 -0
  594. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  595. package/nitrogen/generated/android/c++/JSleepTimer.hpp +4 -4
  596. package/nitrogen/generated/android/c++/JSleepTimerEndOfTrack.hpp +2 -2
  597. package/nitrogen/generated/android/c++/JSleepTimerTime.hpp +2 -2
  598. package/nitrogen/generated/android/c++/JStallReason.hpp +2 -2
  599. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  600. package/nitrogen/generated/android/c++/JTimedMetadata.hpp +2 -2
  601. package/nitrogen/generated/android/c++/JTrack.hpp +27 -48
  602. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +18 -12
  603. package/nitrogen/generated/android/c++/JTrackMetadata.hpp +2 -2
  604. package/nitrogen/generated/android/c++/JTrackRequest.hpp +2 -2
  605. package/nitrogen/generated/android/c++/JTrackStyle.hpp +47 -19
  606. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +2 -2
  607. package/nitrogen/generated/android/c++/JVariant_Boolean_AndroidAudioOffloadSettings.hpp +3 -3
  608. package/nitrogen/generated/android/c++/JVariant_Boolean_RetryConfig.hpp +3 -3
  609. package/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp +3 -3
  610. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
  611. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
  612. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +20 -8
  613. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +3 -3
  614. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioOffloadSettings.kt +15 -0
  615. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +20 -3
  616. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  617. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +37 -0
  618. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +15 -0
  619. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryOptimizationStatusChangedEvent.kt +13 -0
  620. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryWarningPendingChangedEvent.kt +13 -0
  621. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseError.kt +13 -0
  622. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseResult.kt +16 -7
  623. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowserSourceCallbackParam.kt +15 -0
  624. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  625. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  626. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ChapterMetadata.kt +19 -0
  627. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/EqualizerSettings.kt +27 -0
  628. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FavoriteChangedEvent.kt +15 -0
  629. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNavigationErrorParams.kt +25 -3
  630. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +21 -0
  631. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +15 -0
  632. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +0 -2
  633. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +0 -2
  634. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +0 -2
  635. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.kt +0 -2
  636. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +0 -2
  637. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +0 -2
  638. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +0 -2
  639. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.kt +0 -2
  640. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +0 -2
  641. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.kt +0 -2
  642. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +0 -2
  643. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.kt +0 -2
  644. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.kt +0 -2
  645. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +78 -0
  646. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt +0 -2
  647. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void.kt +0 -2
  648. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryOptimizationStatusChangedEvent.kt +0 -2
  649. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryWarningPendingChangedEvent.kt +0 -2
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_EqualizerSettings.kt +0 -2
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_FavoriteChangedEvent.kt +0 -2
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_GateEvent.kt +0 -2
  653. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NavigationErrorEvent.kt +0 -2
  654. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NowPlayingMetadata.kt +0 -2
  655. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Options.kt +0 -2
  656. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Output.kt +0 -2
  657. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Playback.kt +0 -2
  658. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackActiveTrackChangedEvent.kt +0 -2
  659. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackErrorEvent.kt +0 -2
  660. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackPlayWhenReadyChangedEvent.kt +0 -2
  661. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackProgressUpdatedEvent.kt +0 -2
  662. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackQueueEndedEvent.kt +0 -2
  663. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlayingState.kt +0 -2
  664. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpBackwardEvent.kt +0 -2
  665. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpForwardEvent.kt +0 -2
  666. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlayIdEvent.kt +0 -2
  667. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlaySearchEvent.kt +0 -2
  668. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSeekEvent.kt +0 -2
  669. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSkipEvent.kt +0 -2
  670. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RepeatModeChangedEvent.kt +0 -2
  671. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TimedMetadata.kt +0 -2
  672. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TrackMetadata.kt +0 -2
  673. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +0 -2
  674. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_double.kt +0 -2
  675. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_FormattedNavigationError_.kt +0 -2
  676. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_ResolvedTrack_.kt +0 -2
  677. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.kt +0 -2
  678. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__string.kt +0 -2
  679. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_ChapterMetadata_.kt +0 -2
  680. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_Track_.kt +0 -2
  681. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Gate.kt +15 -0
  682. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateDecision.kt +15 -0
  683. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateEvent.kt +13 -0
  684. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +25 -0
  685. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +3 -1
  686. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +25 -3
  687. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageContext.kt +15 -0
  688. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageQueryParams.kt +15 -0
  689. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  690. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageSource.kt +19 -0
  691. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +35 -0
  692. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaTransformParams.kt +15 -0
  693. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeAndroidSetupOptions.kt +31 -0
  694. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +51 -5
  695. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeGateRequest.kt +17 -0
  696. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeIOSSetupOptions.kt +21 -0
  697. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +27 -0
  698. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +29 -0
  699. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +23 -0
  700. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationError.kt +19 -0
  701. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorEvent.kt +13 -0
  702. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorKind.kt +23 -0
  703. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +20 -3
  704. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +25 -3
  705. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +29 -0
  706. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +17 -0
  707. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +23 -0
  708. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Output.kt +17 -0
  709. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Playback.kt +15 -0
  710. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackActiveTrackChangedEvent.kt +21 -0
  711. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +21 -0
  712. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorEvent.kt +13 -0
  713. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackPlayWhenReadyChangedEvent.kt +13 -0
  714. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackProgressUpdatedEvent.kt +19 -0
  715. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackQueueEndedEvent.kt +15 -0
  716. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +37 -2
  717. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayingState.kt +15 -0
  718. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Progress.kt +17 -0
  719. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
  720. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +61 -0
  721. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpBackwardEvent.kt +13 -0
  722. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpForwardEvent.kt +13 -0
  723. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlayIdEvent.kt +15 -0
  724. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlaySearchEvent.kt +13 -0
  725. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSeekEvent.kt +13 -0
  726. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSkipEvent.kt +13 -0
  727. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RepeatModeChangedEvent.kt +13 -0
  728. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RequestConfig.kt +27 -0
  729. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +67 -15
  730. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +17 -0
  731. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +27 -0
  732. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +66 -0
  733. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +86 -0
  734. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimer.kt +18 -8
  735. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerEndOfTrack.kt +13 -0
  736. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerTime.kt +13 -0
  737. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  738. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TimedMetadata.kt +21 -0
  739. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +55 -12
  740. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackLoadEvent.kt +17 -0
  741. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackMetadata.kt +43 -0
  742. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +17 -0
  743. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +58 -5
  744. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +31 -0
  745. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_AndroidAudioOffloadSettings.kt +16 -7
  746. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_RetryConfig.kt +16 -7
  747. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_NullType_Double.kt +16 -7
  748. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +24 -15
  749. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +18 -8
  750. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +16 -7
  751. package/nitrogen/generated/ios/AudioBrowser+autolinking.rb +2 -0
  752. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +38 -29
  753. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +779 -665
  754. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +34 -16
  755. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +36 -18
  756. package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
  757. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  758. package/nitrogen/generated/ios/swift/BrowseResult.swift +12 -0
  759. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  760. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  761. package/nitrogen/generated/ios/swift/FormatNavigationErrorParams.swift +7 -2
  762. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
  763. package/nitrogen/generated/ios/swift/GridTile.swift +48 -0
  764. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
  765. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
  766. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  767. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  768. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
  769. package/nitrogen/generated/ios/swift/NavigationErrorKind.swift +40 -0
  770. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
  771. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  772. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  773. package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
  774. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
  775. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
  776. package/nitrogen/generated/ios/swift/Section.swift +82 -0
  777. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  778. package/nitrogen/generated/ios/swift/SleepTimer.swift +13 -0
  779. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  780. package/nitrogen/generated/ios/swift/Track.swift +28 -63
  781. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  782. package/nitrogen/generated/ios/swift/Variant_Bool_AndroidAudioOffloadSettings.swift +12 -0
  783. package/nitrogen/generated/ios/swift/Variant_Bool_RetryConfig.swift +12 -0
  784. package/nitrogen/generated/ios/swift/Variant_NullType_Double.swift +12 -0
  785. package/nitrogen/generated/ios/swift/Variant_NullType_RemoteButtonLayout.swift +30 -0
  786. package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +13 -0
  787. package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +12 -0
  788. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
  789. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  790. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  791. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  792. package/nitrogen/generated/shared/c++/FormatNavigationErrorParams.hpp +9 -2
  793. package/nitrogen/generated/shared/c++/GridTile.hpp +84 -0
  794. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
  795. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
  796. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  797. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  798. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
  799. package/nitrogen/generated/shared/c++/NavigationErrorKind.hpp +76 -0
  800. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
  801. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  802. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  803. package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
  804. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
  805. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
  806. package/nitrogen/generated/shared/c++/Section.hpp +102 -0
  807. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  808. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  809. package/nitrogen/generated/shared/c++/Track.hpp +18 -30
  810. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  811. package/package.json +24 -11
  812. package/src/AudioBrowser.ts +2 -1
  813. package/src/CLAUDE.md +52 -0
  814. package/src/features/battery.test.ts +79 -0
  815. package/src/features/browser-config.test.ts +21 -6
  816. package/src/features/browser-config.ts +12 -8
  817. package/src/features/browser.test.ts +1 -1
  818. package/src/features/browser.ts +11 -3
  819. package/src/features/carConnection.ts +1 -1
  820. package/src/features/equalizer.ts +1 -1
  821. package/src/features/errors.ts +3 -3
  822. package/src/features/favorites.ts +14 -11
  823. package/src/features/gate.ts +16 -1
  824. package/src/features/hook-wiring.test.ts +302 -0
  825. package/src/features/metadata.ts +20 -3
  826. package/src/features/network.ts +1 -1
  827. package/src/features/nowPlaying.ts +1 -1
  828. package/src/features/output.ts +1 -1
  829. package/src/features/playback/controls.ts +4 -2
  830. package/src/features/playback/playWhenReady.ts +1 -1
  831. package/src/features/playback/playing.ts +1 -1
  832. package/src/features/playback/progress.test.ts +169 -0
  833. package/src/features/playback/progress.ts +3 -2
  834. package/src/features/playback/rate.ts +8 -1
  835. package/src/features/playback/state.ts +3 -4
  836. package/src/features/playback/trackPlaybackTime.test.ts +16 -6
  837. package/src/features/playback/trackPlaybackTime.ts +1 -6
  838. package/src/features/playback/volume.ts +1 -1
  839. package/src/features/player/options.ts +126 -76
  840. package/src/features/player/setup.test.ts +24 -7
  841. package/src/features/player/setup.ts +22 -10
  842. package/src/features/player/validateOptions.ts +21 -0
  843. package/src/features/queue/activeTrack.ts +15 -4
  844. package/src/features/queue/queue.ts +13 -6
  845. package/src/features/queue/repeatMode.ts +1 -1
  846. package/src/features/queue/shuffle.ts +1 -1
  847. package/src/features/remoteControls.ts +13 -92
  848. package/src/features/sleepTimer.test.ts +172 -0
  849. package/src/features/sleepTimer.ts +9 -1
  850. package/src/nitro-stub-drift.test.ts +277 -0
  851. package/src/public-surface.test.ts +147 -0
  852. package/src/specs/audio-browser.nitro.ts +5 -4
  853. package/src/style-resolution.test.ts +210 -0
  854. package/src/test-utils/reactNativeStub.ts +60 -0
  855. package/src/types/browser-native.ts +7 -3
  856. package/src/types/browser-nodes.ts +403 -134
  857. package/src/types/browser.ts +97 -63
  858. package/src/utils/LazyNativeEmitter.ts +28 -15
  859. package/src/utils/NativeUpdatedValue.ts +29 -13
  860. package/src/utils/getTrackIdentity.ts +20 -0
  861. package/src/utils/useDebug.test.ts +182 -0
  862. package/src/utils/useNativeUpdatedValue.test.ts +103 -0
  863. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  864. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
  865. package/src/web/NativeAudioBrowser.ts +240 -83
  866. package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
  867. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
  868. package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
  869. package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
  870. package/src/web/TrackPlayer/Player.load.test.ts +137 -1
  871. package/src/web/TrackPlayer/Player.ts +284 -37
  872. package/src/web/TrackPlayer/PlayingStateFactory.test.ts +1 -1
  873. package/src/web/TrackPlayer/PlayingStateFactory.ts +1 -1
  874. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  875. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  876. package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
  877. package/src/web/__tests__/lifecycle.test.ts +345 -0
  878. package/src/web/__tests__/lifecycleHarness.ts +351 -0
  879. package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
  880. package/src/web/__tests__/loadSupersession.test.ts +175 -0
  881. package/src/web/__tests__/retry.test.ts +169 -0
  882. package/src/web/browser/BrowserManager.ts +209 -111
  883. package/src/web/browser/FavoriteManager.test.ts +84 -0
  884. package/src/web/browser/FavoriteManager.ts +28 -23
  885. package/src/web/browser/NavigationErrorManager.ts +3 -1
  886. package/src/web/browser/SearchManager.test.ts +75 -0
  887. package/src/web/browser/SearchManager.ts +25 -28
  888. package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
  889. package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
  890. package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
  891. package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
  892. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  893. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  894. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  895. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  896. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  897. package/src/web/browser/sections.test.ts +91 -0
  898. package/src/web/browser/sections.ts +72 -0
  899. package/src/web/http/RequestConfigBuilder.test.ts +241 -1
  900. package/src/web/http/RequestConfigBuilder.ts +140 -33
  901. package/src/web/http/followMediaRedirect.test.ts +289 -0
  902. package/src/web/http/followMediaRedirect.ts +253 -0
  903. package/src/web/player/NowPlayingManager.ts +4 -3
  904. package/src/web/util/BrowserPathHelper.test.ts +68 -0
  905. package/src/web/util/BrowserPathHelper.ts +70 -13
  906. package/website/guide/android-auto.md +114 -0
  907. package/website/guide/android-certificates.md +409 -0
  908. package/website/guide/artwork.md +255 -0
  909. package/website/guide/audio-output.md +135 -0
  910. package/website/guide/automotive.md +86 -0
  911. package/website/guide/basic-usage.md +151 -0
  912. package/website/guide/battery.md +115 -0
  913. package/website/guide/browser.md +587 -0
  914. package/website/guide/carplay.md +650 -0
  915. package/website/guide/configuration.md +173 -0
  916. package/website/guide/equalizer.md +231 -0
  917. package/website/guide/errors.md +310 -0
  918. package/website/guide/favorites.md +154 -0
  919. package/website/guide/gate.md +143 -0
  920. package/website/guide/getting-started.md +46 -0
  921. package/website/guide/hooks.md +238 -0
  922. package/website/guide/live-streams.md +125 -0
  923. package/website/guide/metadata.md +239 -0
  924. package/website/guide/migrating-from-track-player.md +244 -0
  925. package/website/guide/native-callback-fetch.md +127 -0
  926. package/website/guide/network.md +86 -0
  927. package/website/guide/now-playing.md +175 -0
  928. package/website/guide/playback.md +328 -0
  929. package/website/guide/queue.md +300 -0
  930. package/website/guide/remote-controls.md +256 -0
  931. package/website/guide/search.md +255 -0
  932. package/website/guide/sleep-timer.md +268 -0
  933. package/website/guide/track.md +247 -0
  934. package/ios/Bridge.h +0 -8
  935. package/ios/Browser/MediaResolveComposer.swift +0 -114
  936. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  937. package/ios/Model/MediaURL.swift +0 -67
  938. package/ios/NowPlayingInfo/MediaItemProperty.swift +0 -78
  939. package/ios/NowPlayingInfo/NowPlayingInfoKeyValue.swift +0 -6
  940. package/ios/NowPlayingInfo/NowPlayingInfoProperty.swift +0 -220
  941. package/ios/Observer/PlayerItemNotificationObserver.swift +0 -119
  942. package/ios/Observer/PlayerItemPropertyObserver.swift +0 -133
  943. package/ios/Observer/PlayerStateObserver.swift +0 -50
  944. package/ios/Observer/PlayerTimeObserver.swift +0 -55
  945. package/ios/Option/PitchAlgorithms.swift +0 -17
  946. package/ios/Option/TimeEventFrequency.swift +0 -18
  947. package/ios/Player/LoadSeekCoordinator.swift +0 -99
  948. package/ios/Player/MediaLoaderDelegate.swift +0 -11
  949. package/ios/Player/PlaybackCoordinator.swift +0 -754
  950. package/ios/Player/PlaybackEffectHandler.swift +0 -54
  951. package/ios/Player/PlaybackErrorHandler.swift +0 -175
  952. package/ios/Player/PlaybackStateStore.swift +0 -27
  953. package/ios/Player/PlayingStateManager.swift +0 -33
  954. package/ios/Player/RetryManager.swift +0 -322
  955. package/ios/Player/SeekCompletionHandler.swift +0 -3
  956. package/ios/Player/SleepTimerHandling.swift +0 -15
  957. package/ios/Support/RNTrackPlayer-Bridging-Header.h +0 -7
  958. package/ios/Tests/LoadSeekCoordinatorTests.swift +0 -325
  959. package/ios/Tests/MediaResolveTests.swift +0 -126
  960. package/ios/Tests/OnceValueTests.swift +0 -79
  961. package/ios/Tests/PlaybackErrorHandlerTests.swift +0 -547
  962. package/ios/Tests/PlaybackStateStoreTests.swift +0 -48
  963. package/ios/Tests/PlayingStateManagerTests.swift +0 -57
  964. package/ios/Tests/RetryManagerTests.swift +0 -89
  965. package/ios/TrackPlayer.swift +0 -1014
  966. package/ios/TrackPlayerCallbacks.swift +0 -86
  967. package/ios/Util/MetadataAdapter.swift +0 -131
  968. package/ios/Util/OnceValue.swift +0 -104
  969. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  970. package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +0 -58
  971. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  972. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
  973. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  974. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  975. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
  976. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
  977. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FavoriteConfig.kt +0 -38
  978. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  979. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
  980. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  981. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  982. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  983. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  984. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
  985. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  986. package/nitrogen/generated/ios/swift/Variant_NullType_NotificationButtonLayout.swift +0 -18
  987. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  988. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
  989. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
@@ -1,1014 +0,0 @@
1
- @preconcurrency import AVFoundation
2
- import Foundation
3
- import MediaPlayer
4
- import NitroModules
5
- import os.log
6
-
7
- @MainActor
8
- class TrackPlayer {
9
- let logger = Logger(subsystem: "com.audiobrowser", category: "TrackPlayer")
10
-
11
- // MARK: - Coordinator
12
-
13
- let coordinator: PlaybackCoordinator
14
-
15
- // MARK: - Dependencies
16
-
17
- let nowPlayingInfoController: NowPlayingInfoController
18
- let remoteCommandController: RemoteCommandController
19
- private let retryManager = RetryManager()
20
- private let playbackStateStore = PlaybackStateStore()
21
-
22
- // MARK: - Periodic Persist
23
-
24
- /// Cancelled when playback stops; recreated when it starts.
25
- private var periodicSaveTask: Task<Void, Never>?
26
-
27
- /// Retry configuration for load errors (network failures, timeouts, etc.)
28
- var retryConfig: Variant_Bool_RetryConfig? {
29
- didSet {
30
- retryManager.updatePolicy(from: retryConfig)
31
- }
32
- }
33
-
34
- /// Network monitor for accelerating retries when connectivity is restored.
35
- weak var networkMonitor: NetworkMonitor? {
36
- didSet {
37
- retryManager.networkMonitor = networkMonitor
38
- }
39
- }
40
-
41
- /// Handles media URL resolution, asset creation, and async loading.
42
- let mediaLoader = MediaLoader()
43
-
44
- /// Handles Now Playing metadata and artwork updates.
45
- let nowPlayingUpdater: NowPlayingUpdater
46
-
47
- // MARK: - AVPlayer Properties
48
-
49
- var avPlayer = AVPlayer()
50
-
51
- private lazy var playerObserver: PlayerStateObserver = .init(
52
- onStatusChange: { [weak self] status in
53
- self?.avPlayerStatusDidChange(status)
54
- },
55
- onTimeControlStatusChange: { [weak self] status in
56
- self?.avPlayerDidChangeTimeControlStatus(status)
57
- },
58
- )
59
-
60
- private lazy var playerTimeObserver: PlayerTimeObserver = .init(
61
- onAudioDidStart: { [weak self] in
62
- // Audio rendered: this load has proven itself — retries from here on get
63
- // the full recovery budget, not the short first-connect one.
64
- self?.retryManager.hasPlayed = true
65
- self?.coordinator.audioDidStart()
66
- },
67
- )
68
-
69
- private lazy var playerItemNotificationObserver: PlayerItemNotificationObserver = .init(
70
- onDidPlayToEndTime: { [weak self] in self?.handleDidPlayToEndTime() },
71
- onFailedToPlayToEndTime: { [weak self] error in
72
- let effectiveError = error ?? self?.avPlayer.currentItem?.error
73
- self?.coordinator.errorHandler.handleError(
74
- effectiveError,
75
- context: .playback,
76
- httpStatusCode: self?.currentItemHttpStatusCode,
77
- )
78
- },
79
- // Buffer emptied mid-playback: nudge the player, but don't reconnect — a
80
- // genuine drop is recovered by the retry / network-restore paths.
81
- onPlaybackStalled: { [weak self] in self?.recoverFromStall(reconnectIfLive: false) },
82
- )
83
-
84
- private lazy var playerItemObserver: PlayerItemPropertyObserver = .init(
85
- onDurationUpdate: { [weak self] duration in
86
- self?.callbacks?.playerDidUpdateDuration(duration)
87
- },
88
- onPlaybackLikelyToKeepUpUpdate: { [weak self] isLikely in
89
- self?.avItemDidUpdatePlaybackLikelyToKeepUp(isLikely)
90
- },
91
- onStatusChange: { [weak self] status, error in
92
- self?.avItemStatusDidChange(status, error: error)
93
- },
94
- onTimedMetadataReceived: { [weak self] groups in
95
- self?.callbacks?.playerDidReceiveTimedMetadata(groups)
96
- },
97
- )
98
-
99
- // MARK: - Now-Playing Election
100
-
101
- private let silentAudioPrimer = SilentAudioPrimer()
102
- /// Latched on the first failure of each load; reset when a new track loads.
103
- private var didPrimeNowPlayingElection = false
104
-
105
- // MARK: - Callbacks
106
-
107
- weak var callbacks: TrackPlayerCallbacks? {
108
- didSet {
109
- coordinator.callbacks = callbacks
110
- }
111
- }
112
-
113
- // MARK: - Coordinator Forwarding Properties
114
-
115
- var state: PlaybackState { coordinator.state }
116
- var playbackError: TrackPlayerError.PlaybackError? {
117
- get { coordinator.playbackError }
118
- set { coordinator.playbackError = newValue }
119
- }
120
-
121
- var lastIndex: Int {
122
- get { coordinator.lastIndex }
123
- set { coordinator.lastIndex = newValue }
124
- }
125
-
126
- var lastTrack: Track? {
127
- get { coordinator.lastTrack }
128
- set { coordinator.lastTrack = newValue }
129
- }
130
-
131
- var tracks: [Track] { coordinator.tracks }
132
- var currentIndex: Int { coordinator.currentIndex }
133
- var currentTrack: Track? { coordinator.currentTrack }
134
- var queueSourcePath: String? { coordinator.queueSourcePath }
135
- var nextTracks: [Track] { coordinator.nextTracks }
136
- var previousTracks: [Track] { coordinator.previousTracks }
137
- var isLastInPlaybackOrder: Bool { coordinator.isLastInPlaybackOrder }
138
- var playbackActive: Bool { coordinator.playbackActive }
139
- var sleepTimerManager: SleepTimerManager { coordinator.sleepTimerManager as! SleepTimerManager }
140
- var queue: QueueManager { coordinator.queue }
141
- var loadSeekCoordinator: LoadSeekCoordinator { coordinator.loadSeekCoordinator }
142
- var playingStateManager: PlayingStateManager { coordinator.playingStateManager }
143
- var errorHandler: PlaybackErrorHandler { coordinator.errorHandler }
144
-
145
- var repeatMode: RepeatMode {
146
- get { coordinator.repeatMode }
147
- set { coordinator.repeatMode = newValue }
148
- }
149
-
150
- var shuffleEnabled: Bool {
151
- get { coordinator.shuffleEnabled }
152
- set { coordinator.shuffleEnabled = newValue }
153
- }
154
-
155
- var playWhenReady: Bool {
156
- get { coordinator.playWhenReady }
157
- set { coordinator.playWhenReady = newValue }
158
- }
159
-
160
- /// Time playback must have progressed before a buffer dip counts as a stall, so the initial
161
- /// connect / a seek doesn't read as one. iOS has no native rebuffer-vs-initial signal.
162
- private static let stallGraceSeconds: TimeInterval = 0.5
163
-
164
- /// True while ongoing playback has stalled waiting for data. Approximated from
165
- /// `isPlaybackLikelyToKeepUp`, gated on having actually started and the play intent (so an
166
- /// initial connect / seek doesn't read as a stall). Mirrors the Android `stalled` signal, which
167
- /// the load control distinguishes natively.
168
- var isStalled: Bool {
169
- guard let item = avPlayer.currentItem else { return false }
170
- return currentTime > Self.stallGraceSeconds && playWhenReady && !item.isPlaybackLikelyToKeepUp
171
- }
172
-
173
- /**
174
- Controls the time pitch algorithm applied to each track loaded into the player.
175
- */
176
- var audioTimePitchAlgorithm: AVAudioTimePitchAlgorithm = .timeDomain
177
-
178
- /**
179
- Default remote commands to use for each playing track
180
- */
181
- var remoteCommands: [RemoteCommand] = [] {
182
- didSet {
183
- enableRemoteCommands(remoteCommands)
184
- }
185
- }
186
-
187
- var reasonForWaitingToPlay: AVPlayer.WaitingReason? {
188
- avPlayer.reasonForWaitingToPlay
189
- }
190
-
191
- // MARK: - Getters from AVPlayer
192
-
193
- var currentTime: Double {
194
- let seconds = avPlayer.currentTime().seconds
195
- return seconds.isNaN ? 0 : seconds
196
- }
197
-
198
- var duration: Double {
199
- guard let item = avPlayer.currentItem else { return 0.0 }
200
-
201
- if !item.asset.duration.seconds.isNaN {
202
- return item.asset.duration.seconds
203
- }
204
- if !item.duration.seconds.isNaN {
205
- return item.duration.seconds
206
- }
207
- if let seekable = item.seekableTimeRanges.last?.timeRangeValue.duration.seconds,
208
- !seekable.isNaN
209
- {
210
- return seekable
211
- }
212
- return 0.0
213
- }
214
-
215
- var bufferedPosition: Double {
216
- avPlayer.currentItem?.loadedTimeRanges.last?.timeRangeValue.end.seconds ?? 0
217
- }
218
-
219
- var playerState: PlaybackState {
220
- state
221
- }
222
-
223
- // MARK: - Setters for AVPlayer
224
-
225
- var bufferDuration: Double = 0 {
226
- didSet {
227
- avPlayer.automaticallyWaitsToMinimizeStalling = bufferDuration == 0
228
- mediaLoader.bufferDuration = bufferDuration
229
- }
230
- }
231
-
232
- var automaticallyWaitsToMinimizeStalling: Bool {
233
- get { avPlayer.automaticallyWaitsToMinimizeStalling }
234
- set {
235
- if newValue {
236
- bufferDuration = 0
237
- }
238
- avPlayer.automaticallyWaitsToMinimizeStalling = newValue
239
- }
240
- }
241
-
242
- var volume: Float {
243
- get { avPlayer.volume }
244
- set { avPlayer.volume = newValue }
245
- }
246
-
247
- var isMuted: Bool {
248
- get { avPlayer.isMuted }
249
- set { avPlayer.isMuted = newValue }
250
- }
251
-
252
- var rate: Float {
253
- get { coordinator.rate }
254
- set {
255
- coordinator.rate = newValue
256
- avPlayer.rate = newValue
257
- }
258
- }
259
-
260
- // MARK: - Init
261
-
262
- init(
263
- nowPlayingInfoController: NowPlayingInfoController = NowPlayingInfoController(),
264
- callbacks: TrackPlayerCallbacks? = nil,
265
- ) {
266
- self.nowPlayingInfoController = nowPlayingInfoController
267
- nowPlayingUpdater = NowPlayingUpdater(nowPlayingInfoController: nowPlayingInfoController)
268
- remoteCommandController = RemoteCommandController(callbacks: callbacks)
269
- self.callbacks = callbacks
270
-
271
- let errorHandler = PlaybackErrorHandler(retryHandler: retryManager)
272
- coordinator = PlaybackCoordinator(errorHandler: errorHandler, sleepTimerManager: SleepTimerManager())
273
- coordinator.effectHandler = self
274
- coordinator.callbacks = callbacks
275
-
276
- mediaLoader.delegate = self
277
-
278
- // Configure retry manager
279
- retryManager.shouldRetry = { [weak self] in
280
- self?.playWhenReady ?? false
281
- }
282
- retryManager.onRetry = { [weak self] startFromCurrentTime in
283
- // Re-resolve rather than replay the cached URL: a retry may be recovering
284
- // from an expired short-lived URL/token that only a fresh resolve fixes.
285
- self?.reloadResolving(startFromCurrentTime: startFromCurrentTime)
286
- }
287
-
288
- // Handle command center changes when MPNowPlayingSession is created/destroyed (iOS 16+)
289
- nowPlayingInfoController.onRemoteCommandCenterChanged = { [weak self] newCommandCenter in
290
- MainActor.assumeIsolated {
291
- self?.remoteCommandController.switchCommandCenter(newCommandCenter)
292
- }
293
- }
294
-
295
- setupAVPlayer()
296
- }
297
-
298
- // MARK: - Coordinator Forwarding Methods
299
-
300
- func getPlayback() -> Playback { coordinator.getPlayback() }
301
- func getRepeatMode() -> RepeatMode { coordinator.getRepeatMode() }
302
- func setRepeatMode(_ mode: RepeatMode) { coordinator.setRepeatMode(mode) }
303
- func handlePlayWhenReady(_ playWhenReady: Bool?, action: () throws -> Void) rethrows {
304
- try coordinator.handlePlayWhenReady(playWhenReady, action: action)
305
- }
306
-
307
- func transition(_ event: PlaybackEvent) { coordinator.transition(event) }
308
-
309
- // MARK: - Player Actions
310
-
311
- func load(_ track: Track, playWhenReady: Bool? = nil) {
312
- coordinator.load(track, playWhenReady: playWhenReady)
313
- }
314
-
315
- func togglePlaying() {
316
- switch avPlayer.timeControlStatus {
317
- case .playing, .waitingToPlayAtSpecifiedRate:
318
- pause()
319
- case .paused:
320
- play()
321
- @unknown default:
322
- fatalError("Unknown AVPlayer.timeControlStatus")
323
- }
324
- }
325
-
326
- func play() { coordinator.play() }
327
- func pause() { coordinator.pause() }
328
- func togglePlayback() { coordinator.togglePlayback() }
329
-
330
- func handleInterruptionBegan() { coordinator.handleInterruptionBegan() }
331
- func handleRouteDisconnected() { coordinator.handleRouteDisconnected() }
332
- var willResumeAfterInterruption: Bool { coordinator.willResumeAfterInterruption }
333
- func handleInterruptionEnded(shouldResume: Bool) {
334
- coordinator.handleInterruptionEnded(shouldResume: shouldResume)
335
- }
336
-
337
- func stop() {
338
- coordinator.stop()
339
- if currentTrack?.live != true {
340
- seekTo(0)
341
- }
342
- }
343
-
344
- func reload(startFromCurrentTime: Bool) {
345
- let time = startFromCurrentTime ? resumeTime() : nil
346
- loadAVPlayer()
347
- if let time {
348
- seekTo(time)
349
- }
350
- }
351
-
352
- /// Playback position to resume from on reload, or nil when there is nothing
353
- /// seekable to resume (no item, or an indefinite/live duration).
354
- private func resumeTime() -> Double? {
355
- guard let currentItem = avPlayer.currentItem, !currentItem.duration.isIndefinite else {
356
- return nil
357
- }
358
- return currentItem.currentTime().seconds
359
- }
360
-
361
- /// Re-establish a stalled stream when connectivity is restored.
362
- ///
363
- /// AVPlayer doesn't reliably surface a mid-stream connectivity loss on a live stream as an item
364
- /// failure — it sits in `.waitingToPlayAtSpecifiedRate` (a buffering stall) with no error, so the
365
- /// error-driven `RetryManager` never engages. Without this, the stream stays "reconnecting"
366
- /// indefinitely after the network returns. Mirrors Android, where ExoPlayer surfaces the drop as a
367
- /// retryable load error and re-prepares on restore.
368
- ///
369
- /// Guards: reload only while actually stalled (`.buffering`) with play intent — so a user-initiated
370
- /// pause, or a retry that already gave up (`.error`, past the max retry duration) isn't surprisingly
371
- /// resumed. Skipped when `RetryManager` is already parked waiting for the network: an error *did*
372
- /// schedule a retry that owns its own reload, so we'd otherwise load twice.
373
- func handleNetworkRestored() {
374
- guard playWhenReady,
375
- coordinator.state == .buffering,
376
- !retryManager.isWaitingForNetwork
377
- else { return }
378
- logger.info("Connectivity restored while stalled — re-resolving to reconnect")
379
- // Re-resolve rather than replay the cached URL: a resolver that mints a short-lived URL/token
380
- // may have had it expire during the offline gap, so reconnecting with the stale URL would just
381
- // fail (then recover only via the error path). Matches the error-retry path here, and Android,
382
- // which re-resolves on its network-error reconnect. `reloadResolving` falls back to a plain
383
- // reload when there's no track `src` to resolve.
384
- reloadResolving(startFromCurrentTime: true)
385
- }
386
-
387
- /// Jump to the live edge. No-op for non-live tracks. Live with a seekable
388
- /// window (HLS) seeks to the window end; live without one (non-seekable, e.g.
389
- /// ICY) has no window to seek within, so reconnect to rejoin live.
390
- func seekToLiveEdge() {
391
- guard currentTrack?.live == true, let item = avPlayer.currentItem else { return }
392
- if let range = item.seekableTimeRanges.last?.timeRangeValue, range.duration.seconds > 0 {
393
- avPlayer.seek(to: range.end, toleranceBefore: .zero, toleranceAfter: .zero)
394
- } else {
395
- // Re-resolve on reconnect: this is the stall-recovery path for non-seekable live
396
- // streams, where a short-lived stream URL may have expired during the outage.
397
- reloadResolving(startFromCurrentTime: false)
398
- }
399
- }
400
-
401
- func seekTo(_ seconds: TimeInterval) {
402
- seekTo(seconds, completion: { _ in })
403
- }
404
-
405
- func seekTo(_ seconds: TimeInterval, completion: @escaping @MainActor (Bool) -> Void) {
406
- if state == .loading {
407
- loadSeekCoordinator.capture(position: seconds)
408
- completion(false)
409
- } else if avPlayer.currentItem != nil {
410
- let time = CMTime(seconds: seconds, preferredTimescale: 1000)
411
- let seekSeconds = seconds
412
- let generation = loadSeekCoordinator.generation
413
- avPlayer
414
- .seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { [weak self] finished in
415
- Task { @MainActor in
416
- guard let self else {
417
- completion(false)
418
- return
419
- }
420
- // A completion from before a track change (reset() bumped the
421
- // generation) belongs to the old item — delivering it would emit a
422
- // bogus seek-completed and consume the new track's pending start seek.
423
- guard self.loadSeekCoordinator.isCurrentGeneration(generation) else {
424
- completion(false)
425
- return
426
- }
427
- self.handleSeekCompleted(to: Double(seekSeconds), didFinish: finished)
428
- completion(finished)
429
- }
430
- }
431
- } else {
432
- completion(false)
433
- }
434
- }
435
-
436
- func seekBy(_ offset: TimeInterval) {
437
- let targetTime: TimeInterval
438
- if state == .loading {
439
- targetTime = (loadSeekCoordinator.pendingTime ?? 0) + offset
440
- } else if let currentItem = avPlayer.currentItem {
441
- targetTime = currentItem.currentTime().seconds + offset
442
- } else {
443
- return
444
- }
445
- seekTo(targetTime)
446
- }
447
-
448
- // MARK: - Remote Command Center
449
-
450
- func enableRemoteCommands(_ commands: [RemoteCommand]) {
451
- remoteCommandController.enable(commands: commands)
452
- // Apply current next/previous availability so re-/late-configured commands
453
- // start in the correct enabled state (not unconditionally enabled).
454
- remoteCommandController.setSkipAvailability(
455
- canNext: coordinator.canNext, canPrevious: coordinator.canPrevious,
456
- )
457
- }
458
-
459
- func clear() {
460
- coordinator.clear()
461
- nowPlayingInfoController.unlinkPlayer()
462
- }
463
-
464
- func destroy() {
465
- clear()
466
- remoteCommandController.disableAll()
467
- }
468
-
469
- // MARK: - Progress Updates
470
-
471
- func setProgressUpdateInterval(_ interval: TimeInterval?) {
472
- coordinator.setProgressUpdateInterval(interval)
473
- }
474
-
475
- func setPlaybackIntervalEnabled(_ enabled: Bool) {
476
- coordinator.setPlaybackIntervalEnabled(enabled)
477
- }
478
-
479
- // MARK: - AVPlayer Management
480
-
481
- func setTimePitchingAlgorithmForCurrentItem() {
482
- avPlayer.currentItem?.audioTimePitchAlgorithm = audioTimePitchAlgorithm
483
- }
484
-
485
- func startPlayback() {
486
- avPlayer.play()
487
- if rate != 1.0 {
488
- avPlayer.rate = rate
489
- }
490
- }
491
-
492
- func pausePlayback() {
493
- avPlayer.pause()
494
- }
495
-
496
- func clearCurrentAVItem() {
497
- stopObservingAVPlayerItem()
498
- mediaLoader.clearAsset()
499
- loadSeekCoordinator.reset()
500
- avPlayer.replaceCurrentItem(with: nil)
501
- }
502
-
503
- func startObservingAVPlayerItem(_ avItem: AVPlayerItem) {
504
- playerItemObserver.startObserving(item: avItem)
505
- playerItemNotificationObserver.startObserving(item: avItem)
506
- }
507
-
508
- func stopObservingAVPlayerItem() {
509
- playerItemObserver.stopObservingCurrentItem()
510
- playerItemNotificationObserver.stopObservingCurrentItem()
511
- }
512
-
513
- private func recreateAVPlayer() {
514
- coordinator.playbackError = nil
515
- playerTimeObserver.unregisterForBoundaryTimeEvents()
516
- playerObserver.stopObserving()
517
- stopObservingAVPlayerItem()
518
- clearCurrentAVItem()
519
-
520
- nowPlayingInfoController.unlinkPlayer()
521
-
522
- avPlayer = AVPlayer()
523
- setupAVPlayer()
524
- }
525
-
526
- /// Recover from a media-services reset (`mediaserverd` crashed/reset): every
527
- /// AVPlayer/session handle is invalid, so recreate the player and reload the
528
- /// current track, preserving play intent, so a live stream reconnects instead
529
- /// of going permanently silent. No-op when nothing is loaded.
530
- func handleMediaServicesReset() {
531
- guard currentTrack != nil else { return }
532
- logger.info("Media services were reset — recreating player and reloading current track")
533
- recreateAVPlayer()
534
- reloadResolving(startFromCurrentTime: false)
535
- }
536
-
537
- private func setupAVPlayer() {
538
- avPlayer.allowsExternalPlayback = false
539
-
540
- playerObserver.avPlayer = avPlayer
541
- playerObserver.startObserving()
542
-
543
- playerTimeObserver.avPlayer = avPlayer
544
- playerTimeObserver.registerForBoundaryTimeEvents()
545
-
546
- nowPlayingInfoController.linkPlayer(avPlayer)
547
-
548
- if playWhenReady {
549
- startPlayback()
550
- } else {
551
- avPlayer.defaultRate = rate
552
- }
553
- }
554
-
555
- func loadAVPlayer() {
556
- prepareForReload()
557
- mediaLoader.loadAsset()
558
- }
559
-
560
- /// Teardown shared by the two reload paths: recreate the player after a
561
- /// terminal error, otherwise cancel in-flight loading and drop the current
562
- /// asset, then enter the loading state.
563
- private func prepareForReload() {
564
- if state == .error {
565
- // A restart from terminal error begins a new load of the same track:
566
- // fresh budgets, unproven playback (see resetForNewLoad). Automatic
567
- // retries never enter this branch — the retry loop keeps the state
568
- // non-terminal — so only retry() / play-from-error land here.
569
- coordinator.errorHandler.resetForNewLoad()
570
- recreateAVPlayer()
571
- } else {
572
- mediaLoader.cancelAll()
573
- stopObservingAVPlayerItem()
574
- pausePlayback()
575
- mediaLoader.clearAsset()
576
- }
577
- transition(.trackLoading)
578
- }
579
-
580
- /// Reload by re-running the resolver (fresh URL / headers / user-agent)
581
- /// instead of recreating the asset from the already-resolved URL. Used by the
582
- /// retry path so consumers whose resolver mints short-lived URLs or auth
583
- /// tokens recover once those expire — a plain `reload()` would replay the
584
- /// stale URL and keep failing. Falls back to `reload()` when there's no track
585
- /// `src` to resolve.
586
- func reloadResolving(startFromCurrentTime: Bool) {
587
- guard let track = currentTrack, let src = track.src else {
588
- reload(startFromCurrentTime: startFromCurrentTime)
589
- return
590
- }
591
- let time = startFromCurrentTime ? resumeTime() : nil
592
- prepareForReload()
593
- mediaLoader.resolveAndLoad(src: src, track: track)
594
- if let time {
595
- seekTo(time)
596
- }
597
- }
598
-
599
- func unloadAVPlayer() {
600
- clearCurrentAVItem()
601
- transition(.trackUnloaded)
602
- }
603
-
604
- // MARK: - Observer Callbacks (map AVFoundation → coordinator)
605
-
606
- /// Resolve an end-of-item notification: a genuine end advances the queue; a
607
- /// dropped live stream or a mid-stream underrun reported as EOF is recovered
608
- /// instead of advancing/stopping (see `EndOfTrackJudgement`).
609
- private func handleDidPlayToEndTime() {
610
- let judgement = EndOfTrackJudgement(
611
- isLive: currentTrack?.live == true,
612
- currentTime: currentTime,
613
- duration: duration,
614
- )
615
- switch judgement.outcome {
616
- case .ended:
617
- coordinator.handleTrackDidPlayToEndTime()
618
- case .stalled:
619
- recoverFromStall(reconnectIfLive: true)
620
- }
621
- }
622
-
623
- /// Recover from a stall while the play intent still holds. A live stream that
624
- /// ran out of data (`reconnectIfLive`) rejoins the edge — reloading when there
625
- /// is no seekable window; otherwise just re-issue play() to un-park AVPlayer
626
- /// from `.waitingToPlayAtSpecifiedRate` (data may resume on the same
627
- /// connection; a genuine drop is handled by the retry / network-restore paths).
628
- private func recoverFromStall(reconnectIfLive: Bool) {
629
- guard playWhenReady else { return }
630
- if reconnectIfLive, currentTrack?.live == true {
631
- seekToLiveEdge()
632
- } else {
633
- startPlayback()
634
- }
635
- }
636
-
637
- /// Previous time-control transition + whether its wait reason was
638
- /// `.noItemToPlay`, retained to detect the AirPlay waiting→paused stall.
639
- private var previousTimeControlStatus: PlayerTimeControlStatus?
640
- private var previousWaitingReasonWasNoItemToPlay = false
641
-
642
- /// True when the active audio route is AirPlay.
643
- private var isAirPlayRoute: Bool {
644
- AVAudioSession.sharedInstance().currentRoute.outputs.contains { $0.portType == .airPlay }
645
- }
646
-
647
- private func avPlayerDidChangeTimeControlStatus(_ status: AVPlayer.TimeControlStatus) {
648
- let mapped: PlayerTimeControlStatus
649
- switch status {
650
- case .paused: mapped = .paused
651
- case .waitingToPlayAtSpecifiedRate: mapped = .waitingToPlayAtSpecifiedRate
652
- case .playing: mapped = .playing
653
- @unknown default: return
654
- }
655
- recoverIfAirPlayStalled(transitioningTo: mapped)
656
- coordinator.avPlayerDidChangeTimeControlStatus(mapped)
657
- }
658
-
659
- /// AVPlayer can silently strand us in .paused after a `.noItemToPlay` wait
660
- /// over AirPlay while we still intend to play — re-issue play() on exactly
661
- /// that transition. Also records this transition for the next comparison.
662
- private func recoverIfAirPlayStalled(transitioningTo current: PlayerTimeControlStatus) {
663
- let stalled = AirPlayStallJudgement(
664
- previous: previousTimeControlStatus,
665
- current: current,
666
- previousWaitingReasonWasNoItemToPlay: previousWaitingReasonWasNoItemToPlay,
667
- isAirPlay: isAirPlayRoute,
668
- playWhenReady: playWhenReady,
669
- ).shouldNudge
670
- previousTimeControlStatus = current
671
- previousWaitingReasonWasNoItemToPlay =
672
- current == .waitingToPlayAtSpecifiedRate && avPlayer.reasonForWaitingToPlay == .noItemToPlay
673
- if stalled {
674
- logger.info("AirPlay waiting→paused stall detected — nudging play()")
675
- startPlayback()
676
- }
677
- }
678
-
679
- private func avPlayerStatusDidChange(_ status: AVPlayer.Status) {
680
- if status == .failed {
681
- coordinator.avPlayerStatusDidFail(
682
- error: avPlayer.currentItem?.error,
683
- httpStatusCode: currentItemHttpStatusCode,
684
- )
685
- }
686
- }
687
-
688
- private func avItemStatusDidChange(_ status: AVPlayerItem.Status, error: Error?) {
689
- let mapped: PlayerItemStatus
690
- switch status {
691
- case .unknown: mapped = .unknown
692
- case .readyToPlay: mapped = .readyToPlay
693
- case .failed: mapped = .failed
694
- @unknown default: return
695
- }
696
- coordinator.avItemStatusDidChange(
697
- mapped,
698
- error: error ?? avPlayer.currentItem?.error,
699
- httpStatusCode: currentItemHttpStatusCode,
700
- )
701
- }
702
-
703
- /// The HTTP status the server last answered the current item with, if any.
704
- ///
705
- /// `AVPlayerItem.error` never carries it — a 404 arrives as an opaque
706
- /// CoreMedia error — so the error log is the only place it surfaces. The log
707
- /// is per-item, so its entries belong to the track that just failed; picking
708
- /// among them is `PlaybackErrorHandler`'s job, where it is testable
709
- /// (`AVPlayerItemErrorLogEvent` cannot be constructed).
710
- private var currentItemHttpStatusCode: Int? {
711
- guard let events = avPlayer.currentItem?.errorLog()?.events else { return nil }
712
- return PlaybackErrorHandler.httpStatusCode(
713
- fromErrorStatusCodes: events.map(\.errorStatusCode),
714
- )
715
- }
716
-
717
- private func avItemDidUpdatePlaybackLikelyToKeepUp(_ playbackLikelyToKeepUp: Bool) {
718
- guard playbackLikelyToKeepUp else { return }
719
- guard avPlayer.currentItem != nil else { return }
720
-
721
- // Execute any pending seek that arrived after MediaLoader completed
722
- if loadSeekCoordinator.executeIfPending(on: avPlayer, delegate: self) {
723
- return
724
- }
725
-
726
- coordinator.avItemDidUpdatePlaybackLikelyToKeepUp(playbackLikelyToKeepUp)
727
- }
728
-
729
- func handleSeekCompleted(to seconds: Double, didFinish: Bool) {
730
- if loadSeekCoordinator.seekDidComplete(on: avPlayer, delegate: self), state == .loading {
731
- coordinator.handleSeekCompleted(to: seconds, didFinish: didFinish)
732
- }
733
- callbacks?.playerDidCompleteSeek(position: seconds, didFinish: didFinish)
734
- }
735
- }
736
-
737
- // MARK: - Playback State Persistence
738
-
739
- extension TrackPlayer {
740
- /// Snapshot the current player state to UserDefaults so a cold-start resume
741
- /// can restore it without the JS runtime. Live streams persist `positionMs = nil`.
742
- private func persistPlaybackState() {
743
- guard let track = currentTrack else { return }
744
- // At .ended the position equals the duration — persisting it would make a
745
- // cold-start resume seek to the end and instantly re-end (Android twin:
746
- // savePositionZero).
747
- let positionMs: Double? =
748
- (track.live == true) ? nil : (state == .ended ? 0 : currentTime * 1000)
749
- playbackStateStore.save(
750
- PersistedPlaybackState(
751
- track: JsonTrack(from: track),
752
- positionMs: positionMs,
753
- repeatMode: repeatMode.persistedString,
754
- shuffleEnabled: shuffleEnabled,
755
- playbackSpeed: rate,
756
- )
757
- )
758
- }
759
-
760
- /// Start a 5 s repeating save while playback is active. A previous task is
761
- /// cancelled first so there is never more than one running at a time.
762
- private func startPeriodicSave() {
763
- periodicSaveTask?.cancel()
764
- periodicSaveTask = Task { @MainActor [weak self] in
765
- while !Task.isCancelled {
766
- try? await Task.sleep(nanoseconds: 5_000_000_000)
767
- guard !Task.isCancelled else { return }
768
- self?.persistPlaybackState()
769
- }
770
- }
771
- }
772
-
773
- private func stopPeriodicSave() {
774
- periodicSaveTask?.cancel()
775
- periodicSaveTask = nil
776
- }
777
- }
778
-
779
- private extension RepeatMode {
780
- /// String representation stored in `PersistedPlaybackState.repeatMode`.
781
- var persistedString: String {
782
- switch self {
783
- case .off: "off"
784
- case .track: "track"
785
- case .queue: "queue"
786
- }
787
- }
788
- }
789
-
790
- // MARK: - PlaybackEffectHandler
791
-
792
- extension TrackPlayer: PlaybackEffectHandler {
793
- var hasLoadedAsset: Bool {
794
- mediaLoader.asset != nil
795
- }
796
-
797
- func clearCurrentItem() {
798
- clearCurrentAVItem()
799
- }
800
-
801
- func stopObservingCurrentItem() {
802
- stopObservingAVPlayerItem()
803
- }
804
-
805
- func loadTrack(src: String, track: Track) {
806
- didPrimeNowPlayingElection = false
807
- // A new track starts unproven: its failures get the short first-connect
808
- // budget until it renders audio. Reloads of the current track (retry,
809
- // play-from-error) deliberately keep the flag.
810
- retryManager.hasPlayed = false
811
- mediaLoader.resolveAndLoad(src: src, track: track)
812
- }
813
-
814
- func reloadTrack(startFromCurrentTime: Bool) {
815
- // Re-resolve rather than replay the cached URL: play-from-.error typically happens long
816
- // after the failure (an expired signed URL would just re-fail, often non-retryably),
817
- // matching every other retry path (retry, network restore, media-services reset).
818
- reloadResolving(startFromCurrentTime: startFromCurrentTime)
819
- }
820
-
821
- func unloadTrack() {
822
- unloadAVPlayer()
823
- }
824
-
825
- func cancelMediaLoading() {
826
- mediaLoader.cancelAll()
827
- }
828
-
829
- func seekToStart() {
830
- seekTo(0)
831
- }
832
-
833
- func replayCurrentTrack() {
834
- seekTo(0) { [weak self] succeeded in
835
- if succeeded { self?.play() }
836
- }
837
- }
838
-
839
- func updateNowPlayingState(playWhenReady: Bool) {
840
- nowPlayingInfoController.setPlaybackState(playing: playWhenReady)
841
- if playWhenReady {
842
- startPeriodicSave()
843
- } else {
844
- stopPeriodicSave()
845
- persistPlaybackState()
846
- }
847
- }
848
-
849
- func loadNowPlayingMetadata(for track: Track) {
850
- nowPlayingUpdater.loadMetaValues(for: track)
851
- persistPlaybackState()
852
- }
853
-
854
- func clearNowPlaying() {
855
- nowPlayingInfoController.clear()
856
- }
857
-
858
- func updateRemoteRepeatMode(_ mode: RepeatMode) {
859
- remoteCommandController.updateRepeatMode(mode)
860
- }
861
-
862
- func updateRemoteShuffleMode(_ enabled: Bool) {
863
- remoteCommandController.updateShuffleMode(enabled)
864
- }
865
-
866
- func updateSkipAvailability(canNext: Bool, canPrevious: Bool) {
867
- remoteCommandController.setSkipAvailability(canNext: canNext, canPrevious: canPrevious)
868
- }
869
- }
870
-
871
- // MARK: - SeekCompletionHandler
872
-
873
- extension TrackPlayer: SeekCompletionHandler {}
874
-
875
- // MARK: - TrackSelectionPlayer
876
-
877
- extension TrackPlayer: TrackSelectionPlayer {}
878
-
879
- // MARK: - Queue Methods (thin forwarders)
880
-
881
- extension TrackPlayer {
882
- func replace(_ index: Int, _ track: Track) {
883
- coordinator.replace(index, track)
884
- }
885
-
886
- func setQueue(
887
- _ newTracks: [Track],
888
- initialIndex: Int = 0,
889
- startPositionMs: Double? = nil,
890
- playWhenReady: Bool? = nil,
891
- sourcePath: String? = nil,
892
- ) {
893
- coordinator.setQueue(
894
- newTracks,
895
- initialIndex: initialIndex,
896
- startPositionMs: startPositionMs,
897
- playWhenReady: playWhenReady,
898
- sourcePath: sourcePath,
899
- )
900
- }
901
-
902
- func add(_ tracks: [Track], initialIndex: Int? = nil, playWhenReady: Bool? = nil) {
903
- coordinator.add(tracks, initialIndex: initialIndex, playWhenReady: playWhenReady)
904
- }
905
-
906
- func add(_ tracks: [Track], at index: Int) throws {
907
- try coordinator.add(tracks, at: index)
908
- }
909
-
910
- func next() {
911
- coordinator.next()
912
- }
913
-
914
- func previous() {
915
- coordinator.previous()
916
- }
917
-
918
- func remove(_ index: Int) throws {
919
- try coordinator.remove(index)
920
- }
921
-
922
- func skipTo(_ index: Int, playWhenReady: Bool? = nil) throws {
923
- try coordinator.skipTo(index, playWhenReady: playWhenReady)
924
- }
925
-
926
- func move(fromIndex: Int, toIndex: Int) throws {
927
- try coordinator.move(fromIndex: fromIndex, toIndex: toIndex)
928
- }
929
-
930
- func removeUpcomingTracks() {
931
- coordinator.removeUpcomingTracks()
932
- }
933
-
934
- func replay() {
935
- coordinator.replay()
936
- }
937
- }
938
-
939
- // MARK: - MediaLoaderDelegate
940
-
941
- extension TrackPlayer: MediaLoaderDelegate {
942
- func mediaLoaderDidPrepareItem(_ item: AVPlayerItem) {
943
- nowPlayingInfoController.prepareItem(item)
944
- avPlayer.replaceCurrentItem(with: item)
945
- startObservingAVPlayerItem(item)
946
- if playWhenReady { startPlayback() }
947
-
948
- if !loadSeekCoordinator.executeIfPending(on: avPlayer, delegate: self) {
949
- if item.isPlaybackLikelyToKeepUp {
950
- avItemDidUpdatePlaybackLikelyToKeepUp(true)
951
- }
952
- }
953
- }
954
-
955
- func mediaLoaderDidFailWithRetryableError(_ error: Error) {
956
- attachPublishingCarrierItemIfNeeded()
957
- primeNowPlayingElectionIfNeeded()
958
- coordinator.errorHandler.handleError(error, context: .mediaLoad)
959
- }
960
-
961
- func mediaLoaderDidFailWithUnplayableTrack() {
962
- attachPublishingCarrierItemIfNeeded()
963
- primeNowPlayingElectionIfNeeded()
964
- transition(.errorOccurred(.trackWasUnplayable))
965
- }
966
-
967
- /// `MPNowPlayingSession` publishes only through the linked player's current item, so
968
- /// a load that fails before item creation (a dead host on a cold start) leaves the
969
- /// player item-less: the system never surfaces the app on the lock screen / CarPlay
970
- /// — no metadata, no transport buttons — and the failure line has nowhere to appear.
971
- /// Attach the failed asset's item purely as a publishing carrier. Deliberately not
972
- /// observed: its `.failed` status must not re-report the error the loader already
973
- /// delivered. A successful load (retry, or the next track) replaces it with the
974
- /// real, observed item via `mediaLoaderDidPrepareItem`; retry reloads leave it in
975
- /// place (`prepareForReload` drops only the asset), so the surface doesn't flicker.
976
- private func attachPublishingCarrierItemIfNeeded() {
977
- guard avPlayer.currentItem == nil, let asset = mediaLoader.asset else { return }
978
- logger.notice("Attaching publishing-carrier item for failed load")
979
- let item = AVPlayerItem(asset: asset)
980
- nowPlayingInfoController.prepareItem(item)
981
- avPlayer.replaceCurrentItem(with: item)
982
- }
983
-
984
- /// A failed load renders no audio, and without rendered audio iOS never elects the
985
- /// app onto the system now-playing surfaces — the published failure metadata and
986
- /// transport controls stay invisible (see `SilentAudioPrimer`). On the first
987
- /// failure of a play-intended load, render a silence burst to claim the surface.
988
- /// Once per load: the retry loop re-fails every few seconds, and one burst either
989
- /// elected us or never will. Does not touch the retry loop itself.
990
- private func primeNowPlayingElectionIfNeeded() {
991
- guard playWhenReady, !didPrimeNowPlayingElection else { return }
992
- didPrimeNowPlayingElection = true
993
- silentAudioPrimer.prime()
994
- }
995
-
996
- func mediaLoaderDidFailWithError(_ error: TrackPlayerError.PlaybackError) {
997
- // No carrier here: an invalid/missing src never produced an asset. The
998
- // direct-center publish still carries the metadata once elected.
999
- primeNowPlayingElectionIfNeeded()
1000
- transition(.errorOccurred(error))
1001
- }
1002
-
1003
- func mediaLoaderDidReceiveCommonMetadata(_ items: [AVMetadataItem]) {
1004
- callbacks?.playerDidReceiveCommonMetadata(items)
1005
- }
1006
-
1007
- func mediaLoaderDidReceiveChapterMetadata(_ groups: [AVTimedMetadataGroup]) {
1008
- callbacks?.playerDidReceiveChapterMetadata(groups)
1009
- }
1010
-
1011
- func mediaLoaderDidReceiveTimedMetadata(_ groups: [AVTimedMetadataGroup]) {
1012
- callbacks?.playerDidReceiveTimedMetadata(groups)
1013
- }
1014
- }