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
@@ -1,35 +1,41 @@
1
1
  package com.audiobrowser.player
2
2
 
3
3
  import android.os.Bundle
4
- import androidx.media3.common.HeartRating
5
4
  import androidx.media3.common.MediaItem
6
5
  import androidx.media3.common.MediaMetadata
7
6
  import androidx.media3.common.Rating
8
7
  import androidx.media3.session.LibraryResult
8
+ import androidx.media3.session.MediaConstants
9
9
  import androidx.media3.session.MediaLibraryService
10
10
  import androidx.media3.session.MediaSession
11
- import androidx.media3.session.MediaConstants
12
11
  import androidx.media3.session.SessionCommand
13
12
  import androidx.media3.session.SessionError
14
13
  import androidx.media3.session.SessionResult
15
- import com.audiobrowser.AudioBrowser
16
14
  import com.audiobrowser.browser.handleTrackLoad
15
+ import com.audiobrowser.extension.toTrack
17
16
  import com.audiobrowser.util.BrowserPathHelper
18
- import com.audiobrowser.util.RatingFactory
19
- import com.audiobrowser.util.ResolvedTrackFactory
17
+ import com.audiobrowser.util.RatingFavorites
20
18
  import com.audiobrowser.util.TrackFactory
21
- import com.margelo.nitro.audiobrowser.Track
22
19
  import com.google.common.collect.ImmutableList
23
20
  import com.google.common.util.concurrent.Futures
24
21
  import com.google.common.util.concurrent.ListenableFuture
25
- import com.margelo.nitro.audiobrowser.FavoriteChangedEvent
22
+ import com.margelo.nitro.audiobrowser.Gate
23
+ import com.margelo.nitro.audiobrowser.GateEvent
24
+ import com.margelo.nitro.audiobrowser.GateReason
25
+ import com.margelo.nitro.audiobrowser.MediaReference
26
+ import com.margelo.nitro.audiobrowser.NativeGateRequest
26
27
  import com.margelo.nitro.audiobrowser.NotificationButtonLayout
27
28
  import com.margelo.nitro.audiobrowser.PlayerCapabilities
28
- import com.margelo.nitro.audiobrowser.RemoteSetRatingEvent
29
+ import com.margelo.nitro.audiobrowser.SearchParams
30
+ import com.margelo.nitro.audiobrowser.Track
31
+ import kotlinx.coroutines.CancellationException
29
32
  import kotlinx.coroutines.CoroutineScope
30
33
  import kotlinx.coroutines.Dispatchers
31
34
  import kotlinx.coroutines.SupervisorJob
35
+ import kotlinx.coroutines.TimeoutCancellationException
36
+ import kotlinx.coroutines.cancel
32
37
  import kotlinx.coroutines.guava.future
38
+ import kotlinx.coroutines.launch
33
39
  import kotlinx.coroutines.withContext
34
40
  import timber.log.Timber
35
41
 
@@ -42,7 +48,9 @@ class MediaSessionCallback(private val player: Player) :
42
48
  internal val commandManager = MediaSessionCommandManager()
43
49
  private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
44
50
 
45
- // Track which controllers are subscribed to which media IDs
51
+ // Track which controllers are subscribed to which media IDs.
52
+ // Mutated on the Media3 application thread (onSubscribe/onUnsubscribe) but read from other
53
+ // threads (network observer, JS-triggered notifies) — guard all access with synchronized(this).
46
54
  private val parentIdSubscriptions =
47
55
  mutableMapOf<String, MutableSet<MediaSession.ControllerInfo>>()
48
56
  private var mediaLibrarySession: MediaLibraryService.MediaLibrarySession? = null
@@ -51,7 +59,8 @@ class MediaSessionCallback(private val player: Player) :
51
59
  * Recommended artwork size in pixels from the connected media browser (e.g., Android Auto).
52
60
  * Updated when onGetLibraryRoot is called with EXTRAS_KEY_MEDIA_ART_SIZE_PIXELS hint.
53
61
  */
54
- @Volatile var artworkSizeHintPixels: Int? = null
62
+ @Volatile
63
+ var artworkSizeHintPixels: Int? = null
55
64
  private set
56
65
 
57
66
  init {
@@ -72,22 +81,81 @@ class MediaSessionCallback(private val player: Player) :
72
81
  }
73
82
 
74
83
  /** Creates an offline error MediaItem. */
75
- private fun createOfflineMediaItem(): MediaItem {
76
- val errorTitle = player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error)
77
- val errorSubtitle =
78
- player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error_subtitle)
79
- return MediaItem.Builder()
80
- .setMediaId(BrowserPathHelper.OFFLINE_PATH)
84
+ private fun createOfflineMediaItem(): MediaItem =
85
+ createErrorMediaItem(
86
+ mediaId = BrowserPathHelper.OFFLINE_PATH,
87
+ title = player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error),
88
+ subtitle =
89
+ player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error_subtitle),
90
+ )
91
+
92
+ /**
93
+ * Builds a non-browsable, non-playable [MediaItem] used to surface an error inside an Android
94
+ * Auto / AAOS browse list. Rendered as a greyed-out, non-interactive tile, which is the only
95
+ * side-effect-free way to communicate a browse failure to legacy controllers (Media3 drops
96
+ * [LibraryResult.ofError] on the legacy browse bridge, leaving an empty "No items" screen).
97
+ */
98
+ private fun createErrorMediaItem(mediaId: String, title: String, subtitle: String): MediaItem =
99
+ MediaItem.Builder()
100
+ .setMediaId(mediaId)
81
101
  .setMediaMetadata(
82
102
  MediaMetadata.Builder()
83
- .setTitle(errorTitle)
84
- .setSubtitle(errorSubtitle)
103
+ .setTitle(title)
104
+ .setSubtitle(subtitle)
105
+ // Non-browsable, non-playable. Android Auto ignores these flags and still drills into a
106
+ // tapped tile, but onGetChildren returns an empty list for the sentinel paths so it's a
107
+ // harmless "No Items" dead-end rather than an endless stack of error pages.
85
108
  .setIsBrowsable(false)
86
109
  .setIsPlayable(false)
87
110
  .build()
88
111
  )
