react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.20

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 (660) hide show
  1. package/README.md +35 -21
  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 +389 -164
  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 +48 -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 +24 -5
  11. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -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 +56 -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 +108 -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 +87 -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 +38 -0
  51. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -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 +147 -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 +154 -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 +41 -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/Browser/BrowserConfig.swift +102 -5
  76. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  77. package/ios/Browser/BrowserManager.swift +230 -205
  78. package/ios/Browser/BrowserPathHelper.swift +44 -44
  79. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  80. package/ios/Browser/JsonModels.swift +264 -106
  81. package/ios/Browser/SearchDrillIn.swift +7 -7
  82. package/ios/Browser/SectionPresentation.swift +64 -0
  83. package/ios/Browser/SectionScope.swift +58 -24
  84. package/ios/Browser/StyleResolver.swift +58 -0
  85. package/ios/Browser/TabBarEntries.swift +9 -3
  86. package/ios/Browser/TrackIdTemplate.swift +37 -0
  87. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  88. package/ios/CarPlay/CarPlayController.swift +227 -85
  89. package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
  90. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  91. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  92. package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
  93. package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
  94. package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
  95. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  96. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  97. package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
  98. package/ios/Extension/Section+Copying.swift +29 -0
  99. package/ios/Extension/String+IcyCharset.swift +21 -0
  100. package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
  101. package/ios/Extension/Track+Copying.swift +8 -12
  102. package/ios/Extension/Track+Identity.swift +18 -0
  103. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  104. package/ios/Http/HttpClient.swift +8 -2
  105. package/ios/HybridAudioBrowser.swift +253 -100
  106. package/ios/Model/NitroTypeStubs.swift +125 -23
  107. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  108. package/ios/Model/RemoteCommand.swift +18 -0
  109. package/ios/Model/TrackPlayerError.swift +2 -2
  110. package/ios/Option/SessionCategories.swift +3 -2
  111. package/ios/Player/MediaIntentCriteria.swift +17 -9
  112. package/ios/Player/MediaLoader.swift +33 -9
  113. package/ios/Player/NowPlayingUpdater.swift +54 -6
  114. package/ios/Player/PlaybackCoordinator.swift +13 -6
  115. package/ios/Player/PlaybackErrorHandler.swift +4 -4
  116. package/ios/Player/PlaybackTimer.swift +8 -2
  117. package/ios/Player/QueueManager.swift +46 -13
  118. package/ios/Player/RetryManager.swift +80 -18
  119. package/ios/Player/SeekCompletionHandler.swift +3 -1
  120. package/ios/Player/ShuffleOrder.swift +33 -0
  121. package/ios/Player/SleepTimerHandling.swift +13 -0
  122. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
  123. package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
  124. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  125. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  126. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  127. package/ios/Tests/JsonModelsTests.swift +150 -1
  128. package/ios/Tests/LRUCacheTests.swift +111 -0
  129. package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
  130. package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
  131. package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
  132. package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
  133. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  134. package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
  135. package/ios/Tests/QueueManagerTests.swift +122 -10
  136. package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
  137. package/ios/Tests/RemoteCommandTests.swift +67 -0
  138. package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
  139. package/ios/Tests/RetryManagerTests.swift +89 -0
  140. package/ios/Tests/SearchDrillInTests.swift +16 -16
  141. package/ios/Tests/SectionPresentationTests.swift +90 -0
  142. package/ios/Tests/SectionScopeTests.swift +87 -55
  143. package/ios/Tests/StyleResolverTests.swift +127 -0
  144. package/ios/Tests/Support/TestTypes.swift +8 -1
  145. package/ios/Tests/TabBarEntriesTests.swift +19 -19
  146. package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
  147. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  148. package/ios/Tests/TrackIdentityTests.swift +20 -0
  149. package/ios/Tests/TrackSelectorTests.swift +168 -33
  150. package/ios/TrackPlayer.swift +63 -35
  151. package/ios/TrackPlayerCallbacks.swift +5 -38
  152. package/ios/TrackSelector.swift +45 -30
  153. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  154. package/ios/Util/LRUCache.swift +4 -1
  155. package/ios/Util/NetworkMonitor.swift +43 -26
  156. package/ios/Util/SFSymbolRenderer.swift +24 -2
  157. package/ios/Util/SVGProcessor.swift +9 -2
  158. package/lib/commonjs/AudioBrowser.js +8 -8
  159. package/lib/commonjs/AudioBrowser.js.map +1 -1
  160. package/lib/commonjs/CLAUDE.md +52 -0
  161. package/lib/commonjs/features/browser-config.js +12 -8
  162. package/lib/commonjs/features/browser-config.js.map +1 -1
  163. package/lib/commonjs/features/browser.js +11 -3
  164. package/lib/commonjs/features/browser.js.map +1 -1
  165. package/lib/commonjs/features/carConnection.js +1 -1
  166. package/lib/commonjs/features/equalizer.js +1 -1
  167. package/lib/commonjs/features/errors.js +3 -3
  168. package/lib/commonjs/features/favorites.js +14 -11
  169. package/lib/commonjs/features/favorites.js.map +1 -1
  170. package/lib/commonjs/features/gate.js +16 -1
  171. package/lib/commonjs/features/gate.js.map +1 -1
  172. package/lib/commonjs/features/metadata.js +20 -3
  173. package/lib/commonjs/features/metadata.js.map +1 -1
  174. package/lib/commonjs/features/network.js +1 -1
  175. package/lib/commonjs/features/nowPlaying.js +1 -1
  176. package/lib/commonjs/features/output.js +1 -1
  177. package/lib/commonjs/features/playback/controls.js +4 -2
  178. package/lib/commonjs/features/playback/controls.js.map +1 -1
  179. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  180. package/lib/commonjs/features/playback/playing.js +1 -1
  181. package/lib/commonjs/features/playback/progress.js +3 -2
  182. package/lib/commonjs/features/playback/progress.js.map +1 -1
  183. package/lib/commonjs/features/playback/rate.js +8 -1
  184. package/lib/commonjs/features/playback/rate.js.map +1 -1
  185. package/lib/commonjs/features/playback/state.js +3 -4
  186. package/lib/commonjs/features/playback/state.js.map +1 -1
  187. package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
  188. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
  189. package/lib/commonjs/features/playback/volume.js +1 -1
  190. package/lib/commonjs/features/player/options.js +48 -34
  191. package/lib/commonjs/features/player/options.js.map +1 -1
  192. package/lib/commonjs/features/player/setup.js +18 -9
  193. package/lib/commonjs/features/player/setup.js.map +1 -1
  194. package/lib/commonjs/features/player/validateOptions.js +23 -0
  195. package/lib/commonjs/features/player/validateOptions.js.map +1 -0
  196. package/lib/commonjs/features/queue/activeTrack.js +15 -4
  197. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  198. package/lib/commonjs/features/queue/queue.js +13 -6
  199. package/lib/commonjs/features/queue/queue.js.map +1 -1
  200. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  201. package/lib/commonjs/features/queue/shuffle.js +1 -1
  202. package/lib/commonjs/features/remoteControls.js +13 -92
  203. package/lib/commonjs/features/remoteControls.js.map +1 -1
  204. package/lib/commonjs/features/sleepTimer.js +13 -1
  205. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  206. package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
  207. package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
  208. package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
  209. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  210. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  211. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  212. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  213. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  214. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  215. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  216. package/lib/commonjs/web/NativeAudioBrowser.js +200 -75
  217. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  218. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
  219. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
  220. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  221. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  222. package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
  223. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  224. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  225. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  226. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
  227. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
  228. package/lib/commonjs/web/browser/BrowserManager.js +175 -100
  229. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  230. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  231. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  232. package/lib/commonjs/web/browser/SearchManager.js +18 -23
  233. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  234. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  235. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  236. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  237. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  238. package/lib/commonjs/web/browser/sections.js +89 -0
  239. package/lib/commonjs/web/browser/sections.js.map +1 -0
  240. package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
  241. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  242. package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
  243. package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
  244. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  245. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  246. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  247. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  248. package/lib/module/AudioBrowser.js +2 -1
  249. package/lib/module/AudioBrowser.js.map +1 -1
  250. package/lib/module/CLAUDE.md +52 -0
  251. package/lib/module/features/browser-config.js +12 -8
  252. package/lib/module/features/browser-config.js.map +1 -1
  253. package/lib/module/features/browser.js +11 -3
  254. package/lib/module/features/browser.js.map +1 -1
  255. package/lib/module/features/carConnection.js +1 -1
  256. package/lib/module/features/equalizer.js +1 -1
  257. package/lib/module/features/errors.js +3 -3
  258. package/lib/module/features/favorites.js +14 -11
  259. package/lib/module/features/favorites.js.map +1 -1
  260. package/lib/module/features/gate.js +16 -1
  261. package/lib/module/features/gate.js.map +1 -1
  262. package/lib/module/features/metadata.js +20 -3
  263. package/lib/module/features/metadata.js.map +1 -1
  264. package/lib/module/features/network.js +1 -1
  265. package/lib/module/features/nowPlaying.js +1 -1
  266. package/lib/module/features/output.js +1 -1
  267. package/lib/module/features/playback/controls.js +4 -2
  268. package/lib/module/features/playback/controls.js.map +1 -1
  269. package/lib/module/features/playback/playWhenReady.js +1 -1
  270. package/lib/module/features/playback/playing.js +1 -1
  271. package/lib/module/features/playback/progress.js +3 -2
  272. package/lib/module/features/playback/progress.js.map +1 -1
  273. package/lib/module/features/playback/rate.js +8 -1
  274. package/lib/module/features/playback/rate.js.map +1 -1
  275. package/lib/module/features/playback/state.js +3 -4
  276. package/lib/module/features/playback/state.js.map +1 -1
  277. package/lib/module/features/playback/trackPlaybackTime.js +0 -5
  278. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
  279. package/lib/module/features/playback/volume.js +1 -1
  280. package/lib/module/features/player/options.js +47 -32
  281. package/lib/module/features/player/options.js.map +1 -1
  282. package/lib/module/features/player/setup.js +17 -8
  283. package/lib/module/features/player/setup.js.map +1 -1
  284. package/lib/module/features/player/validateOptions.js +19 -0
  285. package/lib/module/features/player/validateOptions.js.map +1 -0
  286. package/lib/module/features/queue/activeTrack.js +15 -4
  287. package/lib/module/features/queue/activeTrack.js.map +1 -1
  288. package/lib/module/features/queue/queue.js +13 -6
  289. package/lib/module/features/queue/queue.js.map +1 -1
  290. package/lib/module/features/queue/repeatMode.js +1 -1
  291. package/lib/module/features/queue/shuffle.js +1 -1
  292. package/lib/module/features/remoteControls.js +13 -92
  293. package/lib/module/features/remoteControls.js.map +1 -1
  294. package/lib/module/features/sleepTimer.js +13 -1
  295. package/lib/module/features/sleepTimer.js.map +1 -1
  296. package/lib/module/test-utils/reactNativeStub.js +58 -0
  297. package/lib/module/test-utils/reactNativeStub.js.map +1 -0
  298. package/lib/module/utils/LazyNativeEmitter.js +28 -14
  299. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  300. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  301. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  302. package/lib/module/utils/getTrackIdentity.js +20 -0
  303. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  304. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  305. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  306. package/lib/module/web/NativeAudioBrowser.js +200 -75
  307. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  308. package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
  309. package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
  310. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  311. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  312. package/lib/module/web/TrackPlayer/Player.js +254 -39
  313. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  314. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  315. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  316. package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
  317. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
  318. package/lib/module/web/browser/BrowserManager.js +175 -100
  319. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  320. package/lib/module/web/browser/FavoriteManager.js +31 -21
  321. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  322. package/lib/module/web/browser/SearchManager.js +18 -23
  323. package/lib/module/web/browser/SearchManager.js.map +1 -1
  324. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  325. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  326. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  327. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  328. package/lib/module/web/browser/sections.js +83 -0
  329. package/lib/module/web/browser/sections.js.map +1 -0
  330. package/lib/module/web/http/RequestConfigBuilder.js +110 -27
  331. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  332. package/lib/module/web/http/followMediaRedirect.js +215 -0
  333. package/lib/module/web/http/followMediaRedirect.js.map +1 -0
  334. package/lib/module/web/player/NowPlayingManager.js +4 -3
  335. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  336. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  337. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  338. package/lib/typescript/package.json +1 -0
  339. package/lib/typescript/src/AudioBrowser.d.ts +1 -1
  340. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  341. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  342. package/lib/typescript/src/features/browser.d.ts +10 -2
  343. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  344. package/lib/typescript/src/features/carConnection.d.ts +1 -1
  345. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  346. package/lib/typescript/src/features/errors.d.ts +3 -3
  347. package/lib/typescript/src/features/favorites.d.ts +14 -11
  348. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  349. package/lib/typescript/src/features/gate.d.ts +18 -2
  350. package/lib/typescript/src/features/gate.d.ts.map +1 -1
  351. package/lib/typescript/src/features/metadata.d.ts +20 -3
  352. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  353. package/lib/typescript/src/features/network.d.ts +1 -1
  354. package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
  355. package/lib/typescript/src/features/output.d.ts +1 -1
  356. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  357. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  358. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  359. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  360. package/lib/typescript/src/features/playback/progress.d.ts +1 -1
  361. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  362. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  363. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  364. package/lib/typescript/src/features/playback/state.d.ts +3 -4
  365. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  366. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  367. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  368. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  369. package/lib/typescript/src/features/player/options.d.ts +108 -56
  370. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  371. package/lib/typescript/src/features/player/setup.d.ts +14 -4
  372. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  373. package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
  374. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
  375. package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
  376. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  377. package/lib/typescript/src/features/queue/queue.d.ts +7 -4
  378. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  379. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  380. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  381. package/lib/typescript/src/features/remoteControls.d.ts +23 -22
  382. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  383. package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
  384. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  385. package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
  386. package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
  387. package/lib/typescript/src/types/browser-native.d.ts +3 -2
  388. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  389. package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
  390. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  391. package/lib/typescript/src/types/browser.d.ts +82 -57
  392. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  393. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
  394. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  395. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  396. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  397. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  398. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  399. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  400. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  401. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
  402. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
  403. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
  404. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  405. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  406. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  407. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  408. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  409. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  410. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  411. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  412. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  413. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  414. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  415. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  416. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  417. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
  418. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
  419. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  420. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
  421. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  422. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  423. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  424. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  425. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
  426. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
  427. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  428. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  429. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  430. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
  431. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  432. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
  433. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
  434. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
  435. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  436. package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
  437. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  438. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  439. package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
  440. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
  441. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
  442. package/nitrogen/generated/android/c++/JSection.hpp +116 -0
  443. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  444. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  445. package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
  446. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  447. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  448. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
  449. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
  450. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  451. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
  452. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  453. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  454. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  455. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
  456. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  457. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
  458. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  459. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  460. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
  461. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
  462. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  463. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  464. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
  465. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
  466. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
  467. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
  468. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  469. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  470. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -12
  471. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  472. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
  473. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
  474. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
  475. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
  476. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
  477. package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
  478. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  479. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  480. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  481. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
  482. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  483. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
  484. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
  485. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  486. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  487. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
  488. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
  489. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  490. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  491. package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
  492. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
  493. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
  494. package/nitrogen/generated/ios/swift/Section.swift +82 -0
  495. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  496. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  497. package/nitrogen/generated/ios/swift/Track.swift +28 -63
  498. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  499. package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
  500. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
  501. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  502. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  503. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  504. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  505. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
  506. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
  507. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  508. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  509. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
  510. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
  511. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  512. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  513. package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
  514. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
  515. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
  516. package/nitrogen/generated/shared/c++/Section.hpp +102 -0
  517. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  518. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  519. package/nitrogen/generated/shared/c++/Track.hpp +18 -30
  520. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  521. package/package.json +24 -11
  522. package/src/AudioBrowser.ts +2 -1
  523. package/src/CLAUDE.md +52 -0
  524. package/src/features/battery.test.ts +79 -0
  525. package/src/features/browser-config.test.ts +21 -6
  526. package/src/features/browser-config.ts +12 -8
  527. package/src/features/browser.test.ts +1 -1
  528. package/src/features/browser.ts +11 -3
  529. package/src/features/carConnection.ts +1 -1
  530. package/src/features/equalizer.ts +1 -1
  531. package/src/features/errors.ts +3 -3
  532. package/src/features/favorites.ts +14 -11
  533. package/src/features/gate.ts +16 -1
  534. package/src/features/hook-wiring.test.ts +302 -0
  535. package/src/features/metadata.ts +20 -3
  536. package/src/features/network.ts +1 -1
  537. package/src/features/nowPlaying.ts +1 -1
  538. package/src/features/output.ts +1 -1
  539. package/src/features/playback/controls.ts +4 -2
  540. package/src/features/playback/playWhenReady.ts +1 -1
  541. package/src/features/playback/playing.ts +1 -1
  542. package/src/features/playback/progress.test.ts +169 -0
  543. package/src/features/playback/progress.ts +3 -2
  544. package/src/features/playback/rate.ts +8 -1
  545. package/src/features/playback/state.ts +3 -4
  546. package/src/features/playback/trackPlaybackTime.test.ts +16 -6
  547. package/src/features/playback/trackPlaybackTime.ts +1 -6
  548. package/src/features/playback/volume.ts +1 -1
  549. package/src/features/player/options.ts +126 -76
  550. package/src/features/player/setup.test.ts +24 -7
  551. package/src/features/player/setup.ts +22 -10
  552. package/src/features/player/validateOptions.ts +21 -0
  553. package/src/features/queue/activeTrack.ts +15 -4
  554. package/src/features/queue/queue.ts +13 -6
  555. package/src/features/queue/repeatMode.ts +1 -1
  556. package/src/features/queue/shuffle.ts +1 -1
  557. package/src/features/remoteControls.ts +13 -92
  558. package/src/features/sleepTimer.test.ts +172 -0
  559. package/src/features/sleepTimer.ts +9 -1
  560. package/src/nitro-stub-drift.test.ts +277 -0
  561. package/src/public-surface.test.ts +147 -0
  562. package/src/specs/audio-browser.nitro.ts +5 -4
  563. package/src/style-resolution.test.ts +210 -0
  564. package/src/test-utils/reactNativeStub.ts +60 -0
  565. package/src/types/browser-native.ts +7 -3
  566. package/src/types/browser-nodes.ts +396 -134
  567. package/src/types/browser.ts +85 -59
  568. package/src/utils/LazyNativeEmitter.ts +28 -15
  569. package/src/utils/NativeUpdatedValue.ts +29 -13
  570. package/src/utils/getTrackIdentity.ts +20 -0
  571. package/src/utils/useDebug.test.ts +182 -0
  572. package/src/utils/useNativeUpdatedValue.test.ts +103 -0
  573. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  574. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
  575. package/src/web/NativeAudioBrowser.ts +239 -82
  576. package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
  577. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
  578. package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
  579. package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
  580. package/src/web/TrackPlayer/Player.load.test.ts +137 -1
  581. package/src/web/TrackPlayer/Player.ts +284 -37
  582. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  583. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  584. package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
  585. package/src/web/__tests__/lifecycle.test.ts +345 -0
  586. package/src/web/__tests__/lifecycleHarness.ts +351 -0
  587. package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
  588. package/src/web/__tests__/loadSupersession.test.ts +175 -0
  589. package/src/web/__tests__/retry.test.ts +169 -0
  590. package/src/web/browser/BrowserManager.ts +209 -111
  591. package/src/web/browser/FavoriteManager.test.ts +84 -0
  592. package/src/web/browser/FavoriteManager.ts +28 -23
  593. package/src/web/browser/SearchManager.test.ts +75 -0
  594. package/src/web/browser/SearchManager.ts +25 -28
  595. package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
  596. package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
  597. package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
  598. package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
  599. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  600. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  601. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  602. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  603. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  604. package/src/web/browser/sections.test.ts +91 -0
  605. package/src/web/browser/sections.ts +72 -0
  606. package/src/web/http/RequestConfigBuilder.test.ts +241 -1
  607. package/src/web/http/RequestConfigBuilder.ts +140 -33
  608. package/src/web/http/followMediaRedirect.test.ts +289 -0
  609. package/src/web/http/followMediaRedirect.ts +253 -0
  610. package/src/web/player/NowPlayingManager.ts +4 -3
  611. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  612. package/src/web/util/BrowserPathHelper.ts +61 -13
  613. package/website/guide/android-auto.md +114 -0
  614. package/website/guide/android-certificates.md +409 -0
  615. package/website/guide/artwork.md +255 -0
  616. package/website/guide/audio-output.md +135 -0
  617. package/website/guide/automotive.md +86 -0
  618. package/website/guide/basic-usage.md +151 -0
  619. package/website/guide/battery.md +115 -0
  620. package/website/guide/browser.md +581 -0
  621. package/website/guide/carplay.md +620 -0
  622. package/website/guide/configuration.md +173 -0
  623. package/website/guide/equalizer.md +231 -0
  624. package/website/guide/errors.md +310 -0
  625. package/website/guide/favorites.md +154 -0
  626. package/website/guide/gate.md +143 -0
  627. package/website/guide/getting-started.md +46 -0
  628. package/website/guide/hooks.md +238 -0
  629. package/website/guide/live-streams.md +125 -0
  630. package/website/guide/metadata.md +239 -0
  631. package/website/guide/migrating-from-track-player.md +244 -0
  632. package/website/guide/native-callback-fetch.md +127 -0
  633. package/website/guide/network.md +86 -0
  634. package/website/guide/now-playing.md +175 -0
  635. package/website/guide/playback.md +328 -0
  636. package/website/guide/queue.md +300 -0
  637. package/website/guide/remote-controls.md +256 -0
  638. package/website/guide/search.md +255 -0
  639. package/website/guide/sleep-timer.md +268 -0
  640. package/website/guide/track.md +247 -0
  641. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  642. package/ios/Model/MediaURL.swift +0 -67
  643. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  644. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  645. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
  646. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  647. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  648. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
  649. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  653. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  654. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  655. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  656. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
  657. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  658. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  659. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
  660. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
