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

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 (902) hide show
  1. package/AudioBrowser.podspec +1 -1
  2. package/README.md +180 -2
  3. package/android/build.gradle +10 -0
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/AndroidManifest.xml +15 -0
  6. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +401 -141
  7. package/android/src/main/java/com/audiobrowser/Callbacks.kt +4 -6
  8. package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
  9. package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
  10. package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
  11. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +443 -178
  12. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +300 -0
  13. package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
  14. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
  15. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +43 -0
  16. package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
  17. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
  18. package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
  19. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
  20. package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
  21. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +31 -37
  22. package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
  23. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +119 -0
  24. package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
  25. package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
  26. package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
  27. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
  28. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +415 -131
  29. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +154 -276
  30. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +348 -0
  31. package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
  32. package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
  33. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +27 -5
  34. package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
  35. package/android/src/main/java/com/audiobrowser/player/Player.kt +452 -576
  36. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
  37. package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
  38. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
  39. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +117 -53
  40. package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
  41. package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
  42. package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
  43. package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +85 -40
  44. package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
  45. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +141 -0
  46. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
  47. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +41 -0
  48. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +209 -0
  49. package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
  50. package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
  51. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +26 -0
  52. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
  53. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
  54. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
  55. package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
  56. package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
  57. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
  58. package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
  59. package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
  60. package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
  61. package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
  62. package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
  63. package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
  64. package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
  65. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +111 -80
  66. package/android/src/main/res/values/strings.xml +4 -0
  67. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +166 -0
  68. package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
  69. package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
  70. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
  71. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
  72. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
  73. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
  74. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +54 -0
  75. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +96 -0
  76. package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
  77. package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
  78. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +159 -0
  79. package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
  80. package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
  81. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
  82. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +191 -0
  83. package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
  84. package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
  85. package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
  86. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
  87. package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
  88. package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
  89. package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
  90. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +107 -0
  91. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +73 -0
  92. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +75 -0
  93. package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
  94. package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
  95. package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
  96. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
  97. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
  98. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
  99. package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
  100. package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
  101. package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
  102. package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
  103. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
  104. package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
  105. package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
  106. package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
  107. package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
  108. package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
  109. package/ios/Browser/BrowserConfig.swift +41 -11
  110. package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
  111. package/ios/Browser/BrowserManager.swift +303 -110
  112. package/ios/Browser/BrowserPathHelper.swift +32 -3
  113. package/ios/Browser/JsonModels.swift +287 -66
  114. package/ios/Browser/MediaResolveComposer.swift +114 -0
  115. package/ios/Browser/SearchDrillIn.swift +44 -0
  116. package/ios/Browser/SectionScope.swift +35 -0
  117. package/ios/Browser/TabBarEntries.swift +15 -0
  118. package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
  119. package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
  120. package/ios/CarPlay/CarPlayController.swift +739 -149
  121. package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
  122. package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
  123. package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
  124. package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
  125. package/ios/CarPlay/RNABAudioBrowser.h +15 -10
  126. package/ios/CarPlay/RNABAudioBrowser.m +15 -14
  127. package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
  128. package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
  129. package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
  130. package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
  131. package/ios/Extension/Capability+RemoteCommand.swift +7 -9
  132. package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
  133. package/ios/Extension/NavigationError+Conversion.swift +8 -1
  134. package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
  135. package/ios/Extension/RequestConfig+Copying.swift +31 -0
  136. package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
  137. package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
  138. package/ios/Extension/Track+AVPlayer.swift +4 -4
  139. package/ios/Extension/Track+Copying.swift +9 -3
  140. package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
  141. package/ios/Extension/Variant+Artwork.swift +49 -0
  142. package/ios/HybridAudioBrowser.swift +750 -173
  143. package/ios/Model/NitroTypeStubs.swift +299 -224
  144. package/ios/Model/PlayerUpdateOptions.swift +23 -23
  145. package/ios/Model/RemoteCommand.swift +6 -6
  146. package/ios/Model/TrackPlayerError.swift +82 -18
  147. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
  148. package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
  149. package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
  150. package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
  151. package/ios/Observer/PlayerTimeObserver.swift +3 -53
  152. package/ios/Option/SessionCategories.swift +33 -11
  153. package/ios/Player/AirPlayStallJudgement.swift +25 -0
  154. package/ios/Player/EndOfTrackJudgement.swift +32 -0
  155. package/ios/Player/LoadSeekCoordinator.swift +16 -1
  156. package/ios/Player/MediaIntentCriteria.swift +139 -0
  157. package/ios/Player/MediaLoader.swift +31 -11
  158. package/ios/Player/NowPlayingUpdater.swift +119 -26
  159. package/ios/Player/PersistedPlaybackState.swift +11 -0
  160. package/ios/Player/PlaybackCoordinator.swift +285 -51
  161. package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
  162. package/ios/Player/PlaybackEffectHandler.swift +19 -9
  163. package/ios/Player/PlaybackErrorHandler.swift +97 -13
  164. package/ios/Player/PlaybackStateMachine.swift +18 -13
  165. package/ios/Player/PlaybackStateStore.swift +27 -0
  166. package/ios/Player/PlaybackTimer.swift +61 -0
  167. package/ios/Player/PlayingStateManager.swift +3 -2
  168. package/ios/Player/QueueManager.swift +67 -61
  169. package/ios/Player/RetryManager.swift +97 -21
  170. package/ios/Player/SleepTimerHandling.swift +8 -0
  171. package/ios/Player/SleepTimerManager.swift +72 -11
  172. package/ios/Player/VolumeFader.swift +77 -0
  173. package/ios/RemoteCommand/RemoteCommandController.swift +31 -15
  174. package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
  175. package/ios/Tests/BrowserPathHelperTests.swift +28 -0
  176. package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
  177. package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
  178. package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
  179. package/ios/Tests/JsonModelsTests.swift +58 -0
  180. package/ios/Tests/LoadSeekCoordinatorTests.swift +38 -7
  181. package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
  182. package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
  183. package/ios/Tests/MediaLoaderTests.swift +32 -20
  184. package/ios/Tests/MediaResolveTests.swift +126 -0
  185. package/ios/Tests/OnceValueTests.swift +79 -0
  186. package/ios/Tests/PlaybackCoordinatorTests.swift +787 -13
  187. package/ios/Tests/PlaybackErrorHandlerTests.swift +380 -46
  188. package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
  189. package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
  190. package/ios/Tests/PlaybackTimerTests.swift +55 -0
  191. package/ios/Tests/PlayingStateManagerTests.swift +57 -0
  192. package/ios/Tests/QueueManagerTests.swift +149 -18
  193. package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
  194. package/ios/Tests/RetryManagerTests.swift +89 -0
  195. package/ios/Tests/SearchDrillInTests.swift +63 -0
  196. package/ios/Tests/SectionScopeTests.swift +82 -0
  197. package/ios/Tests/SleepTimerManagerTests.swift +107 -0
  198. package/ios/Tests/Support/ManualSleepTimerScheduler.swift +47 -0
  199. package/ios/Tests/Support/TestTypes.swift +61 -20
  200. package/ios/Tests/TabBarEntriesTests.swift +71 -0
  201. package/ios/Tests/TrackSelectorTests.swift +15 -15
  202. package/ios/TrackPlayer.swift +380 -58
  203. package/ios/TrackPlayerCallbacks.swift +0 -6
  204. package/ios/TrackSelector.swift +5 -5
  205. package/ios/Util/ArtworkImageFetcher.swift +4 -2
  206. package/ios/Util/NetworkMonitor.swift +7 -3
  207. package/ios/Util/OnceValue.swift +34 -0
  208. package/lib/commonjs/features/browser-config.js +227 -0
  209. package/lib/commonjs/features/browser-config.js.map +1 -0
  210. package/lib/commonjs/features/browser.js +59 -158
  211. package/lib/commonjs/features/browser.js.map +1 -1
  212. package/lib/commonjs/features/carConnection.js +42 -0
  213. package/lib/commonjs/features/carConnection.js.map +1 -0
  214. package/lib/commonjs/features/errors.js +34 -2
  215. package/lib/commonjs/features/errors.js.map +1 -1
  216. package/lib/commonjs/features/gate.js +149 -0
  217. package/lib/commonjs/features/gate.js.map +1 -0
  218. package/lib/commonjs/features/index.js +22 -0
  219. package/lib/commonjs/features/index.js.map +1 -1
  220. package/lib/commonjs/features/metadata.js.map +1 -1
  221. package/lib/commonjs/features/nowPlaying.js +35 -0
  222. package/lib/commonjs/features/nowPlaying.js.map +1 -1
  223. package/lib/commonjs/features/output.js +37 -23
  224. package/lib/commonjs/features/output.js.map +1 -1
  225. package/lib/commonjs/features/playback/controls.js +9 -0
  226. package/lib/commonjs/features/playback/controls.js.map +1 -1
  227. package/lib/commonjs/features/playback/index.js +11 -0
  228. package/lib/commonjs/features/playback/index.js.map +1 -1
  229. package/lib/commonjs/features/playback/progress.js +28 -12
  230. package/lib/commonjs/features/playback/progress.js.map +1 -1
  231. package/lib/commonjs/features/playback/trackPlaybackTime.js +92 -0
  232. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
  233. package/lib/commonjs/features/playback/volume.js +1 -1
  234. package/lib/commonjs/features/player/options.js +49 -13
  235. package/lib/commonjs/features/player/options.js.map +1 -1
  236. package/lib/commonjs/features/player/setup.js +249 -3
  237. package/lib/commonjs/features/player/setup.js.map +1 -1
  238. package/lib/commonjs/features/queue/queue.js +10 -3
  239. package/lib/commonjs/features/queue/queue.js.map +1 -1
  240. package/lib/commonjs/features/remoteControls.js +1 -12
  241. package/lib/commonjs/features/remoteControls.js.map +1 -1
  242. package/lib/commonjs/features/sleepTimer.js +9 -4
  243. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  244. package/lib/commonjs/native.js +5 -0
  245. package/lib/commonjs/native.js.map +1 -1
  246. package/lib/commonjs/native.web.js +4 -1
  247. package/lib/commonjs/native.web.js.map +1 -1
  248. package/lib/commonjs/utils/LazyNativeEmitter.js +0 -3
  249. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  250. package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
  251. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  252. package/lib/commonjs/utils/artwork.js +31 -0
  253. package/lib/commonjs/utils/artwork.js.map +1 -0
  254. package/lib/commonjs/web/NativeAudioBrowser.js +301 -124
  255. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  256. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
  257. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  258. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
  259. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  260. package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
  261. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  262. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
  263. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  264. package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
  265. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
  266. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
  267. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
  268. package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
  269. package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
  270. package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
  271. package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
  272. package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
  273. package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
  274. package/lib/commonjs/web/TrackPlayer/index.js +13 -5
  275. package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
  276. package/lib/commonjs/web/browser/BrowserManager.js +176 -50
  277. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  278. package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
  279. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
  280. package/lib/commonjs/web/browser/SearchManager.js +11 -9
  281. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  282. package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
  283. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
  284. package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
  285. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
  286. package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
  287. package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
  288. package/lib/commonjs/web/http/HttpClient.js +4 -23
  289. package/lib/commonjs/web/http/HttpClient.js.map +1 -1
  290. package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
  291. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  292. package/lib/commonjs/web/playbackErrorKind.js +57 -0
  293. package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
  294. package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
  295. package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
  296. package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
  297. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  298. package/lib/commonjs/web/player/OptionsManager.js +9 -17
  299. package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
  300. package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
  301. package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
  302. package/lib/module/features/browser-config.js +221 -0
  303. package/lib/module/features/browser-config.js.map +1 -0
  304. package/lib/module/features/browser.js +53 -156
  305. package/lib/module/features/browser.js.map +1 -1
  306. package/lib/module/features/carConnection.js +37 -0
  307. package/lib/module/features/carConnection.js.map +1 -0
  308. package/lib/module/features/errors.js +34 -2
  309. package/lib/module/features/errors.js.map +1 -1
  310. package/lib/module/features/gate.js +144 -0
  311. package/lib/module/features/gate.js.map +1 -0
  312. package/lib/module/features/index.js +2 -0
  313. package/lib/module/features/index.js.map +1 -1
  314. package/lib/module/features/metadata.js.map +1 -1
  315. package/lib/module/features/nowPlaying.js +33 -0
  316. package/lib/module/features/nowPlaying.js.map +1 -1
  317. package/lib/module/features/output.js +32 -19
  318. package/lib/module/features/output.js.map +1 -1
  319. package/lib/module/features/playback/controls.js +8 -0
  320. package/lib/module/features/playback/controls.js.map +1 -1
  321. package/lib/module/features/playback/index.js +1 -0
  322. package/lib/module/features/playback/index.js.map +1 -1
  323. package/lib/module/features/playback/progress.js +28 -12
  324. package/lib/module/features/playback/progress.js.map +1 -1
  325. package/lib/module/features/playback/trackPlaybackTime.js +88 -0
  326. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
  327. package/lib/module/features/playback/volume.js +1 -1
  328. package/lib/module/features/player/options.js +48 -13
  329. package/lib/module/features/player/options.js.map +1 -1
  330. package/lib/module/features/player/setup.js +249 -3
  331. package/lib/module/features/player/setup.js.map +1 -1
  332. package/lib/module/features/queue/queue.js +10 -3
  333. package/lib/module/features/queue/queue.js.map +1 -1
  334. package/lib/module/features/remoteControls.js +0 -11
  335. package/lib/module/features/remoteControls.js.map +1 -1
  336. package/lib/module/features/sleepTimer.js +9 -4
  337. package/lib/module/features/sleepTimer.js.map +1 -1
  338. package/lib/module/native.js +5 -0
  339. package/lib/module/native.js.map +1 -1
  340. package/lib/module/native.web.js +4 -1
  341. package/lib/module/native.web.js.map +1 -1
  342. package/lib/module/utils/LazyNativeEmitter.js +0 -3
  343. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  344. package/lib/module/utils/NativeUpdatedValue.js +0 -3
  345. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  346. package/lib/module/utils/artwork.js +26 -0
  347. package/lib/module/utils/artwork.js.map +1 -0
  348. package/lib/module/web/NativeAudioBrowser.js +302 -125
  349. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  350. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
  351. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  352. package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
  353. package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  354. package/lib/module/web/TrackPlayer/Player.js +90 -26
  355. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  356. package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
  357. package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  358. package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
  359. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
  360. package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
  361. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
  362. package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
  363. package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
  364. package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
  365. package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
  366. package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
  367. package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
  368. package/lib/module/web/TrackPlayer/index.js +2 -1
  369. package/lib/module/web/TrackPlayer/index.js.map +1 -1
  370. package/lib/module/web/browser/BrowserManager.js +176 -50
  371. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  372. package/lib/module/web/browser/NavigationErrorManager.js +3 -1
  373. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
  374. package/lib/module/web/browser/SearchManager.js +11 -9
  375. package/lib/module/web/browser/SearchManager.js.map +1 -1
  376. package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
  377. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
  378. package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
  379. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
  380. package/lib/module/web/browser/parseSearchResponse.js +16 -0
  381. package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
  382. package/lib/module/web/http/HttpClient.js +4 -23
  383. package/lib/module/web/http/HttpClient.js.map +1 -1
  384. package/lib/module/web/http/RequestConfigBuilder.js +107 -58
  385. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  386. package/lib/module/web/playbackErrorKind.js +52 -0
  387. package/lib/module/web/playbackErrorKind.js.map +1 -0
  388. package/lib/module/web/player/MediaSessionManager.js +116 -0
  389. package/lib/module/web/player/MediaSessionManager.js.map +1 -0
  390. package/lib/module/web/player/NowPlayingManager.js +7 -5
  391. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  392. package/lib/module/web/player/OptionsManager.js +9 -17
  393. package/lib/module/web/player/OptionsManager.js.map +1 -1
  394. package/lib/module/web/player/RemoteCommandController.js +92 -0
  395. package/lib/module/web/player/RemoteCommandController.js.map +1 -0
  396. package/lib/typescript/src/features/browser-config.d.ts +12 -0
  397. package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
  398. package/lib/typescript/src/features/browser.d.ts +40 -1
  399. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  400. package/lib/typescript/src/features/carConnection.d.ts +20 -0
  401. package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
  402. package/lib/typescript/src/features/errors.d.ts +58 -3
  403. package/lib/typescript/src/features/errors.d.ts.map +1 -1
  404. package/lib/typescript/src/features/gate.d.ts +77 -0
  405. package/lib/typescript/src/features/gate.d.ts.map +1 -0
  406. package/lib/typescript/src/features/index.d.ts +2 -0
  407. package/lib/typescript/src/features/index.d.ts.map +1 -1
  408. package/lib/typescript/src/features/metadata.d.ts +6 -4
  409. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  410. package/lib/typescript/src/features/nowPlaying.d.ts +27 -0
  411. package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
  412. package/lib/typescript/src/features/output.d.ts +28 -18
  413. package/lib/typescript/src/features/output.d.ts.map +1 -1
  414. package/lib/typescript/src/features/playback/controls.d.ts +5 -0
  415. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  416. package/lib/typescript/src/features/playback/index.d.ts +1 -0
  417. package/lib/typescript/src/features/playback/index.d.ts.map +1 -1
  418. package/lib/typescript/src/features/playback/progress.d.ts +3 -0
  419. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  420. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
  421. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
  422. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  423. package/lib/typescript/src/features/player/options.d.ts +92 -93
  424. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  425. package/lib/typescript/src/features/player/setup.d.ts +206 -167
  426. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  427. package/lib/typescript/src/features/queue/queue.d.ts +9 -2
  428. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  429. package/lib/typescript/src/features/remoteControls.d.ts +0 -13
  430. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  431. package/lib/typescript/src/features/sleepTimer.d.ts +18 -3
  432. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  433. package/lib/typescript/src/index.d.ts.map +1 -1
  434. package/lib/typescript/src/native.d.ts +1 -2
  435. package/lib/typescript/src/native.d.ts.map +1 -1
  436. package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
  437. package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
  438. package/lib/typescript/src/types/browser-native.d.ts +9 -3
  439. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  440. package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
  441. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  442. package/lib/typescript/src/types/browser.d.ts +349 -85
  443. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  444. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +0 -2
  445. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  446. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
  447. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  448. package/lib/typescript/src/utils/artwork.d.ts +19 -0
  449. package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
  450. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
  451. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +3 -1
  452. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
  453. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
  454. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +1 -11
  455. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
  456. package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
  457. package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
  458. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
  459. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
  460. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
  461. package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
  462. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
  463. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
  464. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
  465. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
  466. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
  467. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
  468. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
  469. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
  470. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
  471. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
  472. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
  473. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -0
  474. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +131 -0
  475. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
  476. package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
  477. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
  478. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +9 -4
  479. package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
  480. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
  481. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
  482. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
  483. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
  484. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
  485. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
  486. package/nitrogen/generated/android/c++/JGate.hpp +62 -0
  487. package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
  488. package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
  489. package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
  490. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +236 -212
  491. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +25 -22
  492. package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
  493. package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
  494. package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
  495. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
  496. package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
  497. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
  498. package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
  499. package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
  500. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
  501. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
  502. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +16 -19
  503. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
  504. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +1 -11
  505. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
  506. package/nitrogen/generated/android/c++/JNotificationButton.hpp +3 -3
  507. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
  508. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
  509. package/nitrogen/generated/android/c++/JOptions.hpp +14 -9
  510. package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
  511. package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
  512. package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
  513. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
  514. package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
  515. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
  516. package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
  517. package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
  518. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
  519. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
  520. package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
  521. package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
  522. package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
  523. package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
  524. package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
  525. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
  526. package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
  527. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
  528. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
  529. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
  530. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.cpp +41 -0
  531. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
  532. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
  533. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
  534. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
  535. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +2 -8
  536. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
  537. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
  538. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
  539. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → FavoriteConfig.kt} +6 -6
  540. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
  541. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
  542. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
  543. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
  544. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +80 -0
  545. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
  546. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
  547. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
  548. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
  549. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
  550. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.kt → Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt} +12 -12
  551. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
  552. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
  553. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
  554. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → Gate.kt} +9 -6
  555. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
  556. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
  557. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateReason.kt +23 -0
  558. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +103 -109
  559. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
  560. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
  561. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
  562. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
  563. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
  564. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
  565. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
  566. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
  567. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
  568. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
  569. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
  570. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
  571. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +2 -8
  572. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
  573. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButton.kt +5 -5
  574. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
  575. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
  576. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
  577. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
  578. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
  579. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
  580. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
  581. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
  582. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  583. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -3
  584. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
  585. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
  586. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
  587. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
  588. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
  589. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
  590. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
  591. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
  592. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
  593. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
  594. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
  595. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1098 -447
  596. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +60 -39
  597. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +170 -112
  598. package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
  599. package/nitrogen/generated/ios/swift/AndroidOptions.swift +2 -12
  600. package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
  601. package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
  602. package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
  603. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
  604. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
  605. package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
  606. package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
  607. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
  608. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
  609. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
  610. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
  611. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
  612. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
  613. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
  614. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.swift +77 -0
  615. package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
  616. package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
  617. package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
  618. package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
  619. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  620. package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
  621. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
  622. package/nitrogen/generated/ios/swift/{Func_void_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError___.swift → Func_void_std__shared_ptr_Promise_GateDecision__.swift} +17 -17
  623. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
  624. package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
  625. package/nitrogen/generated/ios/swift/Gate.swift +47 -0
  626. package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
  627. package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
  628. package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
  629. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +20 -14
  630. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +230 -197
  631. package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
  632. package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
  633. package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
  634. package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
  635. package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
  636. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
  637. package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
  638. package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
  639. package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
  640. package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
  641. package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
  642. package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
  643. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +1 -30
  644. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
  645. package/nitrogen/generated/ios/swift/NotificationButton.swift +4 -4
  646. package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
  647. package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
  648. package/nitrogen/generated/ios/swift/Options.swift +10 -4
  649. package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
  650. package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
  651. package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
  652. package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
  653. package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
  654. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
  655. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
  656. package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
  657. package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
  658. package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
  659. package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
  660. package/nitrogen/generated/ios/swift/Track.swift +74 -8
  661. package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
  662. package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
  663. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
  664. package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
  665. package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
  666. package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
  667. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +1 -12
  668. package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
  669. package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
  670. package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
  671. package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
  672. package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
  673. package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
  674. package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
  675. package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
  676. package/nitrogen/generated/shared/c++/{HeartRating.hpp → Gate.hpp} +21 -16
  677. package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateDecision.hpp} +23 -17
  678. package/nitrogen/generated/shared/c++/{PercentageRating.hpp → GateEvent.hpp} +18 -17
  679. package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
  680. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +21 -18
  681. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +44 -35
  682. package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
  683. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
  684. package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
  685. package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
  686. package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
  687. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
  688. package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
  689. package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
  690. package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
  691. package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
  692. package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
  693. package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
  694. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +1 -12
  695. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
  696. package/nitrogen/generated/shared/c++/NotificationButton.hpp +8 -8
  697. package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
  698. package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
  699. package/nitrogen/generated/shared/c++/Options.hpp +8 -8
  700. package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
  701. package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
  702. package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
  703. package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
  704. package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
  705. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
  706. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
  707. package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
  708. package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
  709. package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
  710. package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
  711. package/nitrogen/generated/shared/c++/Track.hpp +24 -5
  712. package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
  713. package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
  714. package/package.json +11 -6
  715. package/src/codegen-no-config-variant.test.ts +70 -0
  716. package/src/features/browser-config.test.ts +110 -0
  717. package/src/features/browser-config.ts +293 -0
  718. package/src/features/browser.test.ts +19 -0
  719. package/src/features/browser.ts +58 -201
  720. package/src/features/carConnection.ts +36 -0
  721. package/src/features/errors.ts +68 -2
  722. package/src/features/gate.test.ts +72 -0
  723. package/src/features/gate.ts +155 -0
  724. package/src/features/index.ts +2 -0
  725. package/src/features/metadata.ts +6 -12
  726. package/src/features/nowPlaying.ts +36 -0
  727. package/src/features/output.ts +35 -22
  728. package/src/features/playback/controls.ts +8 -0
  729. package/src/features/playback/index.ts +1 -0
  730. package/src/features/playback/progress.ts +31 -12
  731. package/src/features/playback/trackPlaybackTime.test.ts +66 -0
  732. package/src/features/playback/trackPlaybackTime.ts +106 -0
  733. package/src/features/playback/volume.ts +1 -1
  734. package/src/features/player/options.test.ts +38 -0
  735. package/src/features/player/options.ts +140 -75
  736. package/src/features/player/setup.test.ts +117 -0
  737. package/src/features/player/setup.ts +390 -175
  738. package/src/features/queue/queue.test.ts +36 -0
  739. package/src/features/queue/queue.ts +15 -4
  740. package/src/features/remoteControls.ts +0 -23
  741. package/src/features/sleepTimer.ts +23 -4
  742. package/src/native.ts +5 -0
  743. package/src/native.web.ts +4 -1
  744. package/src/specs/audio-browser.nitro.ts +131 -41
  745. package/src/types/browser-native.ts +19 -6
  746. package/src/types/browser-nodes.ts +160 -13
  747. package/src/types/browser.ts +377 -89
  748. package/src/utils/LazyNativeEmitter.ts +0 -2
  749. package/src/utils/NativeUpdatedValue.ts +0 -2
  750. package/src/utils/artwork.ts +30 -0
  751. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
  752. package/src/web/NativeAudioBrowser.ts +365 -158
  753. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
  754. package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
  755. package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
  756. package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
  757. package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
  758. package/src/web/TrackPlayer/Player.load.test.ts +86 -0
  759. package/src/web/TrackPlayer/Player.ts +113 -39
  760. package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
  761. package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
  762. package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
  763. package/src/web/TrackPlayer/QueueManager.ts +302 -0
  764. package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
  765. package/src/web/TrackPlayer/RepeatMode.ts +1 -1
  766. package/src/web/TrackPlayer/SleepTimer.ts +38 -3
  767. package/src/web/TrackPlayer/VolumeFader.ts +74 -0
  768. package/src/web/TrackPlayer/index.ts +2 -1
  769. package/src/web/browser/BrowserManager.ts +231 -62
  770. package/src/web/browser/NavigationErrorManager.ts +3 -1
  771. package/src/web/browser/SearchManager.ts +11 -15
  772. package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
  773. package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
  774. package/src/web/browser/assertBrowsePageShape.ts +27 -0
  775. package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
  776. package/src/web/browser/classifyTrackNavigation.ts +35 -0
  777. package/src/web/browser/parseSearchResponse.test.ts +25 -0
  778. package/src/web/browser/parseSearchResponse.ts +15 -0
  779. package/src/web/http/HttpClient.ts +5 -35
  780. package/src/web/http/RequestConfigBuilder.test.ts +59 -0
  781. package/src/web/http/RequestConfigBuilder.ts +120 -63
  782. package/src/web/playbackErrorKind.test.ts +55 -0
  783. package/src/web/playbackErrorKind.ts +56 -0
  784. package/src/web/player/MediaSessionManager.test.ts +220 -0
  785. package/src/web/player/MediaSessionManager.ts +151 -0
  786. package/src/web/player/NowPlayingManager.test.ts +46 -0
  787. package/src/web/player/NowPlayingManager.ts +6 -5
  788. package/src/web/player/OptionsManager.ts +12 -18
  789. package/src/web/player/RemoteCommandController.test.ts +171 -0
  790. package/src/web/player/RemoteCommandController.ts +166 -0
  791. package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
  792. package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
  793. package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
  794. package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
  795. package/ios/CarPlay/Track+CarPlay.swift +0 -63
  796. package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
  797. package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
  798. package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
  799. package/lib/commonjs/features/rating.js +0 -2
  800. package/lib/commonjs/features/rating.js.map +0 -1
  801. package/lib/commonjs/types/player.js +0 -2
  802. package/lib/commonjs/types/player.js.map +0 -1
  803. package/lib/commonjs/web/SimpleRouter.test.js +0 -174
  804. package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
  805. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
  806. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  807. package/lib/module/features/rating.js +0 -2
  808. package/lib/module/features/rating.js.map +0 -1
  809. package/lib/module/types/player.js +0 -2
  810. package/lib/module/types/player.js.map +0 -1
  811. package/lib/module/web/SimpleRouter.test.js +0 -174
  812. package/lib/module/web/SimpleRouter.test.js.map +0 -1
  813. package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
  814. package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  815. package/lib/typescript/src/features/rating.d.ts +0 -14
  816. package/lib/typescript/src/features/rating.d.ts.map +0 -1
  817. package/lib/typescript/src/native.web.d.ts +0 -3
  818. package/lib/typescript/src/native.web.d.ts.map +0 -1
  819. package/lib/typescript/src/types/player.d.ts +0 -64
  820. package/lib/typescript/src/types/player.d.ts.map +0 -1
  821. package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
  822. package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
  823. package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
  824. package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
  825. package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
  826. package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
  827. package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
  828. package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
  829. package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
  830. package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
  831. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
  832. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
  833. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
  834. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
  835. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
  836. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
  837. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
  838. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
  839. package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
  840. package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
  841. package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
  842. package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
  843. package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
  844. package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
  845. package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
  846. package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
  847. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
  848. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
  849. package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
  850. package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
  851. package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
  852. package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
  853. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
  854. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
  855. package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
  856. package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
  857. package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
  858. package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
  859. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
  860. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
  861. package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
  862. package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
  863. package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
  864. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
  865. package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
  866. package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
  867. package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
  868. package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
  869. package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
  870. package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
  871. package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
  872. package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
  873. package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
  874. package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
  875. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
  876. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
  877. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +0 -50
  878. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
  879. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
  880. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
  881. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
  882. package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
  883. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
  884. package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
  885. package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
  886. package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
  887. package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
  888. package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
  889. package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
  890. package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
  891. package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
  892. package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
  893. package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
  894. package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
  895. package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
  896. package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
  897. package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
  898. package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
  899. package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
  900. package/src/features/rating.ts +0 -17
  901. package/src/types/player.ts +0 -69
  902. package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