89
112
  .build()
90
- }
113
+
114
+ /**
115
+ * Builds the gate tile from a per-request chrome: while a gate is active, tabs stay visible but a
116
+ * gated browse/search level serves this single tile (see the gate checks in [onGetChildren] /
117
+ * [onGetSearchResult]). Same shape as the error tiles — non-browsable, non-playable — and
118
+ * deliberately NOT accompanied by a SessionError: a gate is deliberate app state, not a failure.
119
+ * The message renders as the tile's subtitle (newlines collapse to spaces — list rows are
120
+ * single-line).
121
+ */
122
+ /**
123
+ * Wraps a raw external-search query string into the structured [SearchParams] the gate request
124
+ * carries. The car search surfaces only give a free-text query, so the other fields stay null —
125
+ * mirrors BrowserManager's query→SearchParams construction.
126
+ */
127
+ private fun searchParams(query: String): SearchParams =
128
+ SearchParams(
129
+ mode = null,
130
+ query = query,
131
+ genre = null,
132
+ artist = null,
133
+ album = null,
134
+ title = null,
135
+ playlist = null,
136
+ reference = MediaReference.UNKNOWN,
137
+ )
138
+
139
+ private fun createGateMediaItem(gate: Gate): MediaItem =
140
+ createErrorMediaItem(
141
+ mediaId = BrowserPathHelper.GATE_PATH,
142
+ title = gate.title,
143
+ subtitle = gate.message?.replace('\n', ' ') ?: "",
144
+ )
145
+
146
+ /**
147
+ * Builds a generic "something went wrong" error tile for a browse failure. The true offline case
148
+ * is handled separately by the [networkMonitor] guard in [onGetChildren]; anything reaching the
149
+ * catch block is an online-but-failed request (e.g. server down, bad status), so it must NOT be
150
+ * labelled "no internet connection".
151
+ */
152
+ private fun createBrowseErrorMediaItem(): MediaItem =
153
+ createErrorMediaItem(
154
+ mediaId = BrowserPathHelper.ERROR_PATH,
155
+ title = player.context.getString(com.audiobrowser.R.string.audio_browser_browse_error),
156
+ subtitle =
157
+ player.context.getString(com.audiobrowser.R.string.audio_browser_browse_error_subtitle),
158
+ )
91
159
 
92
160
  fun updateMediaSession(
93
161
  mediaSession: MediaSession,
@@ -113,18 +181,6 @@ class MediaSessionCallback(private val player: Player) :
113
181
  return commandManager.buildConnectionResult(session)
114
182
  }
115
183
 
116
- /** Updates the favorite state of the current track and emits onFavoriteChanged. */
117
- private fun setFavorited(favorited: Boolean) {
118
- val currentTrack = player.currentTrack ?: return
119
-
120
- // Update native favorites cache (only tracks with src can be favorited)
121
- currentTrack.src?.let { src -> player.browser?.browserManager?.updateFavorite(src, favorited) }
122
-
123
- player.setActiveTrackFavorited(favorited)
124
- val event = FavoriteChangedEvent(player.currentTrack ?: currentTrack, favorited)
125
- player.callbacks?.onFavoriteChanged(event)
126
- }
127
-
128
184
  override fun onCustomCommand(
129
185
  session: MediaSession,
130
186
  controller: MediaSession.ControllerInfo,
@@ -135,7 +191,7 @@ class MediaSessionCallback(private val player: Player) :
135
191
  if (command.customAction == MediaSessionCommandManager.CUSTOM_ACTION_FAVORITE) {
136
192
  val currentFavorited = player.currentTrack?.favorited ?: false
137
193
  Timber.d("Favorite button tapped - toggling from $currentFavorited to ${!currentFavorited}")
138
- setFavorited(!currentFavorited)
194
+ player.setActiveTrackFavorited(!currentFavorited)
139
195
  return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
140
196
  }
141
197
 
@@ -150,15 +206,10 @@ class MediaSessionCallback(private val player: Player) :
150
206
  controller: MediaSession.ControllerInfo,
151
207
  rating: Rating,
152
208
  ): ListenableFuture<SessionResult> {
153
- if (rating is HeartRating) {
154
- setFavorited(rating.isHeart)
155
- }
156
-
157
- // Also emit onRemoteSetRating for listeners
158
- RatingFactory.media3ToBridge(rating)?.let {
159
- val event = RemoteSetRatingEvent(it)
160
- player.callbacks?.onRemoteSetRating(event)
161
- }
209
+ // A heart rating from a controller (e.g. Google Assistant "I like this") toggles the
210
+ // now-playing favorite. setActiveTrackFavorited fires onFavoriteChanged so the consumer
211
+ // persists it — the same path as the notification / CarPlay heart button.
212
+ RatingFavorites.favoritedFor(rating)?.let { player.setActiveTrackFavorited(it) }
162
213
  return super.onSetRating(session, controller, rating)
163
214
  }