@@ -18,7 +18,7 @@ describe('toNativeConfig route classification', () => {
18
18
 
19
19
  it('keeps a static page with a string artwork out of the RouteConfig branch', () => {
20
20
  const page = {
21
- url: '/x',
21
+ path: '/x',
22
22
  title: 'X',
23
23
  artwork: 'https://img.example.com/x.png',
24
24
  children: []
@@ -28,6 +28,21 @@ describe('toNativeConfig route classification', () => {
28
28
  expect(entry?.artwork).toBeUndefined()
29
29
  })
30
30
 
31
+ it('classifies a static page as static despite its top-level `path` key', () => {
32
+ // `path` is also a RequestConfig field; the page's required `title` must
33
+ // win the classification (a config with a `path` alone is not a page).
34
+ const page = { path: '/x', title: 'X', children: [] }
35
+ const entry = entryFor({ '/x': page }, '/x')
36
+ expect(entry?.browseStatic).toEqual(page)
37
+ expect(entry?.browseConfig).toBeUndefined()
38
+ })
39
+
40
+ it('classifies a path-only request config as a request config', () => {
41
+ const entry = entryFor({ '/x': { path: '/remapped' } }, '/x')
42
+ expect(entry?.browseConfig).toEqual({ path: '/remapped' })
43
+ expect(entry?.browseStatic).toBeUndefined()
44
+ })
45
+
31
46
  it('classifies a method-only request config as a request config', () => {
32
47
  const entry = entryFor({ '/x': { method: 'POST' } }, '/x')
33
48
  expect(entry?.browseConfig).toEqual({ method: 'POST' })
@@ -66,13 +81,13 @@ describe('validateBrowserConfiguration', () => {
66
81
  it('is silent for a valid configuration', () => {
67
82
  validateBrowserConfiguration({
68
83
  routes: {
69
- '/favorites': async () => ({ url: '/favorites', title: 'Favorites' }),
84
+ '/favorites': async () => ({ path: '/favorites', title: 'Favorites' }),
70
85
  '/albums/{id}': { baseUrl: 'https://api.example.com' },
71
86
  '*': { baseUrl: 'https://api.example.com' }
72
87
  },
73
88
  tabs: [
74
- { title: 'Home', url: '/' },
75
- { title: 'Search', url: '/search' }
89
+ { title: 'Home', path: '/' },
90
+ { title: 'Search', path: '/search' }
76
91
  ]
77
92
  })
78
93
  expect(warnings).toEqual([])
@@ -96,14 +111,14 @@ describe('validateBrowserConfiguration', () => {
96
111
 
97
112
  it('does not flag a valid static page as unclassifiable', () => {
98
113
  validateBrowserConfiguration({
99
- routes: { '/x': { url: '/x', title: 'X', children: [] } }
114
+ routes: { '/x': { path: '/x', title: 'X', children: [] } }
100
115
  })
101
116
  expect(warnings).toEqual([])
102
117
  })
103
118
 
104
119
  it('warns on more than 4 static tabs', () => {
105
120
  validateBrowserConfiguration({
106
- tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`, url: `/${n}` }))
121
+ tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`, path: `/${n}` }))
107
122
  })
108
123
  expect(warnings.some((w) => w.includes('at most 4'))).toBe(true)
109
124
  })
@@ -38,9 +38,12 @@ function isTransformableRequestConfig(
38
38
  source: unknown
39
39
  ): source is TransformableRequestConfig {
40
40
  if (!isObjectValue(source)) return false
41
- // Any RequestConfig key marks a request config. A static ResolvedTrack
42
- // shares none of these at the top level (its per-track request override is
43
- // nested under `request`).
41
+ // A static ResolvedTrack page also carries a top-level `path`, so `path`
42
+ // alone cannot mark a request config. A page is recognised by its required
43
+ // `title` — no RequestConfig field is named `title`.
44
+ if ('title' in source) return false
45
+ // Any RequestConfig key marks a request config. (A static ResolvedTrack's
46
+ // per-track request override is nested under `request`, not top-level.)
44
47
  return (
45
48
  'baseUrl' in source ||
46
49
  'path' in source ||
@@ -120,7 +123,7 @@ function searchSourceToRouteEntry(source: SearchSource): NativeRouteEntry {
120
123
  */
121
124
  function wrapTracksAsResolvedTrack(tracks: Track[]): ResolvedTrack {
122
125
  return {
123
- url: TABS_ROUTE_PATH,
126
+ path: TABS_ROUTE_PATH,
124
127
  title: 'Tabs',
125
128
  children: tracks
126
129
  }
@@ -245,14 +248,14 @@ export function validateBrowserConfiguration(
245
248
  !isRouteConfig(source) &&
246
249
  !isTransformableRequestConfig(source)
247
250
  ) {
248
- // Falls through to the static-page branch; a page needs url + title.
251
+ // Falls through to the static-page branch; a page needs path + title.
249
252
  const page = source as Partial<ResolvedTrack>
250
- if (typeof page.url !== 'string' || typeof page.title !== 'string') {
253
+ if (typeof page.path !== 'string' || typeof page.title !== 'string') {
251
254
  warn(
252
255
  `route "${path}" matched no source shape — expected a callback, ` +
253
256
  `a request config ({ baseUrl, path, headers, ... }), a ` +
254
257
  `RouteConfig ({ browse, media, artwork }), or a static page ` +
255
- `({ url, title, children? }). It will be served as static ` +
258
+ `({ path, title, children? }). It will be served as static ` +
256
259
  `content and likely render a blank screen.`
257
260
  )
258
261
  }
@@ -286,8 +289,9 @@ export function toNativeConfig(
286
289
  singleTrack: config.singleTrack,
287
290
  handleTrackLoad: config.handleTrackLoad,
288
291
  androidControllerOfflineError: config.androidControllerOfflineError,
292
+ viewMoreTitle: config.viewMoreTitle,
289
293
  carPlayLoadingTitle: config.carPlayLoadingTitle,
290
- resolveAlbumUrl: config.resolveAlbumUrl,
294
+ resolveAlbumPath: config.resolveAlbumPath,
291
295
  formatNavigationError: config.formatNavigationError
292
296
  }
293
297
  }
@@ -12,7 +12,7 @@ describe('getBrowserConfiguration', () => {
12
12
  })
13
13
 
14
14
  it('returns the last configuration in its public shape', () => {
15
- const config = { tabs: [{ title: 'Home', url: '/' }] }
15
+ const config = { tabs: [{ title: 'Home', path: '/' }] }
16
16
  configureBrowser(config)
17
17
  expect(getBrowserConfiguration()).toBe(config)
18
18
  })
@@ -43,8 +43,8 @@ let currentConfiguration: BrowserConfiguration | undefined
43
43
  * }
44
44
  * },
45
45
  * tabs: [
46
- * { title: 'Home', url: '/' },
47
- * { title: 'Search', url: '/search' }
46
+ * { title: 'Home', path: '/' },
47
+ * { title: 'Search', path: '/search' }
48
48
  * ]
49
49
  * })
50
50
  * ```
@@ -157,8 +157,16 @@ export function hasSearch(): boolean {
157
157
  /**
158
158
  * Searches for tracks using the configured search source.
159
159
  *
160
+ * Resolves `[]` only when the source found nothing; a failed search rejects,
161
+ * so a caller can tell "no results" from "the request failed". The rejection's
162
+ * message describes the failure — native platforms reject with the underlying
163
+ * throwable and JS sees only its message. On web the `Error` also carries a
164
+ * `code`, the {@link NavigationErrorType} the browse path reports as a
165
+ * {@link NavigationError} (`'http-error'`, `'network-error'`).
166
+ *
160
167
  * @param query - The search query string
161
168
  * @returns Promise resolving to an array of matching tracks
169
+ * @throws When the search source or its request fails
162
170
  *
163
171
  * @see {@link configureBrowser}
164
172
  *
@@ -169,5 +177,5 @@ export function hasSearch(): boolean {
169
177
  * ```
170
178
  */
171
179
  export async function search(query: string): Promise<Track[]> {
172
- return nativeBrowser.onSearch(query)
180
+ return nativeBrowser.search(query)
173
181
  }
@@ -19,7 +19,7 @@ export function isCarConnected(): boolean {
19
19
  * Subscribes to car connection changes (CarPlay on iOS, Android Auto /
20
20
  * Android Automotive on Android). Never fires on web.
21
21
  * @param callback - Called with the new connection state
22
- * @returns Cleanup function to unsubscribe
22
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
23
23
  */
24
24
  export const onCarConnectedChanged = NativeUpdatedValue.emitterize<boolean>(
25
25
  (cb) => (nativeBrowser.onCarConnectedChanged = cb)
@@ -46,7 +46,7 @@ export function setEqualizerLevels(levels: number[]): void {
46
46
  /**
47
47
  * Subscribes to equalizer settings changes (Android only).
48
48
  * @param callback - Called when equalizer settings change
49
- * @returns Cleanup function to unsubscribe
49
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
50
50
  */
51
51
  export const onEqualizerChanged =
52
52
  NativeUpdatedValue.emitterize<EqualizerSettings>(
@@ -134,7 +134,7 @@ export function getPlaybackError(): PlaybackError | undefined {
134
134
  /**
135
135
  * Subscribes to playback error events.
136
136
  * @param callback - Called when a playback error occurs
137
- * @returns Cleanup function to unsubscribe
137
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
138
138
  */
139
139
  export const onPlaybackError =
140
140
  NativeUpdatedValue.emitterize<PlaybackErrorEvent>(
@@ -165,7 +165,7 @@ export function getNavigationError(): NavigationError | undefined {
165
165
  /**
166
166
  * Subscribes to navigation error events.
167
167
  * @param callback - Called when a navigation error occurs
168
- * @returns Cleanup function to unsubscribe
168
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
169
169
  */
170
170
  export const onNavigationError =
171
171
  NativeUpdatedValue.emitterize<NavigationErrorEvent>(
@@ -198,7 +198,7 @@ export function getFormattedNavigationError():
198
198
  /**
199
199
  * Subscribes to formatted navigation error changes.
200
200
  * @param callback - Called when the formatted navigation error changes
201
- * @returns Cleanup function to unsubscribe
201
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
202
202
  */
203
203
  export const onFormattedNavigationError = NativeUpdatedValue.emitterize<
204
204
  FormattedNavigationError | undefined
@@ -37,26 +37,29 @@ export function toggleActiveTrackFavorited(): void {
37
37
  }
38
38
 
39
39
  /**
40
- * Sets the list of favorited track source identifiers.
40
+ * Sets the list of favorited track identifiers.
41
41
  *
42
42
  * This syncs your app's favorites with the native favorites cache, enabling
43
43
  * the heart button in media controllers (notification, Android Auto, CarPlay)
44
44
  * to show the correct state.
45
45
  *
46
- * Note: This is only needed if the tracks provided to AudioBrowser do not
47
- * include the `favorited` field. If your API already includes favorite state in
48
- * track responses, the native favorites cache is populated automatically during browsing.
46
+ * Each identifier is matched **exactly** against a track's *identity* — its
47
+ * `id` when set, falling back to `src`. Store favorites as the same stable
48
+ * identifier you put in `Track.id` (or as the full `src` for id-less tracks).
49
49
  *
50
- * When the heart button is tapped in media controllers or when you call
51
- * `setActiveTrackFavorited()`, the native favorites cache is automatically
52
- * updated. You only need to call this on app launch to hydrate the cache.
50
+ * A track whose response already carries `favorited` keeps that value on
51
+ * display surfaces — a caller-set flag wins over cache hydration. The cache
52
+ * itself is written only by this call and by heart toggles
53
+ * (`setActiveTrackFavorited()` / media-controller taps), so you only need to
54
+ * call this on app launch — and again whenever the collection changes outside
55
+ * the player (a sync from another device, say).
53
56
  *
54
- * @param favorites - Array of favorited track `src` values
57
+ * @param favorites - Array of favorited track identities (`id`, or `src` for id-less tracks)
55
58
  *
56
59
  * @example
57
60
  * ```ts
58
- * const favoriteSrcs = await loadFavoritesFromStorage()
59
- * setFavorites(favoriteSrcs)
61
+ * const favoriteIds = await loadFavoritesFromStorage()
62
+ * setFavorites(favoriteIds)
60
63
  * ```
61
64
  */
62
65
  export function setFavorites(favorites: string[]): void {
@@ -82,7 +85,7 @@ export interface FavoriteChangedEvent {
82
85
  * The native side has already updated the track's favorite state and UI.
83
86
  *
84
87
  * @param callback - Called with the track and its new favorite state
85
- * @returns Cleanup function to unsubscribe
88
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
86
89
  *
87
90
  * @example
88
91
  * ```ts
@@ -120,12 +120,20 @@ nativeBrowser.resolveGate = async (
120
120
  * some surfaces (CarPlay) any gate change pops pushed navigation back to root,
121
121
  * so a page the user had drilled into is reset to the tab root. Set the gate
122
122
  * before the car connects and it'll be there the moment it does.
123
+ *
124
+ * @platform ios android
125
+ * A no-op on web, which has no CarPlay or Android Auto surfaces for the gate to
126
+ * cover. Nothing is lost: the gate never covered in-app browse UI on any
127
+ * platform — that stays yours to gate.
123
128
  */
124
129
  export function setGate(gate: Gate, resolve?: GateResolver): void
125
130
  /**
126
131
  * Raises the gate with a resolver only (no default chrome).
127
132
  * The resolver must return a {@link Gate} or `true` for gated requests;
128
133
  * native uses the built-in fallback chrome when `true` and no default is set.
134
+ *
135
+ * @platform ios android
136
+ * A no-op on web — the resolver is never consulted there.
129
137
  */
130
138
  export function setGate(resolve: GateResolver): void
131
139
  export function setGate(a: Gate | GateResolver, b?: GateResolver): void {
@@ -140,6 +148,9 @@ export function setGate(a: Gate | GateResolver, b?: GateResolver): void {
140
148
 
141
149
  /**
142
150
  * Drops the gate — the real content comes back, and the current tab is kept.
151
+ *
152
+ * @platform ios android
153
+ * A no-op on web, where no gate was ever raised.
143
154
  */
144
155
  export function clearGate(): void {
145
156
  resolver = undefined
@@ -148,7 +159,11 @@ export function clearGate(): void {
148
159
 
149
160
  /**
150
161
  * Subscribes to gate events (fired when a request is gated).
151
- * @returns Cleanup function to unsubscribe.
162
+ *
163
+ * @platform ios android
164
+ * Never fires on web — there are no gated serves to report.
165
+ *
166
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
152
167
  */
153
168
  export const onGate = LazyNativeEmitter.emitterize<GateEvent>(
154
169
  (cb) => (nativeBrowser.onGate = cb)
@@ -0,0 +1,302 @@
1
+ /** @vitest-environment happy-dom */
2
+
3
+ import { renderHook, act, cleanup } from '@testing-library/react'
4
+ import { describe, it, expect, afterEach, vi } from 'vitest'
5
+
6
+ /**
7
+ * The hooks that are a single delegation to `useNativeUpdatedValue`. Their
8
+ * behaviour is covered once in `utils/useNativeUpdatedValue.test.ts`; what is
9
+ * per-hook and untestable there is the wiring — which native callback slot the
10
+ * emitter claims, and which `eventKey` is pulled out of the payload.
11
+ * (`useActiveTrack` and `useQueue` are getter-refreshed instead — they update
12
+ * on transitions AND favorite toggles — and are covered in their own block.)
13
+ *
14
+ * Both are easy to get wrong and silent when wrong: the hook simply never
15
+ * updates, or yields the whole event where a field was meant. Five slots
16
+ * already differ in name from the emitter that claims them
17
+ * (`onPlaybackPlayWhenReadyChanged` behind `onPlayWhenReadyChanged`, and
18
+ * friends), so the names cannot be derived — they have to be pinned.
19
+ */
20
+ const slots = vi.hoisted(() => new Map<string, (event: unknown) => void>())
21
+
22
+ vi.mock('../native', () => ({
23
+ nativeBrowser: new Proxy({} as Record<string, unknown>, {
24
+ get(target, prop: string) {
25
+ // Getters are the pre-native fallback; undefined stands in for "native
26
+ // has not answered yet", which is what these tests start from.
27
+ target[prop] ??= vi.fn(() => undefined)
28
+ return target[prop]
29
+ },
30
+ set(target, prop: string, value) {
31
+ if (typeof value === 'function') slots.set(prop, value)
32
+ target[prop] = value
33
+ return true
34
+ }
35
+ })
36
+ }))
37
+
38
+ const { useBatteryOptimizationStatus, useBatteryWarningPending } =
39
+ await import('./battery')
40
+ const { useContent, usePath, useTabs } = await import('./browser')
41
+ const { useCarConnected } = await import('./carConnection')
42
+ const { useEqualizerSettings } = await import('./equalizer')
43
+ const { useFormattedNavigationError, useNavigationError, usePlaybackError } =
44
+ await import('./errors')
45
+ const { useOnline } = await import('./network')
46
+ const { useNowPlaying } = await import('./nowPlaying')
47
+ const { useOutput } = await import('./output')
48
+ const { useOptions } = await import('./player/options')
49
+ const { usePlayingState } = await import('./playback/playing')
50
+ const { usePlayWhenReady } = await import('./playback/playWhenReady')
51
+ const { useProgress } = await import('./playback/progress')
52
+ const { usePlayback } = await import('./playback/state')
53
+ const { useSystemVolume } = await import('./playback/volume')
54
+ const { useActiveTrack } = await import('./queue/activeTrack')
55
+ const { useQueue } = await import('./queue/queue')
56
+ const { useRepeatMode } = await import('./queue/repeatMode')
57
+ const { useShuffle } = await import('./queue/shuffle')
58
+
59
+ afterEach(cleanup)
60
+
61
+ type Wiring = {
62
+ name: string
63
+ hook: () => unknown
64
+ /** The property on the Nitro object the emitter assigns its callback to. */
65
+ slot: string
66
+ event: unknown
67
+ /** What the hook returns — differs from `event` exactly when an eventKey applies. */
68
+ expected: unknown
69
+ }
70
+
71
+ const wirings: Wiring[] = [
72
+ // Payload passed through whole (no eventKey).
73
+ {
74
+ name: 'useOutput',
75
+ hook: useOutput,
76
+ slot: 'onOutputChanged',
77
+ event: { type: 'bluetooth' },
78
+ expected: { type: 'bluetooth' }
79
+ },
80
+ {
81
+ name: 'useNowPlaying',
82
+ hook: useNowPlaying,
83
+ slot: 'onNowPlayingChanged',
84
+ event: { title: 'Song' },
85
+ expected: { title: 'Song' }
86
+ },
87
+ {
88
+ name: 'useOnline',
89
+ hook: useOnline,
90
+ slot: 'onOnlineChanged',
91
+ event: true,
92
+ expected: true
93
+ },
94
+ {
95
+ name: 'useCarConnected',
96
+ hook: useCarConnected,
97
+ slot: 'onCarConnectedChanged',
98
+ event: true,
99
+ expected: true
100
+ },
101
+ {
102
+ name: 'useEqualizerSettings',
103
+ hook: useEqualizerSettings,
104
+ slot: 'onEqualizerChanged',
105
+ event: { enabled: true },
106
+ expected: { enabled: true }
107
+ },
108
+ {
109
+ name: 'usePath',
110
+ hook: usePath,
111
+ slot: 'onPathChanged',
112
+ event: '/artists/1',
113
+ expected: '/artists/1'
114
+ },
115
+ {
116
+ name: 'useContent',
117
+ hook: useContent,
118
+ slot: 'onContentChanged',
119
+ event: { src: 'a' },
120
+ expected: { src: 'a' }
121
+ },
122
+ {
123
+ name: 'useTabs',
124
+ hook: useTabs,
125
+ slot: 'onTabsChanged',
126
+ event: [{ src: 'a' }],
127
+ expected: [{ src: 'a' }]
128
+ },
129
+ {
130
+ name: 'usePlayback',
131
+ hook: usePlayback,
132
+ slot: 'onPlaybackChanged',
133
+ event: { state: 'playing' },
134
+ expected: { state: 'playing' }
135
+ },
136
+ {
137
+ name: 'useSystemVolume',
138
+ hook: useSystemVolume,
139
+ slot: 'onSystemVolumeChanged',
140
+ event: 0.4,
141
+ expected: 0.4
142
+ },
143
+ {
144
+ name: 'useProgress',
145
+ hook: useProgress,
146
+ slot: 'onPlaybackProgressUpdated',
147
+ event: { position: 1, duration: 2, buffered: 3 },
148
+ expected: { position: 1, duration: 2, buffered: 3 }
149
+ },
150
+ {
151
+ name: 'usePlayingState',
152
+ hook: usePlayingState,
153
+ slot: 'onPlaybackPlayingState',
154
+ event: { state: 'ready' },
155
+ expected: { state: 'ready' }
156
+ },
157
+ {
158
+ name: 'useShuffle',
159
+ hook: useShuffle,
160
+ slot: 'onPlaybackShuffleModeChanged',
161
+ event: true,
162
+ expected: true
163
+ },
164
+ {
165
+ name: 'useFormattedNavigationError',
166
+ hook: useFormattedNavigationError,
167
+ slot: 'onFormattedNavigationError',
168
+ event: { title: 'Offline' },
169
+ expected: { title: 'Offline' }
170
+ },
171
+ {
172
+ name: 'useOptions',
173
+ hook: useOptions,
174
+ slot: 'onOptionsChanged',
175
+ event: { playbackSpeed: 1.5 },
176
+ expected: { playbackSpeed: 1.5 }
177
+ },
178
+
179
+ // A field lifted out of the payload — the eventKey cases.
180
+ {
181
+ name: 'usePlaybackError',
182
+ hook: usePlaybackError,
183
+ slot: 'onPlaybackError',
184
+ event: { error: { code: 'network' } },
185
+ expected: { code: 'network' }
186
+ },
187
+ {
188
+ name: 'useNavigationError',
189
+ hook: useNavigationError,
190
+ slot: 'onNavigationError',
191
+ event: { error: { code: 'notFound' } },
192
+ expected: { code: 'notFound' }
193
+ },
194
+ {
195
+ name: 'usePlayWhenReady',
196
+ hook: usePlayWhenReady,
197
+ slot: 'onPlaybackPlayWhenReadyChanged',
198
+ event: { playWhenReady: true },
199
+ expected: true
200
+ },
201
+ {
202
+ name: 'useRepeatMode',
203
+ hook: useRepeatMode,
204
+ slot: 'onPlaybackRepeatModeChanged',
205
+ event: { repeatMode: 'queue' },
206
+ expected: 'queue'
207
+ },
208
+ {
209
+ name: 'useBatteryWarningPending',
210
+ hook: useBatteryWarningPending,
211
+ slot: 'onBatteryWarningPendingChanged',
212
+ event: { pending: true },
213
+ expected: true
214
+ },
215
+ {
216
+ name: 'useBatteryOptimizationStatus',
217
+ hook: useBatteryOptimizationStatus,
218
+ slot: 'onBatteryOptimizationStatusChanged',
219
+ event: { status: 'unrestricted' },
220
+ expected: 'unrestricted'
221
+ }
222
+ ]
223
+
224
+ describe.each(wirings)('$name', ({ hook, slot, event, expected }) => {
225
+ it(`reads $slot`, () => {
226
+ // A slot nothing claimed means the emitter assigns a different property
227
+ // than the one named here — the hook would never update in production.
228
+ expect(slots.has(slot)).toBe(true)
229
+
230
+ const { result } = renderHook(() => hook())
231
+ act(() => slots.get(slot)!(event))
232
+ expect(result.current).toEqual(expected)
233
+ })
234
+ })
235
+
236
+ describe('hook wiring table', () => {
237
+ it('covers every delegating hook exactly once', () => {
238
+ const names = wirings.map((w) => w.name)
239
+ expect(new Set(names).size).toBe(names.length)
240
+ expect(new Set(wirings.map((w) => w.slot)).size).toBe(wirings.length)
241
+ })
242
+ })
243
+
244
+ /**
245
+ * `useActiveTrack` and `useQueue` are getter-refreshed: a favorite toggle
246
+ * mutates the active track in place WITHOUT a transition event —
247
+ * transitions and mutations are separate events — so
248
+ * these hooks re-read their native getter on the transition slot AND on
249
+ * `onFavoriteChanged`.
250
+ */
251
+ const { nativeBrowser } = await import('../native')
252
+
253
+ describe.each([
254
+ {
255
+ name: 'useActiveTrack',
256
+ hook: useActiveTrack,
257
+ getter: 'getActiveTrack',
258
+ transitionSlot: 'onPlaybackActiveTrackChanged',
259
+ initial: { src: 'a', favorited: false },
260
+ mutated: { src: 'a', favorited: true }
261
+ },
262
+ {
263
+ name: 'useQueue',
264
+ hook: useQueue,
265
+ getter: 'getQueue',
266
+ transitionSlot: 'onPlaybackQueueChanged',
267
+ initial: [{ src: 'a', favorited: false }],
268
+ mutated: [{ src: 'a', favorited: true }]
269
+ }
270
+ ])(
271
+ '$name (getter-refreshed)',
272
+ ({ hook, getter, transitionSlot, initial, mutated }) => {
273
+ it(`re-reads ${getter} on ${transitionSlot} and onFavoriteChanged`, () => {
274
+ expect(slots.has(transitionSlot)).toBe(true)
275
+ expect(slots.has('onFavoriteChanged')).toBe(true)
276
+
277
+ const getterMock = (
278
+ nativeBrowser as unknown as Record<string, ReturnType<typeof vi.fn>>
279
+ )[getter]!
280
+
281
+ getterMock.mockReturnValue(initial)
282
+ const { result } = renderHook(() => hook())
283
+ expect(result.current).toEqual(initial)
284
+
285
+ // A transition re-reads the getter (the event payload is not consumed).
286
+ getterMock.mockReturnValue(mutated)
287
+ act(() => slots.get(transitionSlot)!({}))
288
+ expect(result.current).toEqual(mutated)
289
+
290
+ // A favorite toggle mutates in place — no transition event fires, but
291
+ // the hook must still refresh.
292
+ getterMock.mockReturnValue(initial)
293
+ act(() =>
294
+ slots.get('onFavoriteChanged')!({
295
+ track: { src: 'a' },
296
+ favorited: false
297
+ })
298
+ )
299
+ expect(result.current).toEqual(initial)
300
+ })
301
+ }
302
+ )
@@ -98,8 +98,13 @@ export interface ChapterMetadata {
98
98
 
99
99
  /**
100
100
  * Subscribes to chapter metadata events.
101
+ *
102
+ * @platform ios android
103
+ * Never fires on web — the web player does not read chapter markers. See
104
+ * {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
105
+ *
101
106
  * @param callback - Called when chapter metadata is received
102
- * @returns Cleanup function to unsubscribe
107
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
103
108
  */
104
109
  export const onChapterMetadata = NativeUpdatedValue.emitterize<
105
110
  ChapterMetadata[]
@@ -107,8 +112,13 @@ export const onChapterMetadata = NativeUpdatedValue.emitterize<
107
112
 
108
113
  /**
109
114
  * Subscribes to track metadata events.
115
+ *
116
+ * @platform ios android
117
+ * Never fires on web — the web player does not read embedded file tags. See
118
+ * {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
119
+ *
110
120
  * @param callback - Called when static track metadata is received
111
- * @returns Cleanup function to unsubscribe
121
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
112
122
  */
113
123
  export const onTrackMetadata = NativeUpdatedValue.emitterize<TrackMetadata>(
114
124
  (cb) => (nativeBrowser.onTrackMetadata = cb)
@@ -116,8 +126,15 @@ export const onTrackMetadata = NativeUpdatedValue.emitterize<TrackMetadata>(
116
126
 
117
127
  /**
118
128
  * Subscribes to timed metadata events (ICY/ID3 from live streams).
129
+ *
130
+ * @platform ios android
131
+ * Never fires on web. ICY metadata is stripped by the browser before the player
132
+ * can see it, and no browser API exposes it; in-band ID3 from HLS is reachable
133
+ * but not yet wired. See
134
+ * {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
135
+ *
119
136
  * @param callback - Called when stream metadata is received (title, artist from ICY/ID3 tags)
120
- * @returns Cleanup function to unsubscribe
137
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
121
138
  */
122
139
  export const onTimedMetadata = NativeUpdatedValue.emitterize<TimedMetadata>(
123
140
  (cb) => (nativeBrowser.onTimedMetadata = cb)
@@ -17,7 +17,7 @@ export function getOnline(): boolean {
17
17
  /**
18
18
  * Subscribes to network connectivity changes.
19
19
  * @param callback - Called when network state changes
20
- * @returns Cleanup function to unsubscribe
20
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
21
21
  */
22
22
  export const onOnlineChanged = NativeUpdatedValue.emitterize<boolean>(
23
23
  (cb) => (nativeBrowser.onOnlineChanged = cb)
@@ -74,7 +74,7 @@ export function clearNowPlayingFlash(): void {
74
74
  * Subscribes to now playing metadata change events.
75
75
  * Fires when updateNowPlaying is called or when the track changes.
76
76
  * @param callback - Called when now playing metadata changes
77
- * @returns Cleanup function to unsubscribe
77
+ * @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
78
78
  */
79
79
  export const onNowPlayingChanged =
80
80
  NativeUpdatedValue.emitterize<NowPlayingMetadata>(