@@ -8,6 +8,7 @@ import android.os.Handler
8
8
  import android.os.IBinder
9
9
  import android.os.Looper
10
10
  import androidx.annotation.Keep
11
+ import androidx.car.app.connection.CarConnection
11
12
  import androidx.lifecycle.DefaultLifecycleObserver
12
13
  import androidx.lifecycle.LifecycleOwner
13
14
  import androidx.lifecycle.ProcessLifecycleOwner
@@ -15,38 +16,41 @@ import androidx.media3.session.MediaBrowser
15
16
  import androidx.media3.session.SessionToken
16
17
  import com.audiobrowser.browser.BrowserConfig
17
18
  import com.audiobrowser.browser.BrowserManager
18
- import com.audiobrowser.browser.handleTrackLoad
19
19
  import com.audiobrowser.browser.CallbackException
20
20
  import com.audiobrowser.browser.ContentNotFoundException
21
21
  import com.audiobrowser.browser.HttpStatusException
22
22
  import com.audiobrowser.browser.NetworkException
23
+ import com.audiobrowser.browser.handleTrackLoad
24
+ import com.audiobrowser.browser.resolveMediaUrl
23
25
  import com.audiobrowser.extension.NumberExt.Companion.toSeconds
24
- import com.audiobrowser.http.RequestConfigBuilder
25
- import com.audiobrowser.model.PlaybackMetadata
26
26
  import com.audiobrowser.model.PlayerSetupOptions
