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
@@ -2,46 +2,43 @@
2
2
  import Foundation
3
3
  import Testing
4
4
 
5
- /// Helper to build a coordinator with mocks for testing.
5
+ /// Helper to build a player with mocks for testing.
6
6
  @MainActor
7
7
  private func makeCoordinator() -> (
8
- coordinator: PlaybackCoordinator,
9
- effectHandler: MockPlaybackEffectHandler,
10
- callbacks: MockPlaybackCoordinatorCallbacks,
11
- sleepTimer: MockSleepTimerHandling,
8
+ player: Player,
9
+ engine: MockPlayerEngine,
10
+ callbacks: MockPlayerCallbacks,
11
+ sleepTimer: SleepTimerManager,
12
12
  ) {
13
- let retryHandler = MockRetryHandling()
14
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
15
- let sleepTimer = MockSleepTimerHandling()
16
- let coordinator = PlaybackCoordinator(errorHandler: errorHandler, sleepTimerManager: sleepTimer)
17
- let effectHandler = MockPlaybackEffectHandler()
18
- let callbacks = MockPlaybackCoordinatorCallbacks()
19
- coordinator.effectHandler = effectHandler
20
- coordinator.callbacks = callbacks
21
- return (coordinator, effectHandler, callbacks, sleepTimer)
13
+ let sleepTimer = SleepTimerManager(scheduler: ManualSleepTimerScheduler())
14
+ let engine = MockPlayerEngine()
15
+ let player = makePlayer(engine: engine, retries: false, sleepTimer: sleepTimer)
16
+ let callbacks = MockPlayerCallbacks()
17
+ player.callbacks = callbacks
18
+ return (player, engine, callbacks, sleepTimer)
22
19
  }
23
20
 
24
- /// Helper to load a track into the coordinator's queue so it has a currentTrack.
21
+ /// Helper to load a track into the player's queue so it has a currentTrack.
25
22
  @MainActor
26
23
  private func loadTrack(
27
- _ coordinator: PlaybackCoordinator,
24
+ _ player: Player,
28
25
  id: String = "t1",
29
26
  src: String? = "https://example.com/audio.mp3",
30
27
  title: String = "Test Track",
31
28
  ) {
32
29
  let track = Track(id: id, src: src, title: title)
33
- coordinator.setQueue([track])
30
+ player.setQueue([track])
34
31
  }
35
32
 
36
33
  // MARK: - Transition + Side Effects
37
34
 