164
215
 
@@ -220,25 +271,55 @@ class MediaSessionCallback(private val player: Player) :
220
271
  )
221
272
  return scope.future {
222
273
  // Wait for browser to be registered if it's not available yet
223
- val browserManager =
224
- player
225
- .awaitBrowser()
226
- .also { Timber.d("Browser ready, proceeding with onGetChildren") }
227
- .browserManager
274
+ val audioBrowser =
275
+ player.awaitBrowser().also { Timber.d("Browser ready, proceeding with onGetChildren") }
276
+ val browserManager = audioBrowser.browserManager
277
+
278
+ // While a gate is active, tabs stay visible (the root keeps serving them below) but each
279
+ // non-root level is resolved per request: a gated path — including re-queries of the gate
280
+ // tile's own sentinel path — serves the single gate tile, so drilling into it re-shows the
281
+ // message instead of dead-ending in "No items"; an allowed path falls through to real
282
+ // children. Checked before the offline guard: gated content isn't coming back with
283
+ // connectivity, so the gate copy is the truer message.
284
+ if (parentId != BrowserPathHelper.ROOT_PATH) {
285
+ val outcome =
286
+ audioBrowser.gateDecision(
287
+ NativeGateRequest(reason = GateReason.BROWSE, path = parentId, search = null)
288
+ )
289
+ if (outcome.gated) {
290
+ audioBrowser.onGate(GateEvent(GateReason.BROWSE))
291
+ // gateDecision guarantees non-null chrome on a gated decision (override → default →
292
+ // built-in).
293
+ return@future LibraryResult.ofItemList(
294
+ ImmutableList.of(createGateMediaItem(outcome.chrome!!)),
295
+ params,
296
+ )
297
+ }
298
+ }
299
+
300
+ // The error / offline tiles are dead-ends. Some controllers (e.g. Android Auto when online)
301
+ // treat a non-browsable tile as tappable and subscribe to its mediaId anyway; returning the
302
+ // error tile again here would push an endless stack of error pages. Return nothing instead.
303
+ // For the offline tile, re-send the alert when still offline so a tap surfaces the
304
+ // explanation again — but only then, since these paths are also re-queried by
305
+ // notifyChildrenChanged (e.g. when connectivity returns) without any user action.
306
+ if (parentId == BrowserPathHelper.OFFLINE_PATH || parentId == BrowserPathHelper.ERROR_PATH) {
307
+ if (parentId == BrowserPathHelper.OFFLINE_PATH && !player.networkMonitor.isOnline.value) {
308
+ sendBrowseError(session, browser, offline = true)
309
+ }
310
+ return@future LibraryResult.ofItemList(ImmutableList.of<MediaItem>(), params)
311
+ }
228
312
 
229
313
  // Show offline error when offline:
230
314
  if (
231
315
  !player.networkMonitor.isOnline.value && browserManager.config.androidControllerOfflineError
232
316
  ) {
233
317
  Timber.w("Network offline - returning error message for: $parentId")
318
+ sendBrowseError(session, browser, offline = true)
234
319
  return@future LibraryResult.ofItemList(ImmutableList.of(createOfflineMediaItem()), params)
235
320
  }
236
321
 
237
322
  try {
238
- // Get context and imageLoader for SVG pre-rendering
239
- val context = player.context
240
- val imageLoader = player.imageLoader
241
-
242
323
  val children =
243
324
  if (parentId == BrowserPathHelper.RECENT_PATH) {
244
325
  // TODO: implement recent media items
@@ -246,36 +327,90 @@ class MediaSessionCallback(private val player: Player) :
246
327
  } else if (parentId == BrowserPathHelper.ROOT_PATH) {
247
328
  // Return tabs as root children (limited to 4 for automotive platform compatibility)
248
329
  // TODO: Check what Android Auto does with empty tabs list - may need to return error?
249
- val tabs = browserManager.queryTabs().take(4)
250
- if (imageLoader != null) {
251
- tabs.map { tab -> TrackFactory.toMedia3WithSvgSupport(tab, context, imageLoader) }
252
- } else {
253
- tabs.map { tab -> TrackFactory.toMedia3(tab) }
330
+ val tabs = browserManager.queryTabs()
331
+ if (tabs.size > 4) {
332
+ Timber.w(
333
+ "Root has ${tabs.size} tabs; dropping ${tabs.size - 4} (Android Auto root limit)"
334
+ )
254
335
  }
336
+ toMediaItems(tabs.take(4))
255
337
  } else {
256
338
  // Resolve the specific path and get its children
257
339
  val resolvedTrack = browserManager.resolve(parentId)
258
340
 
259
341
  // Convert children to MediaItems (url is already set to contextual URL)
260
- val trackChildren = resolvedTrack.children
261
- ?: throw IllegalStateException(
262
- "Expected browsed ResolvedTrack to have a children array"
263
- )
264
- if (imageLoader != null) {
265
- trackChildren.map { track -> TrackFactory.toMedia3WithSvgSupport(track, context, imageLoader) }
266
- } else {
267
- trackChildren.map { track -> TrackFactory.toMedia3(track) }
268
- }
342
+ val trackChildren =
343
+ resolvedTrack.children
344
+ ?: throw IllegalStateException(
345
+ "Expected browsed ResolvedTrack to have a children array"
346
+ )
347
+ toMediaItems(trackChildren.toList())
269
348
  }
270
349
 
271
350
  LibraryResult.ofItemList(ImmutableList.copyOf(children.paginate(page, pageSize)), params)
272
351
  } catch (e: Exception) {
352
+ // A cancelled browse (controller disconnected) is not a failure — don't render it as an
353
+ // error tile. awaitBrowser's TimeoutCancellationException IS a real failure, keep that.
354
+ if (e is CancellationException && e !is TimeoutCancellationException) throw e
273
355
  Timber.e(e, "Error getting children for parentId: $parentId")
274
- LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
356
+ // Surface an error tile instead of a bare ofError(): Media3 drops the error message on the
357
+ // legacy browse bridge, which would otherwise leave an empty "No items" screen in Android
358
+ // Auto. See https://github.com/androidx/media/issues/2901
359
+ //
360
+ // Verified on a head unit (2026-06): the AA browse list NEVER renders error text — not for
361
+ // transient sendError, sticky non-fatal replication, or fatal replication
362
+ // (setLibraryErrorReplicationMode). Fatal replication does show the message on the
363
+ // playback screen, but presents the session as STATE_ERROR with no actions, hiding the
364
+ // now-playing item and transport controls — not worth it. Tiles remain the only in-browse
365
+ // signal we control.
366
+ sendBrowseError(session, browser, offline = false)
367
+ LibraryResult.ofItemList(ImmutableList.of(createBrowseErrorMediaItem()), params)
275
368
  }
276
369
  }
277
370
  }