27
27
  import com.audiobrowser.model.PlayerUpdateOptions
28
+ import com.audiobrowser.player.Player
28
29
  import com.audiobrowser.util.BatteryOptimizationHelper
29
30
  import com.audiobrowser.util.BatteryWarningStore
30
31
  import com.audiobrowser.util.BrowserPathHelper
31
- import com.audiobrowser.util.CoilBitmapLoader
32
+ import com.audiobrowser.util.OutputMonitor
33
+ import com.audiobrowser.util.OutputSwitcher
32
34
  import com.audiobrowser.util.SystemVolumeMonitor
33
35
  import com.facebook.proguard.annotations.DoNotStrip
34
36
  import com.google.common.util.concurrent.ListenableFuture
35
37
  import com.margelo.nitro.NitroModules
36
- import com.margelo.nitro.audiobrowser.ChapterMetadata
37
- import com.margelo.nitro.audiobrowser.TimedMetadata
38
- import com.margelo.nitro.audiobrowser.TrackMetadata
39
38
  import com.margelo.nitro.audiobrowser.BatteryOptimizationStatus
40
39
  import com.margelo.nitro.audiobrowser.BatteryOptimizationStatusChangedEvent
41
40
  import com.margelo.nitro.audiobrowser.BatteryWarningPendingChangedEvent
