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
@@ -0,0 +1,244 @@
1
+ ---
2
+ description: 'Porting from react-native-track-player v4: what carries over unchanged, what was renamed or reshaped, and what is new.'
3
+ ---
4
+
5
+ # Migrating from react-native-track-player
6
+
7
+ If you're coming from [react-native-track-player](https://github.com/doublesymmetry/react-native-track-player/tree/v4)
8
+ (RNTP) v4, most of your playback and queue code ports over almost unchanged — the
9
+ function names and semantics were deliberately kept familiar. This page maps the
10
+ parts that _do_ change.
11
+
12
+ `react-native-audio-browser` started as a fork of RNTP v4, and we wrote much of
13
+ that original code. We've since rethought and rewritten it almost entirely. The
14
+ API stays familiar by lineage, not coincidence, but almost everything underneath
15
+ is new. RNTP has itself moved to a commercial license; `react-native-audio-browser`
16
+ stays MIT.
17
+
18
+ ## What ports over unchanged
19
+
20
+ These are the same calls with the same meaning — `import` them from
21
+ `react-native-audio-browser` instead of `react-native-track-player`:
22
+
23
+ [`setupPlayer`](/api/features/player/#setupplayer), [`updateOptions`](/api/features/player/#updateoptions),
24
+ [`add`](/api/features/queue/#add), [`remove`](/api/features/queue/#remove), [`move`](/api/features/queue/#move),
25
+ [`skip`](/api/features/queue/#skip), [`skipToNext`](/api/features/queue/#skiptonext),
26
+ [`skipToPrevious`](/api/features/queue/#skiptoprevious), [`setQueue`](/api/features/queue/#setqueue),
27
+ [`getQueue`](/api/features/queue/#getqueue), [`getTrack`](/api/features/queue/#gettrack),
28
+ [`removeUpcomingTracks`](/api/features/queue/#removeupcomingtracks), [`load`](/api/features/queue/#load),
29
+ [`reset`](/api/features/playback/#reset), [`retry`](/api/features/playback/#retry),
30
+ [`play`](/api/features/playback/#play), [`pause`](/api/features/playback/#pause),
31
+ [`stop`](/api/features/playback/#stop), [`seekTo`](/api/features/playback/#seekto),
32
+ [`seekBy`](/api/features/playback/#seekby), [`setRate`](/api/features/playback/#setrate),
33
+ [`getRate`](/api/features/playback/#getrate), [`setVolume`](/api/features/playback/#setvolume),
34
+ [`getVolume`](/api/features/playback/#getvolume), [`setRepeatMode`](/api/features/queue/#setrepeatmode),
35
+ [`getRepeatMode`](/api/features/queue/#getrepeatmode), [`setPlayWhenReady`](/api/features/playback/#setplaywhenready),
36
+ [`getPlayWhenReady`](/api/features/playback/#getplaywhenready), [`getActiveTrack`](/api/features/queue/#getactivetrack),
37
+ [`getActiveTrackIndex`](/api/features/queue/#getactivetrackindex).
38
+
39
+ The differences below are what to watch for.
40
+
41
+ ## Getters are synchronous — and there are hooks
42
+
43
+ RNTP getters return Promises; audio-browser getters return the value
44
+ **synchronously**, and most have a reactive **hook**. Drop the `await` (awaiting a
45
+ non-Promise still works, so this isn't urgent), and prefer the hook in React.
46
+
47
+ | RNTP v4 | audio-browser |
48
+ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `await getPosition()` | [`getProgress().position`](/api/features/playback/#position) |
50
+ | `await getDuration()` | [`getProgress().duration`](/api/features/playback/#duration) |
51
+ | `await getBufferedPosition()` | [`getProgress().buffered`](/api/features/playback/#buffered) |
52
+ | `await getProgress()` | [`getProgress()`](/api/features/playback/#getprogress) (sync) / [`usePolledProgress()`](/api/features/playback/#usepolledprogress) |
53
+ | `await getState()` / `getPlaybackState()` | [`getPlayback().state`](/api/features/playback/#state) / [`usePlayback()`](/api/features/playback/#useplayback) |
54
+ | `await getActiveTrack()` | [`getActiveTrack()`](/api/features/queue/#getactivetrack) / [`useActiveTrack()`](/api/features/queue/#useactivetrack) |
55
+ | `await getQueue()` | [`getQueue()`](/api/features/queue/#getqueue) / [`useQueue()`](/api/features/queue/#usequeue) |
56
+ | `await getRepeatMode()` | [`getRepeatMode()`](/api/features/queue/#getrepeatmode) / [`useRepeatMode()`](/api/features/queue/#userepeatmode) |
57
+
58
+ Two hook gotchas:
59
+
60
+ - **`useProgress` changed meaning.** RNTP's `useProgress(interval)` is polling;
61
+ its drop-in is [`usePolledProgress(intervalMs)`](/api/features/playback/#usepolledprogress).
62
+ audio-browser _also_ has a [`useProgress()`](/api/features/playback/#useprogress), but it's **event-based and takes no
63
+ interval** (and needs `progressUpdateEventInterval` enabled) — so copying
64
+ `useProgress(2000)` over compiles but silently ignores the arg. Use
65
+ `usePolledProgress` if you relied on the polling interval.
66
+ - **`useIsPlaying()` → [`usePlayingState()`](/api/features/playback/#useplayingstate)**,
67
+ but the field is renamed: RNTP's `{ playing, bufferingDuringPlay }` becomes
68
+ `{ playing, buffering }`.
69
+
70
+ ## Events: per-event `on*` / `use*`, no `Event` enum
71
+
72
+ RNTP centralizes events behind the `Event` enum with `addEventListener` /
73
+ `useTrackPlayerEvents`. audio-browser exposes one emitter (and usually a hook)
74
+ per event, and `addListener` returns an **unsubscribe function** (not a
75
+ subscription with `.remove()`):
76
+
77
+ ```ts
78
+ // RNTP
79
+ import TrackPlayer, { Event } from 'react-native-track-player'
80
+ const sub = TrackPlayer.addEventListener(
81
+ Event.PlaybackActiveTrackChanged,
82
+ (e) => {}
83
+ )
84
+ sub.remove()
85
+
86
+ // audio-browser
87
+ import { onActiveTrackChanged } from 'react-native-audio-browser'
88
+ const unsubscribe = onActiveTrackChanged.addListener((e) => {})
89
+ unsubscribe()
90
+ ```
91
+
92
+ | RNTP `Event.*` | audio-browser |
93
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
94
+ | `PlaybackActiveTrackChanged` | [`onActiveTrackChanged`](/api/features/queue/#onactivetrackchanged) / [`useActiveTrack()`](/api/features/queue/#useactivetrack) |
95
+ | `PlaybackState` | [`onPlaybackChanged`](/api/features/playback/#onplaybackchanged) / [`usePlayback()`](/api/features/playback/#useplayback) |
96
+ | `PlaybackProgressUpdated` | [`onProgressUpdated`](/api/features/playback/#onprogressupdated) / [`useProgress()`](/api/features/playback/#useprogress) |
97
+ | `PlaybackQueueEnded` | [`onQueueEnded`](/api/features/queue/#onqueueended) |
98
+ | `PlaybackError` / `PlayerError` | [`onPlaybackError`](/api/features/errors/#onplaybackerror) / [`usePlaybackError()`](/api/features/errors/#useplaybackerror) |
99
+ | `PlaybackPlayWhenReadyChanged` | [`onPlayWhenReadyChanged`](/api/features/playback/#onplaywhenreadychanged) / [`usePlayWhenReady()`](/api/features/playback/#useplaywhenready) |
100
+ | `Remote*` (Play, Pause, Next, …) | `handleRemote*` to override, `onRemote*` to observe — see [Remote Controls](/guide/remote-controls) |
101
+ | `Metadata*Received` | [`onTimedMetadata`](/api/features/metadata/#ontimedmetadata) / [`onChapterMetadata`](/api/features/metadata/#onchaptermetadata) / [`onTrackMetadata`](/api/features/metadata/#ontrackmetadata) — see [Metadata](/guide/metadata) |
102
+
103
+ ## Delete the playback service
104
+
105
+ RNTP requires `registerPlaybackService(() => require('./service'))` and a service
106
+ file that wires `Event.RemotePlay → TrackPlayer.play()`, etc. **audio-browser has
107
+ no playback service** — the library wires the native side itself, and remote
108
+ controls drive the player by default. Delete the service file and the
109
+ registration call. To customize a remote control, set a
110
+ [`handleRemote*`](/guide/remote-controls) override (pass `undefined` to restore
111
+ the default); to just observe, use `onRemote*`:
112
+
113
+ ```ts
114
+ import { handleRemoteNext, skipToNext } from 'react-native-audio-browser'
115
+
116
+ // Override "next" (call the default action yourself if you still want it):
117
+ handleRemoteNext(() => skipToNext())
118
+ handleRemoteNext(undefined) // restore the default
119
+ ```
120
+
121
+ ## `capabilities`: an object, not an array
122
+
123
+ ```ts
124
+ // RNTP
125
+ import { Capability } from 'react-native-track-player'
126
+ await TrackPlayer.updateOptions({
127
+ capabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext]
128
+ })
129
+
130
+ // audio-browser — most are ON by default; specify only what differs
131
+ updateOptions({ capabilities: { skipToPrevious: false } })
132
+ ```
133
+
134
+ Pass `capabilities` to **either** [`setupPlayer`](/api/features/player/#setupplayer)
135
+ (initial) or [`updateOptions`](/api/features/player/#updateoptions) (later) — both
136
+ accept it. See
137
+ [Configuration → Capabilities](/guide/configuration#capabilities) for the
138
+ defaults (everything on except `jumpForward`, `jumpBackward`, `favorite`).
139
+ `Capability.Like` / `RatingType` map to the [`favorite`](/guide/favorites)
140
+ capability.
141
+
142
+ ## Enums become string literals
143
+
144
+ audio-browser has no enum objects — use the string values directly:
145
+
146
+ | RNTP | audio-browser |
147
+ | ------------------------------------ | --------------------------------------------- |
148
+ | `State.Playing`, `State.Paused`, … | `'playing'`, `'paused'`, … (same strings) |
149
+ | `RepeatMode.Off` / `Track` / `Queue` | `'off'` / `'track'` / `'queue'` |
150
+ | `Capability.Play` (in an array) | `{ play: true }` (in the capabilities object) |
151
+
152
+ `State`'s string values are identical, so `state === 'playing'` checks port
153
+ directly — you just drop the enum import.
154
+
155
+ ## `Track`: `url` → `src`
156
+
157
+ The playable URL field is renamed. In audio-browser, the **playable source**
158
+ is [`src`](/api/types/browser-nodes/#src); a track's **browse address** (for the
159
+ [browse tree](/guide/browser)) is a separate field, `path`:
160
+
161
+ ```ts
162
+ // RNTP
163
+ {
164
+ url: ('https://cdn.example.com/song.mp3', title, artist, artwork)
165
+ }
166
+
167
+ // audio-browser
168
+ {
169
+ src: ('https://cdn.example.com/song.mp3', title, artist, artwork)
170
+ }
171
+ ```
172
+
173
+ Other field changes: `isLiveStream` → [`live`](/api/types/browser-nodes/#live); per-track `headers`
174
+ move to a [media request config](/guide/browser); `rating` → favorites (see
175
+ above). `title` / `artist` / `album` / `artwork` / `duration` are unchanged.
176
+
177
+ ## Now-playing metadata
178
+
179
+ ```ts
180
+ // RNTP
181
+ TrackPlayer.updateNowPlayingMetadata({ title, artist })
182
+ TrackPlayer.clearNowPlayingMetadata()
183
+
184
+ // audio-browser
185
+ import { updateNowPlaying } from 'react-native-audio-browser'
186
+ updateNowPlaying({ title, artist })
187
+ updateNowPlaying(null) // clear
188
+ ```
189
+
190
+ For live streams, prefer the [Now Playing formatter](/guide/now-playing) over
191
+ imperative updates — it re-derives the lock-screen lines on every metadata /
192
+ state change.
193
+
194
+ ## Setup, before and after
195
+
196
+ ```ts
197
+ // RNTP — index.js + startup
198
+ TrackPlayer.registerPlaybackService(() => require('./service'))
199
+ await TrackPlayer.setupPlayer()
200
+ await TrackPlayer.updateOptions({
201
+ capabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext],
202
+ progressUpdateEventInterval: 2
203
+ })
204
+ ```
205
+
206
+ ```ts
207
+ // audio-browser — one declarative call, no service
208
+ import { setupPlayer } from 'react-native-audio-browser'
209
+
210
+ await setupPlayer({
211
+ progressUpdateEventInterval: 2
212
+ // capabilities are on by default; nothing to register
213
+ })
214
+ ```
215
+
216
+ See [Configuration](/guide/configuration) for the full options surface and what's
217
+ setup-only vs runtime-updatable.
218
+
219
+ ## What you gain
220
+
221
+ audio-browser adds capabilities RNTP doesn't have — worth adopting once you've
222
+ ported:
223
+
224
+ - A [**browse tree**](/guide/browser) that powers native **CarPlay** and
225
+ **Android Auto** menus (and voice [search](/guide/search)) — see the
226
+ [Automotive overview](/guide/automotive).
227
+ - A [**now-playing formatter**](/guide/now-playing) for live-stream metadata and
228
+ transient status lines.
229
+ - [**Content gating**](/guide/gate), a [**sleep timer**](/guide/sleep-timer),
230
+ an [**equalizer**](/guide/equalizer), and [**favorites**](/guide/favorites).
231
+
232
+ ## Quick reference
233
+
234
+ | Area | RNTP v4 | audio-browser |
235
+ | --------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- |
236
+ | Getters | `await TrackPlayer.getX()` | `getX()` (sync) or `useX()` |
237
+ | Events | `Event` + `addEventListener` / `useTrackPlayerEvents` | per-event `onX` / `useX` |
238
+ | Unsubscribe | `subscription.remove()` | the function `addListener` returns |
239
+ | Remote handling | a registered playback **service** | default behavior + `handleRemote*` |
240
+ | Capabilities | `Capability[]` | `capabilities` object of booleans |
241
+ | Enums | `State.*`, `RepeatMode.*` | string literals (`'playing'`, `'queue'`) |
242
+ | Playable URL | `track.url` | [`track.src`](/api/types/browser-nodes/#src) |
243
+ | Live flag | `track.isLiveStream` | [`track.live`](/api/types/browser-nodes/#live) |
244
+ | Now playing | `updateNowPlayingMetadata` / `clearNowPlayingMetadata` | [`updateNowPlaying`](/api/features/nowPlaying/#updatenowplaying)`(update \| null)` |
@@ -0,0 +1,127 @@
1
+ ---
2
+ description: "Why React Native's fetch hangs inside browse resolvers, search sources, and request transforms when the device is asleep, and what to use instead."
3
+ ---
4
+
5
+ # Networking in native callbacks (don't use `fetch`)
6
+
7
+ **If a browse route resolver, search source, or request `transform` does its own
8
+ networking with React Native's built-in `fetch` — and it works in your app but
9
+ _hangs_ when the device is asleep (browsing on CarPlay or Android Auto with the
10
+ screen off, or answering a voice intent) — you are in the right place.** The
11
+ browse screen goes blank because the request never resolves. The fix is to fetch
12
+ with
13
+ [`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch),
14
+ or to let the library do the request natively.
15
+
16
+ ## The problem
17
+
18
+ The library invokes the async callbacks you provide **from native code**,
19
+ whenever the system asks for content — including while your app is asleep:
20
+
21
+ - browse **route resolvers** (`configureBrowser({ routes })`),
22
+ - the **search source** (`configureBrowser({ search })`),
23
+ - request **`transform`** callbacks.
24
+
25
+ A locked iPhone browsing your library on CarPlay, or a "play jazz" voice command
26
+ with the screen dark, runs these callbacks with the app backgrounded. The first
27
+ native call into JS runs fine — but the moment your callback `await`s a `fetch`,
28
+ it hangs until the phone wakes.
29
+
30
+ ### Why `fetch` hangs while asleep
31
+
32
+ React Native's `fetch` is the [`whatwg-fetch`](https://github.com/JakeChampion/fetch)
33
+ polyfill, and it resolves its promise with a **zero-delay timer**, not a
34
+ microtask:
35
+
36
+ ```js
37
+ // inside whatwg-fetch
38
+ setTimeout(function () {
39
+ resolve(new Response(body, options))
40
+ }, 0)
41
+ ```
42
+
43
+ When the device is asleep, React Native's timer module is paused — on iOS,
44
+ `RCTTiming` pauses the `CADisplayLink` that drives JS timers — so `setTimeout`
45
+ callbacks don't fire. Native networking (`RCTNetworking`) **does** perform the
46
+ request and receive the response, but the `setTimeout(0)` that would hand it back
47
+ to your `await` never runs. The promise sits unresolved until the screen wakes,
48
+ and your browse list spins or shows blank. Android suspends JS timers the same
49
+ way while the app is backgrounded behind Android Auto, so the symptom and the fix
50
+ are identical there.
51
+
52
+ This isn't specific to this library — it affects any `fetch` on a sleeping
53
+ device, and upgrading doesn't help: current React Native still ships
54
+ `whatwg-fetch`.
55
+
56
+ ## The fix: `react-native-nitro-fetch`
57
+
58
+ [`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch)
59
+ is a drop-in `fetch` replacement that runs the request on a native thread
60
+ (`URLSession` on iOS, Cronet on Android) and resolves through **Nitro's
61
+ microtask queue** — no `setTimeout`, no `whatwg-fetch`, no `RCTTiming`
62
+ dependency. So it completes even when the callback runs while the device is
63
+ asleep. Since this library is already a Nitro module, it's a natural fit.
64
+
65
+ ```bash
66
+ npm install react-native-nitro-fetch react-native-nitro-modules
67
+ ```
68
+
69
+ You already have `react-native-nitro-modules` (it's a peer dependency of this
70
+ library); you'll need a native rebuild (`pod install` + rebuild) after
71
+ installing.
72
+
73
+ Then add one import at the top of any module with a native-invoked callback — it
74
+ shadows the global `fetch`, so the call sites don't change:
75
+
76
+ ```ts
77
+ import { configureBrowser } from 'react-native-audio-browser'
78
+ import { fetch } from 'react-native-nitro-fetch'
79
+
80
+ configureBrowser({
81
+ tabs: [{ title: 'Browse', path: '/browse' }],
82
+ routes: {
83
+ '/browse': async ({ path }) => {
84
+ const res = await fetch('https://api.example.com/browse')
85
+ const { children } = await res.json()
86
+ return { path: path, title: 'Browse', children }
87
+ }
88
+ }
89
+ })
90
+ ```
91
+
92
+ Use the same import in your search source and request `transform`s.
93
+
94
+ ## Even simpler: let the library fetch natively
95
+
96
+ If a route just maps a path to an HTTP request, you don't have to write a JS
97
+ callback at all. Give the browse source a `baseUrl` and the **library performs
98
+ the request natively** for each navigated path — no JS networking, nothing to
99
+ stall:
100
+
101
+ ```ts
102
+ configureBrowser({
103
+ // Each browse path is fetched over HTTP on the native side.
104
+ browse: { baseUrl: 'https://api.example.com' },
105
+ tabs: [{ title: 'Browse', path: '/browse' }]
106
+ })
107
+ ```
108
+
109
+ The navigated path is appended to `baseUrl`; to remap or shape the request, use a
110
+ [`transform`](/guide/browser) (still native). The search source accepts the same
111
+ request-config form, so it can be served natively too. Reach for
112
+ `react-native-nitro-fetch` only when you need logic a request config can't
113
+ express (combining sources, reading local storage, custom shaping).
114
+
115
+ ## When it's safe to use plain `fetch`
116
+
117
+ This only affects code the library invokes from native while the app is asleep.
118
+ Networking driven by **your own app UI in the foreground** — a screen the user is
119
+ looking at — runs on the normal JS loop and is unaffected; plain `fetch` is fine
120
+ there. Rule of thumb: if the OS can run the code with the screen off, don't
121
+ depend on `fetch`.
122
+
123
+ ## Further reading
124
+
125
+ - [`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch)
126
+ - [JS timers don't fire when the app is backgrounded (RN #38711)](https://github.com/facebook/react-native/issues/38711)
127
+ - [Timers & fetch freeze behind a native activity (RN #54534)](https://github.com/facebook/react-native/issues/54534)
@@ -0,0 +1,86 @@
1
+ ---
2
+ description: 'Track the device''s validated internet connectivity with useOnline, getOnline, and onOnlineChanged, and what "online" means on each platform.'
3
+ ---
4
+
5
+ # Network
6
+
7
+ The library tracks the device's internet connectivity with a native monitor and
8
+ exposes it three ways:
9
+
10
+ - [`useOnline()`](/api/features/network/#useonline) — a reactive hook for your UI.
11
+ - [`getOnline()`](/api/features/network/#getonline) — a one-off snapshot.
12
+ - [`onOnlineChanged`](/api/features/network/#ononlinechanged) — subscribe to
13
+ changes outside React.
14
+
15
+ All three report the same boolean: `true` when the device has a working internet
16
+ connection. **"Online" means _validated_ internet, not just "connected to a
17
+ network".** Android is strict here — it requires the OS to confirm the network
18
+ actually reaches the internet, so Wi-Fi with no real connectivity reads as
19
+ `false`. iOS derives the signal from the system path monitor (`NWPathMonitor`),
20
+ which is slightly more permissive about such captive networks.
21
+
22
+ The UI snippet imports `Text` from `react-native`; everything else is from
23
+ `react-native-audio-browser`.
24
+
25
+ ## In your UI
26
+
27
+ `useOnline` re-renders when connectivity flips — enough for an offline banner:
28
+
29
+ ```tsx
30
+ import { Text } from 'react-native'
31
+ import { useOnline } from 'react-native-audio-browser'
32
+
33
+ function OfflineBanner() {
34
+ const online = useOnline()
35
+ if (online) return null
36
+ return <Text>You're offline</Text>
37
+ }
38
+ ```
39
+
40
+ ## Outside React
41
+
42
+ Read a snapshot with `getOnline()`, or subscribe with `onOnlineChanged`
43
+ (`addListener` returns an unsubscribe function):
44
+
45
+ ```ts
46
+ import { getOnline, onOnlineChanged } from 'react-native-audio-browser'
47
+
48
+ getOnline() // → true | false
49
+
50
+ const unsubscribe = onOnlineChanged.addListener((online) => {
51
+ if (online) refetchSomething()
52
+ })
53
+
54
+ // later:
55
+ unsubscribe()
56
+ ```
57
+
58
+ ## You may not need this
59
+
60
+ The player already reacts to connectivity on its own, so you usually don't wire
61
+ these up for playback:
62
+
63
+ - When the connection drops mid-stream, the now-playing formatter receives
64
+ `stalled: 'offline'` (vs `'buffering'` when online) — see
65
+ [Now Playing → the formatter](/guide/now-playing#the-formatter-derived-continuous).
66
+ - When the connection returns while the stream sits **stalled** (state
67
+ `'buffering'` with play intent), the player re-establishes it automatically —
68
+ you don't need to listen for reconnect and re-`play()`. If the drop instead
69
+ surfaced as a playback **error**, recovery belongs to
70
+ [automatic retry](/guide/errors#automatic-retry) (`setupPlayer({ retry })`,
71
+ off by default); once retry gives up — or without it — the player stays in
72
+ `'error'` until the listener (or your code) calls `retry()` / `play()`.
73
+
74
+ Reach for `useOnline` / `onOnlineChanged` for **your own** UI and data — an
75
+ offline banner, disabling a control, refetching a list on reconnect.
76
+
77
+ ## API summary
78
+
79
+ | API | Purpose |
80
+ | ----------------- | --------------------------------------------------------------------- |
81
+ | `useOnline()` | Reactive `boolean`; re-renders on connectivity change. |
82
+ | `getOnline()` | One-off snapshot `boolean`. |
83
+ | `onOnlineChanged` | Subscribe outside React; `addListener(cb)` returns an unsubscribe fn. |
84
+
85
+ `true` = validated internet connection; `false` = offline (including a network
86
+ with no real internet on Android).
@@ -0,0 +1,175 @@
1
+ ---
2
+ description: 'What the system surfaces show: which metadata field lands where, the four layers that decide its value at any moment, and reading the result back in your own UI.'
3
+ ---
4
+
5
+ # Now Playing
6
+
7
+ The now-playing surface is everything that displays the current track on the _system's_ surfaces: the iOS lock screen and Control Center, the Android notification, CarPlay and Android Auto's Now Playing screens, and Bluetooth head units. The library publishes one metadata dictionary and the _operating system_ renders it — each surface picks different fields, composes them differently, and ignores the rest. The same published metadata is also readable inside your own app — see [Reading now-playing in your own UI](#reading-now-playing-in-your-own-ui).
8
+
9
+ This guide covers: **which fields land where** (the static matrix), **the four layers that decide what those fields contain at any moment** (the dynamic model), and **reading the result back** in your own UI.
10
+
11
+ ## Which field shows where
12
+
13
+ Apple and Google document very little of this; the table below reflects observed behavior.
14
+
15
+ | Field | iOS lock screen / Control Center | CarPlay Now Playing | Android notification | Android Auto Now Playing | Bluetooth (AVRCP) |
16
+ | --------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------- | ------------------------ | ----------------------- |
17
+ | `title` | Primary line | Primary line | Primary line | Primary line | Title |
18
+ | `artist` | **Secondary line (sole source — `album` is never used as a fallback)** | Second line | Secondary line | Secondary line | Artist |
19
+ | `album` | Not shown | **Third line — also the tappable album/artist button (see below)** | Not shown | Rarely shown | Album (some head units) |
20
+ | `artwork` | Shown | Shown | Shown | Shown | — |
21
+ | `live` | "LIVE" indicator replaces the time scrubber | "LIVE" indicator | — | — | — |
22
+
23
+ The time scrubber (elapsed, duration, playback rate) is not driven by track fields at all — every surface derives it from the player itself. `Track.duration` is informational metadata for your app (echoed back through now-playing events); it does not affect the scrubber.
24
+
25
+ `artwork` is the URL you _set_ on a track. (`Track.artworkSource` is a separate, output-only `ImageSource` the library populates for you to render in your own `<Image>` — it isn't published to surfaces.) Note that artwork isn't one of the overridable text fields: the formatter, override, and flash layers below carry only `title` / `artist` / `album`. Now-playing artwork comes from the track's `artwork`, optionally resolved through the [`nowPlayingArtwork`](#now-playing-artwork) config.
26
+
27
+ ## The four metadata layers
28
+
29
+ What actually renders is decided by a fixed priority order. Each layer only overrides the fields it sets — anything it leaves `undefined` falls through to the layer below:
30
+
31
+ | Priority | Layer | API | Lifetime |
32
+ | ------------ | ---------------- | -------------------------------------- | ------------------------------------ |
33
+ | 1 (highest) | **Flash** | `flashNowPlaying(update, durationMs)` | `durationMs`, on a native timer |
34
+ | 2 | **Formatter** | `setupPlayer({ nowPlaying })` | as long as it's configured |
35
+ | 3 | **Override** | `updateNowPlaying(update)` | until cleared or the track changes |
36
+ | 4 (baseline) | **Track fields** | `Track.title` / `artist` / `album` / … | the track's time as the active track |
37
+
38
+ ### Track fields — the baseline
39
+
40
+ With nothing else configured, the active track's own fields are published as-is. For on-demand content this is usually all you need.
41
+
42
+ ### The formatter — derived, continuous
43
+
44
+ The [`nowPlaying`](/api/features/player/#formatnowplayingcallback) formatter hands you the now-playing text lines outright, re-invoked whenever they could change: on track change, on every timed-metadata update, on every playback transition (play / pause, a stall starting or recovering, an error), and on connectivity changes. It's the right layer for anything _derived from playback state_ — the live song from ICY/ID3 metadata, a "Reconnecting…" line during a stall, an error message:
45
+
46
+ ```ts
47
+ setupPlayer({
48
+ nowPlaying: ({ timedMetadata, playWhenReady, stalled, error }) => {
49
+ // `yourErrorLine` is your own kind→copy mapping — branch on `error.kind`,
50
+ // never `error.message`, which is developer English and never localized.
51
+ // This line is read by listeners, on the lock screen and in the car.
52
+ // See [Playback errors](/guide/errors#playback-errors).
53
+ if (error) return { artist: yourErrorLine(error.kind) }
54
+ if (stalled) {
55
+ return {
56
+ artist: stalled === 'offline' ? 'No connection' : 'Reconnecting…'
57
+ }
58
+ }
59
+ // The live song, only while actually playing — a paused stream's last
60
+ // song is stale, so fall back to the track default (return undefined).
61
+ if (!playWhenReady || !timedMetadata?.title) return
62
+ return { artist: timedMetadata.title }
63
+ }
64
+ })
65
+ ```
66
+
67
+ The callback receives a single [`FormatNowPlayingParams`](/api/features/player/#formatnowplayingparams) object:
68
+
69
+ | Field | Type | Meaning |
70
+ | --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
71
+ | `track` | `Track` | The currently playing track. |
72
+ | `timedMetadata` | `TimedMetadata?` | The ICY / ID3 "now playing song", if any. |
73
+ | `playWhenReady` | `boolean` | Play/pause intent — stays `true` through buffers, so the song line won't flicker. |
74
+ | `stalled` | `StallReason?` | Truthy only during a mid-stream stall: `'buffering'` (rebuffering while online) or `'offline'` (no connectivity). Use `if (stalled)`, compare `=== 'offline'` for the reason. |
75
+ | `error` | `PlaybackError?` | The current playback error, if playback failed. Switch on `error.kind` to pick your own localized line — see [Playback errors](/guide/errors#playback-errors). While [automatic retry](/guide/errors#automatic-retry) is still working, this carries the advisory error (`error.retrying === true`) — the formatter re-runs when it arrives, changes, and clears, so you can show provisional copy ("… — retrying") that hardens into the final line when the player gives up. |
76
+
77
+ It returns a [`NowPlayingUpdate`](/api/features/metadata/#nowplayingupdate) (`{ title?, artist?, album? }`) or `undefined`. The callback is synchronous and should stay cheap — it's a pure formatting function, no I/O. Each returned field falls back **independently** to the track's value when omitted; returning `undefined` (or `{}`) uses the track default entirely. Identical results across a rapid burst of transitions are de-duplicated natively, so they won't flicker the surface.
78
+
79
+ Timed metadata is never auto-applied: the library surfaces ICY (Shoutcast/Icecast) and in-band ID3 (HLS) frames to your code, and the formatter is where you decide what reaches the now-playing line. (To consume those frames outside the formatter, subscribe to [`onTimedMetadata`](/api/features/metadata/#ontimedmetadata).)
80
+
81
+ ### The override — imperative, sticky
82
+
83
+ [`updateNowPlaying({ artist: '…' })`](/api/features/nowPlaying/#updatenowplaying) pins fields until you pass `null` or the track changes. It's the layer to reach for when you _don't_ configure a formatter.
84
+
85
+ **If a formatter is configured, it outranks the override** — the formatter's result is applied on top, falling back per-field to `override ?? track`. Mixing the two is rarely what you want: with a formatter in place, feed it state instead of calling `updateNowPlaying` around it. (The override predates the formatter; the formatter is the newer, preferred layer for anything derived from playback state.)
86
+
87
+ ### The flash — transient, top priority
88
+
89
+ [`flashNowPlaying(update, durationMs)`](/api/features/nowPlaying/#flashnowplaying) is the toast of the now-playing world: it briefly replaces the fields it sets, then reverts to whatever the lower layers say. External surfaces have no notification primitive, so a transient metadata swap is the only way to give feedback there — the canonical use is answering a refused remote command:
90
+
91
+ ```ts
92
+ // A radio product with an hourly skip allowance:
93
+ handleRemoteNext(() => {
94
+ if (skipsRemaining() > 0) {
95
+ skipToNext()
96
+ } else {
97
+ flashNowPlaying({ artist: 'Skip limit reached' }, 3000)
98
+ }
99
+ })
100
+ ```
101
+
102
+ Three properties make it a dedicated layer rather than sugar over `updateNowPlaying`:
103
+
104
+ - **It outranks the formatter.** While a flash is active the formatter pass is skipped entirely, so a live station's next metadata tick can't overwrite the message mid-window.
105
+ - **The revert runs on a native timer.** A JS `setTimeout` pauses with a backgrounded host on Android — and remote commands from the lock screen arrive exactly when the host is backgrounded. The native timer fires regardless.
106
+ - **A track change clears it early.** A flash is feedback about a moment; it never carries over to a new track.
107
+
108
+ Repeated calls restart the window. [`clearNowPlayingFlash()`](/api/features/nowPlaying/#clearnowplayingflash) cancels one imperatively — for example when the condition the flash complained about resolves mid-window.
109
+
110
+ ## Reading now-playing in your own UI
111
+
112
+ The metadata you publish is also what your own app should render — a mini-player, a full-screen "now playing" view — so the in-app UI matches the lock screen exactly, formatter and all. Use the [`useNowPlaying`](/api/features/nowPlaying/#usenowplaying) hook:
113
+
114
+ ```tsx
115
+ import { Text } from 'react-native'
116
+ import { useNowPlaying } from 'react-native-audio-browser'
117
+
118
+ function MiniPlayer() {
119
+ const nowPlaying = useNowPlaying()
120
+ if (!nowPlaying) return null
121
+ return (
122
+ <Text>
123
+ {nowPlaying.title} — {nowPlaying.artist}
124
+ </Text>
125
+ )
126
+ }
127
+ ```
128
+
129
+ `useNowPlaying` returns the resolved [`NowPlayingMetadata`](/api/features/metadata/#nowplayingmetadata) — the same dictionary the system surfaces see, _after_ the four layers are applied — or `undefined` when nothing is playing. It carries `title` / `artist` / `album` / `artwork` (and `elapsedTime`). For the live scrubber position, pair it with [`useProgress`](/guide/playback#progress).
130
+
131
+ Outside React, [`getNowPlaying()`](/api/features/nowPlaying/#getnowplaying) reads a snapshot and [`onNowPlayingChanged`](/api/features/nowPlaying/#onnowplayingchanged) subscribes to changes (it fires when the override changes or the track changes).
132
+
133
+ To render artwork, don't use this `artwork` field directly — it's a raw URL string. Use the active track's ready-to-use `artworkSource` instead (`<Image source={track.artworkSource} />`) — see [Track](/guide/track).
134
+
135
+ ## Gotchas worth knowing
136
+
137
+ **CarPlay's tappable line renders from `album`.** When a track has an `albumPath` (or `resolveAlbumPath` returns a path), the _album line_ becomes tappable — rendered as a third metadata line with a chevron, navigating the browse stack. A track without an `album` has no such line, so there is nothing to render or tap even though a destination exists. See the [CarPlay guide](/guide/carplay#album-line-navigation).
138
+
139
+ **The iOS lock screen never shows `album`.** Its secondary line comes from `artist` alone. If you move information from `artist` to `album` (for example to feed the CarPlay button line), it disappears from the lock screen.
140
+
141
+ **`artist` and `album` render as adjacent lines on CarPlay.** Giving both the same string displays it twice. If both fields must carry related context (a live stream's location, say), differentiate the copy — the album line reads well as an action since it can be tappable.
142
+
143
+ **One metadata dictionary feeds every surface.** There is no per-surface metadata: whatever you publish appears (or doesn't) everywhere simultaneously, per the table above. You cannot hide a field from one surface without hiding it from all of them.
144
+
145
+ **A formatter quietly disables `updateNowPlaying` for the fields it returns.** See [the override](#the-override--imperative-sticky) — the formatter's result wins per-field. If a line you set imperatively keeps reverting, a formatter is overwriting it; either move that logic into the formatter or use `flashNowPlaying` if it was meant to be transient.
146
+
147
+ **Browse lists are separate.** `subtitle` drives browse-list rows (CarPlay list detail text, Android Auto list subtitle) and is never shown on now-playing surfaces; `artist` drives now-playing and is never shown in browse lists. Neither falls back to the other.
148
+
149
+ ## Now-playing artwork
150
+
151
+ Artwork is the one surface field the text layers (formatter / override / flash) can't touch — they carry only `title` / `artist` / `album`. The now-playing image comes from the active track's `artwork`.
152
+
153
+ By default that's the same image as the track's browse-list thumbnail. To resolve it differently — a larger, lock-screen-quality image without bloating list thumbnails — set the `nowPlayingArtwork` config kind, which builds the now-playing image from its own request (e.g. `{ path: '/artwork/{id}' }`):
154
+
155
+ ```ts
156
+ configureBrowser({
157
+ nowPlayingArtwork: { path: '/artwork/{id}' }
158
+ // ...tabs, routes
159
+ })
160
+ ```
161
+
162
+ See [`BrowserConfiguration.nowPlayingArtwork`](/api/types/browser/#browserconfiguration).
163
+
164
+ ## API summary
165
+
166
+ | API | Purpose |
167
+ | ----------------------------------------- | ------------------------------------------------------------ |
168
+ | `setupPlayer({ nowPlaying })` | Configure the formatter — derived lines from playback state. |
169
+ | `updateNowPlaying(update \| null)` | Imperatively override fields; `null` clears. |
170
+ | `flashNowPlaying(update, durationMs)` | Transient, top-priority swap (refused-command feedback). |
171
+ | `clearNowPlayingFlash()` | Cancel an active flash early. |
172
+ | `useNowPlaying()` / `getNowPlaying()` | Read the resolved metadata in your own UI. |
173
+ | `onNowPlayingChanged` | Subscribe to metadata changes outside React. |
174
+ | `onTimedMetadata` | Subscribe to ICY / ID3 stream metadata directly. |
175
+ | `configureBrowser({ nowPlayingArtwork })` | Resolve now-playing artwork from its own request. |