278
371
 
372
+ /**
373
+ * Sends a non-fatal [SessionError] to the controller whose browse failed, complementing the error
374
+ * tile. Media3 browsers receive it via MediaController.Listener.onError; for legacy controllers
375
+ * Media3 transiently attaches the error code/message to the platform session's playback state
376
+ * without entering STATE_ERROR.
377
+ *
378
+ * NOTE: current Android Auto renders NOTHING for this (verified on a head unit — see
379
+ * androidx/media#2901; the transient state is cleared microseconds after being set). It is kept
380
+ * because it is the correct Media3-API error signal, costs nothing, and becomes user-visible if
381
+ * Android Auto moves to consuming SessionError via the Media3 controller API.
382
+ *
383
+ * Dispatched to the main thread: the session and its legacy stub are application-thread bound.
384
+ */
385
+ @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
386
+ private fun sendBrowseError(
387
+ session: MediaLibraryService.MediaLibrarySession,
388
+ browser: MediaSession.ControllerInfo,
389
+ offline: Boolean,
390
+ ) {
391
+ val message =
392
+ player.context.getString(
393
+ if (offline) com.audiobrowser.R.string.audio_browser_offline_error
394
+ else com.audiobrowser.R.string.audio_browser_browse_error
395
+ )
396
+ val code = if (offline) SessionError.ERROR_IO else SessionError.ERROR_UNKNOWN
397
+ scope.launch(Dispatchers.Main) { session.sendError(browser, SessionError(code, message)) }
398
+ }
399
+
400
+ /**
401
+ * Converts tracks to MediaItems for browse delivery, routing http(s) artwork through the
402
+ * content:// provider so Android Auto can load it via the ArtworkContentProvider. Image-row
403
+ * tracks (a CarPlay-only rendering) are expanded into their items as regular grouped rows
404
+ * first — see [TrackFactory.expandImageRows].
405
+ */
406
+ private fun toMediaItems(tracks: List<Track>): List<MediaItem> {
407
+ val registry = player.browseArtworkRegistry
408
+ val authority = com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
409
+ return TrackFactory.expandImageRows(tracks).map {
410
+ TrackFactory.toBrowseMediaItem(it, registry, authority)
411
+ }
412
+ }
413
+
279
414
  override fun onGetItem(
280
415
  session: MediaLibraryService.MediaLibrarySession,
281
416
  browser: MediaSession.ControllerInfo,
@@ -288,6 +423,30 @@ class MediaSessionCallback(private val player: Player) :
288
423
  return@future LibraryResult.ofItem(createOfflineMediaItem(), null)
289
424
  }
290
425
 