41
+ import com.margelo.nitro.audiobrowser.ChapterMetadata
42
42
  import com.margelo.nitro.audiobrowser.EqualizerSettings
43
43
  import com.margelo.nitro.audiobrowser.FavoriteChangedEvent
44
44
  import com.margelo.nitro.audiobrowser.FormatNavigationErrorParams
45
45
  import com.margelo.nitro.audiobrowser.FormattedNavigationError
46
+ import com.margelo.nitro.audiobrowser.Gate
47
+ import com.margelo.nitro.audiobrowser.GateDecision
48
+ import com.margelo.nitro.audiobrowser.GateEvent
46
49
  import com.margelo.nitro.audiobrowser.HybridAudioBrowserSpec
47
- import com.margelo.nitro.audiobrowser.IosOutput
48
50
  import com.margelo.nitro.audiobrowser.MediaRequestConfig
49
51
  import com.margelo.nitro.audiobrowser.NativeBrowserConfiguration
52
+ import com.margelo.nitro.audiobrowser.NativeGateRequest
53
+ import com.margelo.nitro.audiobrowser.NativeSetupPlayerOptions
50
54
  import com.margelo.nitro.audiobrowser.NativeUpdateOptions
51
55
  import com.margelo.nitro.audiobrowser.NavigationError
52
56
  import com.margelo.nitro.audiobrowser.NavigationErrorEvent
@@ -54,7 +58,7 @@ import com.margelo.nitro.audiobrowser.NavigationErrorType
54
58
  import com.margelo.nitro.audiobrowser.NowPlayingMetadata
55
59
  import com.margelo.nitro.audiobrowser.NowPlayingUpdate
56
60
  import com.margelo.nitro.audiobrowser.Options
57
- import com.margelo.nitro.audiobrowser.PartialSetupPlayerOptions
61
+ import com.margelo.nitro.audiobrowser.Output
58
62
  import com.margelo.nitro.audiobrowser.Playback
59
63
  import com.margelo.nitro.audiobrowser.PlaybackActiveTrackChangedEvent
60
64
  import com.margelo.nitro.audiobrowser.PlaybackError
@@ -62,6 +66,7 @@ import com.margelo.nitro.audiobrowser.PlaybackErrorEvent
62
66
  import com.margelo.nitro.audiobrowser.PlaybackPlayWhenReadyChangedEvent
63
67
  import com.margelo.nitro.audiobrowser.PlaybackProgressUpdatedEvent
64
68
  import com.margelo.nitro.audiobrowser.PlaybackQueueEndedEvent
69
+ import com.margelo.nitro.audiobrowser.PlaybackState
65
70
  import com.margelo.nitro.audiobrowser.PlayingState
66
71
  import com.margelo.nitro.audiobrowser.Progress
67
72
  import com.margelo.nitro.audiobrowser.RemoteJumpBackwardEvent
@@ -69,15 +74,14 @@ import com.margelo.nitro.audiobrowser.RemoteJumpForwardEvent
69
74
  import com.margelo.nitro.audiobrowser.RemotePlayIdEvent
70
75
  import com.margelo.nitro.audiobrowser.RemotePlaySearchEvent
71
76
  import com.margelo.nitro.audiobrowser.RemoteSeekEvent
72
- import com.margelo.nitro.audiobrowser.RemoteSetRatingEvent
73
77
  import com.margelo.nitro.audiobrowser.RemoteSkipEvent
74
78
  import com.margelo.nitro.audiobrowser.RepeatMode
75
79
  import com.margelo.nitro.audiobrowser.RepeatModeChangedEvent
76
- import com.margelo.nitro.audiobrowser.RequestConfig
77
80
  import com.margelo.nitro.audiobrowser.ResolvedTrack
78
81
  import com.margelo.nitro.audiobrowser.SleepTimer
82
+ import com.margelo.nitro.audiobrowser.TimedMetadata
79
83
  import com.margelo.nitro.audiobrowser.Track
80
- import com.margelo.nitro.audiobrowser.UpdateOptions
84
+ import com.margelo.nitro.audiobrowser.TrackMetadata
81
85
  import com.margelo.nitro.core.Promise
82
86
  import java.util.concurrent.TimeUnit
83
87
  import kotlin.coroutines.resume
@@ -95,6 +99,14 @@ import timber.log.Timber
95
99
  @Keep
96
100
  @DoNotStrip
97
101
  class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
102
+
103
+ /** Called by Nitro when the JS object is destroyed (incl. JS runtime reloads). */
104
+ override fun dispose() {
105
+ systemVolumeMonitor.destroy()
106
+ outputMonitor.destroy()
107
+ super.dispose()
108
+ }
109
+
98
110
  private val mainScope = MainScope()
99
111
  private var navigationJob: Job? = null
100
112
  private val handler = Handler(Looper.getMainLooper())
@@ -107,14 +119,18 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
107
119
  NativeBrowserConfiguration(
108
120
  path = null,
109
121
  request = null,
122
+ requestResolver = null,
123
+ browse = null,
124
+ browseResolver = null,
110
125
  media = null,
111
126
  artwork = null,
127
+ nowPlayingArtwork = null,
112
128
  routes = null,
113
129
  singleTrack = null,
114
130
  handleTrackLoad = null,
115
131
  androidControllerOfflineError = null,
116
- carPlayUpNextButton = null,
117
- carPlayNowPlayingButtons = null,
132
+ carPlayLoadingTitle = null,
133
+ resolveAlbumUrl = null,
118
134
  formatNavigationError = null,
119
135
  )
120
136
 
@@ -123,9 +139,11 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
123
139
  setOnPathChanged { path -> onPathChanged(path) }
124
140
  setOnContentChanged { content -> onContentChanged(content) }
125
141
  setOnTabsChanged { tabs -> onTabsChanged(tabs) }
142
+ setOnArtworkRegistriesCleared { connectedService?.player?.browseArtworkRegistry?.clear() }
126
143
  }
127
144
 
128
145
  private val systemVolumeMonitor = SystemVolumeMonitor(context)
146
+ private val outputMonitor = OutputMonitor(context)
129
147
 
130
148
  // MARK: Player state
131
149
  private var updateOptions: PlayerUpdateOptions = PlayerUpdateOptions()
@@ -134,6 +152,11 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
134
152
  private var connectedService: Service? = null
135
153
  private var setupPromise: ((Unit) -> Unit)? = null
136
154
 
