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

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,255 @@
1
+ ---
2
+ description: 'Set Track.artwork once and render it everywhere: the fetch and transform pipeline, per-surface sizing, SF Symbols, tinting, light/dark pairs, Android vector icons, and SVG.'
3
+ ---
4
+
5
+ # Artwork
6
+
7
+ **Artwork** is the image shown for a track — the thumbnail in a browse list, the cover on the lock screen, the icon on a CarPlay row. You set one field, `artwork`, on a [`Track`](/guide/track); the library resolves and transforms it per surface and per requested size, and hands the ready-to-render result back on `artworkSource`.
8
+
9
+ This guide covers the mental model (the two configs and the output field), **which artwork renders where**, the **transform pipeline** that turns your URL into a sized request, and the platform-specific bits: SF Symbols on iOS, tinting, light/dark artwork, Android vector icons, and SVG.
10
+
11
+ ## Mental model
12
+
13
+ Three names do all the work:
14
+
15
+ | Name | What it is | You |
16
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- |
17
+ | `Track.artwork` | The image **you set** — an `https` URL, an `sf:` symbol (iOS), a platform URI, or a `{ light, dark }` pair. | set it |
18
+ | `artwork` / `nowPlayingArtwork` config | How the library **fetches** that image (base URL, headers, signing, sizing). | configure once (override per route / for now-playing) |
19
+ | `Track.artworkSource` | The resolved, ready-to-render [`ImageSource`](/api/types/browser-nodes/#imagesource) (URL + headers) the library **produces**. | read it |
20
+
21
+ `artwork` is the only field you set per track. `artworkSource` is **output-only** — the library populates it; never set it yourself.
22
+
23
+ ```tsx
24
+ import { useActiveTrack } from 'react-native-audio-browser'
25
+ import { Image } from 'react-native'
26
+
27
+ function Cover() {
28
+ const track = useActiveTrack()
29
+ // artworkSource is the transformed result, shaped for <Image>
30
+ return <Image source={track?.artworkSource} />
31
+ }
32
+ ```
33
+
34
+ ## Which artwork renders where
35
+
36
+ The library publishes art to several surfaces; each gets its image from a field/config and (where the surface knows its display size) a size hint:
37
+
38
+ | Surface | Image from | Size hint | Tinting |
39
+ | ----------------------------------------------- | --------------------------------------------- | --------- | ------------------------------------------------------------------------------ |
40
+ | **Browse rows / tabs** (CarPlay / Android Auto) | `artwork` (per-route override allowed) | no | CarPlay: `style: { artworkRendering: 'stencil' }`, or a `{ light, dark }` pair |
41
+ | **In-app `<Image>`** | `artworkSource` (output) | no | your UI |
42
+ | **iOS lock screen / Control Center** | `nowPlayingArtwork` → falls back to `artwork` | yes | — |
43
+ | **CarPlay Now Playing** | `nowPlayingArtwork` → `artwork` | yes | — |
44
+ | **Android notification / Android Auto** | `nowPlayingArtwork` → `artwork` | yes | — (Android Auto is dark-only) |
45
+ | **CarPlay tile sections** (`display: 'grid'`) | `artwork` (each tile is a Track) | yes | CarPlay: `style: { artworkRendering: 'stencil' }`, or a `{ light, dark }` pair |
46
+
47
+ **Size hint** = the surface tells the library the pixels it needs, delivered as an [`ImageContext`](/api/types/browser/#imagecontext) to your `transform` / `imageQueryParams`. Browse-time resolution has none.
48
+
49
+ Two things to take from the table:
50
+
51
+ - **One image by default.** With only `artwork` set, the same source feeds browse rows _and_ every now-playing surface. Set `nowPlayingArtwork` only when the now-playing image should differ (a larger, lock-screen-quality cover without bloating list thumbnails).
52
+ - **Size hints exist only where the surface knows its size.** Browse-time resolution has no size, so `imageQueryParams` and `context` (below) do nothing there; they kick in at load time on CarPlay / Android Auto / now-playing.
53
+ - **Tinting is for browse icons only.** `artworkRendering: 'stencil'` is applied when CarPlay renders browse content — list rows, tabs, and tiles — not to now-playing cover art.
54
+
55
+ ## The transform pipeline
56
+
57
+ `artwork` is an [`ArtworkRequestConfig`](/api/types/browser/#artworkrequestconfig). When the library needs a track's image, it builds the request in layers, each overriding the last:
58
+
59
+ 1. **`request`** — shared base config (user agent, common headers).
60
+ 2. **`artwork`** — the static image config (`baseUrl`, `headers`, …).
61
+ 3. **`resolve(track)` / `resolveSync(track)`** — per-track overrides built from the track's metadata.
62
+ 4. **`imageQueryParams`** — appends the surface's requested size (when it reports one).
63
+ 5. **`transform({ request, context })` / `transformSync`** — final tweak, with the requested size in `context`.
64
+
65
+ To build a per-track URL, use `resolve` (the track is the argument). The `{id}` token is **not** a general feature — only `nowPlayingArtwork` templates it (see below); static values in `artwork` are sent verbatim.
66
+
67
+ A typical CDN setup never needs more than `baseUrl` plus `imageQueryParams`:
68
+
69
+ ```ts
70
+ import { configureBrowser } from 'react-native-audio-browser'
71
+
72
+ configureBrowser({
73
+ // ...the rest of your config (tabs, routes, …)
74
+ artwork: {
75
+ baseUrl: 'https://images.example.com',
76
+ // tell the library which query params carry the requested size
77
+ imageQueryParams: { width: 'w', height: 'h' }
78
+ }
79
+ })
80
+ ```
81
+
82
+ With [`imageQueryParams`](/api/types/browser/#imagequeryparams) the requested size from CarPlay / Android Auto is appended automatically — `?w=200&h=200` — so each surface gets right-sized art. Omit `height` to send width only. This only fires on surfaces that report a size; browse rows (no size hint) get the URL without these params.
83
+
84
+ For anything `imageQueryParams` can't express — per-track paths, signed URLs — use `resolve` and `transform` (or their sync variants):
85
+
86
+ ```ts
87
+ import { configureBrowser } from 'react-native-audio-browser'
88
+ import HmacSHA256 from 'crypto-js/hmac-sha256' // add: yarn add crypto-js
89
+
90
+ const CDN_KEY = process.env.CDN_KEY ?? '' // your CDN signing key
91
+
92
+ // sign the path locally with your key — a pure, sync function
93
+ const signUrl = (path: string) => HmacSHA256(path, CDN_KEY).toString()
94
+
95
+ configureBrowser({
96
+ artwork: {
97
+ baseUrl: 'https://images.example.com',
98
+ // build the path from the track (sync — no await needed)
99
+ resolveSync: (track) => ({ path: `/covers/${track.id}.jpg` }),
100
+ // then sign it + size it. both are sync, so no Promise is
101
+ // allocated per image
102
+ transformSync: ({ request, context }) => ({
103
+ ...request,
104
+ query: {
105
+ ...request.query,
106
+ w: context?.width ? String(context.width) : '600',
107
+ // request.path is optional — guard it before signing
108
+ sig: signUrl(request.path ?? '')
109
+ }
110
+ })
111
+ }
112
+ })
113
+ ```
114
+
115
+ A `transform` (or `transformSync`) **replaces** the request — it doesn't merge — so spread `...request` (and `...request.query`) or you'll drop the `baseUrl` and other layers. The [`context`](/api/types/browser/#imagecontext) is an `ImageContext` with `width` / `height` in **pixels**, present only when the surface knows its display size (so guard with `context?.width`). `request.path` is also optional — it's only set if a `resolve` ran first — so guard it before signing (as above).
116
+
117
+ `resolveSync` / `transformSync` are no-Promise variants of `resolve` / `transform`. Reach for them when the per-track config or final tweak needs no `await` — a synced query param, a locally-computed path — to skip a Promise allocation per image (the example above is fully sync for that reason). If you set _both_ the sync and async form of the **same** stage (e.g. `transform` _and_ `transformSync`), the async one runs first and the sync one tweaks its result; `resolve` and `transform` are independent stages that both always run.
118
+
119
+ ::: tip Per-route artwork
120
+ A route can override the global `artwork` with its own config — `routes: { '/premium': { artwork: { baseUrl: '…' } } }` — so different content can load images from different hosts. See the [Browser](/guide/browser) guide.
121
+ :::
122
+
123
+ ## SF Symbols (iOS)
124
+
125
+ Prefix `artwork` with `sf:` to render an Apple [SF Symbol](https://developer.apple.com/sf-symbols/) instead of fetching an image — ideal for tab and category icons. Optional `bg` (background, transparent if omitted) and `fg` (symbol color, black if omitted) query params set the colors:
126
+
127
+ ```ts
128
+ {
129
+ title: 'Favorites',
130
+ path: '/favorites',
131
+ artwork: 'sf:heart.fill?bg=#FF0090&fg=#fff'
132
+ }
133
+ ```
134
+
135
+ On CarPlay, an SF Symbol with **no explicit colors** is handed to CarPlay untinted, so CarPlay renders it in the appearance's own color (adapting to light/dark); give it a `bg`/`fg` and you get exactly those colors instead. The library renders the symbol to an image on device and caches it. This is iOS-only; on Android an `sf:` value is not a valid image.
136
+
137
+ ## Tinting and platform icons
138
+
139
+ For monochrome **icons** (not full-color album art), let the system tint them to stay legible on either appearance:
140
+
141
+ - **iOS CarPlay** — declare [`style: { artworkRendering: 'stencil' }`](/api/types/browser-nodes/#trackstyle) on the track. Inside a page it's an inherited style property (a section- or page-wide value covers every item); tabs render without a section context, so a tab icon needs the declaration on the tab track itself. CarPlay tints the glyph per appearance: black in light mode, white in dark.
142
+ - **Android Auto** is dark-only, so there's no per-appearance tinting — ship an appropriately colored (e.g. white) icon. When a layout is declared (`display` on the section, page, or parent item), an `android.resource://…/drawable/…` artwork URI switches Android Auto to the **category** variant of that layout (which adds icon margins and lets Android Auto render it as an icon). The library only sets the category style; the visual treatment is Android Auto's — see [Browse display](/guide/android-auto#browse-display).
143
+
144
+ ```ts
145
+ {
146
+ title: 'Settings',
147
+ path: '/settings',
148
+ artwork: 'sf:gear',
149
+ style: { artworkRendering: 'stencil' }
150
+ }
151
+ ```
152
+
153
+ **SVG** is supported on both platforms and detected by a `.svg` URL suffix — serve SVGs with a path ending in `.svg` and they decode as vectors rather than raster images.
154
+
155
+ ## Light and dark artwork
156
+
157
+ Tinting recolors _one_ image. When the two appearances need genuinely **different** images — a logo whose colors change, a mark with a light-on-dark counterpart — set `artwork` to an [`ArtworkVariants`](/api/types/browser-nodes/#artworkvariants) pair instead of a URL:
158
+
159
+ ```ts
160
+ {
161
+ title: 'Playlists',
162
+ path: '/playlists',
163
+ artwork: {
164
+ light: 'https://images.example.com/playlists-light.png',
165
+ dark: 'https://images.example.com/playlists-dark.png'
166
+ }
167
+ }
168
+ ```
169
+
170
+ Both fields are required — a pair with one side missing has no sensible fallback at render time.
171
+
172
+ On CarPlay browse rows and tabs the library fetches both and registers them as a single adaptive image, so switching appearance mid-drive swaps the image **in place** — no re-fetch, and no re-query of the browse tree. Each URL runs through the [transform pipeline](#the-transform-pipeline) independently, so `baseUrl`, headers, `imageQueryParams` and `resolve` / `transform` apply to both.
173
+
174
+ Everywhere a single image is required, a pair resolves to its `dark` URL:
175
+
176
+ | Surface | Uses |
177
+ | ---------------------------------------- | ----------------------------- |
178
+ | **CarPlay browse rows / tabs** | both, adapting per appearance |
179
+ | **Android Auto** | `dark` (dark-only platform) |
180
+ | **Now-playing** (all platforms) | `dark` |
181
+ | **`artworkSource`** (your own `<Image>`) | `dark` |
182
+
183
+ ::: tip Pair or tint?
184
+ Prefer [`artworkRendering: 'stencil'`](#tinting-and-platform-icons) when recoloring the same shape gives the right result: it's one fetch instead of two, the variants can't drift apart, and it works from the image's alpha so the source color is irrelevant. Reach for a pair only when the appearances need different artwork.
185
+ :::
186
+
187
+ ::: warning Android Auto fetches only `http(s)` artwork
188
+ Browse artwork is served to Android Auto through an internal content provider that only fetches `http`/`https` URLs. `android.resource://` and `file://` URIs pass through and render directly; other custom schemes won't load there.
189
+ :::
190
+
191
+ ## Now-playing artwork
192
+
193
+ By default the now-playing image (lock screen, Control Center, CarPlay / Android Auto Now Playing) is the active track's `artwork` — the same image as its browse-row thumbnail. To resolve it differently, set `nowPlayingArtwork`, which builds the now-playing image from its **own** request and uniquely supports an `{id}` template filled from the active track's `id`:
194
+
195
+ ```ts
196
+ import { configureBrowser } from 'react-native-audio-browser'
197
+
198
+ configureBrowser({
199
+ nowPlayingArtwork: {
200
+ baseUrl: 'https://images.example.com',
201
+ // {id} is replaced with the active track's id
202
+ path: '/artwork/{id}',
203
+ // ask the CDN for a lock-screen-quality variant
204
+ imageQueryParams: { width: 'w', height: 'h' }
205
+ }
206
+ })
207
+ ```
208
+
209
+ Now-playing surfaces request a **much larger** image than list thumbnails — on iOS, up to the screen width in pixels, capped at 1200px — so with `imageQueryParams` set you fetch a fittingly large variant rather than upscaling a tiny thumbnail.
210
+
211
+ Two separate ideas here: the **`{id}` template** is replaced with the active track's `id`; and if you _don't_ set `nowPlayingArtwork` at all, now-playing surfaces fall back to the `artwork` config (and to `track.artwork`).
212
+
213
+ `nowPlayingArtwork` is an `ArtworkRequestConfig` like `artwork`, so it takes the same `resolve` / `transform` / `imageQueryParams`. It is **native-only**: the `{id}` template and the `nowPlayingArtwork` config are not applied by the web implementation, where now-playing artwork simply uses the `artwork` config like any other surface. Artwork is also the one now-playing field the text layers can't touch — the formatter / override / flash carry only `title` / `artist` / `album`. See [Now Playing](/guide/now-playing#now-playing-artwork) for the metadata side.
214
+
215
+ ::: warning Now-playing artwork follows the active track
216
+ Now-playing artwork is resolved **once per active track**, keyed on its `id` — so it won't re-resolve while the same track keeps playing. There is no imperative way to swap _only_ the image mid-stream: `updateNowPlaying()` overrides `title` / `artist` / `album`, not artwork. Changing it means making a _new_ active track (a different `id` plus the new `artwork`) the current one, which reloads playback — so it's not a fit for, say, updating live-radio cover art on each song. An in-place update API is tracked in [issue #76](https://github.com/radio-garden/react-native-audio-browser/issues/76).
217
+ :::
218
+
219
+ ## Tile sections
220
+
221
+ A page's [`Section`](/api/types/browser-nodes/#section) can render its tracks as tappable artwork tiles by declaring `style: { display: 'grid' }` (wrapping; add `gridWrap: false` for a single line of tiles):
222
+
223
+ ```ts
224
+ {
225
+ path: '/home',
226
+ title: 'Home',
227
+ sections: [
228
+ {
229
+ title: 'Featured',
230
+ style: { display: 'grid', gridWrap: false },
231
+ path: '/browse/featured', // header / "view all" target
232
+ children: [
233
+ { title: 'Jazz', path: '/browse/jazz', artwork: 'https://…/jazz.jpg' },
234
+ { title: 'Rock', path: '/browse/rock', artwork: 'https://…/rock.jpg' }
235
+ ]
236
+ }
237
+ ]
238
+ }
239
+ ```
240
+
241
+ Each tile is an ordinary Track, so its `artwork` runs through the same pipeline as any other. Tile styles presume artwork: a track without any renders as a placeholder tile plus its title — the artwork `resolve` hook is the place to supply fallback art. On CarPlay a single-line grid (`gridWrap: false`) shows the tiles that fit (roughly eight, width-dependent; the rest are truncated) and a wrapping grid takes as many lines as it needs on iOS 26+ (rendering as a list before that); Android Auto's grid always wraps, so it renders both identically. See [Browser → Presentation](/guide/browser#presentation) for the full per-surface rundown.
242
+
243
+ ## API summary
244
+
245
+ | Symbol | Purpose |
246
+ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
247
+ | [`Track.artwork`](/api/types/browser-nodes/#track) | The image you set — `https` URL, `sf:` symbol (iOS), or platform URI. |
248
+ | [`Track.artworkSource`](/api/types/browser-nodes/#track) | Output-only resolved `ImageSource` for your own `<Image>`. |
249
+ | [`TrackStyle.artworkRendering`](/api/types/browser-nodes/#trackstyle) | `'stencil'` tints a monochrome icon per CarPlay light/dark (iOS only). |
250
+ | [`ArtworkVariants`](/api/types/browser-nodes/#artworkvariants) | A `{ light, dark }` pair set on `artwork` when the appearances need different images. |
251
+ | [`artwork`](/api/types/browser/#artworkrequestconfig) | Config for browse-row image requests (and the default everywhere). |
252
+ | `nowPlayingArtwork` | Separate config for now-playing art; supports `{id}`; native-only. |
253
+ | [`imageQueryParams`](/api/types/browser/#imagequeryparams) | Map the surface's requested size to your CDN's query params. |
254
+ | [`ImageContext`](/api/types/browser/#imagecontext) | Requested `width`/`height` in pixels, passed to `transform`. |
255
+ | [`Section.style`](/api/types/browser-nodes/#sectionstyle) | `display: 'grid'` (± `gridWrap: false`) renders a section's tracks as artwork tiles. |
@@ -0,0 +1,135 @@
1
+ ---
2
+ description: 'Read where audio is currently playing (speaker, Bluetooth, AirPlay, the car), react when the route changes, and open the system output switcher.'
3
+ ---
4
+
5
+ # Audio Output
6
+
7
+ Read where audio is currently playing (speaker, Bluetooth, AirPlay, the car…),
8
+ react when it changes, and open the system output switcher so the listener can
9
+ move playback to another device. Both layers are **cross-platform**:
10
+
11
+ - **Reading the current output** — `getOutput()` / `useOutput()` /
12
+ `onOutputChanged`. iOS reports the active route while a session is active;
13
+ Android reports the active output route from `AudioManager` (the `type` is
14
+ coarse — e.g. wired headphones may report as `speaker`).
15
+ - **Opening the switcher** — `openOutputPicker()` / `supportsOutputSwitcher()`:
16
+ the system route picker on iOS, the system Output Switcher on Android 11+.
17
+
18
+ ::: tip Gate the switcher button on `supportsOutputSwitcher()`
19
+ Surface your output button only when `supportsOutputSwitcher()` is `true` — it's
20
+ `true` on iOS and Android 11+ (API 30), `false` on older Android and web.
21
+ Reading the current output works more broadly, but `getOutput()` can still be
22
+ `undefined` (before playback, or when unknown) — so guard for it.
23
+ :::
24
+
25
+ The UI snippets import `Text` / `Button` from `react-native`; everything else is
26
+ from `react-native-audio-browser`.
27
+
28
+ ## The current output
29
+
30
+ [`useOutput()`](/api/features/output/#useoutput) returns the active output and
31
+ re-renders whenever it changes (AirPods connect, headphones unplug, a Bluetooth
32
+ speaker is selected):
33
+
34
+ ```tsx
35
+ import { Text } from 'react-native'
36
+ import { useOutput } from 'react-native-audio-browser'
37
+
38
+ function OutputLabel() {
39
+ const output = useOutput()
40
+ if (!output) return null // not yet known
41
+ return <Text>Playing on {output.name}</Text>
42
+ }
43
+ ```
44
+
45
+ An [`Output`](/api/features/output/#output) is:
46
+
47
+ | Field | Type | Meaning |
48
+ | ---------- | ------------ | ------------------------------------------------------------------------------- |
49
+ | `name` | `string` | Human-readable device name, e.g. `"AirPods Pro"`, `"Kitchen speaker"`. |
50
+ | `type` | `OutputType` | The output kind (see below). |
51
+ | `external` | `boolean` | `false` only for the built-in speaker/receiver; `true` for everything external. |
52
+
53
+ `type` is one of `'speaker'`, `'receiver'` (the iOS earpiece), `'headphones'`,
54
+ `'bluetooth'`, `'airplay'`, `'car'`, `'hdmi'`, `'usb'`, `'cast'` (a remote
55
+ speaker / TV), or `'other'`. Both platforms map their native ports/routes into
56
+ this shared set — e.g. iOS's separate Bluetooth ports all collapse to
57
+ `'bluetooth'`.
58
+
59
+ ::: warning Android reports a coarser type
60
+ On Android the type comes from the system route and is coarser: wired headphones
61
+ often report as `'speaker'` (the built-in route), and really only `'bluetooth'`
62
+ is reliably distinguished from it (`'cast'` requires active route discovery,
63
+ which the reader doesn't run, so it won't appear here). The device **name** still
64
+ comes through.
65
+ :::
66
+
67
+ Outside React, read a snapshot with `getOutput()` or subscribe with
68
+ [`onOutputChanged`](/api/features/output/#onoutputchanged):
69
+
70
+ ```ts
71
+ import { getOutput, onOutputChanged } from 'react-native-audio-browser'
72
+
73
+ getOutput() // Output | undefined
74
+
75
+ const unsubscribe = onOutputChanged.addListener((output) => {
76
+ console.log('now playing on', output.name)
77
+ })
78
+
79
+ // later:
80
+ unsubscribe()
81
+ ```
82
+
83
+ ## Opening the output picker
84
+
85
+ [`openOutputPicker()`](/api/features/output/#openoutputpicker) presents the
86
+ system output switcher — the AirPlay / Bluetooth / speaker chooser on iOS, and
87
+ the Bluetooth / speaker / Cast switcher on Android (11+). Wire it to your own
88
+ output button, shown only when
89
+ [`supportsOutputSwitcher()`](/api/features/output/#supportsoutputswitcher) is
90
+ `true`:
91
+
92
+ ```tsx
93
+ import { Button } from 'react-native'
94
+ import {
95
+ openOutputPicker,
96
+ supportsOutputSwitcher
97
+ } from 'react-native-audio-browser'
98
+
99
+ function OutputButton() {
100
+ if (!supportsOutputSwitcher()) return null // older Android / web
101
+ return <Button title="Output" onPress={() => openOutputPicker()} />
102
+ }
103
+ ```
104
+
105
+ `openOutputPicker()` is safe to call unconditionally — it's a no-op where no
106
+ switcher exists — but gating the button on `supportsOutputSwitcher()` keeps you
107
+ from showing a control that would do nothing.
108
+
109
+ On Android the switcher is the system one (the picker from the media
110
+ notification); selecting a Bluetooth speaker there routes audio at the OS level.
111
+ The library can't force playback to a _specific_ Bluetooth device itself — route
112
+ selection is the system's job, which is what this hands off to. Sonos appears on
113
+ iOS (as an AirPlay target); on Android it isn't a system route, so it won't show.
114
+
115
+ ## You may not need this for unplug handling
116
+
117
+ When the current output **disappears** — headphones unplugged, a Bluetooth
118
+ speaker powered off — the player **pauses automatically** rather than blaring out
119
+ of the built-in speaker (the convention every media app follows). It's a
120
+ deliberate pause: the listener presses play again when ready. So you don't need
121
+ to watch `onOutputChanged` just to handle unplugging — reach for these APIs to
122
+ _display_ the output or to _offer_ a switcher.
123
+
124
+ ## API summary
125
+
126
+ | API | Platforms | Purpose |
127
+ | -------------------------- | ---------------- | -------------------------------------------------------------------------- |
128
+ | `openOutputPicker()` | iOS, Android 11+ | Present the system output switcher (Bluetooth / AirPlay / speaker / Cast). |
129
+ | `supportsOutputSwitcher()` | all | Whether a switcher can be shown — gate your output button on it. |
130
+ | `useOutput()` | iOS, Android | Reactive current output (`Output \| undefined`). |
131
+ | `getOutput()` | iOS, Android | One-off snapshot of the current output. |
132
+ | `onOutputChanged` | iOS, Android | Subscribe outside React; `addListener(cb)` returns an unsubscribe fn. |
133
+
134
+ The whole API is cross-platform; only the switcher needs Android 11+ (gate on
135
+ `supportsOutputSwitcher()`), and Android reports coarser output `type`s than iOS.
@@ -0,0 +1,86 @@
1
+ ---
2
+ description: 'How CarPlay, Android Auto, and Android Automotive all reuse the browse tree, queue, now-playing metadata, and search source you have already built.'
3
+ ---
4
+
5
+ # In the car
6
+
7
+ CarPlay, Android Auto, and Android Automotive all show your content on the car
8
+ screen and play through the same player as your app. You don't build a separate
9
+ "car app" — the car surfaces are driven by the **same cross-cutting pieces you've
10
+ already built**:
11
+
12
+ - your [browse tree](/guide/browser) becomes the car's browsable menus,
13
+ - your [now-playing metadata](/guide/now-playing) fills the car's now-playing
14
+ screen,
15
+ - the [player and queue](/guide/playback) are shared, so playback is continuous
16
+ whether the user started it in-app or in the car,
17
+ - your [`search` source](/guide/search) answers in-car voice commands
18
+ ("play jazz"),
19
+ - [favorites](/guide/favorites) surface a heart and a "play my favorites" intent.
20
+
21
+ Build those once and they light up every car surface. The per-platform pages
22
+ handle the platform-specific **setup**: **[Android Auto](/guide/android-auto)**
23
+ (also Android Automotive) and **[CarPlay](/guide/carplay)**.
24
+
25
+ ## Shared vs platform-specific
26
+
27
+ | Concern | Where it's handled |
28
+ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
29
+ | Browse tree / menus | Shared — [Browser](/guide/browser) |
30
+ | Now-playing metadata & artwork | Shared — [Now Playing](/guide/now-playing) |
31
+ | Player, queue, playback | Shared — [Playback](/guide/playback) |
32
+ | Voice search ("play …") | Shared — your [`search`](/guide/search) source |
33
+ | Favorites heart & "play my favorites" | Shared — [Favorites](/guide/favorites) |
34
+ | App setup, entitlements, manifest | Per-platform — [Android Auto](/guide/android-auto) / [CarPlay](/guide/carplay) |
35
+ | Now-playing buttons | Per-platform — [Button layout](/guide/remote-controls#button-layout-android) (Android) / [CarPlay](/guide/carplay) (iOS) |
36
+
37
+ The takeaway: spend your effort on the shared content model; reach for a platform
38
+ page only for that platform's setup and buttons.
39
+
40
+ ## Detecting a car connection
41
+
42
+ To adapt your in-app UI while connected to a car (a "now playing in your car"
43
+ state, a simplified screen), use
44
+ [`useCarConnected()`](/api/features/carConnection/#usecarconnected). It's `true`
45
+ for a CarPlay connection on iOS, and an Android Auto **or Android Automotive**
46
+ connection on Android (`false` on web):
47
+
48
+ ```tsx
49
+ import { Text } from 'react-native'
50
+ import { useCarConnected } from 'react-native-audio-browser'
51
+
52
+ function CarBadge() {
53
+ const inCar = useCarConnected()
54
+ if (!inCar) return null
55
+ return <Text>Playing in your car</Text>
56
+ }
57
+ ```
58
+
59
+ Outside React, read a snapshot with
60
+ [`isCarConnected()`](/api/features/carConnection/#iscarconnected) or subscribe
61
+ with [`onCarConnectedChanged`](/api/features/carConnection/#oncarconnectedchanged)
62
+ (`addListener` returns an unsubscribe function):
63
+
64
+ ```ts
65
+ import {
66
+ isCarConnected,
67
+ onCarConnectedChanged
68
+ } from 'react-native-audio-browser'
69
+
70
+ isCarConnected() // boolean
71
+
72
+ const unsubscribe = onCarConnectedChanged.addListener((connected) => {
73
+ // ...
74
+ })
75
+ // later: unsubscribe()
76
+ ```
77
+
78
+ ## Next steps
79
+
80
+ 1. Set up the platform(s) you ship to —
81
+ **[Android Auto](/guide/android-auto)** (and Android Automotive) and/or
82
+ **[CarPlay](/guide/carplay)**.
83
+ 2. Make sure your [browse tree](/guide/browser) and
84
+ [now-playing](/guide/now-playing) are in good shape — that's what the car
85
+ renders.
86
+ 3. Wire your [`search` source](/guide/search) so voice commands work in the car.
@@ -0,0 +1,151 @@
1
+ ---
2
+ description: 'The browse tree in one page: browsable versus playable Tracks, setting up the player, declaring content, and playing it.'
3
+ ---
4
+
5
+ # Basic Usage
6
+
7
+ This guide covers the core idea — the **browse tree** — and the minimal code to set up the player, define some content, and play it. Installation is in [Getting Started](/guide/getting-started).
8
+
9
+ ## The browse tree
10
+
11
+ The library models all content as one navigable tree of **Tracks**. A single Track is **browsable**, **playable**, or both, depending on which fields you set:
12
+
13
+ - **Browsable** — has a `path`; navigating into it resolves its children.
14
+ - **Playable** — has a `src`; the player can stream it.
15
+
16
+ Three fields at a glance — each answers a different question about a Track:
17
+
18
+ | Field | Answers | Meaning |
19
+ | ------ | ------------------ | --------------------------------------------------------- |
20
+ | `path` | where does it go? | the browse-tree address navigating opens |
21
+ | `src` | what does it play? | the media identifier the player streams |
22
+ | `id` | which item is it? | identity across surfaces — falls back to `src` when unset |
23
+
24
+ ```mermaid
25
+ graph TD
26
+ Root[Root]
27
+ Root --> Favorites[Favorites]
28
+ Root --> Recent[Recently Played]
29
+ Root --> Browse[Browse]
30
+
31
+ Browse --> Jazz[Jazz]
32
+ Browse --> Rock[Rock]
33
+
34
+ Jazz --> J1[Smooth Floret FM]
35
+ Jazz --> J2[The Stalk 88.5]
36
+ Rock --> R90[Vintage]
37
+ Rock --> R1[Crunchy Greens]
38
+ Rock --> R2[Stem City Radio]
39
+ R90 --> R3[Funky Floret 101.3]
40
+ R90 --> R4[Wilted Greens FM]
41
+
42
+ classDef browsable fill:#e3f2fd,stroke:#1976d2
43
+ classDef playable fill:#e8f5e9,stroke:#388e3c
44
+
45
+ class Root,Favorites,Recent,Browse,Jazz,Rock,R90 browsable
46
+ class J1,J2,R1,R2,R3,R4 playable
47
+ ```
48
+
49
+ <div style="display: flex; gap: 1rem; margin-top: 0.5rem; font-size: 0.9em;">
50
+ <span><span style="display: inline-block; width: 12px; height: 12px; background: #e3f2fd; border: 1px solid #1976d2; margin-right: 4px;"></span> Browsable: opens to children</span>
51
+ <span><span style="display: inline-block; width: 12px; height: 12px; background: #e8f5e9; border: 1px solid #388e3c; margin-right: 4px;"></span> Playable: streams audio</span>
52
+ </div>
53
+
54
+ One tree powers both your in-app UI and the native browse views on CarPlay and Android Auto.
55
+
56
+ ## Set up the player
57
+
58
+ Call `setupPlayer()` once at startup to initialize the player:
59
+
60
+ ```ts
61
+ import { setupPlayer } from 'react-native-audio-browser'
62
+
63
+ await setupPlayer()
64
+ ```
65
+
66
+ ## Define a browse tree
67
+
68
+ Describe the tree with `configureBrowser`: **tabs** along the top, and **routes** that resolve each path to its children. The simplest source is static data declared inline:
69
+
70
+ ```ts
71
+ import { configureBrowser } from 'react-native-audio-browser'
72
+
73
+ configureBrowser({
74
+ tabs: [{ title: 'Browse', path: '/browse' }],
75
+ routes: {
76
+ '/browse': {
77
+ path: '/browse',
78
+ title: 'Browse',
79
+ children: [
80
+ {
81
+ title: 'Jazz',
82
+ // A path makes this browsable — tapping it opens that path.
83
+ path: '/browse/jazz'
84
+ },
85
+ {
86
+ title: 'Smooth Floret FM',
87
+ // A src makes this playable — tapping it streams the track.
88
+ src: 'https://example.com/floret.mp3'
89
+ }
90
+ ]
91
+ },
92
+ '/browse/jazz': {
93
+ path: '/browse/jazz',
94
+ title: 'Jazz',
95
+ children: [
96
+ {
97
+ title: 'The Stalk 88.5',
98
+ src: 'https://example.com/stalk.mp3'
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ })
104
+ ```
105
+
106
+ A route value is a [`BrowserSource`](/api/types/browser/#browsersource): the static page object shown above, an **async callback** that returns one (`'/path': async ({ routeParams }) => ({ title, children })`), or an HTTP request config that fetches it from your API — handy for trees too large to declare upfront.
107
+
108
+ A page can also group its tracks into titled, styled **sections** (`sections: [{ title, style, children }, …]`) — a flat `children` list like the ones above is shorthand for a single untitled section. See [Browser → How a path resolves](/guide/browser#how-a-path-resolves).
109
+
110
+ ## Play a track
111
+
112
+ On CarPlay and Android Auto, tapping a playable Track plays it for you. To drive playback from your own UI, set a queue and call `play`:
113
+
114
+ ```ts
115
+ import { setQueue, play } from 'react-native-audio-browser'
116
+
117
+ setQueue([{ title: 'Smooth Floret FM', src: 'https://example.com/floret.mp3' }])
118
+ play()
119
+ ```
120
+
121
+ ## The queue
122
+
123
+ The player works through a **queue** of Tracks. The Track at the current position is the **active track** — it's what plays, and what external next/previous controls (car, headphones, lock screen) move between.
124
+
125
+ **Now Playing** is the metadata shown on the lock screen, notification, and car surfaces. By default it mirrors the active track; for live streams you can override it as the current song changes — see [Now Playing](/guide/now-playing).
126
+
127
+ ## Read state in your UI
128
+
129
+ Reactive hooks keep your in-app UI in sync with playback — they re-render automatically as it changes:
130
+
131
+ ```tsx
132
+ import {
133
+ togglePlayback,
134
+ usePlayingState,
135
+ useActiveTrack
136
+ } from 'react-native-audio-browser'
137
+
138
+ function PlayPauseButton() {
139
+ const { playing } = usePlayingState()
140
+ const track = useActiveTrack()
141
+
142
+ return (
143
+ <Button
144
+ title={playing ? `Pause ${track?.title ?? ''}` : 'Play'}
145
+ onPress={() => togglePlayback()}
146
+ />
147
+ )
148
+ }
149
+ ```
150
+
151
+ See the [API reference](/api/) for the full set of hooks (`useProgress`, `useQueue`, `useNowPlaying`, and more).