426
+ // Return the error tile as a non-browsable item so tapping it can't re-browse into a
427
+ // failing path (which would push another error page onto the stack).
428
+ if (mediaId == BrowserPathHelper.ERROR_PATH) {
429
+ return@future LibraryResult.ofItem(createBrowseErrorMediaItem(), null)
430
+ }
431
+
432
+ if (mediaId == BrowserPathHelper.GATE_PATH) {
433
+ // A direct fetch of the gate tile's own sentinel (a controller re-reading the item it was
434
+ // served). Resolve the chrome for the gate path but do NOT emit onGate — that fires at the
435
+ // browse/search serve sites, not on an item lookup.
436
+ val outcome =
437
+ player
438
+ .awaitBrowser()
439
+ .gateDecision(
440
+ NativeGateRequest(
441
+ reason = GateReason.BROWSE,
442
+ path = BrowserPathHelper.GATE_PATH,
443
+ search = null,
444
+ )
445
+ )
446
+ if (!outcome.gated) return@future LibraryResult.ofError(SessionError.ERROR_BAD_VALUE)
447
+ return@future LibraryResult.ofItem(createGateMediaItem(outcome.chrome!!), null)
448
+ }
449
+
291
450
  if (mediaId == BrowserPathHelper.ROOT_PATH || mediaId == BrowserPathHelper.RECENT_PATH) {
292
451
  return@future LibraryResult.ofItem(
293
452
  MediaItem.Builder()
@@ -303,11 +462,33 @@ class MediaSessionCallback(private val player: Player) :
303
462
  // Wait for browser to be registered if it's not available yet
304
463
  val browserManager = player.awaitBrowser().browserManager
305
464
 
465
+ // Serve tracks from the track cache first (keyed by url and src). Besides avoiding an HTTP
466
+ // resolve, this keeps item identity correct for contextual mediaIds: resolve() strips
467
+ // __trackId and would return the *parent container's* metadata as the item.
468
+ val browseAuthority =
469
+ com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
470
+ browserManager.getCachedTrack(mediaId)?.let { track ->
471
+ return@future LibraryResult.ofItem(
472
+ TrackFactory.toBrowseMediaItem(track, player.browseArtworkRegistry, browseAuthority),
473
+ null,
474
+ )
475
+ }
476
+
306
477
  try {
307
478
  val resolvedTrack = browserManager.resolve(mediaId)
308
- val mediaItem = ResolvedTrackFactory.toMedia3(resolvedTrack)
309
- LibraryResult.ofItem(mediaItem, null)
479
+ // Through the one Track conversion, so the resolve path renders identically
480
+ // to the cached-track path above (list line from subtitle, favorited heart)
481
+ // and the item's tag is a Track, as fromMedia3 expects.
482
+ LibraryResult.ofItem(
483
+ TrackFactory.toBrowseMediaItem(
484
+ resolvedTrack.toTrack(),
485
+ player.browseArtworkRegistry,
486
+ browseAuthority,
487
+ ),
488
+ null,
489
+ )
310
490
  } catch (e: Exception) {
491
+ if (e is CancellationException && e !is TimeoutCancellationException) throw e
311
492
  Timber.e(e, "Error getting item for mediaId: $mediaId")
312
493
  LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
313
494
  }
@@ -322,8 +503,17 @@ class MediaSessionCallback(private val player: Player) :
322
503
  ): ListenableFuture<LibraryResult<Void>> {
323
504
  Timber.d("onSubscribe: ${browser.packageName}, parentId = $parentId")
324
505
 
325
- parentIdSubscriptions.getOrPut(parentId) { mutableSetOf() }.add(browser)
326
- return super.onSubscribe(session, browser, parentId, params)
506
+ synchronized(this) { parentIdSubscriptions.getOrPut(parentId) { mutableSetOf() }.add(browser) }
507
+
508
+ // Don't call super: Media3's default onSubscribe validates the parent by calling
509
+ // onGetItem(parentId), which for us is a full resolve() — an HTTP fetch whose result the
510
+ // legacy stub discards for Android Auto controllers. onGetChildren already surfaces browse
511
+ // failures. Keep the default's other behavior: notify Media3 (non-legacy) browsers so they
512
+ // fetch the children; legacy browsers get onLoadChildren from the framework after subscribing.
513
+ if (browser.controllerVersion != MediaSession.ControllerInfo.LEGACY_CONTROLLER_VERSION) {
514
+ session.notifyChildrenChanged(browser, parentId, Int.MAX_VALUE, params)
515
+ }
516
+ return Futures.immediateFuture(LibraryResult.ofVoid())
327
517
  }
328
518
 
329
519
  override fun onUnsubscribe(
@@ -333,9 +523,11 @@ class MediaSessionCallback(private val player: Player) :
333
523
  ): ListenableFuture<LibraryResult<Void>> {
334
524
  Timber.d("onUnsubscribe: ${browser.packageName}, parentId = $parentId")
335
525
 
336
- parentIdSubscriptions[parentId]?.remove(browser)
337
- if (parentIdSubscriptions[parentId]?.isEmpty() == true) {
338
- parentIdSubscriptions.remove(parentId)
526
+ synchronized(this) {
527
+ parentIdSubscriptions[parentId]?.remove(browser)
528
+ if (parentIdSubscriptions[parentId]?.isEmpty() == true) {
529
+ parentIdSubscriptions.remove(parentId)
530
+ }
339
531
  }
340
532
 
341
533
  return super.onUnsubscribe(session, browser, parentId)
@@ -344,22 +536,38 @@ class MediaSessionCallback(private val player: Player) :
344
536
  /**
345
537
  * Notifies all subscribed controllers to refresh their content. Called internally when network
346
538
  * state changes to refresh all subscribed paths.
539
+ *
540
+ * Safe to call from any thread: snapshots the subscribed paths and dispatches the session calls
541
+ * to the main (Media3 application) thread.
347
542
  */
348
543
  private fun notifySubscribedChildrenChanged() {
349
- parentIdSubscriptions.keys.forEach { parentId ->
350
- mediaLibrarySession?.notifyChildrenChanged(parentId, Int.MAX_VALUE, null)
544
+ val session = mediaLibrarySession ?: return
545
+ val parentIds = synchronized(this) { parentIdSubscriptions.keys.toList() }
546
+ scope.launch(Dispatchers.Main) {
547
+ parentIds.forEach { parentId -> session.notifyChildrenChanged(parentId, Int.MAX_VALUE, null) }
351
548
  }
352
549
  }
353
550
 
354
551
  /**
355
552
  * Notifies external controllers that content at the given path has changed. Controllers
356
- * subscribed to this path will refresh their UI.
553
+ * subscribed to this path will refresh their UI. Safe to call from any thread.
357
554
  *
358
555
  * @param path The path where content has changed
359
556
  */
360
557
  fun notifyContentChanged(path: String) {
361
558
  Timber.d("Notifying content changed for path: $path")
362
- mediaLibrarySession?.notifyChildrenChanged(path, Int.MAX_VALUE, null)
559
+ val session = mediaLibrarySession ?: return
560
+ scope.launch(Dispatchers.Main) { session.notifyChildrenChanged(path, Int.MAX_VALUE, null) }
561
+ }
562
+
563
+ /**
564
+ * Notifies all subscribed controllers that content everywhere has changed (e.g. on a locale
565
+ * switch) so they re-query their children. Pairs with AudioBrowser.invalidateAllContent(), which
566
+ * clears the content cache first.
567
+ */
568
+ fun invalidateAllContent() {
569
+ Timber.d("Invalidating all content - notifying all subscribed paths")
570
+ notifySubscribedChildrenChanged()
363
571
  }
364
572
 
365
573
  /**
@@ -367,10 +575,15 @@ class MediaSessionCallback(private val player: Player) :
367
575
  * controllers to refresh their content.
368
576
  */
369
577
  fun notifyBrowserReady() {
370
- Timber.d("Browser ready - notifying ${parentIdSubscriptions.size} subscribed paths")
578
+ Timber.d("Browser ready - notifying all subscribed paths")
371
579
  notifySubscribedChildrenChanged()
372
580
  }
373
581
 
582
+ /** Cancels in-flight browse/search work. Call when the owning player is destroyed. */
583
+ fun destroy() {
584
+ scope.cancel()
585
+ }
586
+
374
587
  override fun onSearch(
375
588
  session: MediaLibraryService.MediaLibrarySession,
376
589
  browser: MediaSession.ControllerInfo,
@@ -379,11 +592,27 @@ class MediaSessionCallback(private val player: Player) :
379
592
  ): ListenableFuture<LibraryResult<Void>> {
380
593
  Timber.d("onSearch: ${browser.packageName}, query = $query")
381
594
  return scope.future {
382
- val browserManager = player.browser?.browserManager
595
+ // Wait for browser registration like onGetChildren does, so a cold-start voice search
596
+ // doesn't fail before JS has configured the browser.
597
+ val audioBrowser =
598
+ try {
599
+ player.awaitBrowser()
600
+ } catch (e: TimeoutCancellationException) {
601
+ Timber.w("Timed out waiting for browser - search not available")
602
+ return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
603
+ }
604
+ val browserManager = audioBrowser.browserManager
383
605
 
384
- if (browserManager == null) {
385
- Timber.w("AudioBrowser not registered - search not available")
386
- return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
606
+ // External-surface search is gated with the rest of the browse tree — otherwise search is
607
+ // a way around the gate. One "result": the gate tile (see onGetSearchResult).
608
+ val searchOutcome =
609
+ audioBrowser.gateDecision(
610
+ NativeGateRequest(reason = GateReason.SEARCH, path = null, search = searchParams(query))
611
+ )
612
+ if (searchOutcome.gated) {
613
+ audioBrowser.onGate(GateEvent(GateReason.SEARCH))
614
+ session.notifySearchResultChanged(browser, query, 1, params)
615
+ return@future LibraryResult.ofVoid()
387
616
  }
388
617
 
389
618
  // Check if search is configured
@@ -404,6 +633,7 @@ class MediaSessionCallback(private val player: Player) :
404
633
 
405
634
  LibraryResult.ofVoid()
406
635
  } catch (e: Exception) {
636
+ if (e is CancellationException && e !is TimeoutCancellationException) throw e
407
637
  Timber.e(e, "Error during search for query: $query")
408
638
  LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
409
639
  }
@@ -422,21 +652,38 @@ class MediaSessionCallback(private val player: Player) :
422
652
  "onGetSearchResult: ${browser.packageName}, query = $query, page = $page, pageSize = $pageSize"
423
653
  )
424
654
  return scope.future {
425
- val browserManager = player.browser?.browserManager
655
+ val audioBrowser =
656
+ try {
657
+ player.awaitBrowser()
658
+ } catch (e: TimeoutCancellationException) {
659
+ Timber.w("Timed out waiting for browser - search not available")
660
+ return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
661
+ }
662
+ val browserManager = audioBrowser.browserManager
426
663
 
427
- if (browserManager == null) {
428
- Timber.w("AudioBrowser not registered - search not available")
429
- return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
664
+ // Gated: the single search "result" is the gate tile (paired with onSearch's count of 1).
665
+ val searchOutcome =
666
+ audioBrowser.gateDecision(
667
+ NativeGateRequest(reason = GateReason.SEARCH, path = null, search = searchParams(query))
668
+ )
669
+ if (searchOutcome.gated) {
670
+ audioBrowser.onGate(GateEvent(GateReason.SEARCH))
671
+ return@future LibraryResult.ofItemList(
672
+ ImmutableList.of(createGateMediaItem(searchOutcome.chrome!!)),
673
+ params,
674
+ )
430
675
  }
431
676
 
432
677
  try {
433
678
  // Get cached search results from BrowserManager
434
679
  browserManager.getCachedSearchResults(query)?.let { tracks ->
435
680
  // Convert to MediaItems
681
+ val searchAuthority =
682
+ com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
436
683
  val mediaItems =
437
684
  tracks.map { track ->
438
685
  Timber.d("Search result: ${track.title} (url=${track.url}, src=${track.src})")
439
- TrackFactory.toMedia3(track)
686
+ TrackFactory.toBrowseMediaItem(track, player.browseArtworkRegistry, searchAuthority)
440
687
  }
441
688
 
442
689
  val paginatedItems = mediaItems.paginate(page, pageSize)
@@ -448,6 +695,7 @@ class MediaSessionCallback(private val player: Player) :
448
695
  LibraryResult.ofItemList(ImmutableList.of(), params)
449
696
  }
450
697
  } catch (e: Exception) {
698
+ if (e is CancellationException && e !is TimeoutCancellationException) throw e
451
699
  Timber.e(e, "Error getting search results for query: $query")
452
700
  LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
453
701
  }
@@ -462,65 +710,84 @@ class MediaSessionCallback(private val player: Player) :
462
710
  startPositionMs: Long,
463
711
  ): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
464
712
  Timber.Forest.d(
465
- "onSetMediaItems: ${controller.packageName}, count=${mediaItems.size}, mediaId=${mediaItems[0].mediaId}, uri=${mediaItems[0].localConfiguration?.uri}, title=${mediaItems[0].mediaMetadata.title}"
713
+ "onSetMediaItems: ${controller.packageName}, count=${mediaItems.size}, mediaId=${mediaItems.firstOrNull()?.mediaId}, uri=${mediaItems.firstOrNull()?.localConfiguration?.uri}, title=${mediaItems.firstOrNull()?.mediaMetadata?.title}"
466
714
  )
467
715
 
716
+ if (mediaItems.isEmpty()) {
717
+ return Futures.immediateFuture(
718
+ MediaSession.MediaItemsWithStartPosition(emptyList(), 0, startPositionMs)
719
+ )
720
+ }
721
+
468
722
  return scope.future {
469
723
  val audioBrowser = player.awaitBrowser()
470
724
 
471
- // Helper: returns the current player state unchanged so Media3 doesn't modify playback
472
- fun currentPlayerState(): MediaSession.MediaItemsWithStartPosition {
473
- val currentItems = player.tracks.map { TrackFactory.toMedia3(it) }
474
- val currentIndex = player.currentIndex ?: 0
475
- return MediaSession.MediaItemsWithStartPosition(
476
- currentItems,
477
- currentIndex,
478
- startPositionMs,
479
- )
480
- }
725
+ // Helper: returns the current player state unchanged so Media3 doesn't modify playback.
726
+ // ExoPlayer is main-thread confined; this future runs on IO, so hop to Main for the reads.
727
+ suspend fun currentPlayerState(): MediaSession.MediaItemsWithStartPosition =
728
+ withContext(Dispatchers.Main) {
729
+ val currentItems = player.tracks.map { TrackFactory.toMedia3(it) }
730
+ val currentIndex = player.currentIndex ?: 0
731
+ MediaSession.MediaItemsWithStartPosition(currentItems, currentIndex, startPositionMs)
732
+ }
481
733
 
482
- // Check if this is a single contextual URL that matches the current queue source
483
- if (mediaItems.size == 1) {
484
- val mediaId = mediaItems[0].mediaId
485
- if (BrowserPathHelper.isContextual(mediaId)) {
486
- val parentPath = BrowserPathHelper.stripTrackId(mediaId)
487
- val trackId = BrowserPathHelper.extractTrackId(mediaId)
488
-
489
- // Check if queue already came from this parent path - just skip to the track
490
- if (trackId != null && parentPath == player.queueSourcePath) {
491
- val index = player.tracks.indexOfFirst { it.src == trackId }
492
- if (index >= 0) {
493
- Timber.d("Queue already from $parentPath, skipping to index $index")
494
- val track = player.tracks[index]
495
- return@future handleTrackLoad(audioBrowser.configuration.handleTrackLoad, track, player.tracks, index.toDouble(), ::currentPlayerState) {
496
- // Return the existing queue items with the new start index
497
- val existingItems = player.tracks.map { TrackFactory.toMedia3(it) }
498
- MediaSession.MediaItemsWithStartPosition(
499
- existingItems,
500
- index,
501
- startPositionMs,
502
- )
503
- }
734
+ val browserManager = audioBrowser.browserManager
735
+
736
+ // A single tapped item resolves to the contextual url of the list it was
737
+ // tapped in: directly for contextual mediaIds, via the track cache for
738
+ // stable-id mediaIds (see BrowserManager.contextualUrlFor). A search
739
+ // selection is not a list tap — its queue comes from the search results
740
+ // (resolveMediaItemsForPlayback), never from a browsed container.
741
+ val singleContextualUrl =
742
+ mediaItems
743
+ .singleOrNull()
744
+ ?.takeIf { it.requestMetadata.searchQuery == null }
745
+ ?.let { browserManager.contextualUrlFor(it.mediaId) }
746
+
747
+ // Check if this is a single item from the current queue source
748
+ if (singleContextualUrl != null) {
749
+ val parentPath = BrowserPathHelper.stripTrackId(singleContextualUrl)
750
+ val trackId = BrowserPathHelper.extractTrackId(singleContextualUrl)
751
+
752
+ // Check if queue already came from this parent path - just skip to the track
753
+ if (trackId != null && parentPath == player.queueSourcePath) {
754
+ val queueTracks = withContext(Dispatchers.Main) { player.tracks }
755
+ val index = queueTracks.indexOfFirst { it.src == trackId }
756
+ if (index >= 0) {
757
+ Timber.d("Queue already from $parentPath, skipping to index $index")
758
+ val track = queueTracks[index]
759
+ return@future handleTrackLoad(
760
+ audioBrowser.configuration.handleTrackLoad,
761
+ track,
762
+ queueTracks,
763
+ index.toDouble(),
764
+ ::currentPlayerState,
765
+ ) {
766
+ // Return the existing queue items with the new start index
767
+ val existingItems = queueTracks.map { TrackFactory.toMedia3(it) }
768
+ MediaSession.MediaItemsWithStartPosition(existingItems, index, startPositionMs)
504
769
  }
505
770
  }
506
771
  }
507
772
  }
508
773
 
509
- val browserManager = audioBrowser.browserManager
510
774
  val result =
511
775
  browserManager.resolveMediaItemsForPlayback(mediaItems, startIndex, startPositionMs)
512
776
 
513
777
  val tracks = result.mediaItems.map { TrackFactory.fromMedia3(it) }.toTypedArray()
514
778
  val selectedTrack = tracks.getOrElse(result.startIndex) { tracks.first() }
515
779
 
516
- handleTrackLoad(audioBrowser.configuration.handleTrackLoad, selectedTrack, tracks, result.startIndex.toDouble(), ::currentPlayerState) {
780
+ handleTrackLoad(
781
+ audioBrowser.configuration.handleTrackLoad,
782
+ selectedTrack,
783
+ tracks,
784
+ result.startIndex.toDouble(),
785
+ ::currentPlayerState,
786
+ ) {
517
787
  // If this was a contextual URL expansion, track the source path (only for default behavior)
518
- if (mediaItems.size == 1) {
519
- val mediaId = mediaItems[0].mediaId
520
- if (BrowserPathHelper.isContextual(mediaId)) {
521
- val parentPath = BrowserPathHelper.stripTrackId(mediaId)
522
- withContext(Dispatchers.Main) { player.queueSourcePath = parentPath }
523
- }
788
+ if (singleContextualUrl != null) {
789
+ val parentPath = BrowserPathHelper.stripTrackId(singleContextualUrl)
790
+ withContext(Dispatchers.Main) { player.queueSourcePath = parentPath }
524
791
  }
525
792
  result
526
793
  }
@@ -537,25 +804,42 @@ class MediaSessionCallback(private val player: Player) :
537
804
  *
538
805
  * @param mediaSession The media session
539
806
  * @param controller The controller requesting resumption
540
- * @param playback True if playback should start, false if just gathering info for UI
807
+ * @param isForPlayback True if this should start playback; false if just gathering info for the
808
+ * boot-time resumption notification (no network; local metadata only).
541
809
  * @see https://developer.android.com/media/media3/session/background-playback#resumption
542
810
  */
543
811
  override fun onPlaybackResumption(
544
812
  mediaSession: MediaSession,
545
813
  controller: MediaSession.ControllerInfo,
814
+ isForPlayback: Boolean,
546
815
  ): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
547
816
  Timber.d("${controller.packageName}")
548
817
 
549
818
  return scope.future {
550
- // Restore player settings and persisted state
819
+ // A pure read first: restore()'s side effects (it applies repeat/shuffle/speed to the
820
+ // live player, emitting option-changed events to JS) belong to actual resumption only.
551
821
  val state =
552
- // restore() sets player properties which must happen on main thread
553
- withContext(Dispatchers.Main) { player.playbackStateStore.restore() }
822
+ player.playbackStateStore.get()
554
823
  ?: run {
555
824
  Timber.w("No persisted playback state found")
556
825
  throw IllegalStateException("No playback state to resume")
557
826
  }
558
827
 
828
+ // isForPlayback == false is the device-boot-time notification case: network may be
829
+ // unavailable and we must not start playback. Return just the locally-stored track with its
830
+ // already-local metadata; skip the settings restore and network queue expansion below.
831
+ if (!isForPlayback) {
832
+ Timber.d("Info-gathering resumption (boot-time); returning stored track without expansion")
833
+ return@future MediaSession.MediaItemsWithStartPosition(
834
+ ImmutableList.of(TrackFactory.toMedia3(state.track)),
835
+ 0,
836
+ state.positionMs,
837
+ )
838
+ }
839
+
840
+ // restore() sets player properties which must happen on main thread
841
+ withContext(Dispatchers.Main) { player.playbackStateStore.restore() }
842
+
559
843
  val url = state.track.url
560
844
  Timber.d("Resuming from url=$url, positionMs=${state.positionMs}")
561
845