155
+ // Initial player state staged before the player exists — from setup options or the imperative
156
+ // setters called pre-bind. Strict last-write-wins; consumed when the player comes up.
157
+ private var pendingPlayWhenReady: Boolean? = null
158
+ private var pendingRepeatMode: RepeatMode? = null
159
+
137
160
  /** Post callback to main handler for consistent async delivery to JS - avoids deadlocks */
138
161
  private fun post(block: () -> Unit) = handler.post(block)
139
162
 
@@ -144,13 +167,21 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
144
167
  override var onNavigationError: (NavigationErrorEvent) -> Unit = {}
145
168
  override var onFormattedNavigationError: (FormattedNavigationError?) -> Unit = {}
146
169
 
170
+ // MARK: Gate callbacks (native→JS; set from JS, native CALLS them)
171
+ // Default resolver: only reachable in the init window where the native side has recorded
172
+ // hasResolver=true but JS hasn't yet assigned the real `resolveGate` slot. It DENIES by default
173
+ // (gated=true) so an active gate never serves content during that window — same fail-closed
174
+ // direction as the resolver-error path in [gateDecision]. The static-gate fast path skips it
175
+ // entirely once a resolver-less gate is set.
176
+ override var resolveGate: (request: NativeGateRequest) -> Promise<Promise<GateDecision>> = {
177
+ Promise.resolved(Promise.resolved(GateDecision(gated = true, gate = null)))
178
+ }
179
+ override var onGate: (event: GateEvent) -> Unit = {}
180
+
147
181
  // MARK: Player callbacks
148
182
  override var onPlaybackChanged: (data: Playback) -> Unit = {}
149
- override var onRemoteBookmark: () -> Unit = {}
150
- override var onRemoteDislike: () -> Unit = {}
151
183
  override var onRemoteJumpBackward: (RemoteJumpBackwardEvent) -> Unit = {}
152
184
  override var onRemoteJumpForward: (RemoteJumpForwardEvent) -> Unit = {}
153
- override var onRemoteLike: () -> Unit = {}
154
185
  override var onRemoteNext: () -> Unit = {}
155
186
  override var onRemotePause: () -> Unit = {}
156
187
  override var onChapterMetadata: (chapters: Array<ChapterMetadata>) -> Unit = {}
@@ -162,6 +193,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
162
193
  {}
163
194
  override var onPlaybackPlayingState: (data: PlayingState) -> Unit = {}
164
195
  override var onPlaybackProgressUpdated: (data: PlaybackProgressUpdatedEvent) -> Unit = {}
196
+ override var onPlaybackInterval: () -> Unit = {}
165
197
  override var onPlaybackQueueEnded: (data: PlaybackQueueEndedEvent) -> Unit = {}
166
198
  override var onPlaybackQueueChanged: (queue: Array<Track>) -> Unit = {}
167
199
  override var onPlaybackRepeatModeChanged: (data: RepeatModeChangedEvent) -> Unit = {}
@@ -171,7 +203,6 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
171
203
  override var onRemotePlaySearch: (RemotePlaySearchEvent) -> Unit = {}
172
204
  override var onRemotePrevious: () -> Unit = {}
173
205
  override var onRemoteSeek: (RemoteSeekEvent) -> Unit = {}
174
- override var onRemoteSetRating: (RemoteSetRatingEvent) -> Unit = {}
175
206
  override var onRemoteSkip: (RemoteSkipEvent) -> Unit = {}
176
207
  override var onRemoteStop: () -> Unit = {}
177
208
  override var onOptionsChanged: (Options) -> Unit = {}
@@ -184,14 +215,11 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
184
215
  override var onBatteryOptimizationStatusChanged: (BatteryOptimizationStatusChangedEvent) -> Unit =
185
216
  {}
186
217
  override var onSystemVolumeChanged: (Double) -> Unit = {}
187
- override var onIosOutputChanged: (IosOutput) -> Unit = {}
218
+ override var onOutputChanged: (Output) -> Unit = {}
188
219
 
189
220
  // MARK: Remote handlers
190
- override var handleRemoteBookmark: (() -> Unit)? = null
191
- override var handleRemoteDislike: (() -> Unit)? = null
192
221
  override var handleRemoteJumpBackward: ((RemoteJumpBackwardEvent) -> Unit)? = null
193
222
  override var handleRemoteJumpForward: ((RemoteJumpForwardEvent) -> Unit)? = null
194
- override var handleRemoteLike: (() -> Unit)? = null
195
223
  override var handleRemoteNext: (() -> Unit)? = null
196
224
  override var handleRemotePause: (() -> Unit)? = null
197
225
  override var handleRemotePlay: (() -> Unit)? = null
@@ -232,9 +260,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
232
260
  handler.post { ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleObserver) }
233
261
 
234
262
  // Observe system volume changes
235
- systemVolumeMonitor.setOnVolumeChanged { volume ->
236
- post { onSystemVolumeChanged(volume) }
237
- }
263
+ systemVolumeMonitor.setOnVolumeChanged { volume -> post { onSystemVolumeChanged(volume) } }
264
+
265
+ // Observe current audio output changes (active media route via AudioManager)
266
+ outputMonitor.setOnOutputChanged { output -> post { onOutputChanged(output) } }
267
+ outputMonitor.start()
268
+
269
+ // Observe the car connection (Android Auto / Android Automotive)
270
+ startCarConnectionObserver(context, handler)
238
271
  }
239
272
 
240
273
  // ============================================================================