38
- @Suite("PlaybackCoordinator - transition + side effects")
35
+ @Suite("Player - transition + side effects")
39
36
  struct TransitionTests {
40
37
  @Test @MainActor
41
38
  func toReady_startsPlayback_whenPlayWhenReady() {
42
39
  let (c, eh, _, _) = makeCoordinator()
43
40
  loadTrack(c)
44
- c.playWhenReady = true
41
+ c.setPlayWhenReady(true)
45
42
  eh.startPlaybackCallCount = 0 // reset from playWhenReady setter
46
43
 
47
44
  c.transition(.bufferingSufficient)
@@ -77,19 +74,19 @@ struct TransitionTests {
77
74
  c.transition(.errorOccurred(.playbackFailed))
78
75
 
79
76
  #expect(c.state == .error)
80
- #expect(c.playbackError == .playbackFailed)
77
+ #expect(c.errorStatus.error == .playbackFailed)
81
78
  }
82
79
 
83
80
  @Test @MainActor
84
81
  func leavingError_clearsPlaybackError() {
85
82
  let (c, _, _, _) = makeCoordinator()
86
83
  c.transition(.errorOccurred(.playbackFailed))
87
- #expect(c.playbackError == .playbackFailed)
84
+ #expect(c.errorStatus.error == .playbackFailed)
88
85
 
89
86
  c.transition(.trackLoading)
90
87
 
91
88
  #expect(c.state == .loading)
92
- #expect(c.playbackError == nil)
89
+ #expect(c.errorStatus.error == nil)
93
90
  }
94
91
 
95
92
  @Test @MainActor
@@ -103,7 +100,7 @@ struct TransitionTests {
103
100
 
104
101
  // State stays .error but error is updated
105
102
  #expect(c.state == .error)
106
- #expect(c.playbackError == .notConnectedToInternet)
103
+ #expect(c.errorStatus.error == .notConnectedToInternet)
107
104
  #expect(cb.playbackChanges.count == 1)
108
105
  #expect(cb.errorEvents.count == 1)
109
106
  }
@@ -114,29 +111,21 @@ struct TransitionTests {
114
111
  /// Advisory errors from the retry loop: attached to the current non-terminal
115
112
  /// state via `playerDidChangePlayback`, never a state transition and never a
116
113
  /// `playerDidError`.
117
- /// Builds a coordinator whose retry handler will accept the error and
118
- /// pretend the (instant) retry succeeded, so only the advisory path runs.
119
- /// The effect handler is returned solely to keep the weak reference alive.
114
+ /// Builds a player whose retries reload at once, so the advisory path is what shows.
120
115
  @MainActor
121
116
  private func makeRetrying() -> (
122
- PlaybackCoordinator, MockRetryHandling, MockPlaybackCoordinatorCallbacks,
123
- MockPlaybackEffectHandler
117
+ Player, RetryLoop, MockPlayerCallbacks,
118
+ MockPlayerEngine,
124
119
  ) {
125
- let retryHandler = MockRetryHandling()
126
- retryHandler.isRetryableResult = true
127
- retryHandler.attemptRetryResult = true
128
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
129
- let coordinator = PlaybackCoordinator(
130
- errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
131
- )
132
- let effectHandler = MockPlaybackEffectHandler()
133
- coordinator.effectHandler = effectHandler
134
- let callbacks = MockPlaybackCoordinatorCallbacks()
135
- coordinator.callbacks = callbacks
136
- return (coordinator, retryHandler, callbacks, effectHandler)
120
+ let retryHandler = makeRetryLoop()
121
+ let engine = MockPlayerEngine()
122
+ let player = makePlayer(engine: engine, retry: retryHandler)
123
+ let callbacks = MockPlayerCallbacks()
124
+ player.callbacks = callbacks
125
+ return (player, retryHandler, callbacks, engine)
137
126
  }
138
127
 
139
- @Suite("PlaybackCoordinator - retrying errors")
128
+ @Suite("Player - retrying errors")
140
129
  struct RetryingErrorTests {
141
130
  @Test @MainActor
142
131
  func retryingError_attachesToCurrentState() async {
@@ -144,16 +133,16 @@ struct RetryingErrorTests {
144
133
  c.transition(.trackLoading)
145
134
  cb.playbackChanges.removeAll()
146
135
 
147
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
136
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
148
137
 
149
138
  #expect(c.state == .loading)
150
- #expect(c.playbackError == .hostUnreachable)
139
+ #expect(c.errorStatus.error == .hostUnreachable)
151
140
  #expect(cb.playbackChanges.count == 1)
152
141
  #expect(cb.playbackChanges.last?.state == .loading)
153
142
  #expect(cb.playbackChanges.last?.error?.retrying == true)
154
143
  // Advisory, not terminal: the error event is reserved for .error.
155
144
  #expect(cb.errorEvents.isEmpty)
156
- await c.errorHandler.pendingRetryTask?.value
145
+ await c.retry.pendingTask?.value
157
146
  }
158
147
 
159
148
  /// Every failed attempt re-reports through `handleError`; identical repeats
@@ -164,10 +153,10 @@ struct RetryingErrorTests {
164
153
  c.transition(.trackLoading)
165
154
  cb.playbackChanges.removeAll()
166
155
 
167
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
168
- await c.errorHandler.pendingRetryTask?.value
169
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
170
- await c.errorHandler.pendingRetryTask?.value
156
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
157
+ await c.retry.pendingTask?.value
158
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
159
+ await c.retry.pendingTask?.value
171
160
 
172
161
  #expect(cb.playbackChanges.count == 1)
173
162
  }
@@ -180,11 +169,11 @@ struct RetryingErrorTests {
180
169
  c.transition(.avPlayerPlaying)
181
170
  cb.playbackChanges.removeAll()
182
171
 
183
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .playback)
172
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .playback)
184
173
 
185
- #expect(c.playbackError == nil)
174
+ #expect(c.errorStatus.error == nil)
186
175
  #expect(cb.playbackChanges.isEmpty)
187
- await c.errorHandler.pendingRetryTask?.value
176
+ await c.retry.pendingTask?.value
188
177
  }
189
178
 
190
179
  /// Reaching .ready proves the retry recovered: the advisory error must not
@@ -193,13 +182,13 @@ struct RetryingErrorTests {
193
182
  func recovery_clearsTheRetryingError() async {
194
183
  let (c, _, cb, _) = makeRetrying()
195
184
  c.transition(.trackLoading)
196
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
197
- await c.errorHandler.pendingRetryTask?.value
185
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
186
+ await c.retry.pendingTask?.value
198
187
 
199
188
  c.transition(.loadSeekCompleted)
200
189
 
201
190
  #expect(c.state == .ready)
202
- #expect(c.playbackError == nil)
191
+ #expect(c.errorStatus.error == nil)
203
192
  #expect(cb.playbackChanges.last?.error == nil)
204
193
  }
205
194
 
@@ -209,12 +198,12 @@ struct RetryingErrorTests {
209
198
  func exhaustion_hardensIntoTerminalError() async {
210
199
  let (c, retryHandler, cb, _) = makeRetrying()
211
200
  c.transition(.trackLoading)
212
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
213
- await c.errorHandler.pendingRetryTask?.value
201
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
202
+ await c.retry.pendingTask?.value
214
203
 
215
- retryHandler.attemptRetryResult = false
216
- c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
217
- await c.errorHandler.pendingRetryTask?.value
204
+ retryHandler.shouldRetry = { false }
205
+ c.retry.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
206
+ await c.retry.pendingTask?.value
218
207
 
219
208
  #expect(c.state == .error)
220
209
  #expect(cb.playbackChanges.last?.state == .error)
@@ -225,7 +214,7 @@ struct RetryingErrorTests {
225
214
 
226
215
  // MARK: - emitStateChange
227
216
 
228
- @Suite("PlaybackCoordinator - emitStateChange")
217
+ @Suite("Player - emitStateChange")
229
218
  struct EmitStateChangeTests {
230
219
  @Test @MainActor
231
220
  func alwaysEmits_playerDidChangePlayback() {
@@ -275,7 +264,7 @@ struct EmitStateChangeTests {
275
264
 
276
265
  // MARK: - Observer Guards
277
266
 
278
- @Suite("PlaybackCoordinator - observer guards")
267
+ @Suite("Player - observer guards")
279
268
  struct ObserverGuardTests {
280
269
  @Test @MainActor
281
270
  func timeControlStatus_paused_nearTrackEnd_ignored() {
@@ -312,7 +301,7 @@ struct ObserverGuardTests {
312
301
  eh.hasLoadedAsset = true
313
302
  eh.currentTime = 10
314
303
  eh.duration = 100
315
- c.playWhenReady = false
304
+ c.setPlayWhenReady(false)
316
305
 
317
306
  c.avPlayerDidChangeTimeControlStatus(.paused)
318
307
 
@@ -326,7 +315,7 @@ struct ObserverGuardTests {
326
315
  eh.hasLoadedAsset = true
327
316
  eh.currentTime = 10
328
317
  eh.duration = 100
329
- c.playWhenReady = true
318
+ c.setPlayWhenReady(true)
330
319
 
331
320
  c.avPlayerDidChangeTimeControlStatus(.paused)
332
321
 
@@ -414,28 +403,39 @@ struct ObserverGuardTests {
414
403
 
415
404
  // MARK: - playWhenReady
416
405
 
417
- @Suite("PlaybackCoordinator - playWhenReady")
406
+ @Suite("Player - playWhenReady")
418
407
  struct PlayWhenReadyTests {
419
408
  @Test @MainActor
420
409
  func true_fromError_triggersReload() {
421
410
  let (c, eh, _, _) = makeCoordinator()
411
+ loadTrack(c)
412
+ eh.emit(.itemPrepared(likelyToKeepUp: false))
422
413
  c.transition(.errorOccurred(.playbackFailed))
414
+ eh.resumePosition = 42
415
+ let resolvesBefore = eh.resolveAndLoadCalls.count
423
416
 
424
- c.playWhenReady = true
417
+ c.setPlayWhenReady(true)
425
418
 
426
- #expect(eh.reloadTrackCalls.count == 1)
427
- #expect(eh.reloadTrackCalls.first == true) // startFromCurrentTime for error
419
+ // A restart from .error is a new Load on a new AVPlayer, resuming where it failed.
420
+ #expect(eh.recreateCount == 1)
421
+ #expect(eh.resolveAndLoadCalls.count == resolvesBefore + 1)
422
+ #expect(c.state == .loading)
423
+ #expect(c.deferredSeek.target == 42)
428
424
  }
429
425
 
430
426
  @Test @MainActor
431
427
  func true_fromStopped_triggersReload() {
432
428
  let (c, eh, _, _) = makeCoordinator()
429
+ loadTrack(c)
433
430
  c.transition(.stopped)
431
+ eh.resumePosition = 42
432
+ let resolvesBefore = eh.resolveAndLoadCalls.count
434
433
 
435
- c.playWhenReady = true
434
+ c.setPlayWhenReady(true)
436
435
 
437
- #expect(eh.reloadTrackCalls.count == 1)
438
- #expect(eh.reloadTrackCalls.first == false) // startFromCurrentTime for stopped
436
+ #expect(eh.recreateCount == 0)
437
+ #expect(eh.resolveAndLoadCalls.count == resolvesBefore + 1)
438
+ #expect(c.deferredSeek.target == nil) // a stop restarts from the start
439
439
  }
440
440
 
441
441
  @Test @MainActor
@@ -443,7 +443,7 @@ struct PlayWhenReadyTests {
443
443
  let (c, eh, _, _) = makeCoordinator()
444
444
  c.transition(.bufferingSufficient) // .ready state
445
445
 
446
- c.playWhenReady = true
446
+ c.setPlayWhenReady(true)
447
447
 
448
448
  #expect(eh.startPlaybackCallCount >= 1)
449
449
  }
@@ -452,10 +452,10 @@ struct PlayWhenReadyTests {
452
452
  func false_notLoading_pausesPlayback() {
453
453
  let (c, eh, _, _) = makeCoordinator()
454
454
  c.transition(.avPlayerPlaying)
455
- c.playWhenReady = true
455
+ c.setPlayWhenReady(true)
456
456
  eh.pausePlaybackCallCount = 0
457
457
 
458
- c.playWhenReady = false
458
+ c.setPlayWhenReady(false)
459
459
 
460
460
  #expect(eh.pausePlaybackCallCount == 1)
461
461
  }
@@ -467,7 +467,7 @@ struct PlayWhenReadyTests {
467
467
  eh.startPlaybackCallCount = 0
468
468
  eh.pausePlaybackCallCount = 0
469
469
 
470
- c.playWhenReady = true
470
+ c.setPlayWhenReady(true)
471
471
 
472
472
  // During loading, should NOT call start/pause
473
473
  #expect(eh.startPlaybackCallCount == 0)
@@ -478,7 +478,7 @@ struct PlayWhenReadyTests {
478
478
  func emitsCallback_onChange() {
479
479
  let (c, _, cb, _) = makeCoordinator()
480
480
 
481
- c.playWhenReady = true
481
+ c.setPlayWhenReady(true)
482
482
 
483
483
  #expect(cb.playWhenReadyChanges.count == 1)
484
484
  #expect(cb.playWhenReadyChanges.first == true)
@@ -487,7 +487,7 @@ struct PlayWhenReadyTests {
487
487
 
488
488
  // MARK: - Skip availability (next/previous greying)
489
489
 
490
- @Suite("PlaybackCoordinator - skip availability")
490
+ @Suite("Player - skip availability")
491
491
  struct SkipAvailabilityCoordinatorTests {
492
492
  private static func multiTrack() -> [Track] {
493
493
  [
@@ -541,25 +541,25 @@ struct SkipAvailabilityCoordinatorTests {
541
541
 
542
542
  // MARK: - handleCurrentTrackChanged
543
543
 
544
- @Suite("PlaybackCoordinator - handleCurrentTrackChanged")
544
+ @Suite("Player - handleCurrentTrackChanged")
545
545
  struct HandleCurrentTrackChangedTests {
546
546
  @Test @MainActor
547
547
  func clearsError_beforeTransition() {
548
548
  let (c, eh, _, _) = makeCoordinator()
549
549
  eh.hasLoadedAsset = true
550
550
  c.transition(.errorOccurred(.playbackFailed))
551
- #expect(c.playbackError != nil)
551
+ #expect(c.errorStatus.error != nil)
552
552
 
553
553
  loadTrack(c)
554
554
 
555
555
  // After loading a track, the error should be cleared
556
- #expect(c.playbackError == nil)
556
+ #expect(c.errorStatus.error == nil)
557
557
  }
558
558
 
559
559
  @Test @MainActor
560
560
  func preservesPlayWhenReady_acrossLoading() {
561
561
  let (c, _, _, _) = makeCoordinator()
562
- c.playWhenReady = true
562
+ c.setPlayWhenReady(true)
563
563
 
564
564
  loadTrack(c)
565
565
 
@@ -575,7 +575,7 @@ struct HandleCurrentTrackChangedTests {
575
575
  c.setQueue([track])
576
576
 
577
577
  #expect(c.state == .error)
578
- #expect(c.playbackError == .invalidSourceUrl("nil"))
578
+ #expect(c.errorStatus.error == .invalidSourceUrl("nil"))
579
579
  #expect(cb.errorEvents.count >= 1)
580
580
  }
581
581
 
@@ -595,19 +595,19 @@ struct HandleCurrentTrackChangedTests {
595
595
 
596
596
  // MARK: - handleTrackDidPlayToEndTime
597
597
 
598
- @Suite("PlaybackCoordinator - handleTrackDidPlayToEndTime")
598
+ @Suite("Player - handleTrackDidPlayToEndTime")
599
599
  struct HandleTrackDidPlayToEndTimeTests {
600
600
  @Test @MainActor
601
601
  func repeatTrack_replays() {
602
602
  let (c, eh, _, _) = makeCoordinator()
603
- loadTrack(c)
603
+ startPlaying(c, eh)
604
604
  c.repeatMode = .track
605
- c.playWhenReady = true
605
+ eh.startPlaybackCallCount = 0
606
606
 
607
607
  c.handleTrackDidPlayToEndTime()
608
608
 
609
- // replay delegates to effectHandler.replayCurrentTrack (seek to 0 + play)
610
- #expect(eh.replayCurrentTrackCallCount == 1)
609
+ #expect(eh.seekCalls == [0])
610
+ #expect(eh.startPlaybackCallCount == 1)
611
611
  }
612
612
 
613
613
  /// The end-of-track sleep timer pauses (clears intent) just before this
@@ -620,19 +620,18 @@ struct HandleTrackDidPlayToEndTimeTests {
620
620
  let (c, eh, cb, _) = makeCoordinator()
621
621
  loadTrack(c)
622
622
  c.repeatMode = .track
623
- c.playWhenReady = false
623
+ c.setPlayWhenReady(false)
624
624
 
625
625
  c.handleTrackDidPlayToEndTime()
626
626
 
627
627
  #expect(cb.queueEndedEvents.isEmpty)
628
- #expect(eh.replayCurrentTrackCallCount == 0)
628
+ #expect(eh.seekCalls.isEmpty)
629
629
  #expect(c.state == .ended)
630
630
  }
631
631
 
632
632
  @Test @MainActor
633
633
  func lastTrack_noRepeat_transitionsToEnded() {
634
634
  let (c, eh, cb, _) = makeCoordinator()
635
- _ = eh // retain weak effectHandler
636
635
  loadTrack(c)
637
636
  c.repeatMode = .off
638
637
 
@@ -666,7 +665,7 @@ struct HandleTrackDidPlayToEndTimeTests {
666
665
  /// `playWhenReady` must not stay true. Keeping it inverted the play/pause
667
666
  /// toggle, held the audio session forever, armed interruption auto-resume into
668
667
  /// silence, and made play-from-ended a silent no-op.
669
- @Suite("PlaybackCoordinator - natural end drops play intent")
668
+ @Suite("Player - natural end drops play intent")
670
669
  struct NaturalEndPlayIntentTests {
671
670
  @Test @MainActor
672
671
  func naturalEnd_clearsPlayWhenReady() {
@@ -697,11 +696,13 @@ struct NaturalEndPlayIntentTests {
697
696
  let (c, eh, _, _) = makeCoordinator()
698
697
  startPlaying(c, eh)
699
698
  c.handleTrackDidPlayToEndTime()
700
- eh.reloadTrackCalls.removeAll()
699
+ eh.resumePosition = 300
700
+ let resolvesBefore = eh.resolveAndLoadCalls.count
701
701
 
702
702
  c.play()
703
703
 
704
- #expect(eh.reloadTrackCalls == [false])
704
+ #expect(eh.resolveAndLoadCalls.count == resolvesBefore + 1)
705
+ #expect(c.deferredSeek.target == nil)
705
706
  }
706
707
 
707
708
  /// The cleared intent opens the call-site `!playWhenReady` gate, so a stray
@@ -724,13 +725,13 @@ struct NaturalEndPlayIntentTests {
724
725
  let (c, eh, _, _) = makeCoordinator()
725
726
  startPlaying(c, eh)
726
727
  c.handleTrackDidPlayToEndTime()
727
- eh.reloadTrackCalls.removeAll()
728
+ let resolvesBefore = eh.resolveAndLoadCalls.count
728
729
 
729
730
  c.handleInterruptionBegan()
730
731
  c.handleInterruptionEnded(shouldResume: true)
731
732
 
732
733
  #expect(c.playWhenReady == false) // nothing was playing — never resume
733
- #expect(eh.reloadTrackCalls.isEmpty)
734
+ #expect(eh.resolveAndLoadCalls.count == resolvesBefore)
734
735
  }
735
736
  }
736
737
 
@@ -740,153 +741,69 @@ struct NaturalEndPlayIntentTests {
740
741
  /// window/attempt counters must refill (mirrors Android's healthy-playback
741
742
  /// refill) — otherwise a long-lived stream permanently loses retry after its
742
743
  /// first recovered blip.
743
- @Suite("PlaybackCoordinator - healthy playback resets retry budget")
744
+ @Suite("Player - healthy playback resets retry budget")
744
745
  struct HealthyPlaybackRetryResetTests {
746
+ /// A player whose retry loop has already executed one retry, so a reset shows.
747
+ @MainActor
748
+ private func makeAfterOneRetry(delay: TimeInterval = 0) async -> (Player, RetryLoop, MockPlayerEngine) {
749
+ let retry = makeRetryLoop(delay: delay)
750
+ let engine = MockPlayerEngine()
751
+ let player = makePlayer(engine: engine, retry: retry)
752
+ if delay == 0 {
753
+ retry.handleError(URLError(.timedOut), context: .playback)
754
+ await retry.pendingTask?.value
755
+ }
756
+ return (player, retry, engine)
757
+ }
758
+
745
759
  @Test @MainActor
746
760
  func sustainedPlayback_resetsRetryBudget() async {
747
- let retryHandler = MockRetryHandling()
748
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
749
- let coordinator = PlaybackCoordinator(
750
- errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
751
- )
752
- let effectHandler = MockPlaybackEffectHandler()
753
- coordinator.effectHandler = effectHandler
754
- coordinator.healthyPlaybackDuration = 0.01
761
+ let (player, retry, _) = await makeAfterOneRetry()
762
+ #expect(retry.attempts == 1)
763
+ player.healthyPlaybackDuration = 0.01
755
764
 
756
- coordinator.transition(.avPlayerPlaying)
757
- await coordinator.healthyPlaybackTask?.value
765
+ player.transition(.avPlayerPlaying)
766
+ await player.healthyPlaybackTask?.value
758
767
 
759
- #expect(retryHandler.resetCallCount == 1)
768
+ #expect(retry.attempts == 0)
760
769
  }
761
770
 
762
- /// resetRetry() cancels a pending retry — firing the refill mid-episode
771
+ /// reset() cancels a pending retry — firing the refill mid-episode
763
772
  /// would kill an in-flight reconnect silently (stuck .playing forever).
764
773
  @Test @MainActor
765
774
  func refill_skipsWhileRetryIsPending() async {
766
- let retryHandler = MockRetryHandling()
767
- retryHandler.isRetryableResult = true
768
- retryHandler.attemptRetryDelayNs = 1_000_000_000
769
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
770
- let coordinator = PlaybackCoordinator(
771
- errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
772
- )
773
- let effectHandler = MockPlaybackEffectHandler()
774
- coordinator.effectHandler = effectHandler
775
- coordinator.healthyPlaybackDuration = 0.01
775
+ let (player, retry, _) = await makeAfterOneRetry(delay: 1)
776
+ player.healthyPlaybackDuration = 0.01
776
777
 
777
- coordinator.transition(.avPlayerPlaying)
778
- errorHandler.handleError(URLError(.timedOut), context: .playback)
779
- await coordinator.healthyPlaybackTask?.value
778
+ player.transition(.avPlayerPlaying)
779
+ retry.handleError(URLError(.timedOut), context: .playback)
780
+ await player.healthyPlaybackTask?.value
780
781
 
781
- #expect(retryHandler.resetCallCount == 0)
782
- #expect(errorHandler.pendingRetryTask != nil)
782
+ #expect(retry.pendingTask != nil)
783
+ retry.cancel()
783
784
  }
784
785
 
785
786
  @Test @MainActor
786
787
  func playbackInterruptedBeforeThreshold_doesNotReset() async {
787
- let retryHandler = MockRetryHandling()
788
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
789
- let coordinator = PlaybackCoordinator(
790
- errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
791
- )
792
- let effectHandler = MockPlaybackEffectHandler()
793
- coordinator.effectHandler = effectHandler
794
- effectHandler.hasLoadedAsset = true
795
- coordinator.healthyPlaybackDuration = 0.5
788
+ let (player, retry, engine) = await makeAfterOneRetry()
789
+ engine.hasLoadedAsset = true
790
+ player.healthyPlaybackDuration = 0.5
796
791
 
797
- coordinator.transition(.avPlayerPlaying)
798
- let refill = coordinator.healthyPlaybackTask
799
- coordinator.transition(.avPlayerWaiting) // stalls before the threshold
792
+ player.transition(.avPlayerPlaying)
793
+ let refill = player.healthyPlaybackTask
794
+ player.transition(.avPlayerWaiting) // stalls before the threshold
800
795
 
801
796
  // Leaving .playing cancels the refill; awaiting the cancelled task is the
802
797
  // deterministic way to reach the point where it would have fired.
803
798
  await refill?.value
804
799
 
805
- #expect(retryHandler.resetCallCount == 0)
806
- }
807
- }
808
-
809
- @Suite("PlaybackCoordinator - now playing state")
810
- struct PlaybackCoordinatorNowPlayingStateTests {
811
- @Test @MainActor
812
- func change_updatesNowPlayingState_evenDuringLoading() {
813
- let (c, eh, _, _) = makeCoordinator()
814
- c.transition(.trackLoading) // mid track-load; start/pause is deferred
815
- eh.updateNowPlayingStateCalls.removeAll()
816
-
817
- c.playWhenReady = true
818
-
819
- // The lock-screen / CarPlay play-pause button reflects the user's play
820
- // *intent* immediately, even while the new item is still loading — otherwise
821
- // it stays stuck showing "paused" until a later state transition repairs it.
822
- #expect(eh.updateNowPlayingStateCalls.last == true)
823
- }
824
-
825
- @Test @MainActor
826
- func staysPlaying_whenBufferRunsDry() {
827
- let (c, eh, _, _) = makeCoordinator()
828
- c.playWhenReady = true
829
- c.transition(.avPlayerPlaying)
830
- eh.updateNowPlayingStateCalls.removeAll()
831
-
832
- c.transition(.avPlayerWaiting) // buffer ran dry → .buffering
833
-
834
- // A buffer underrun doesn't change play intent, so the button stays "playing"
835
- // (the system shows buffering) — it must NOT flip to paused like a user pause.
836
- #expect(eh.updateNowPlayingStateCalls.last == true)
837
- }
838
-
839
- @Test @MainActor
840
- func fallsBackToPaused_onError() {
841
- let (c, eh, _, _) = makeCoordinator()
842
- c.playWhenReady = true
843
- c.transition(.trackLoading) // active + intent → playing
844
- eh.updateNowPlayingStateCalls.removeAll()
845
-
846
- c.transition(.errorOccurred(.playbackFailed))
847
-
848
- // On a terminal error the button must fall back to paused — never stick on
849
- // "playing" — even though playWhenReady is still true.
850
- #expect(eh.updateNowPlayingStateCalls.last == false)
851
- }
852
-
853
- @Test @MainActor
854
- func change_doesNotStampNowPlayingState_fromTerminalState() {
855
- let (c, eh, _, _) = makeCoordinator()
856
- c.transition(.errorOccurred(.playbackFailed)) // terminal, not playbackActive
857
- eh.updateNowPlayingStateCalls.removeAll()
858
-
859
- c.playWhenReady = true
860
-
861
- // Must NOT stamp .playing from a terminal state: the reload may fail and no
862
- // active transition would repair a premature "playing", leaving a phantom
863
- // playing button. The reload's own .loading/.ready transition stamps it.
864
- #expect(eh.updateNowPlayingStateCalls.isEmpty)
865
- }
866
-
867
- @Test @MainActor
868
- func selectingNewTrack_whileStopped_showsPlayingInNowPlaying() {
869
- let (c, eh, _, _) = makeCoordinator()
870
- let tracks = [
871
- Track(id: "t1", src: "https://example.com/1.mp3", title: "Track 1"),
872
- Track(id: "t2", src: "https://example.com/2.mp3", title: "Track 2"),
873
- ]
874
- c.setQueue(tracks) // loads t1 (playWhenReady defaults to false)
875
- c.transition(.stopped) // user had paused/stopped
876
- eh.updateNowPlayingStateCalls.removeAll()
877
-
878
- // User selects another track, intending to play it.
879
- try? c.skipTo(1, playWhenReady: true)
880
-
881
- // Audio will play, so the now-playing button must end up "playing" — not the
882
- // stale "paused" captured before playWhenReady flipped true during loading.
883
- #expect(eh.updateNowPlayingStateCalls.last == true)
800
+ #expect(retry.attempts == 1)
884
801
  }
885
802
  }
886
803
 
887
804
  // MARK: - Sleep Timer Fade
888
805
 
889
- @Suite("PlaybackCoordinator - sleep timer fade")
806
+ @Suite("Player - sleep timer fade")
890
807
  struct SleepTimerFadeTests {
891
808
  @Test @MainActor
892
809
  func fadeStart_capturesPreFadeVolume() {
@@ -903,7 +820,7 @@ struct SleepTimerFadeTests {
903
820
  func completion_pausesThenRestoresPreFadeVolume() {
904
821
  let (c, eh, _, st) = makeCoordinator()
905
822
  loadTrack(c)
906
- c.playWhenReady = true
823
+ c.setPlayWhenReady(true)
907
824
  c.transition(.bufferingSufficient) // leave .loading so pause reaches the player
908
825
  eh.pausePlaybackCallCount = 0
909
826
 
@@ -922,7 +839,7 @@ struct SleepTimerFadeTests {
922
839
  func completion_withoutFade_justPauses() {
923
840
  let (c, eh, _, st) = makeCoordinator()
924
841
  loadTrack(c)
925
- c.playWhenReady = true
842
+ c.setPlayWhenReady(true)
926
843
  eh.volume = 0.8
927
844
 
928
845
  st.onComplete?()
@@ -949,38 +866,41 @@ struct SleepTimerFadeTests {
949
866
  func pauseDuringFade_clearsSleepTimer() {
950
867
  let (c, _, _, st) = makeCoordinator()
951
868
  loadTrack(c)
952
- c.playWhenReady = true
869
+ c.setPlayWhenReady(true)
870
+ st.set(seconds: 60, fadeDuration: 10)
953
871
 
954
872
  st.onFadeStart?(10)
955
873
  c.pause()
956
874
 
957
- #expect(st.clearCallCount == 1)
875
+ #expect(st.get() == nil)
958
876
  }
959
877
 
960
878
  @Test @MainActor
961
879
  func pauseWithoutFade_leavesSleepTimerAlone() {
962
880
  let (c, _, _, st) = makeCoordinator()
963
881
  loadTrack(c)
964
- c.playWhenReady = true
882
+ c.setPlayWhenReady(true)
883
+ st.set(seconds: 60, fadeDuration: 10)
965
884
 
966
885
  c.pause()
967
886
 
968
- #expect(st.clearCallCount == 0)
887
+ #expect(st.get() != nil)
969
888
  }
970
889
  }
971
890
 
972
891
  // MARK: - Audio Session Interruptions
973
892
 
974
- /// Helper to drive the coordinator into an actively-playing state.
893
+ /// Helper to drive the player into an actively-playing state.
975
894
  @MainActor
976
- private func startPlaying(_ c: PlaybackCoordinator, _ eh: MockPlaybackEffectHandler) {
895
+ private func startPlaying(_ c: Player, _ eh: MockPlayerEngine) {
977
896
  loadTrack(c)
978
897
  eh.hasLoadedAsset = true
979
- c.playWhenReady = true
898
+ eh.hasItem = true
899
+ c.setPlayWhenReady(true)
980
900
  c.transition(.avPlayerPlaying)
981
901
  }
982
902
 
983
- @Suite("PlaybackCoordinator - audio session interruptions")
903
+ @Suite("Player - audio session interruptions")
984
904
  struct InterruptionTests {
985
905
  @Test @MainActor
986
906
  func began_whilePlaying_pauses() {
@@ -1090,13 +1010,14 @@ struct InterruptionTests {
1090
1010
  let (c, eh, _, _) = makeCoordinator()
1091
1011
  startPlaying(c, eh)
1092
1012
  c.handleInterruptionBegan()
1013
+ let resolvesBefore = eh.resolveAndLoadCalls.count
1093
1014
 
1094
1015
  c.stop()
1095
1016
  c.handleInterruptionEnded(shouldResume: true)
1096
1017
 
1097
1018
  #expect(c.state == .stopped)
1098
1019
  #expect(c.playWhenReady == false)
1099
- #expect(eh.reloadTrackCalls.isEmpty)
1020
+ #expect(eh.resolveAndLoadCalls.count == resolvesBefore)
1100
1021
  }
1101
1022
 
1102
1023
  /// A user who plays then pauses during the interruption has taken ownership
@@ -1119,7 +1040,7 @@ struct InterruptionTests {
1119
1040
  let (c, eh, _, _) = makeCoordinator()
1120
1041
  startPlaying(c, eh)
1121
1042
  // Pause before the interruption (e.g. user already paused).
1122
- c.playWhenReady = false
1043
+ c.setPlayWhenReady(false)
1123
1044
  c.avPlayerDidChangeTimeControlStatus(.paused)
1124
1045
  #expect(c.state == .paused)
1125
1046
 
@@ -1132,10 +1053,9 @@ struct InterruptionTests {
1132
1053
 
1133
1054
  // MARK: - Skip availability freshness
1134
1055
 
1135
- /// tracks.didSet pushes availability mid-mutation, while currentIndex and the
1136
- /// shuffle order are still stale — the coordinator must re-push after the
1137
- /// mutation settles or the lock-screen buttons keep the wrong state.
1138
- @Suite("PlaybackCoordinator - skip availability after mutations")
1056
+ /// A mutation pushes next/previous availability once, after currentIndex and the
1057
+ /// shuffle order have settled; an earlier push leaves the lock-screen buttons wrong.
1058
+ @Suite("Player - skip availability after mutations")
1139
1059
  struct MutationSkipAvailabilityTests {
1140
1060
  @Test @MainActor
1141
1061
  func move_pushesPostMutationAvailability() throws {
@@ -1149,6 +1069,7 @@ struct MutationSkipAvailabilityTests {
1149
1069
  try c.move(fromIndex: 0, toIndex: 1)
1150
1070
 
1151
1071
  // Current track is now last: nothing to skip to.
1072
+ #expect(eh.updateSkipAvailabilityCalls.count == 1)
1152
1073
  #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
1153
1074
  }
1154
1075
 
@@ -1167,49 +1088,180 @@ struct MutationSkipAvailabilityTests {
1167
1088
  try c.remove(0)
1168
1089
 
1169
1090
  // Current settled at index 1 of 2: previous available, next not.
1091
+ #expect(eh.updateSkipAvailabilityCalls.count == 1)
1170
1092
  #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
1171
1093
  #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == true)
1172
1094
  }
1173
1095
  }
1174
1096
 
1097
+ // MARK: - Publishing
1098
+
1099
+ @Suite("Player - publishing")
1100
+ struct PublishingTests {
1101
+ /// The order JS has always seen: the intent, then the playing state, then the playback. A
1102
+ /// natural end clears the intent on the way in; each change still goes out once.
1103
+ @Test @MainActor
1104
+ func endingPublishesTheIntentThenThePlayingStateThenThePlayback() {
1105
+ let (c, _, cb, _) = makeCoordinator()
1106
+ c.setPlayWhenReady(true)
1107
+ c.transition(.avPlayerPlaying)
1108
+ cb.publishOrder.removeAll()
1109
+
1110
+ c.transition(.trackEndedNaturally)
1111
+
1112
+ #expect(cb.publishOrder == ["playWhenReady", "playingState", "playback"])
1113
+ }
1114
+
1115
+ @Test @MainActor
1116
+ func aTransitionPublishesThePlayingStateBeforeThePlayback() {
1117
+ let (c, _, cb, _) = makeCoordinator()
1118
+ c.setPlayWhenReady(true)
1119
+ cb.publishOrder.removeAll()
1120
+
1121
+ c.transition(.avPlayerPlaying)
1122
+
1123
+ #expect(cb.publishOrder == ["playingState", "playback"])
1124
+ }
1125
+
1126
+ @Test @MainActor
1127
+ func aRepeatedTerminalErrorIsReportedAgain() {
1128
+ let (c, _, cb, _) = makeCoordinator()
1129
+ c.transition(.errorOccurred(.playbackFailed))
1130
+ c.transition(.errorOccurred(.playbackFailed))
1131
+
1132
+ #expect(cb.errorEvents.count == 2)
1133
+ }
1134
+ }
1135
+
1136
+ // MARK: - Load
1137
+
1138
+ @Suite("Player - Load")
1139
+ struct LoadTests {
1140
+ @MainActor
1141
+ private func track(_ id: String) -> Track {
1142
+ Track(id: id, src: "https://example.com/\(id).mp3", title: id)
1143
+ }
1144
+
1145
+ @Test @MainActor
1146
+ func aNewTrackStartsAnUnprovenLoad() {
1147
+ let (c, _, _, _) = makeCoordinator()
1148
+ c.setQueue([track("a"), track("b")])
1149
+ let first = c.load
1150
+ c.audioDidStart()
1151
+ #expect(c.load?.hasPlayed == true)
1152
+
1153
+ c.next()
1154
+
1155
+ #expect(c.load != nil && c.load?.id != first?.id)
1156
+ #expect(c.load?.hasPlayed == false)
1157
+ }
1158
+
1159
+ /// The healthy-playback refill resets the retry budgets mid-play; the load, and with it the
1160
+ /// recovery budget, must survive it.
1161
+ @Test @MainActor
1162
+ func aRetryResetKeepsTheLoad() {
1163
+ let (c, _, _, _) = makeCoordinator()
1164
+ c.setQueue([track("a")])
1165
+ c.audioDidStart()
1166
+ let load = c.load
1167
+
1168
+ c.retry.reset()
1169
+
1170
+ #expect(c.load == load)
1171
+ #expect(c.load?.hasPlayed == true)
1172
+ }
1173
+
1174
+ @Test @MainActor
1175
+ func theNowPlayingPrimeRunsOncePerLoad() {
1176
+ let (c, _, _, _) = makeCoordinator()
1177
+ c.setQueue([track("a")])
1178
+ #expect(c.claimNowPlayingPrime())
1179
+ #expect(!c.claimNowPlayingPrime())
1180
+
1181
+ c.beginLoad()
1182
+
1183
+ #expect(c.claimNowPlayingPrime())
1184
+ }
1185
+ }
1186
+
1187
+ // MARK: - Queue publication
1188
+
1189
+ @Suite("Player - queue publication")
1190
+ struct QueuePublicationTests {
1191
+ @MainActor
1192
+ private func threeTracks() -> [Track] {
1193
+ ["a", "b", "c"].map { Track(id: $0, src: "https://example.com/\($0).mp3", title: $0) }
1194
+ }
1195
+
1196
+ @Test @MainActor
1197
+ func setQueue_publishesTheQueueBeforeTheActiveTrack() {
1198
+ let (c, _, cb, _) = makeCoordinator()
1199
+ c.setQueue(threeTracks())
1200
+ #expect(cb.queueChanges.map { $0.map(\.id) } == [["a", "b", "c"]])
1201
+ #expect(cb.queueAndTrackOrder == ["queue", "activeTrack"])
1202
+ }
1203
+
1204
+ @Test @MainActor
1205
+ func move_publishesTheSettledQueueOnce() throws {
1206
+ let (c, _, cb, _) = makeCoordinator()
1207
+ c.setQueue(threeTracks())
1208
+ cb.queueChanges.removeAll()
1209
+ try c.move(fromIndex: 0, toIndex: 2)
1210
+ #expect(cb.queueChanges.map { $0.map(\.id) } == [["b", "c", "a"]])
1211
+ }
1212
+
1213
+ @Test @MainActor
1214
+ func clear_publishesAnEmptyQueue() {
1215
+ let (c, _, cb, _) = makeCoordinator()
1216
+ c.setQueue(threeTracks())
1217
+ cb.queueChanges.removeAll()
1218
+ c.clear()
1219
+ #expect(cb.queueChanges.map(\.count) == [0])
1220
+ }
1221
+
1222
+ @Test @MainActor
1223
+ func navigation_publishesNoQueue() throws {
1224
+ let (c, _, cb, _) = makeCoordinator()
1225
+ c.setQueue(threeTracks())
1226
+ cb.queueChanges.removeAll()
1227
+ c.next()
1228
+ c.previous()
1229
+ try c.skipTo(2)
1230
+ #expect(cb.queueChanges.isEmpty)
1231
+ }
1232
+ }
1233
+
1175
1234
  // MARK: - Stop cancels pending retry
1176
1235
 
1177
- @Suite("PlaybackCoordinator - stop cancels pending retry")
1236
+ @Suite("Player - stop cancels pending retry")
1178
1237
  struct StopCancelsRetryTests {
1179
1238
  /// A retry surviving stop() gives up later (intent dropped) and surfaced
1180
1239
  /// .errorOccurred over the deliberate stop — the UI showed an error seconds
1181
1240
  /// after the user stopped.
1182
1241
  @Test @MainActor
1183
1242
  func stop_cancelsPendingRetry_andStaysSilent() async {
1184
- let retryHandler = MockRetryHandling()
1185
- retryHandler.isRetryableResult = true
1186
- retryHandler.attemptRetryResult = false
1187
- retryHandler.attemptRetryDelayNs = 50_000_000
1188
- let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
1189
- let coordinator = PlaybackCoordinator(
1190
- errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
1191
- )
1192
- let effectHandler = MockPlaybackEffectHandler()
1193
- let callbacks = MockPlaybackCoordinatorCallbacks()
1194
- coordinator.effectHandler = effectHandler
1195
- coordinator.callbacks = callbacks
1243
+ let errorHandler = makeRetryLoop(delay: 0.05)
1244
+ let engine = MockPlayerEngine()
1245
+ let player = makePlayer(engine: engine, retry: errorHandler)
1246
+ let callbacks = MockPlayerCallbacks()
1247
+ player.callbacks = callbacks
1196
1248
 
1197
1249
  errorHandler.handleError(URLError(.timedOut), context: .playback)
1198
- let task = errorHandler.pendingRetryTask
1250
+ let task = errorHandler.pendingTask
1199
1251
  #expect(task != nil)
1200
1252
 
1201
- coordinator.stop()
1202
- #expect(errorHandler.pendingRetryTask == nil)
1253
+ player.stop()
1254
+ #expect(errorHandler.pendingTask == nil)
1203
1255
 
1204
1256
  await task?.value
1205
- #expect(coordinator.state == .stopped)
1257
+ #expect(player.state == .stopped)
1206
1258
  #expect(callbacks.errorEvents.isEmpty)
1207
1259
  }
1208
1260
  }
1209
1261
 
1210
1262
  // MARK: - Audio session release (#60)
1211
1263
 
1212
- @Suite("PlaybackCoordinator - audio session release")
1264
+ @Suite("Player - audio session release")
1213
1265
  struct SessionReleaseTests {
1214
1266
  @Test @MainActor
1215
1267
  func deliberatePause_requestsRelease() {