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,5 +1,7 @@
1
1
  package com.audiobrowser.browser
2
2
 
3
+ import com.audiobrowser.util.artworkOf
4
+
3
5
  import android.util.LruCache
4
6
  import androidx.media3.common.MediaItem
5
7
  import androidx.media3.session.MediaSession
@@ -9,8 +11,10 @@ import com.audiobrowser.util.BrowserPathHelper
9
11
  import com.audiobrowser.util.TrackFactory
10
12
  import com.margelo.nitro.audiobrowser.ArtworkRequestConfig
11
13
  import com.margelo.nitro.audiobrowser.BrowserSourceCallbackParam
14
+ import com.margelo.nitro.audiobrowser.FavoritesMatchMode
15
+ import com.margelo.nitro.audiobrowser.Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____
12
16
  import com.margelo.nitro.audiobrowser.ImageContext
13
- import com.margelo.nitro.audiobrowser.ImageSource
17
+ import com.margelo.nitro.audiobrowser.MediaReference
14
18
  import com.margelo.nitro.audiobrowser.MediaRequestConfig
15
19
  import com.margelo.nitro.audiobrowser.NativeRouteEntry
16
20
  import com.margelo.nitro.audiobrowser.RequestConfig
@@ -46,6 +50,7 @@ class BrowserManager {
46
50
  private var onPathChanged: ((String) -> Unit)? = null
47
51
  private var onContentChanged: ((ResolvedTrack?) -> Unit)? = null
48
52
  private var onTabsChanged: ((Array<Track>) -> Unit)? = null
53
+ private var onArtworkRegistriesCleared: (() -> Unit)? = null
49
54
 
50
55
  private var path: String = "/"
51
56
  set(value) {
@@ -86,28 +91,64 @@ class BrowserManager {
86
91
  // Invalidated via invalidateContentCache() when content changes
87
92
  private val contentCache = LruCache<String, ResolvedTrack>(20)
88
93
 
89
- // Cache for search results - keyed by query string
90
- private var lastSearchQuery: String? = null
91
- private var lastSearchResults: Array<Track>? = null
94
+ // Cache for the most recent search - the query string and its results stored as one pair, so a
95
+ // concurrent reader never sees one query matched with another query's results.
96
+ @Volatile private var lastSearch: Pair<String, Array<Track>>? = null
92
97
 
93
98
  // Set of favorited track identifiers (src)
94
99
  private var favoriteIds = setOf<String>()
95
100
 
101
+ // Favorite match mode, propagated from the player's `favorite` capability.
102
+ // null = favoriting disabled (no row hearts). Set via setFavoriteMatch.
103
+ private var favoriteMatch: FavoritesMatchMode? = null
104
+
96
105
  // Navigation tracking to prevent race conditions
97
106
  @Volatile private var currentNavigationId = 0
98
107
 
99
108
  /**
100
109
  * Browser configuration containing routes, search, tabs, and request settings. This can be
101
110
  * updated dynamically when the configuration changes.
111
+ *
112
+ * Setting a new config bumps [layerGeneration] so the cached request/browse resolver layers are
113
+ * re-resolved on the next request (the resolvers may close over config-derived state).
102
114
  */
103
115
  var config: BrowserConfig = BrowserConfig()
116
+ set(value) {
117
+ field = value
118
+ layerGeneration += 1
119
+ // Registered artwork resolutions pin configs (and their JS callback handles)
120
+ // from the previous configuration — never resolve through them again.
121
+ artworkResolutions.clear()
122
+ onArtworkRegistriesCleared?.invoke()
123
+ }
124
+
125
+ // Resolver-layer caching. The request/browse layers may be resolver thunks
126
+ // (config.requestResolver / config.browseResolver) resolved once per *content generation*:
127
+ // re-resolved when content is invalidated (clearContentCache, from invalidateAllContent) or when
128
+ // a new config is set, cached, and merged per request.
129
+ private var layerGeneration = 0
130
+ private var resolvedLayerGeneration = -1
131
+ private var resolvedRequestLayer: TransformableRequestConfig? = null
132
+ private var resolvedBrowseLayer: TransformableRequestConfig? = null
133
+
134
+ /** Test-only accessors for the resolver-layer cache state (see ensureLayersResolved). */
135
+ internal val layerGenerationForTest: Int
136
+ get() = layerGeneration
137
+
138
+ internal val resolvedLayerGenerationForTest: Int
139
+ get() = resolvedLayerGeneration
140
+
141
+ internal val resolvedRequestLayerForTest: TransformableRequestConfig?
142
+ get() = resolvedRequestLayer
143
+
144
+ internal val resolvedBrowseLayerForTest: TransformableRequestConfig?
145
+ get() = resolvedBrowseLayer
104
146
 
105
147
  /**
106
- * Callback to transform artwork URLs for tracks. Takes a track and optional per-route artwork
107
- * config, returns ImageSource or null. Injected by AudioBrowser when artwork config is set.
108
- * The ImageContext parameter provides size hints for CDN URL generation.
148
+ * Maps produced artwork URIs back to (Track, artwork-config kind) so display-time bitmap loading
149
+ * can re-resolve Track-first. See [ArtworkResolutionRegistry].
109
150
  */
110
- var artworkUrlResolver: (suspend (Track, ArtworkRequestConfig?, ImageContext?) -> ImageSource?)? = null
151
+ val artworkResolutions = ArtworkResolutionRegistry()
111
152
 
112
153
  /**
113
154
  * Sets the favorited track identifiers. Tracks will have their favorited field hydrated based on
@@ -118,6 +159,14 @@ class BrowserManager {
118
159
  Timber.d("Set ${favoriteIds.size} favorite IDs")
119
160
  }
120
161
 
162
+ /**
163
+ * Sets the favorite match mode (propagated from the `favorite` capability). null disables
164
+ * row-heart hydration.
165
+ */
166
+ fun setFavoriteMatch(match: FavoritesMatchMode?) {
167
+ favoriteMatch = match
168
+ }
169
+
121
170
  /**
122
171
  * Updates the favorite state for a single track identifier. Called when the heart button is
123
172
  * tapped in media controllers.
@@ -133,40 +182,30 @@ class BrowserManager {
133
182
  }
134
183
 
135
184
  /**
136
- * Hydrates the favorited field on a track based on the favoriteIds set. Only hydrates if
137
- * track.favorited is null (doesn't overwrite API-provided values). Only tracks with src can be
138
- * favorited.
185
+ * Hydrates the favorited field on a track based on the favoriteIds set. No-op unless favoriting
186
+ * is enabled (the `favorite` capability). Only playable (src-bearing) tracks are favoritable; the
187
+ * flag is set to true OR false so non-favorited tracks still show an (empty) heart. Doesn't
188
+ * overwrite API-provided values.
139
189
  */
140
190
  private fun hydrateFavorite(track: Track): Track {
191
+ val match = favoriteMatch ?: return track
141
192
  // Don't overwrite API-provided favorites
142
193
  if (track.favorited != null) return track
143
- if (favoriteIds.isEmpty()) return track
144
-
145
- val isFavorited = track.src?.let { favoriteIds.contains(it) } ?: false
146
- if (!isFavorited) return track
147
-
148
- return Track(
149
- url = track.url,
150
- src = track.src,
151
- artwork = track.artwork,
152
- artworkSource = track.artworkSource,
153
- artworkCarPlayTinted = track.artworkCarPlayTinted,
154
- title = track.title,
155
- subtitle = track.subtitle,
156
- artist = track.artist,
157
- album = track.album,
158
- description = track.description,
159
- genre = track.genre,
160
- duration = track.duration,
161
- style = track.style,
162
- childrenStyle = track.childrenStyle,
163
- favorited = true,
164
- groupTitle = track.groupTitle,
165
- live = track.live,
166
- imageRow = track.imageRow,
167
- )
194
+ // Only playable tracks are favoritable
195
+ val src = track.src ?: return track
196
+
197
+ val isFavorited = isFavorite(src, match)
198
+
199
+ return track.copy(favorited = isFavorited)
168
200
  }
169
201
 
202
+ /** Whether [src] is favorited under the given match mode. */
203
+ private fun isFavorite(src: String, match: FavoritesMatchMode): Boolean =
204
+ when (match) {
205
+ FavoritesMatchMode.EXACT -> favoriteIds.contains(src)
206
+ FavoritesMatchMode.PARTIAL -> favoriteIds.any { BrowserPathHelper.containsSegment(src, it) }
207
+ }
208
+
170
209
  /** Hydrates favorites on all children of a ResolvedTrack. */
171
210
  private fun hydrateChildren(resolvedTrack: ResolvedTrack): ResolvedTrack {
172
211
  val children = resolvedTrack.children ?: return resolvedTrack
@@ -174,20 +213,29 @@ class BrowserManager {
174
213
  return resolvedTrack.copy(children = hydratedChildren)
175
214
  }
176
215
 
177
- /** Cache a track by both url and src for O(1) lookup from either key. */
216
+ /** Cache a track by id, url, and src for O(1) lookup from any mediaId form. */
178
217
  private fun cacheTrack(track: Track) {
218
+ track.id?.takeUnless { it.isBlank() }?.let { trackCache.put(it, track) }
179
219
  track.url?.let { trackCache.put(it, track) }
180
220
  track.src?.let { trackCache.put(it, track) }
181
221
  }
182
222
 
183
223
  private fun cacheChildren(resolvedTrack: ResolvedTrack) {
184
- resolvedTrack.children?.forEach { track -> cacheTrack(track) }
224
+ resolvedTrack.children?.forEach { track ->
225
+ cacheTrack(track)
226
+ // Image-row items are playable surfaces of their own: cache them (with
227
+ // their stamped contextual urls) so an id-keyed mediaId from an
228
+ // expanded Android Auto tile can find its way back to queue expansion.
229
+ track.imageRow?.forEach { item ->
230
+ cacheTrack(with(TrackFactory) { item.toTrack(groupTitle = track.title) })
231
+ }
232
+ }
185
233
  }
186
234
 
187
235
  /**
188
- * Get a cached Track by mediaId (url or src), or null if not cached. Used by Media3 to rehydrate
189
- * MediaItem shells with full track metadata. Re-hydrates favorites in case setFavoriteStates was
190
- * called after caching.
236
+ * Get a cached Track by mediaId (stable id, url, or src), or null if not cached. Used by Media3
237
+ * to rehydrate MediaItem shells with full track metadata. Re-hydrates favorites in case
238
+ * setFavoriteStates was called after caching.
191
239
  */
192
240
  fun getCachedTrack(mediaId: String): Track? {
193
241
  // Try direct lookup first (matches url or src)
@@ -212,27 +260,76 @@ class BrowserManager {
212
260
  }
213
261
 
214
262
  /**
215
- * Resolves multiple media IDs from cache. Throws IllegalStateException if any mediaId is not
216
- * found in cache.
263
+ * Resolves a tapped mediaId to the contextual url to expand a queue from, or null when there is
264
+ * none. A contextual mediaId is its own; a stable-id mediaId (see TrackFactory.buildMediaItem)
265
+ * resolves through the track cache to the contextual url of the container it was most recently
266
+ * browsed in — a legacy car controller round-trips only the mediaId, so the cache is what
267
+ * remembers which list the row came from.
268
+ */
269
+ fun contextualUrlFor(mediaId: String): String? {
270
+ if (BrowserPathHelper.isContextual(mediaId)) return mediaId
271
+ return getCachedTrack(mediaId)?.url?.takeIf { BrowserPathHelper.isContextual(it) }
272
+ }
273
+
274
+ /**
275
+ * Resolves a single Media3 MediaItem to a Track. Prefers the track cache (keyed by id, url, and
276
+ * src).
277
+ *
278
+ * A cache miss is legitimately reachable: a controller can replay a mediaId this process never
279
+ * browsed — e.g. a search-result track after process death. When the mediaId is a playable URL,
280
+ * or the item's requestMetadata carries the playable uri (stamped by TrackFactory for stable-id
281
+ * mediaIds, and round-tripped by Media3 controllers), fall back to a minimal track built from the
282
+ * item's own metadata instead of failing playback.
217
283
  *
218
- * @param mediaIds The media IDs to resolve
219
- * @return List of Track objects
284
+ * @throws IllegalStateException if the mediaId is not cached and no playable URL is available
220
285
  */
221
- fun resolveMediaIdsFromCache(mediaIds: List<String>): List<Track> {
222
- return mediaIds.map { mediaId ->
223
- Timber.d("=== Resolving from cache: mediaId='$mediaId' ===")
286
+ private fun resolveMediaItemToTrack(mediaItem: MediaItem): Track {
287
+ val mediaId = mediaItem.mediaId
224
288
 
225
- getCachedTrack(mediaId)?.let { cachedTrack ->
226
- Timber.d("→ Found cached Track: '${cachedTrack.title}'")
227
- return@map cachedTrack
228
- }
289
+ getCachedTrack(mediaId)?.let { cachedTrack ->
290
+ Timber.d("Resolved mediaId='$mediaId' from cache: '${cachedTrack.title}'")
291
+ return cachedTrack
292
+ }
229
293
 
230
- // Cache miss - this indicates a bug in our caching system
231
- Timber.e("→ Cache MISS for mediaId='$mediaId' - this should not happen")
232
- throw IllegalStateException(
233
- "MediaItem not found in cache: $mediaId. This indicates a bug in the caching system."
294
+ val fallbackSrc =
295
+ if (mediaId.startsWith("http://") || mediaId.startsWith("https://")) {
296
+ mediaId
297
+ } else {
298
+ mediaItem.requestMetadata.mediaUri?.toString()
299
+ }
300
+ if (fallbackSrc != null) {
301
+ Timber.w("Cache MISS for mediaId='$mediaId' - building minimal track from media item")
302
+ val metadata = mediaItem.mediaMetadata
303
+ return hydrateFavorite(
304
+ Track(
305
+ // A mediaId distinct from the playable uri is the track's stable id —
306
+ // keep it so the item's identity (car now-playing row match) survives.
307
+ id = mediaId.takeIf { it != fallbackSrc },
308
+ url = null,
309
+ src = fallbackSrc,
310
+ artwork = artworkOf(metadata.artworkUri?.toString()),
311
+ artworkSource = null,
312
+ request = null,
313
+ artworkCarPlayTinted = null,
314
+ title = metadata.title?.toString() ?: mediaId,
315
+ subtitle = metadata.artist?.toString(),
316
+ artist = null,
317
+ albumUrl = null,
318
+ album = metadata.albumTitle?.toString(),
319
+ description = metadata.description?.toString(),
320
+ genre = metadata.genre?.toString(),
321
+ duration = null,
322
+ style = null,
323
+ childrenStyle = null,
324
+ favorited = null,
325
+ groupTitle = null,
326
+ live = null,
327
+ imageRow = null,
328
+ )
234
329
  )
235
330
  }
331
+
332
+ throw IllegalStateException("MediaItem not found in cache: $mediaId")
236
333
  }
237
334
 
238
335
  /**
@@ -249,7 +346,7 @@ class BrowserManager {
249
346
  * @param startIndex Index of the item to start playing
250
347
  * @param startPositionMs Position within the start item to begin playback
251
348
  * @return MediaSession.MediaItemsWithStartPosition ready for Media3
252
- * @throws IllegalStateException if any mediaId is not found in cache
349
+ * @throws IllegalStateException if a mediaId is neither cached nor a playable URL
253
350
  */
254
351
  suspend fun resolveMediaItemsForPlayback(
255
352
  mediaItems: List<MediaItem>,
@@ -270,10 +367,13 @@ class BrowserManager {
270
367
  val searchTracks = searchResults.children
271
368
 
272
369
  if (searchTracks != null && searchTracks.isNotEmpty()) {
273
- // Find the selected track in search results
370
+ // Find the selected track in search results (mediaId is the stable id
371
+ // when the track has one, else url/src — see TrackFactory)
274
372
  val mediaId = mediaItem.mediaId
275
373
  val selectedIndex =
276
- searchTracks.indexOfFirst { track -> track.url == mediaId || track.src == mediaId }
374
+ searchTracks.indexOfFirst { track ->
375
+ track.id == mediaId || track.url == mediaId || track.src == mediaId
376
+ }
277
377
 
278
378
  if (selectedIndex >= 0) {
279
379
  Timber.d(
@@ -297,11 +397,14 @@ class BrowserManager {
297
397
  }
298
398
 
299
399
  val mediaId = mediaItems[0].mediaId
400
+ // A failed search match falls through to plain resolution, never to browse
401
+ // expansion — the user asked for that one result, not a browsed list.
402
+ val contextualUrl = if (searchQuery == null) contextualUrlFor(mediaId) else null
300
403
 
301
- if (BrowserPathHelper.isContextual(mediaId)) {
302
- Timber.d("Attempting queue expansion for mediaId='$mediaId'")
404
+ if (contextualUrl != null) {
405
+ Timber.d("Attempting queue expansion for mediaId='$mediaId' via '$contextualUrl'")
303
406
 
304
- val expanded = expandQueueFromContextualUrl(mediaId)
407
+ val expanded = expandQueueFromContextualUrl(contextualUrl)
305
408
 
306
409
  if (expanded != null) {
307
410
  val (tracks, selectedIndex) = expanded
@@ -318,12 +421,11 @@ class BrowserManager {
318
421
  }
319
422
  }
320
423
 
321
- // No expansion - resolve from cache
322
- val mediaIds = mediaItems.map { it.mediaId }
323
- val cachedTracks = resolveMediaIdsFromCache(mediaIds)
424
+ // No expansion - resolve from cache (with a minimal-track fallback for replayed mediaIds)
425
+ val resolvedTracks = mediaItems.map { resolveMediaItemToTrack(it) }
324
426
 
325
427
  // Convert to Media3 MediaItems
326
- val resolvedMediaItems = cachedTracks.map { track -> TrackFactory.toMedia3(track) }
428
+ val resolvedMediaItems = resolvedTracks.map { track -> TrackFactory.toMedia3(track) }
327
429
 
328
430
  return MediaSession.MediaItemsWithStartPosition(resolvedMediaItems, startIndex, startPositionMs)
329
431
  }
@@ -355,6 +457,11 @@ class BrowserManager {
355
457
  if (useCache) {
356
458
  contentCache.get(normalizedPath)?.let { cached ->
357
459
  Timber.d("Content cache HIT for path='$normalizedPath'")
460
+ // Re-key the track cache even on a hit: an id-keyed lookup (stable-id
461
+ // mediaId → contextual url, see contextualUrlFor) must reflect the
462
+ // most recently *browsed* container, which a cached re-display
463
+ // otherwise wouldn't re-register.
464
+ cacheChildren(cached)
358
465
  // Re-hydrate favorites in case they changed since caching
359
466
  return hydrateChildren(cached)
360
467
  }
@@ -387,35 +494,37 @@ class BrowserManager {
387
494
  Timber.d("Invalidated content cache for path='$path'")
388
495
  }
389
496
 
390
- /** Clears all cached content. Used when artwork resolver is wired up to force re-fetch. */
497
+ /** Clears all cached content. */
391
498
  fun clearContentCache() {
392
499
  contentCache.evictAll()
500
+ // Bump the layer generation so request/browse resolver thunks are re-resolved on the next
501
+ // request (invalidateAllContent → clearContentCache is the documented re-resolve trigger).
502
+ layerGeneration += 1
503
+ // Invalidated content's artwork resolutions go with it (same staleness rule as the
504
+ // config setter).
505
+ artworkResolutions.clear()
506
+ onArtworkRegistriesCleared?.invoke()
393
507
  Timber.d("Cleared all content cache")
394
508
  }
395
509
 
396
510
  private suspend fun resolveUncached(path: String): ResolvedTrack {
397
- val routes = config.routes
398
- if (routes.isNullOrEmpty()) {
399
- Timber.e("No routes configured for path: $path")
400
- throw ContentNotFoundException(path)
511
+ // Match an explicit route (or the '*' default). With no match, fall back to
512
+ // the implicit default: fetch the path via the request + browse config.
513
+ val match = config.routes?.let { findBestRouteMatch(path, it) }
514
+
515
+ val resolvedTrack: ResolvedTrack
516
+ val effectiveArtworkConfig: ArtworkRequestConfig?
517
+ if (match != null) {
518
+ val (routeEntry, routeParams) = match
519
+ Timber.d("Matched route: ${routeEntry.path} with params: $routeParams")
520
+ resolvedTrack = resolveRouteEntry(routeEntry, path, routeParams)
521
+ effectiveArtworkConfig = routeEntry.artwork ?: config.artwork
522
+ } else {
523
+ Timber.d("No route matched for path: $path — using implicit default")
524
+ resolvedTrack = executeApiRequest(null, path, mapOf("path" to path))
525
+ effectiveArtworkConfig = config.artwork
401
526
  }
402
527
 
403
- // Find best matching route
404
- val (routeEntry, routeParams) =
405
- findBestRouteMatch(path, routes)
406
- ?: run {
407
- Timber.e("No route matched for path: $path")
408
- throw ContentNotFoundException(path)
409
- }
410
-
411
- Timber.d("Matched route: ${routeEntry.path} with params: $routeParams")
412
-
413
- // Resolve the track from the route
414
- val resolvedTrack = resolveRouteEntry(routeEntry, path, routeParams)
415
-
416
- // Get effective artwork config: per-route overrides global
417
- val effectiveArtworkConfig = routeEntry.artwork ?: config.artwork
418
-
419
528
  // Transform children: generate contextual URLs and transform artwork URLs
420
529
  val transformedChildren =
421
530
  resolvedTrack.children?.let { children ->
@@ -423,8 +532,11 @@ class BrowserManager {
423
532
  children
424
533
  .mapIndexed { index, track ->
425
534
  async {
426
- // Validate that track has stable identifier
427
- validateTrack(track, "Child track")
535
+ // Validate that track has stable identifier. A track carrying an
536
+ // imageRow is exempt: a url-less row is a pure preview — never
537
+ // selected, navigated to, or cached (its items carry their own
538
+ // identity; on Android Auto it expands into them).
539
+ if (track.imageRow.isNullOrEmpty()) validateTrack(track, "Child track")
428
540
 
429
541
  var transformedTrack = track
430
542
 
@@ -446,22 +558,35 @@ class BrowserManager {
446
558
  )
447
559
  }
448
560
 
449
- // Transform artwork URL if resolver is configured
450
- // At browse-time, we don't have display size info
451
- val resolver = artworkUrlResolver
452
- if (resolver != null) {
453
- val browseContext = ImageContext(null, null)
561
+ // Playable image-row items get contextual URLs like any list
562
+ // row, so a tile tap expands into its section's queue instead
563
+ // of a queue of one (ADR 0006).
564
+ transformedTrack.imageRow?.let { items ->
454
565
  transformedTrack =
455
- transformArtworkUrl(
456
- transformedTrack,
457
- effectiveArtworkConfig,
458
- resolver,
459
- path,
460
- index,
461
- browseContext,
566
+ transformedTrack.copy(
567
+ imageRow =
568
+ items
569
+ .map { item ->
570
+ if (item.url == null && item.src != null) {
571
+ item.copy(url = BrowserPathHelper.build(path, item.src))
572
+ } else {
573
+ item
574
+ }
575
+ }
576
+ .toTypedArray()
462
577
  )
463
578
  }
464
579
 
580
+ // Transform artwork URL. At browse-time there is no display size info.
581
+ transformedTrack =
582
+ transformArtworkUrl(
583
+ transformedTrack,
584
+ effectiveArtworkConfig,
585
+ path,
586
+ index,
587
+ ImageContext(null, null),
588
+ )
589
+
465
590
  transformedTrack
466
591
  }
467
592
  }
@@ -478,7 +603,7 @@ class BrowserManager {
478
603
  }
479
604
 
480
605
  /**
481
- * Transforms a track's artwork using the configured resolver. Populates artworkSource with the
606
+ * Transforms a track's artwork via [resolveArtworkUrl]. Populates artworkSource with the
482
607
  * transformed ImageSource, keeping artwork unchanged. Handles all edge cases: undefined returns,
483
608
  * errors, missing artwork.
484
609
  *
@@ -487,7 +612,6 @@ class BrowserManager {
487
612
  private suspend fun transformArtworkUrl(
488
613
  track: Track,
489
614
  artworkConfig: ArtworkRequestConfig?,
490
- resolver: suspend (Track, ArtworkRequestConfig?, ImageContext?) -> ImageSource?,
491
615
  path: String,
492
616
  index: Int,
493
617
  imageContext: ImageContext? = null,
@@ -498,7 +622,7 @@ class BrowserManager {
498
622
  }
499
623
 
500
624
  return try {
501
- val imageSource = resolver(track, artworkConfig, imageContext)
625
+ val imageSource = resolveArtworkUrl(track, artworkConfig, imageContext)
502
626
 
503
627
  when {
504
628
  // resolve returned null → no artwork source
@@ -511,6 +635,15 @@ class BrowserManager {
511
635
  // resolve returned ImageSource → set artworkSource
512
636
  else -> {
513
637
  Timber.d("[$path] Child[$index] '${track.title}': artworkSource set: ${imageSource.uri}")
638
+ // Remember how this URI was produced so display-time loading (which only
639
+ // gets a URI from Media3) can re-resolve Track-first with a size hint.
640
+ // Register the per-route config only when it isn't the global fallback,
641
+ // so display-time resolution reads the *current* config.artwork.
642
+ artworkResolutions.register(
643
+ imageSource.uri,
644
+ track,
645
+ artworkConfig?.takeIf { it !== config.artwork },
646
+ )
514
647
  track.copy(artworkSource = imageSource)
515
648
  }
516
649
  }
@@ -549,8 +682,20 @@ class BrowserManager {
549
682
  return null
550
683
  }
551
684
 
685
+ // Queue scope is the tapped section, not the whole page (ADR 0006). An
686
+ // id that no longer appears on the page aborts the expansion — the
687
+ // caller falls back to the stored single track; silently queueing the
688
+ // changed list would resume the wrong station.
689
+ val sectionTracks =
690
+ when (val section = SectionScope.section(children.toList(), trackId)) {
691
+ is SectionScope.Section.ImageRow ->
692
+ with(TrackFactory) { section.items.map { it.toTrack(groupTitle = null) } }
693
+ is SectionScope.Section.Run -> section.tracks
694
+ null -> return null
695
+ }
696
+
552
697
  // Filter to only playable tracks (tracks with src)
553
- val playableTracks = children.filter { track -> track.src != null }
698
+ val playableTracks = sectionTracks.filter { track -> track.src != null }
554
699
 
555
700
  if (playableTracks.isEmpty()) {
556
701
  Timber.w("Parent has no playable tracks, cannot expand queue")
@@ -642,8 +787,9 @@ class BrowserManager {
642
787
  * @return Array of Track results, or null if not found
643
788
  */
644
789
  fun getCachedSearchResults(query: String): Array<Track>? {
645
- if (query != lastSearchQuery) return null
646
- return lastSearchResults?.map { hydrateFavorite(it) }?.toTypedArray()
790
+ val (cachedQuery, cachedResults) = lastSearch ?: return null
791
+ if (query != cachedQuery) return null
792
+ return cachedResults.map { hydrateFavorite(it) }.toTypedArray()
647
793
  }
648
794
 
649
795
  /**
@@ -662,6 +808,7 @@ class BrowserManager {
662
808
  album = null,
663
809
  title = null,
664
810
  playlist = null,
811
+ reference = MediaReference.UNKNOWN,
665
812
  )
666
813
  )
667
814
  }
@@ -686,10 +833,11 @@ class BrowserManager {
686
833
 
687
834
  // Check if result is browsable-only (container/route) vs playable
688
835
  // If it's browsable but also playable (has src or playable=true), treat it as playable
836
+ val firstResultUrl = firstResult.url
689
837
  val tracksToFilter =
690
- if (firstResult.src == null) {
691
- Timber.d("First search result is browsable-only, resolving: ${firstResult.url}")
692
- val resolvedTrack = resolve(firstResult.url!!)
838
+ if (firstResult.src == null && firstResultUrl != null) {
839
+ Timber.d("First search result is browsable-only, resolving: $firstResultUrl")
840
+ val resolvedTrack = resolve(firstResultUrl)
693
841
  resolvedTrack.children
694
842
  ?.filter { it.src != null }
695
843
  ?.takeIf { it.isNotEmpty() }
@@ -717,6 +865,7 @@ class BrowserManager {
717
865
  album = null,
718
866
  title = null,
719
867
  playlist = null,
868
+ reference = MediaReference.UNKNOWN,
720
869
  )
721
870
  )
722
871
  }
@@ -735,20 +884,34 @@ class BrowserManager {
735
884
  val searchPath = BrowserPathHelper.createSearchPath(params.query)
736
885
 
737
886
  try {
738
- // Execute search
739
- val searchResults = resolveSearch(params)
887
+ // Execute search. Drop results without a stable identifier (url or src): search results come
888
+ // from server/JS data that doesn't pass through validateTrack like browse children do, and
889
+ // downstream conversion (TrackFactory.toMedia3) and browsable-result resolution require one.
890
+ val searchResults =
891
+ resolveSearch(params)
892
+ .filter { track ->
893
+ val valid = track.url != null || track.src != null
894
+ if (!valid) {
895
+ Timber.w("Dropping search result without url or src: '${track.title}'")
896
+ }
897
+ valid
898
+ }
899
+ .toTypedArray()
740
900
 
741
901
  // Create ResolvedTrack
742
902
  val searchResolvedTrack =
743
903
  ResolvedTrack(
904
+ id = null,
744
905
  url = searchPath,
745
906
  title = "Search: ${params.query}",
746
907
  children = searchResults,
747
908
  carPlaySiriListButton = null,
748
909
  artwork = null,
749
910
  artworkSource = null,
911
+ request = null,
750
912
  artworkCarPlayTinted = null,
751
913
  artist = null,
914
+ albumUrl = null,
752
915
  description = null,
753
916
  subtitle = null,
754
917
  album = null,
@@ -764,8 +927,7 @@ class BrowserManager {
764
927
  )
765
928
 
766
929
  // Cache search results for getCachedSearchResults()
767
- lastSearchQuery = params.query
768
- lastSearchResults = searchResults
930
+ lastSearch = params.query to searchResults
769
931
 
770
932
  // Cache individual tracks for Media3 lookups
771
933
  cacheChildren(searchResolvedTrack)
@@ -780,14 +942,17 @@ class BrowserManager {
780
942
  // Return empty search result on error
781
943
  val emptySearchResult =
782
944
  ResolvedTrack(
945
+ id = null,
783
946
  url = searchPath,
784
947
  title = "Search: ${params.query}",
785
948
  children = emptyArray(),
786
949
  carPlaySiriListButton = null,
787
950
  artwork = null,
788
951
  artworkSource = null,
952
+ request = null,
789
953
  artworkCarPlayTinted = null,
790
954
  artist = null,
955
+ albumUrl = null,
791
956
  description = null,
792
957
  subtitle = null,
793
958
  album = null,
@@ -848,6 +1013,15 @@ class BrowserManager {
848
1013
  onTabsChanged = callback
849
1014
  }
850
1015
 
1016
+ /**
1017
+ * Set callback invoked whenever both [artworkResolutions] and the browse-artwork registry are
1018
+ * cleared (config swap or content invalidation). The caller should clear any parallel
1019
+ * [com.audiobrowser.browser.BrowseArtworkRegistry] instance it owns.
1020
+ */
1021
+ fun setOnArtworkRegistriesCleared(callback: () -> Unit) {
1022
+ onArtworkRegistriesCleared = callback
1023
+ }
1024
+
851
1025
  /**
852
1026
  * Query navigation tabs from the configured tabs source. This is an async operation that resolves
853
1027
  * the tabs configuration.
@@ -925,15 +1099,25 @@ class BrowserManager {
925
1099
  entry.browseCallback?.let { callback ->
926
1100
  Timber.d("Resolving route via callback")
927
1101
  val param = BrowserSourceCallbackParam(path, routeParams)
928
- val promise = callback.invoke(param)
929
- val innerPromise = promise.await()
930
- val result = innerPromise.await()
931
-
932
- // Handle BrowseResult variant: either ResolvedTrack or BrowseError
933
- return result.match(
934
- first = { resolvedTrack -> resolvedTrack },
935
- second = { browseError -> throw CallbackException(browseError.error) }
936
- )
1102
+ // BrowserSourceCallback may return a BrowseResult synchronously or via a
1103
+ // Promise. Nitro flattens (ResolvedTrack | BrowseError) | Promise<BrowseResult>
1104
+ // into a 3-arm variant: sync track, sync error, or a Promise resolving to a
1105
+ // BrowseResult (which is itself a ResolvedTrack | BrowseError variant).
1106
+ return callback
1107
+ .invoke(param)
1108
+ .await()
1109
+ .match(
1110
+ first = { resolvedTrack -> resolvedTrack },
1111
+ second = { browseError -> throw CallbackException(browseError.error) },
1112
+ third = { promise ->
1113
+ promise
1114
+ .await()
1115
+ .match(
1116
+ first = { resolvedTrack -> resolvedTrack },
1117
+ second = { browseError -> throw CallbackException(browseError.error) },
1118
+ )
1119
+ },
1120
+ )
937
1121
  }
938
1122
 
939
1123
  entry.browseConfig?.let { apiConfig ->
@@ -999,34 +1183,119 @@ class BrowserManager {
999
1183
  }
1000
1184
 
1001
1185
  /**
1002
- * Execute an API request for browser content. Handles URL parameter substitution, config merging,
1003
- * and transforms.
1186
+ * Resolves a single request/browse layer. When a resolver thunk is present it is invoked and its
1187
+ * result awaited. The resolver is Promise-only (the TS layer normalizes a sync-or-async thunk via
1188
+ * Promise.resolve), so its native shape is `Promise<Promise<TransformableRequestConfig>>` — a
1189
+ * double await. When there is no resolver the static layer config is returned as-is.
1190
+ */
1191
+ private suspend fun resolveLayer(
1192
+ staticConfig: TransformableRequestConfig?,
1193
+ resolver: Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____?,
1194
+ ): TransformableRequestConfig? {
1195
+ if (resolver == null) return staticConfig
1196
+ // Promise-only resolver (the TS layer wraps a sync-or-async thunk in
1197
+ // Promise.resolve) → double await: the bridge promise, then the JS promise.
1198
+ return resolver.invoke().await().await()
1199
+ }
1200
+
1201
+ /**
1202
+ * Ensures the request/browse resolver layers are resolved for the current [layerGeneration],
1203
+ * caching the result. Re-resolves when the generation changes (config set or content
1204
+ * invalidation). Uses a simple generation guard with no in-flight cache: a benign idempotent
1205
+ * double-resolve under concurrent first-requests is acceptable, and on a thrown resolver nothing
1206
+ * is cached so the next request retries naturally.
1207
+ */
1208
+ internal suspend fun ensureLayersResolved() {
1209
+ if (resolvedLayerGeneration == layerGeneration) return
1210
+ val generation = layerGeneration
1211
+ val req = resolveLayer(config.request, config.requestResolver)
1212
+ val brw = resolveLayer(config.browse, config.browseResolver)
1213
+ // A newer generation started while we were awaiting — drop this stale result.
1214
+ if (generation != layerGeneration) return
1215
+ resolvedRequestLayer = req
1216
+ resolvedBrowseLayer = brw
1217
+ resolvedLayerGeneration = generation
1218
+ }
1219
+
1220
+ /**
1221
+ * Ensures the request layer is resolved for the current generation and returns it (the resolver
1222
+ * result when a [BrowserConfig.requestResolver] is configured, else the static
1223
+ * [BrowserConfig.request]).
1224
+ *
1225
+ * Consumers outside the browse path (media URL building, artwork) must obtain the request layer
1226
+ * through this accessor rather than reading the static `config.request`, so a resolver-only
1227
+ * consumer still gets a baseUrl/headers/transform for media, artwork, and now-playing artwork.
1228
+ */
1229
+ internal suspend fun resolvedRequestConfig(): TransformableRequestConfig? {
1230
+ ensureLayersResolved()
1231
+ return resolvedRequestLayer
1232
+ }
1233
+
1234
+ /**
1235
+ * Builds the HTTP request for an API-backed path by layering request (shared) → kind
1236
+ * (browse/search) → route configs. Each layer's transform receives the previous layer's output; a
1237
+ * layer with no transform merges its static fields.
1238
+ *
1239
+ * `initialQuery` seeds query params onto the BASE the kind layer receives (e.g. search q/mode/…):
1240
+ * a layer with a transform "wins completely" and is handed only the base, so params placed on a
1241
+ * layer's own static query would be dropped before the transform runs. The same goes for `path`:
1242
+ * it is carried from the base through every layer (only a transform may change it), so a kind
1243
+ * whose config supplies the path (search) must seed it via the `path` parameter. Mirrors iOS
1244
+ * `buildApiRequest`.
1245
+ *
1246
+ * @throws ContentNotFoundException when no layer supplies a baseUrl — there is nothing to fetch,
1247
+ * so the path is genuinely "not found" rather than a network error (mirrors iOS's `guard let
1248
+ * baseUrl`).
1249
+ */
1250
+ internal suspend fun buildApiRequest(
1251
+ kindConfig: TransformableRequestConfig?,
1252
+ routeConfig: TransformableRequestConfig?,
1253
+ path: String?,
1254
+ params: Map<String, String>,
1255
+ initialQuery: Map<String, String>? = null,
1256
+ ): HttpClient.HttpRequest {
1257
+ // Resolve the request/browse resolver thunks once per content generation (cached).
1258
+ ensureLayersResolved()
1259
+
1260
+ var merged =
1261
+ RequestConfig(
1262
+ method = null,
1263
+ path = path,
1264
+ baseUrl = null,
1265
+ headers = null,
1266
+ query = null,
1267
+ body = null,
1268
+ contentType = null,
1269
+ userAgent = null,
1270
+ )
1271
+ resolvedRequestLayer?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
1272
+ if (!initialQuery.isNullOrEmpty()) {
1273
+ merged = merged.copy(query = (merged.query ?: emptyMap()) + initialQuery)
1274
+ }
1275
+ kindConfig?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
1276
+ routeConfig?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
1277
+
1278
+ if (merged.baseUrl.isNullOrBlank()) {
1279
+ throw ContentNotFoundException(path ?: "")
1280
+ }
1281
+ return RequestConfigBuilder.buildHttpRequest(merged)
1282
+ }
1283
+
1284
+ /**
1285
+ * Execute an API request for browser content. Request building (layering + transforms + baseUrl
1286
+ * guard) lives in [buildApiRequest]; this adds the browse-specific response shape (a
1287
+ * ResolvedTrack page object).
1004
1288
  */
1005
1289
  private suspend fun executeApiRequest(
1006
- apiConfig: TransformableRequestConfig,
1290
+ apiConfig: TransformableRequestConfig?,
1007
1291
  path: String,
1008
1292
  routeParams: Map<String, String>,
1009
1293
  ): ResolvedTrack {
1010
1294
  return withContext(Dispatchers.IO) {
1011
- // 1. Start with base config, using the navigation path as default
1012
- val baseConfig =
1013
- config.request?.let { req ->
1014
- RequestConfigBuilder.toRequestConfig(req).copy(path = req.path ?: path)
1015
- }
1016
- ?: RequestConfig(
1017
- path = path,
1018
- method = null,
1019
- baseUrl = null,
1020
- headers = null,
1021
- query = null,
1022
- body = null,
1023
- contentType = null,
1024
- userAgent = null,
1025
- )
1026
- val mergedConfig = RequestConfigBuilder.mergeConfig(baseConfig, apiConfig, routeParams)
1027
-
1028
- // 2. Build and execute HTTP request
1029
- val httpRequest = RequestConfigBuilder.buildHttpRequest(mergedConfig)
1295
+ // request (shared) → browse (kind) → route. apiConfig is null for the
1296
+ // implicit default (an unmatched browse path → fetch via request + browse + path).
1297
+ ensureLayersResolved()
1298
+ val httpRequest = buildApiRequest(resolvedBrowseLayer, apiConfig, path, routeParams)
1030
1299
  val response = httpClient.request(httpRequest)
1031
1300
 
1032
1301
  response.fold(
@@ -1066,24 +1335,10 @@ class BrowserManager {
1066
1335
  ): Array<Track> {
1067
1336
  return withContext(Dispatchers.IO) {
1068
1337
  try {
1069
- // 1. Start with base config
1070
- val baseConfig =
1071
- config.request?.let { RequestConfigBuilder.toRequestConfig(it) }
1072
- ?: RequestConfig(
1073
- method = null,
1074
- path = null,
1075
- baseUrl = null,
1076
- headers = null,
1077
- query = null,
1078
- body = null,
1079
- contentType = null,
1080
- userAgent = null,
1081
- )
1082
-
1083
- // 2. Build query parameters from SearchParams
1084
1338
  val searchQueryParams = buildMap {
1085
1339
  put("q", params.query)
1086
1340
  params.mode?.let { put("mode", it.toString().lowercase()) }
1341
+ if (params.reference == MediaReference.MY) put("reference", "my")
1087
1342
  params.genre?.let { put("genre", it) }
1088
1343
  params.artist?.let { put("artist", it) }
1089
1344
  params.album?.let { put("album", it) }
@@ -1091,31 +1346,26 @@ class BrowserManager {
1091
1346
  params.playlist?.let { put("playlist", it) }
1092
1347
  }
1093
1348
 
1094
- // 3. Create a copy of API config with added search parameters
1095
- val searchConfig =
1096
- TransformableRequestConfig(
1097
- transform = apiConfig.transform,
1098
- method = apiConfig.method,
1349
+ // request (shared) → search (kind); no browse layer and no route — search
1350
+ // is its own kind. The search params seed the base (see buildApiRequest
1351
+ // docs), and so does the search config's path: a layer's static path
1352
+ // never applies (the path is carried from the base), so the caller
1353
+ // seeds it — mirrors the web stub's fetchSearchResults.
1354
+ val httpRequest =
1355
+ buildApiRequest(
1356
+ kindConfig = apiConfig,
1357
+ routeConfig = null,
1099
1358
  path = apiConfig.path,
1100
- baseUrl = apiConfig.baseUrl,
1101
- headers = apiConfig.headers,
1102
- query = (apiConfig.query ?: emptyMap()) + searchQueryParams,
1103
- body = apiConfig.body,
1104
- contentType = apiConfig.contentType,
1105
- userAgent = apiConfig.userAgent,
1359
+ params = emptyMap(),
1360
+ initialQuery = searchQueryParams,
1106
1361
  )
1107
-
1108
- // 3. Merge configs and apply transform if provided
1109
- var mergedConfig = RequestConfigBuilder.mergeConfig(baseConfig, searchConfig, emptyMap())
1110
-
1111
- // 4. Build and execute HTTP request
1112
- val httpRequest = RequestConfigBuilder.buildHttpRequest(mergedConfig)
1113
1362
  val response = httpClient.request(httpRequest)
1114
1363
 
1115
1364
  response.fold(
1116
1365
  onSuccess = { httpResponse ->
1117
1366
  if (httpResponse.isSuccessful) {
1118
- // 4. Parse response as Track array
1367
+ // The search endpoint returns a bare Track array (unlike browse,
1368
+ // which returns a page object). iOS parses it the same way.
1119
1369
  val jsonTracks = json.decodeFromString<List<JsonTrack>>(httpResponse.body)
1120
1370
  jsonTracks.map { it.toNitro() }.toTypedArray()
1121
1371
  } else {
@@ -1156,8 +1406,23 @@ class CallbackException(message: String) : Exception(message)
1156
1406
  */
1157
1407
  data class BrowserConfig(
1158
1408
  val request: TransformableRequestConfig? = null,
1409
+ // Resolver thunk for the shared request layer. When set, it is resolved once per content
1410
+ // generation (re-resolved after invalidateAllContent), cached, and merged per request — instead
1411
+ // of carrying a static `request`. `request` and `requestResolver` are mutually exclusive in
1412
+ // practice (the consumer sets one or the other), but both are merged if present.
1413
+ val requestResolver:
1414
+ Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____? =
1415
+ null,
1416
+ val browse: TransformableRequestConfig? = null,
1417
+ // Resolver thunk for the browse layer. See `requestResolver`.
1418
+ val browseResolver:
1419
+ Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____? =
1420
+ null,
1159
1421
  val media: MediaRequestConfig? = null,
1160
1422
  val artwork: ArtworkRequestConfig? = null,
1423
+ // Now-playing-only artwork configuration (lock screen / notification / Android Auto now-playing).
1424
+ // A distinct kind from `artwork`; the now-playing path falls back to `artwork` when this is null.
1425
+ val nowPlayingArtwork: ArtworkRequestConfig? = null,
1161
1426
  // Routes as array with flattened entries (includes __tabs__, __search__, and __default__ special
1162
1427
  // routes)
1163
1428
  val routes: Array<NativeRouteEntry>? = null,