@@ -244,8 +277,12 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
244
277
  internal fun buildConfig(): BrowserConfig {
245
278
  return BrowserConfig(
246
279
  request = _configuration.request,
280
+ requestResolver = _configuration.requestResolver,
281
+ browse = _configuration.browse,
282
+ browseResolver = _configuration.browseResolver,
247
283
  media = _configuration.media,
248
284
  artwork = _configuration.artwork,
285
+ nowPlayingArtwork = _configuration.nowPlayingArtwork,
249
286
  routes = _configuration.routes,
250
287
  singleTrack = _configuration.singleTrack ?: false,
251
288
  androidControllerOfflineError = _configuration.androidControllerOfflineError ?: true,
@@ -253,34 +290,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
253
290
  }
254
291
 
255
292
  /**
256
- * Returns the artwork configuration for use by CoilBitmapLoader. This provides access to the base
257
- * request config and artwork-specific config.
258
- */
259
- fun getArtworkConfig(): CoilBitmapLoader.ArtworkConfig? {
260
- val artworkConfig = _configuration.artwork ?: return null
261
- val baseConfig =
262
- _configuration.request?.let { RequestConfigBuilder.toRequestConfig(it) }
263
- ?: RequestConfig(null, null, null, null, null, null, null, null)
264
- return CoilBitmapLoader.ArtworkConfig(baseConfig, artworkConfig)
265
- }
266
-
267
- /**
268
- * Creates a request config for media URL transformation by merging base and media configs.
269
- * Returns null if no media configuration is set.
293
+ * Media URL transformation for [com.audiobrowser.player.TransformingDataSource]. Resolution lives
294
+ * in [resolveMediaUrl] (browser/BrowserUrlResolution.kt); this shell owns only the blocking
295
+ * bridge: it runs on ExoPlayer's IO thread (TransformingDataSource.open), so blocking here is
296
+ * safe and intentional.
270
297
  */
271
298
  fun getMediaRequestConfig(originalUrl: String): MediaRequestConfig? {
272
- val mediaConfig = _configuration.media ?: return null
273
-
274
299
  return try {
275
- // Create base request config with the original URL as path
276
- val baseConfig =
277
- _configuration.request?.let { RequestConfigBuilder.toRequestConfig(it) }
278
- ?: RequestConfig(null, null, null, null, null, null, null, null)
279
- val urlRequestConfig = RequestConfig(null, originalUrl, null, null, null, null, null, null)
280
- val mergedBaseConfig = RequestConfigBuilder.mergeConfig(baseConfig, urlRequestConfig)
281
-
282
- // Apply media transformation
283
- runBlocking { RequestConfigBuilder.mergeConfig(mergedBaseConfig, mediaConfig) }
300
+ runBlocking { browserManager.resolveMediaUrl(originalUrl) }
284
301
  } catch (e: Exception) {
285
302
  Timber.e(e, "Failed to transform media URL: $originalUrl")
286
303
  null
@@ -292,10 +309,15 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
292
309
  return _configuration.routes?.any { it.path != BrowserManager.SEARCH_ROUTE_PATH } == true
293
310
  }
294
311
 
295
- private fun getDefaultPath(): String? {
312
+ // Suspending on purpose: queryTabs() resolves tabs by invoking JS callbacks,
313
+ // which can only run on the JS thread. Callers MUST invoke this from a
314
+ // coroutine (off the JS thread) — never via runBlocking on a synchronous Nitro
315
+ // setter, which runs on the JS thread and would deadlock (the JS thread blocks
316
+ // waiting for callbacks that need that same thread to run).
317
+ private suspend fun getDefaultPath(): String? {
296
318
  return try {
297
319
  browserManager.config = buildConfig()
298
- val tabs = runBlocking { browserManager.queryTabs() }
320
+ val tabs = browserManager.queryTabs()
299
321
  if (tabs.isNotEmpty()) {
300
322
  Timber.d("Using first tab as default path: ${tabs[0].url}")
301
323
  tabs[0].url
@@ -303,6 +325,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
303
325
  Timber.d("Using root path as default: /")
304
326
  "/"
305
327
  }
328
+ } catch (e: CancellationException) {
329
+ throw e // Rethrow so cooperative cancellation isn't swallowed
306
330
  } catch (e: Exception) {
307
331
  Timber.e(e, "Failed to get default path, falling back to /")
308
332
  "/"
@@ -318,23 +342,25 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
318
342
  set(value) {
319
343
  if (hasValidConfiguration()) {
320
344
  browserManager.config = buildConfig()
321
- (value ?: getDefaultPath())?.let { path ->
322
- clearNavigationError()
323
-
324
- // Cancel previous navigation to avoid race conditions
325
- navigationJob?.cancel()
326
-
327
- navigationJob =
328
- mainScope.launch {
329
- try {
330
- browserManager.navigate(path)
331
- } catch (e: CancellationException) {
332
- throw e // Rethrow to properly cancel
333
- } catch (e: Exception) {
334
- handleBrowserException(e, path, "setting path: $path")
335
- }
345
+ clearNavigationError()
346
+
347
+ // Cancel previous navigation to avoid race conditions
348
+ navigationJob?.cancel()
349
+
350
+ // Resolve the default path inside the coroutine (getDefaultPath queries
351
+ // tabs via JS callbacks) so it never blocks the JS thread this setter
352
+ // runs on.
353
+ navigationJob =
354
+ mainScope.launch {
355
+ val path = value ?: getDefaultPath() ?: return@launch
356
+ try {
357
+ browserManager.navigate(path)
358
+ } catch (e: CancellationException) {
359
+ throw e // Rethrow to properly cancel
360
+ } catch (e: Exception) {
361
+ handleBrowserException(e, path, "setting path: $path")
336
362
  }
337
- }
363
+ }
338
364
  }
339
365
  }
340
366
 
@@ -352,45 +378,54 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
352
378
  // This allows Android Auto to start browsing content
353
379
  connectedService?.player?.notifyBrowserConfigurationReady()
354
380
 
355
- // Navigate to initial path or default to first tab
356
- (value.path ?: getDefaultPath())?.let { path ->
357
- clearNavigationError()
358
-
359
- // Cancel previous navigation to avoid race conditions
360
- navigationJob?.cancel()
361
-
362
- navigationJob =
363
- mainScope.launch {
364
- try {
365
- browserManager.navigate(path)
366
- } catch (e: CancellationException) {
367
- throw e // Rethrow to properly cancel
368
- } catch (e: Exception) {
369
- handleBrowserException(e, path, "setting configuration path: $path")
370
- }
381
+ clearNavigationError()
382
+
383
+ // Cancel previous navigation to avoid race conditions
384
+ navigationJob?.cancel()
385
+
386
+ // Navigate to the initial path, or default to the first tab. The default
387
+ // path is resolved inside the coroutine (getDefaultPath queries tabs via
388
+ // JS callbacks) so it never blocks the JS thread this setter runs on —
389
+ // doing it synchronously here via runBlocking deadlocked: the JS callbacks
390
+ // can't run because the JS thread is blocked waiting for them.
391
+ navigationJob =
392
+ mainScope.launch {
393
+ val path = value.path ?: getDefaultPath() ?: return@launch
394
+ try {
395
+ browserManager.navigate(path)
396
+ } catch (e: CancellationException) {
397
+ throw e // Rethrow to properly cancel
398
+ } catch (e: Exception) {
399
+ handleBrowserException(e, path, "setting configuration path: $path")
371
400
  }
372
- }
401
+ }
373
402
  }
374
403
 
375
404
  private var navigationError: NavigationError? = null
376
405
  private var formattedNavigationError: FormattedNavigationError? = null
377
406
 
378
407
  override fun getNavigationError(): NavigationError? = navigationError
408
+
379
409
  override fun getFormattedNavigationError(): FormattedNavigationError? = formattedNavigationError
380
410
 
381
411
  /** Creates a default formatted error from a NavigationError */
382
412
  private fun defaultFormattedError(error: NavigationError): FormattedNavigationError {
383
- val title = when (error.code) {
384
- NavigationErrorType.CONTENT_NOT_FOUND -> "Content Not Found"
385
- NavigationErrorType.NETWORK_ERROR -> "Network Error"
386
- NavigationErrorType.HTTP_ERROR -> {
387
- // Use system-localized HTTP status text (e.g., "Not Found", "Service Unavailable")
388
- error.statusCode?.let { httpStatusText(it.toInt()) } ?: "Server Error"
413
+ val title =
414
+ when (error.code) {
415
+ NavigationErrorType.CONTENT_NOT_FOUND -> "Content Not Found"
416
+ NavigationErrorType.NETWORK_ERROR -> "Network Error"
417
+ NavigationErrorType.HTTP_ERROR -> {
418
+ // Use system-localized HTTP status text (e.g., "Not Found", "Service Unavailable")
419
+ error.statusCode?.let { httpStatusText(it.toInt()) } ?: "Server Error"
420
+ }
421
+ NavigationErrorType.CALLBACK_ERROR -> "Error"
422
+ NavigationErrorType.UNKNOWN_ERROR -> "Error"
423
+ // Not a failure — a container that resolved with no children. Neutral copy. See ADR 0001.
424
+ NavigationErrorType.EMPTY_CONTENT -> "Nothing here"
425
+ NavigationErrorType.TIMEOUT -> "Couldn't load"
389
426
  }
390
- NavigationErrorType.CALLBACK_ERROR -> "Error"
391
- NavigationErrorType.UNKNOWN_ERROR -> "Error"
392
- }
393
- return FormattedNavigationError(title, error.message)
427
+ // Omit an empty message so it renders as title-only (e.g. the empty-content case).
428
+ return FormattedNavigationError(title, error.message.takeIf { it.isNotEmpty() })
394
429
  }
395
430
 
396
431
  /** Returns localized HTTP status text for the given status code */
@@ -457,19 +492,35 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
457
492
  }
458
493
  is NetworkException -> {
459
494
  Timber.e(e, "Network error $logContext")
460
- setNavigationError(NavigationErrorType.NETWORK_ERROR, e.message ?: "Network request failed", path)
495
+ setNavigationError(
496
+ NavigationErrorType.NETWORK_ERROR,
497
+ e.message ?: "Network request failed",
498
+ path,
499
+ )
461
500
  }
462
501
  is ContentNotFoundException -> {
463
502
  Timber.e(e, "Content not found $logContext")
464
- setNavigationError(NavigationErrorType.CONTENT_NOT_FOUND, e.message ?: "Content not found", path)
503
+ setNavigationError(
504
+ NavigationErrorType.CONTENT_NOT_FOUND,
505
+ e.message ?: "Content not found",
506
+ path,
507
+ )
465
508
  }
466
509
  is CallbackException -> {
467
510
  Timber.e(e, "Callback error $logContext")
468
- setNavigationError(NavigationErrorType.CALLBACK_ERROR, e.message ?: "An error occurred", path)
511
+ setNavigationError(
512
+ NavigationErrorType.CALLBACK_ERROR,
513
+ e.message ?: "An error occurred",
514
+ path,
515
+ )
469
516
  }
470
517
  else -> {
471
518
  Timber.e(e, "Unexpected error $logContext")
472
- setNavigationError(NavigationErrorType.UNKNOWN_ERROR, e.message ?: "An unexpected error occurred", path)
519
+ setNavigationError(
520
+ NavigationErrorType.UNKNOWN_ERROR,
521
+ e.message ?: "An unexpected error occurred",
522
+ path,
523
+ )
473
524
  }
474
525
  }
475
526
  }
@@ -529,10 +580,17 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
529
580
  val index = player.tracks.indexOfFirst { it.src == trackId }
530
581
  if (index >= 0) {
531
582
  Timber.d("Queue already from $parentPath, skipping to index $index")
532
- handleTrackLoad(_configuration.handleTrackLoad, track, player.tracks, index.toDouble(), intercepted = {}, defaultBehavior = {
533
- player.skipTo(index)
534
- player.play()
535
- })
583
+ handleTrackLoad(
584
+ _configuration.handleTrackLoad,
585
+ track,
586
+ player.tracks,
587
+ index.toDouble(),
588
+ intercepted = {},
589
+ defaultBehavior = {
590
+ player.skipTo(index)
591
+ player.play()
592
+ },
593
+ )
536
594
  return@launch
537
595
  }
538
596
  }
@@ -545,17 +603,31 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
545
603
  Timber.d(
546
604
  "Loading expanded queue: ${tracks.size} tracks, starting at index $startIndex"
547
605
  )
548
- handleTrackLoad(_configuration.handleTrackLoad, track, tracks, startIndex.toDouble(), intercepted = {}, defaultBehavior = {
549
- // Replace queue and seek to selected track
550
- // Use internal player methods directly to avoid blocking on main thread
551
- player.setQueue(tracks, startIndex, sourcePath = parentPath)
552
- player.play()
553
- })
606
+ handleTrackLoad(
607
+ _configuration.handleTrackLoad,
608
+ track,
609
+ tracks,
610
+ startIndex.toDouble(),
611
+ intercepted = {},
612
+ defaultBehavior = {
613
+ // Replace queue and seek to selected track
614
+ // Use internal player methods directly to avoid blocking on main thread
615
+ player.setQueue(tracks, startIndex, sourcePath = parentPath)
616
+ player.play()
617
+ },
618
+ )
554
619
  return@launch
555
620
  } else {
556
621
  // Fallback: just load the single track
557
622
  Timber.w("Queue expansion failed, loading single track")
558
- handleTrackLoad(_configuration.handleTrackLoad, track, arrayOf(track), 0.0, intercepted = {}, defaultBehavior = { player.load(track) })
623
+ handleTrackLoad(
624
+ _configuration.handleTrackLoad,
625
+ track,
626
+ arrayOf(track),
627
+ 0.0,
628
+ intercepted = {},
629
+ defaultBehavior = { player.load(track) },
630
+ )
559
631
  }
560
632
  }
561
633
  // Navigate to browsable track to show browsing UI
@@ -566,7 +638,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
566
638
  // If track is playable (has src), load it into player
567
639
  track.src != null -> {
568
640
  Timber.d("Loading playable track into player: ${track.title}")
569
- handleTrackLoad(_configuration.handleTrackLoad, track, arrayOf(track), 0.0, intercepted = {}, defaultBehavior = { player.load(track) })
641
+ handleTrackLoad(
642
+ _configuration.handleTrackLoad,
643
+ track,
644
+ arrayOf(track),
645
+ 0.0,
646
+ intercepted = {},
647
+ defaultBehavior = { player.load(track) },
648
+ )
570
649
  }
571
650
  else -> {
572
651
  throw IllegalArgumentException("Track must have either an 'url' or an 'src' property")
@@ -607,20 +686,125 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
607
686
  }
608
687
  }
609
688
 
689
+ override fun invalidateAllContent() {
690
+ Timber.d("Invalidating all content")
691
+
692
+ // Clear all cached content so every path re-fetches fresh data
693
+ browserManager.clearContentCache()
694
+
695
+ // Notify external media controllers (Android Auto) to refresh subscribed paths
696
+ connectedService?.player?.invalidateAllContent()
697
+
698
+ // Refresh whatever the browser is currently viewing
699
+ mainScope.launch { browserManager.refresh() }
700
+ }
701
+
610
702
  override fun setFavorites(favorites: Array<String>) {
611
703
  browserManager.setFavorites(favorites.toList())
612
704
  }
613
705
 
706
+ // MARK: Gate
707
+
708
+ /** A gate decision for one request: whether to gate, and which chrome to render. */
709
+ data class GateOutcome(val gated: Boolean, val chrome: Gate?)
710
+
711
+ // The minimal chrome rendered when a gate is active but no override or stored
712
+ // default chrome exists (the resolver-only setGate overload).
713
+ private val builtInGate = Gate("Unavailable", null)
714
+
715
+ /**
716
+ * The three gate fields as one immutable triple so [gateDecision] reads them as a unit. Written
717
+ * from the JS thread, read from the Media3 application thread (the enforcement sites in
718
+ * MediaSessionCallback consult it) — hence the single field is @Volatile.
719
+ *
720
+ * Folding the three former @Volatile fields into one reference is what makes the read atomic:
721
+ *
722
+ * @Volatile gives per-field visibility but NO atomicity across a compound read, so reading three
723
+ * separate volatiles in [gateDecision] could interleave with a concurrent [clearGate] and yield
724
+ * a torn `(active=true, chrome=null)` state that crashed the force-unwrap at the serve sites.
725
+ * One volatile reference read once cannot tear.
726
+ */
727
+ private data class GateState(val active: Boolean, val chrome: Gate?, val hasResolver: Boolean)
728
+
729
+ @Volatile private var gateState = GateState(active = false, chrome = null, hasResolver = false)
730
+
731
+ override fun setGate(gate: Gate?, hasResolver: Boolean) {
732
+ // Single atomic assignment — readers never see a half-updated triple.
733
+ gateState = GateState(active = true, chrome = gate, hasResolver = hasResolver)
734
+ // Re-query every subscribed parent so a connected controller (Android
735
+ // Auto) swaps its lists for the gate tile without reconnecting. Notify
736
+ // only — the content cache stays warm for when the gate clears.
737
+ connectedService?.player?.invalidateAllContent()
738
+ }
739
+
740
+ override fun clearGate() {
741
+ if (!gateState.active) return
742
+ // Single atomic assignment — readers never see a half-cleared triple.
743
+ gateState = GateState(active = false, chrome = null, hasResolver = false)
744
+ connectedService?.player?.invalidateAllContent()
745
+ }
746
+
747
+ /**
748
+ * The single gate choke point. Each enforcement site (browse / search) calls this for the current
749
+ * request and serves the gate chrome when [GateOutcome.gated] is true. Structurally similar to
750
+ * the iOS `gateDecision(for:)` helper, but note the concurrency model differs: iOS confines gate
751
+ * state to `@MainActor`; here the consistent read comes from snapshotting the single [gateState]
752
+ * reference once (see [GateState]).
753
+ * - No active gate → allow.
754
+ * - Active gate, no resolver → static fast path: gate with the stored default chrome (or built-in
755
+ * if none), no JS hop.
756
+ * - Active gate with a resolver → ask JS per request. A resolver that throws / rejects / times
757
+ * out FAILS CLOSED by design: while a gate is active the consumer has declared content blocked,
758
+ * so the only safe fallback when we cannot compute the per-request decision is the gate's own
759
+ * chrome — never the content. A *successful* `gated:false` still allows. Chrome order on a
760
+ * gated decision: override → stored default → built-in.
761
+ *
762
+ * Every gated return guards chrome against null (`?: builtInGate`) so a gated outcome always
763
+ * carries a renderable chrome — the serve sites force-unwrap it.
764
+ */
765
+ suspend fun gateDecision(request: NativeGateRequest): GateOutcome {
766
+ // Read the whole gate triple once; a single volatile reference can't tear (see [GateState]).
767
+ val s = gateState
768
+ if (!s.active) return GateOutcome(false, null)
769
+ if (!s.hasResolver) return GateOutcome(true, s.chrome ?: builtInGate) // static fast path
770
+ val decision =
771
+ runCatching { resolveGate(request).await().await() }.getOrNull()
772
+ ?: return GateOutcome(true, s.chrome ?: builtInGate) // fail CLOSED on resolver error
773
+ if (!decision.gated) return GateOutcome(false, null) // explicit allow
774
+ return GateOutcome(true, decision.gate ?: s.chrome ?: builtInGate)
775
+ }
776
+
777
+ // MARK: Car connection (Android Auto / Android Automotive)
778
+
779
+ override fun isCarConnected(): Boolean = carConnected
780
+
781
+ override var onCarConnectedChanged: (Boolean) -> Unit = {}
782
+ set(value) {
783
+ field = value
784
+ carConnectionTarget = value
785
+ // Immediately notify current state (the car may have connected before
786
+ // this JS runtime subscribed) — mirrors the iOS behavior.
787
+ value(carConnected)
788
+ }
789
+
614
790
  // ============================================================================
615
791
  // MARK: Player Setup and Options
616
792
  // ============================================================================
617
793
 
618
- override fun setupPlayer(options: PartialSetupPlayerOptions): Promise<Unit> {
794
+ override fun setupPlayer(options: NativeSetupPlayerOptions): Promise<Unit> {
619
795
  return Promise.async(mainScope) {
620
796
  setupOptions.update(options)
797
+ // The bundled runtime options and initial state are part of the atomic launch
798
+ // description: stage them here, apply them together with the engine below (or at
799
+ // service connect). Last-write-wins with their imperative counterparts.
800
+ options.options?.let { updateOptions.updateFromBridge(it) }
801
+ options.repeatMode?.let { pendingRepeatMode = it }
802
+ options.playWhenReady?.let { pendingPlayWhenReady = it }
621
803
 
622
804
  connectedService?.let {
805
+ it.player.applyOptions(updateOptions)
623
806
  it.player.setup(setupOptions)
807
+ applyPendingPlayerState(it.player)
624
808
  return@async
625
809
  }
626
810
 
@@ -652,8 +836,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
652
836
  connectedService?.let { player.applyOptions(updateOptions) }
653
837
  }
654
838
 
655
- override fun getOptions(): UpdateOptions {
656
- return player.getOptions().toNitro()
839
+ override fun getOptions(): Options {
840
+ // The holder is the source of truth whether or not the player exists yet — updateOptions
841
+ // merges into it and setup/connect applies it to the player.
842
+ return updateOptions.toNitro()
657
843
  }
658
844
 
659
845
  // ============================================================================
@@ -668,6 +854,9 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
668
854
  player.stop()
669
855
  delay(300) // Allow playback to stop
670
856
  player.clear()
857
+ // clear() while IDLE emits no engine event, so the state machine never
858
+ // leaves STOPPED — a reset queue is "nothing loaded".
859
+ player.setPlaybackState(PlaybackState.NONE)
671
860
  }
672
861
 
673
862
  override fun play() = runBlockingOnMain { player.play() }
@@ -679,10 +868,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
679
868
  override fun stop() = runBlockingOnMain { player.stop() }
680
869
 
681
870
  override fun setPlayWhenReady(playWhenReady: Boolean) = runBlockingOnMain {
682
- player.playWhenReady = playWhenReady
871
+ // Pre-setup this stages the intent for the player to come up with — never throws.
872
+ connectedService?.player?.let { it.playWhenReady = playWhenReady }
873
+ ?: run { pendingPlayWhenReady = playWhenReady }
683
874
  }
684
875
 
685
- override fun getPlayWhenReady(): Boolean = runBlockingOnMain { player.playWhenReady }
876
+ override fun getPlayWhenReady(): Boolean = runBlockingOnMain {
877
+ connectedService?.player?.playWhenReady ?: pendingPlayWhenReady ?: false
878
+ }
686
879
 
687
880
  override fun seekTo(position: Double) = runBlockingOnMain {
688
881
  player.seekTo((position * 1000).toLong(), TimeUnit.MILLISECONDS)
@@ -692,6 +885,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
692
885
  player.seekBy((offset * 1000).toLong(), TimeUnit.MILLISECONDS)
693
886
  }
694
887
 
888
+ override fun seekToLiveEdge() = runBlockingOnMain { player.seekToLiveEdge() }
889
+
695
890
  override fun setVolume(level: Double) = runBlockingOnMain { player.volume = level.toFloat() }
696
891
 
697
892
  override fun getVolume(): Double = runBlockingOnMain { player.volume.toDouble() }
@@ -712,9 +907,14 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
712
907
 
713
908
  override fun getPlayingState(): PlayingState = runBlockingOnMain { player.getPlayingState() }
714
909
 
715
- override fun getRepeatMode(): RepeatMode = runBlockingOnMain { player.repeatMode }
910
+ override fun getRepeatMode(): RepeatMode = runBlockingOnMain {
911
+ connectedService?.player?.repeatMode ?: pendingRepeatMode ?: RepeatMode.OFF
912
+ }
716
913
 
717
- override fun setRepeatMode(mode: RepeatMode) = runBlockingOnMain { player.repeatMode = mode }
914
+ override fun setRepeatMode(mode: RepeatMode) = runBlockingOnMain {
915
+ // Pre-setup this stages the mode for the player to come up with — never throws.
916
+ connectedService?.player?.let { it.repeatMode = mode } ?: run { pendingRepeatMode = mode }
917
+ }
718
918
 
719
919
  override fun getShuffleEnabled(): Boolean = runBlockingOnMain { player.shuffleMode }
720
920
 
@@ -722,6 +922,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
722
922
  player.shuffleMode = enabled
723
923
  }
724
924
 
925
+ override fun setPlaybackIntervalEnabled(enabled: Boolean) = runBlockingOnMain {
926
+ player.setPlaybackIntervalEnabled(enabled)
927
+ }
928
+
725
929
  override fun getPlaybackError(): PlaybackError? = runBlockingOnMain { player.playbackError }
726
930
 
727
931
  override fun retry() = runBlockingOnMain { player.prepare() }
@@ -770,9 +974,9 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
770
974
  }
771
975
  }
772
976
 
773
- override fun setQueue(tracks: Array<Track>, startIndex: Double?, startPositionMs: Double?) =
977
+ override fun setQueue(tracks: Array<Track>, startIndex: Double?, startPosition: Double?) =
774
978
  runBlockingOnMain {
775
- player.setQueue(tracks, startIndex?.toInt() ?: 0, startPositionMs?.toLong() ?: 0)
979
+ player.setQueue(tracks, startIndex?.toInt() ?: 0, ((startPosition ?: 0.0) * 1000).toLong())
776
980
  }
777
981
 
778
982
  override fun setActiveTrackFavorited(favorited: Boolean) = runBlockingOnMain {
@@ -804,10 +1008,20 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
804
1008
  // ============================================================================
805
1009
 
806
1010
  override fun updateNowPlaying(update: NowPlayingUpdate?) = runBlockingOnMain {
807
- player.updateNowPlaying(update)
1011
+ player.nowPlaying.updateNowPlaying(update)
808
1012
  }
809
1013
 
810
- override fun getNowPlaying(): NowPlayingMetadata? = runBlockingOnMain { player.getNowPlaying() }
1014
+ override fun flashNowPlaying(update: NowPlayingUpdate, durationMs: Double) = runBlockingOnMain {
1015
+ player.nowPlaying.flashNowPlaying(update, durationMs)
1016
+ }
1017
+
1018
+ override fun clearNowPlayingFlash() = runBlockingOnMain {
1019
+ player.nowPlaying.clearNowPlayingFlash()
1020
+ }
1021
+
1022
+ override fun getNowPlaying(): NowPlayingMetadata? = runBlockingOnMain {
1023
+ player.nowPlaying.getNowPlaying()
1024
+ }
811
1025
 
812
1026
  // ============================================================================
813
1027
  // MARK: Network Connectivity
@@ -824,13 +1038,15 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
824
1038
  override fun setSystemVolume(volume: Double) = systemVolumeMonitor.setVolume(volume)
825
1039
 
826
1040
  // ============================================================================
827
- // MARK: External Audio Output (iOS only, stub on Android)
1041
+ // MARK: External Audio Output
828
1042
  // ============================================================================
829
1043
 
830
- override fun getIosOutput(): IosOutput? = null
1044
+ override fun getOutput(): Output? = outputMonitor.current
1045
+
1046
+ override fun supportsOutputSwitcher(): Boolean = OutputSwitcher.isSupported()
831
1047
 
832
- override fun openIosOutputPicker() {
833
- // No-op on Android
1048
+ override fun openOutputPicker() {
1049
+ OutputSwitcher.open(context)
834
1050
  }
835
1051
 
836
1052
  // ============================================================================
@@ -838,34 +1054,34 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
838
1054
  // ============================================================================
839
1055
 
840
1056
  override fun getEqualizerSettings(): EqualizerSettings? = runBlockingOnMain {
841
- player.getEqualizerSettings()
1057
+ player.equalizer.getSettings()
842
1058
  }
843
1059
 
844
1060
  override fun setEqualizerEnabled(enabled: Boolean) = runBlockingOnMain {
845
- player.setEqualizerEnabled(enabled)
1061
+ player.equalizer.setEnabled(enabled)
846
1062
  }
847
1063
 
848
1064
  override fun setEqualizerPreset(preset: String) = runBlockingOnMain {
849
- player.setEqualizerPreset(preset)
1065
+ player.equalizer.setPreset(preset)
850
1066
  }
851
1067
 
852
1068
  override fun setEqualizerLevels(levels: DoubleArray) = runBlockingOnMain {
853
- player.setEqualizerLevels(levels)
1069
+ player.equalizer.setLevels(levels)
854
1070
  }
855
1071
 
856
1072
  // ============================================================================
857
1073
  // MARK: Sleep Timer
858
1074
  // ============================================================================
859
1075
 
860
- override fun getSleepTimer(): SleepTimer = runBlockingOnMain { player.getSleepTimer() }
1076
+ override fun getSleepTimer(): SleepTimer = runBlockingOnMain { player.sleepTimer.get() }
861
1077
 
862
- override fun setSleepTimer(seconds: Double) = runBlockingOnMain { player.setSleepTimer(seconds) }
863
-
864
- override fun setSleepTimerToEndOfTrack() = runBlockingOnMain {
865
- player.setSleepTimerToEndOfTrack()
1078
+ override fun setSleepTimer(seconds: Double, fadeDuration: Double?) = runBlockingOnMain {
1079
+ player.sleepTimer.setAfter(seconds, fadeDuration)
866
1080
  }
867
1081
 
868
- override fun clearSleepTimer(): Boolean = runBlockingOnMain { player.clearSleepTimer() }
1082
+ override fun setSleepTimerToEndOfTrack() = runBlockingOnMain { player.sleepTimer.setEndOfTrack() }
1083
+
1084
+ override fun clearSleepTimer(): Boolean = runBlockingOnMain { player.sleepTimer.clear() }
869
1085
 
870
1086
  // ============================================================================
871
1087
  // MARK: Battery Optimization (Android only)
@@ -945,6 +1161,7 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
945
1161
  player.setCallbacks(callbacks)
946
1162
  player.applyOptions(updateOptions)
947
1163
  player.setup(setupOptions)
1164
+ applyPendingPlayerState(player)
948
1165
  // Start observing network connectivity changes
949
1166
  player.observeNetworkConnectivity(mainScope)
950
1167
  // Set browser reference for media URL transformation
@@ -974,6 +1191,21 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
974
1191
  }
975
1192
  }
976
1193
 
1194
+ /**
1195
+ * Applies initial player state staged before the player existed. Consumed on apply so a later
1196
+ * re-setup doesn't replay stale state.
1197
+ */
1198
+ private fun applyPendingPlayerState(player: Player) {
1199
+ pendingRepeatMode?.let {
1200
+ player.repeatMode = it
1201
+ pendingRepeatMode = null
1202
+ }
1203
+ pendingPlayWhenReady?.let {
1204
+ player.playWhenReady = it
1205
+ pendingPlayWhenReady = null
1206
+ }
1207
+ }
1208
+
977
1209
  override fun onServiceDisconnected(name: ComponentName) {
978
1210
  mainScope.coroutineContext.cancelChildren()
979
1211
  mediaBrowserFuture = null
@@ -1017,6 +1249,10 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1017
1249
  post { this@AudioBrowser.onPlaybackProgressUpdated(event) }
1018
1250
  }
1019
1251
 
1252
+ override fun onPlaybackInterval() {
1253
+ post { this@AudioBrowser.onPlaybackInterval() }
1254
+ }
1255
+
1020
1256
  override fun onPlaybackPlayWhenReadyChanged(event: PlaybackPlayWhenReadyChangedEvent) {
1021
1257
  post { this@AudioBrowser.onPlaybackPlayWhenReadyChanged(event) }
1022
1258
  }
@@ -1166,12 +1402,8 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1166
1402
  return handled
1167
1403
  }
1168
1404
 
1169
- override fun onRemoteSetRating(event: RemoteSetRatingEvent) {
1170
- post { this@AudioBrowser.onRemoteSetRating(event) }
1171
- }
1172
-
1173
1405
  override fun onOptionsChanged(options: PlayerUpdateOptions) {
1174
- // Not currently emitted to JS
1406
+ post { this@AudioBrowser.onOptionsChanged(options.toNitro()) }
1175
1407
  }
1176
1408
 
1177
1409
  override fun onFavoriteChanged(event: FavoriteChangedEvent) {
@@ -1194,4 +1426,32 @@ class AudioBrowser : HybridAudioBrowserSpec(), ServiceConnection {
1194
1426
  post { this@AudioBrowser.onSleepTimerChanged(timer) }
1195
1427
  }
1196
1428
  }
1429
+
1430
+ companion object {
1431
+ // Process-wide car-connection state, from the androidx.car.app
1432
+ // CarConnection provider — the documented Android Auto / Automotive
1433
+ // connection signal, served by the car system app. Observed ONCE per
1434
+ // process: AudioBrowser instances are re-created on a JS reload, and a
1435
+ // per-instance observeForever would leak each one. The latest instance's
1436
+ // callback registers as the single notification target (mirrors the iOS
1437
+ // static + shared-instance pattern).
1438
+ @Volatile private var carConnected = false
1439
+ @Volatile private var carConnectionTarget: ((Boolean) -> Unit)? = null
1440
+ private var carConnectionObserverStarted = false
1441
+
1442
+ private fun startCarConnectionObserver(context: Context, handler: Handler) {
1443
+ // Posted to main: LiveData observation is main-thread only, and the
1444
+ // single-start guard is then only ever touched from one thread.
1445
+ handler.post {
1446
+ if (carConnectionObserverStarted) return@post
1447
+ carConnectionObserverStarted = true
1448
+ CarConnection(context.applicationContext).type.observeForever { type ->
1449
+ val connected = type != CarConnection.CONNECTION_TYPE_NOT_CONNECTED
1450
+ if (connected == carConnected) return@observeForever
1451
+ carConnected = connected
1452
+ carConnectionTarget?.invoke(connected)
1453
+ }
1454
+ }
1455
+ }
1456
+ }
1197
1457
  }