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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (660) hide show
  1. package/README.md +35 -21
  2. package/android/build.gradle +35 -12
  3. package/android/gradle.properties +2 -2
  4. package/android/src/main/AndroidManifest.xml +3 -1
  5. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
  6. package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
  7. package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
  8. package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
  9. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
  10. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
  11. package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
  12. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
  13. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
  14. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
  15. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -7
  16. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  17. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  18. package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
  19. package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
  20. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
  21. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
  22. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
  23. package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
  24. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
  25. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
  26. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
  27. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
  28. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
  29. package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
  30. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  31. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
  32. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
  33. package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
  34. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
  35. package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
  36. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
  37. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
  38. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
  39. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
  40. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -40
  41. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  42. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
  43. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
  44. package/android/src/main/res/values/strings.xml +25 -5
  45. package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
  46. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
  47. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  48. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
  49. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
  50. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  51. package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
  52. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
  53. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
  54. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
  55. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
  56. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  57. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  58. package/android/src/test/java/com/audiobrowser/model/IcyTitleTest.kt +49 -0
  59. package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
  60. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
  61. package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
  62. package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
  63. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  64. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
  65. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  66. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
  67. package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
  68. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
  69. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
  70. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
  71. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
  72. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  73. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -0
  74. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  75. package/ios/Browser/BrowserConfig.swift +102 -5
  76. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  77. package/ios/Browser/BrowserManager.swift +230 -205
  78. package/ios/Browser/BrowserPathHelper.swift +44 -44
  79. package/ios/Browser/InertStyleDiagnostic.swift +279 -0
  80. package/ios/Browser/JsonModels.swift +264 -106
  81. package/ios/Browser/SearchDrillIn.swift +7 -7
  82. package/ios/Browser/SectionPresentation.swift +64 -0
  83. package/ios/Browser/SectionScope.swift +58 -24
  84. package/ios/Browser/StyleResolver.swift +58 -0
  85. package/ios/Browser/TabBarEntries.swift +9 -3
  86. package/ios/Browser/TrackIdTemplate.swift +37 -0
  87. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  88. package/ios/CarPlay/CarPlayController.swift +227 -85
  89. package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
  90. package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
  91. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  92. package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
  93. package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
  94. package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
  95. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  96. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  97. package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
  98. package/ios/Extension/Section+Copying.swift +29 -0
  99. package/ios/Extension/String+IcyCharset.swift +21 -0
  100. package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
  101. package/ios/Extension/Track+Copying.swift +8 -12
  102. package/ios/Extension/Track+Identity.swift +18 -0
  103. package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
  104. package/ios/Http/HttpClient.swift +8 -2
  105. package/ios/HybridAudioBrowser.swift +253 -100
  106. package/ios/Model/NitroTypeStubs.swift +125 -23
  107. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  108. package/ios/Model/RemoteCommand.swift +18 -0
  109. package/ios/Model/TrackPlayerError.swift +2 -2
  110. package/ios/Option/SessionCategories.swift +3 -2
  111. package/ios/Player/MediaIntentCriteria.swift +17 -9
  112. package/ios/Player/MediaLoader.swift +33 -9
  113. package/ios/Player/NowPlayingUpdater.swift +54 -6
  114. package/ios/Player/PlaybackCoordinator.swift +13 -6
  115. package/ios/Player/PlaybackErrorHandler.swift +4 -4
  116. package/ios/Player/PlaybackTimer.swift +8 -2
  117. package/ios/Player/QueueManager.swift +46 -13
  118. package/ios/Player/RetryManager.swift +80 -18
  119. package/ios/Player/SeekCompletionHandler.swift +3 -1
  120. package/ios/Player/ShuffleOrder.swift +33 -0
  121. package/ios/Player/SleepTimerHandling.swift +13 -0
  122. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
  123. package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
  124. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  125. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  126. package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
  127. package/ios/Tests/JsonModelsTests.swift +150 -1
  128. package/ios/Tests/LRUCacheTests.swift +111 -0
  129. package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
  130. package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
  131. package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
  132. package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
  133. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  134. package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
  135. package/ios/Tests/QueueManagerTests.swift +122 -10
  136. package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
  137. package/ios/Tests/RemoteCommandTests.swift +67 -0
  138. package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
  139. package/ios/Tests/RetryManagerTests.swift +89 -0
  140. package/ios/Tests/SearchDrillInTests.swift +16 -16
  141. package/ios/Tests/SectionPresentationTests.swift +90 -0
  142. package/ios/Tests/SectionScopeTests.swift +87 -55
  143. package/ios/Tests/StyleResolverTests.swift +127 -0
  144. package/ios/Tests/Support/TestTypes.swift +8 -1
  145. package/ios/Tests/TabBarEntriesTests.swift +19 -19
  146. package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
  147. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  148. package/ios/Tests/TrackIdentityTests.swift +20 -0
  149. package/ios/Tests/TrackSelectorTests.swift +168 -33
  150. package/ios/TrackPlayer.swift +63 -35
  151. package/ios/TrackPlayerCallbacks.swift +5 -38
  152. package/ios/TrackSelector.swift +45 -30
  153. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  154. package/ios/Util/LRUCache.swift +4 -1
  155. package/ios/Util/NetworkMonitor.swift +43 -26
  156. package/ios/Util/SFSymbolRenderer.swift +24 -2
  157. package/ios/Util/SVGProcessor.swift +9 -2
  158. package/lib/commonjs/AudioBrowser.js +8 -8
  159. package/lib/commonjs/AudioBrowser.js.map +1 -1
  160. package/lib/commonjs/CLAUDE.md +52 -0
  161. package/lib/commonjs/features/browser-config.js +12 -8
  162. package/lib/commonjs/features/browser-config.js.map +1 -1
  163. package/lib/commonjs/features/browser.js +11 -3
  164. package/lib/commonjs/features/browser.js.map +1 -1
  165. package/lib/commonjs/features/carConnection.js +1 -1
  166. package/lib/commonjs/features/equalizer.js +1 -1
  167. package/lib/commonjs/features/errors.js +3 -3
  168. package/lib/commonjs/features/favorites.js +14 -11
  169. package/lib/commonjs/features/favorites.js.map +1 -1
  170. package/lib/commonjs/features/gate.js +16 -1
  171. package/lib/commonjs/features/gate.js.map +1 -1
  172. package/lib/commonjs/features/metadata.js +20 -3
  173. package/lib/commonjs/features/metadata.js.map +1 -1
  174. package/lib/commonjs/features/network.js +1 -1
  175. package/lib/commonjs/features/nowPlaying.js +1 -1
  176. package/lib/commonjs/features/output.js +1 -1
  177. package/lib/commonjs/features/playback/controls.js +4 -2
  178. package/lib/commonjs/features/playback/controls.js.map +1 -1
  179. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  180. package/lib/commonjs/features/playback/playing.js +1 -1
  181. package/lib/commonjs/features/playback/progress.js +3 -2
  182. package/lib/commonjs/features/playback/progress.js.map +1 -1
  183. package/lib/commonjs/features/playback/rate.js +8 -1
  184. package/lib/commonjs/features/playback/rate.js.map +1 -1
  185. package/lib/commonjs/features/playback/state.js +3 -4
  186. package/lib/commonjs/features/playback/state.js.map +1 -1
  187. package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
  188. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
  189. package/lib/commonjs/features/playback/volume.js +1 -1
  190. package/lib/commonjs/features/player/options.js +48 -34
  191. package/lib/commonjs/features/player/options.js.map +1 -1
  192. package/lib/commonjs/features/player/setup.js +18 -9
  193. package/lib/commonjs/features/player/setup.js.map +1 -1
  194. package/lib/commonjs/features/player/validateOptions.js +23 -0
  195. package/lib/commonjs/features/player/validateOptions.js.map +1 -0
  196. package/lib/commonjs/features/queue/activeTrack.js +15 -4
  197. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  198. package/lib/commonjs/features/queue/queue.js +13 -6
  199. package/lib/commonjs/features/queue/queue.js.map +1 -1
  200. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  201. package/lib/commonjs/features/queue/shuffle.js +1 -1
  202. package/lib/commonjs/features/remoteControls.js +13 -92
  203. package/lib/commonjs/features/remoteControls.js.map +1 -1
  204. package/lib/commonjs/features/sleepTimer.js +13 -1
  205. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  206. package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
  207. package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
  208. package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
  209. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  210. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  211. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  212. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  213. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  214. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  215. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  216. package/lib/commonjs/web/NativeAudioBrowser.js +200 -75
  217. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  218. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
  219. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
  220. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  221. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  222. package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
  223. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  224. package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
  225. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
  226. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
  227. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
  228. package/lib/commonjs/web/browser/BrowserManager.js +175 -100
  229. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  230. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  231. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  232. package/lib/commonjs/web/browser/SearchManager.js +18 -23
  233. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  234. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  235. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  236. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  237. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  238. package/lib/commonjs/web/browser/sections.js +89 -0
  239. package/lib/commonjs/web/browser/sections.js.map +1 -0
  240. package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
  241. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  242. package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
  243. package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
  244. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  245. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  246. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  247. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  248. package/lib/module/AudioBrowser.js +2 -1
  249. package/lib/module/AudioBrowser.js.map +1 -1
  250. package/lib/module/CLAUDE.md +52 -0
  251. package/lib/module/features/browser-config.js +12 -8
  252. package/lib/module/features/browser-config.js.map +1 -1
  253. package/lib/module/features/browser.js +11 -3
  254. package/lib/module/features/browser.js.map +1 -1
  255. package/lib/module/features/carConnection.js +1 -1
  256. package/lib/module/features/equalizer.js +1 -1
  257. package/lib/module/features/errors.js +3 -3
  258. package/lib/module/features/favorites.js +14 -11
  259. package/lib/module/features/favorites.js.map +1 -1
  260. package/lib/module/features/gate.js +16 -1
  261. package/lib/module/features/gate.js.map +1 -1
  262. package/lib/module/features/metadata.js +20 -3
  263. package/lib/module/features/metadata.js.map +1 -1
  264. package/lib/module/features/network.js +1 -1
  265. package/lib/module/features/nowPlaying.js +1 -1
  266. package/lib/module/features/output.js +1 -1
  267. package/lib/module/features/playback/controls.js +4 -2
  268. package/lib/module/features/playback/controls.js.map +1 -1
  269. package/lib/module/features/playback/playWhenReady.js +1 -1
  270. package/lib/module/features/playback/playing.js +1 -1
  271. package/lib/module/features/playback/progress.js +3 -2
  272. package/lib/module/features/playback/progress.js.map +1 -1
  273. package/lib/module/features/playback/rate.js +8 -1
  274. package/lib/module/features/playback/rate.js.map +1 -1
  275. package/lib/module/features/playback/state.js +3 -4
  276. package/lib/module/features/playback/state.js.map +1 -1
  277. package/lib/module/features/playback/trackPlaybackTime.js +0 -5
  278. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
  279. package/lib/module/features/playback/volume.js +1 -1
  280. package/lib/module/features/player/options.js +47 -32
  281. package/lib/module/features/player/options.js.map +1 -1
  282. package/lib/module/features/player/setup.js +17 -8
  283. package/lib/module/features/player/setup.js.map +1 -1
  284. package/lib/module/features/player/validateOptions.js +19 -0
  285. package/lib/module/features/player/validateOptions.js.map +1 -0
  286. package/lib/module/features/queue/activeTrack.js +15 -4
  287. package/lib/module/features/queue/activeTrack.js.map +1 -1
  288. package/lib/module/features/queue/queue.js +13 -6
  289. package/lib/module/features/queue/queue.js.map +1 -1
  290. package/lib/module/features/queue/repeatMode.js +1 -1
  291. package/lib/module/features/queue/shuffle.js +1 -1
  292. package/lib/module/features/remoteControls.js +13 -92
  293. package/lib/module/features/remoteControls.js.map +1 -1
  294. package/lib/module/features/sleepTimer.js +13 -1
  295. package/lib/module/features/sleepTimer.js.map +1 -1
  296. package/lib/module/test-utils/reactNativeStub.js +58 -0
  297. package/lib/module/test-utils/reactNativeStub.js.map +1 -0
  298. package/lib/module/utils/LazyNativeEmitter.js +28 -14
  299. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  300. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  301. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  302. package/lib/module/utils/getTrackIdentity.js +20 -0
  303. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  304. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  305. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  306. package/lib/module/web/NativeAudioBrowser.js +200 -75
  307. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  308. package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
  309. package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
  310. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  311. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  312. package/lib/module/web/TrackPlayer/Player.js +254 -39
  313. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  314. package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
  315. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
  316. package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
  317. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
  318. package/lib/module/web/browser/BrowserManager.js +175 -100
  319. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  320. package/lib/module/web/browser/FavoriteManager.js +31 -21
  321. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  322. package/lib/module/web/browser/SearchManager.js +18 -23
  323. package/lib/module/web/browser/SearchManager.js.map +1 -1
  324. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  325. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  326. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  327. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  328. package/lib/module/web/browser/sections.js +83 -0
  329. package/lib/module/web/browser/sections.js.map +1 -0
  330. package/lib/module/web/http/RequestConfigBuilder.js +110 -27
  331. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  332. package/lib/module/web/http/followMediaRedirect.js +215 -0
  333. package/lib/module/web/http/followMediaRedirect.js.map +1 -0
  334. package/lib/module/web/player/NowPlayingManager.js +4 -3
  335. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  336. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  337. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  338. package/lib/typescript/package.json +1 -0
  339. package/lib/typescript/src/AudioBrowser.d.ts +1 -1
  340. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  341. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  342. package/lib/typescript/src/features/browser.d.ts +10 -2
  343. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  344. package/lib/typescript/src/features/carConnection.d.ts +1 -1
  345. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  346. package/lib/typescript/src/features/errors.d.ts +3 -3
  347. package/lib/typescript/src/features/favorites.d.ts +14 -11
  348. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  349. package/lib/typescript/src/features/gate.d.ts +18 -2
  350. package/lib/typescript/src/features/gate.d.ts.map +1 -1
  351. package/lib/typescript/src/features/metadata.d.ts +20 -3
  352. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  353. package/lib/typescript/src/features/network.d.ts +1 -1
  354. package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
  355. package/lib/typescript/src/features/output.d.ts +1 -1
  356. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  357. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  358. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  359. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  360. package/lib/typescript/src/features/playback/progress.d.ts +1 -1
  361. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  362. package/lib/typescript/src/features/playback/rate.d.ts +8 -1
  363. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
  364. package/lib/typescript/src/features/playback/state.d.ts +3 -4
  365. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  366. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  367. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  368. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  369. package/lib/typescript/src/features/player/options.d.ts +108 -56
  370. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  371. package/lib/typescript/src/features/player/setup.d.ts +14 -4
  372. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  373. package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
  374. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
  375. package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
  376. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  377. package/lib/typescript/src/features/queue/queue.d.ts +7 -4
  378. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  379. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  380. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  381. package/lib/typescript/src/features/remoteControls.d.ts +23 -22
  382. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  383. package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
  384. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  385. package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
  386. package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
  387. package/lib/typescript/src/types/browser-native.d.ts +3 -2
  388. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  389. package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
  390. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  391. package/lib/typescript/src/types/browser.d.ts +82 -57
  392. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  393. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
  394. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  395. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  396. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  397. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  398. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  399. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  400. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  401. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
  402. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
  403. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
  404. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  405. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
  406. package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
  407. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  408. package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
  409. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
  410. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
  411. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
  412. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
  413. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
  414. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
  415. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
  416. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
  417. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
  418. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
  419. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
  420. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
  421. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
  422. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
  423. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
  424. package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
  425. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
  426. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
  427. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  428. package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
  429. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
  430. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
  431. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
  432. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
  433. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
  434. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
  435. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  436. package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
  437. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
  438. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  439. package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
  440. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
  441. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
  442. package/nitrogen/generated/android/c++/JSection.hpp +116 -0
  443. package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
  444. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  445. package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
  446. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
  447. package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
  448. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
  449. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
  450. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
  451. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
  452. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  453. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  454. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
  455. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
  456. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
  457. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
  458. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  459. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
  460. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
  461. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
  462. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  463. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  464. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
  465. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
  466. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
  467. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
  468. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
  469. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  470. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -12
  471. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
  472. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
  473. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
  474. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
  475. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
  476. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
  477. package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
  478. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  479. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  480. package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
  481. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
  482. package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
  483. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
  484. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
  485. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  486. package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
  487. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
  488. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
  489. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  490. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  491. package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
  492. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
  493. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
  494. package/nitrogen/generated/ios/swift/Section.swift +82 -0
  495. package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
  496. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  497. package/nitrogen/generated/ios/swift/Track.swift +28 -63
  498. package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
  499. package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
  500. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
  501. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  502. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  503. package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
  504. package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
  505. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
  506. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
  507. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  508. package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
  509. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
  510. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
  511. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  512. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  513. package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
  514. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
  515. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
  516. package/nitrogen/generated/shared/c++/Section.hpp +102 -0
  517. package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
  518. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  519. package/nitrogen/generated/shared/c++/Track.hpp +18 -30
  520. package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
  521. package/package.json +24 -11
  522. package/src/AudioBrowser.ts +2 -1
  523. package/src/CLAUDE.md +52 -0
  524. package/src/features/battery.test.ts +79 -0
  525. package/src/features/browser-config.test.ts +21 -6
  526. package/src/features/browser-config.ts +12 -8
  527. package/src/features/browser.test.ts +1 -1
  528. package/src/features/browser.ts +11 -3
  529. package/src/features/carConnection.ts +1 -1
  530. package/src/features/equalizer.ts +1 -1
  531. package/src/features/errors.ts +3 -3
  532. package/src/features/favorites.ts +14 -11
  533. package/src/features/gate.ts +16 -1
  534. package/src/features/hook-wiring.test.ts +302 -0
  535. package/src/features/metadata.ts +20 -3
  536. package/src/features/network.ts +1 -1
  537. package/src/features/nowPlaying.ts +1 -1
  538. package/src/features/output.ts +1 -1
  539. package/src/features/playback/controls.ts +4 -2
  540. package/src/features/playback/playWhenReady.ts +1 -1
  541. package/src/features/playback/playing.ts +1 -1
  542. package/src/features/playback/progress.test.ts +169 -0
  543. package/src/features/playback/progress.ts +3 -2
  544. package/src/features/playback/rate.ts +8 -1
  545. package/src/features/playback/state.ts +3 -4
  546. package/src/features/playback/trackPlaybackTime.test.ts +16 -6
  547. package/src/features/playback/trackPlaybackTime.ts +1 -6
  548. package/src/features/playback/volume.ts +1 -1
  549. package/src/features/player/options.ts +126 -76
  550. package/src/features/player/setup.test.ts +24 -7
  551. package/src/features/player/setup.ts +22 -10
  552. package/src/features/player/validateOptions.ts +21 -0
  553. package/src/features/queue/activeTrack.ts +15 -4
  554. package/src/features/queue/queue.ts +13 -6
  555. package/src/features/queue/repeatMode.ts +1 -1
  556. package/src/features/queue/shuffle.ts +1 -1
  557. package/src/features/remoteControls.ts +13 -92
  558. package/src/features/sleepTimer.test.ts +172 -0
  559. package/src/features/sleepTimer.ts +9 -1
  560. package/src/nitro-stub-drift.test.ts +277 -0
  561. package/src/public-surface.test.ts +147 -0
  562. package/src/specs/audio-browser.nitro.ts +5 -4
  563. package/src/style-resolution.test.ts +210 -0
  564. package/src/test-utils/reactNativeStub.ts +60 -0
  565. package/src/types/browser-native.ts +7 -3
  566. package/src/types/browser-nodes.ts +396 -134
  567. package/src/types/browser.ts +85 -59
  568. package/src/utils/LazyNativeEmitter.ts +28 -15
  569. package/src/utils/NativeUpdatedValue.ts +29 -13
  570. package/src/utils/getTrackIdentity.ts +20 -0
  571. package/src/utils/useDebug.test.ts +182 -0
  572. package/src/utils/useNativeUpdatedValue.test.ts +103 -0
  573. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  574. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
  575. package/src/web/NativeAudioBrowser.ts +239 -82
  576. package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
  577. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
  578. package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
  579. package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
  580. package/src/web/TrackPlayer/Player.load.test.ts +137 -1
  581. package/src/web/TrackPlayer/Player.ts +284 -37
  582. package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
  583. package/src/web/TrackPlayer/QueueManager.ts +14 -0
  584. package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
  585. package/src/web/__tests__/lifecycle.test.ts +345 -0
  586. package/src/web/__tests__/lifecycleHarness.ts +351 -0
  587. package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
  588. package/src/web/__tests__/loadSupersession.test.ts +175 -0
  589. package/src/web/__tests__/retry.test.ts +169 -0
  590. package/src/web/browser/BrowserManager.ts +209 -111
  591. package/src/web/browser/FavoriteManager.test.ts +84 -0
  592. package/src/web/browser/FavoriteManager.ts +28 -23
  593. package/src/web/browser/SearchManager.test.ts +75 -0
  594. package/src/web/browser/SearchManager.ts +25 -28
  595. package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
  596. package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
  597. package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
  598. package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
  599. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  600. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  601. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  602. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  603. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  604. package/src/web/browser/sections.test.ts +91 -0
  605. package/src/web/browser/sections.ts +72 -0
  606. package/src/web/http/RequestConfigBuilder.test.ts +241 -1
  607. package/src/web/http/RequestConfigBuilder.ts +140 -33
  608. package/src/web/http/followMediaRedirect.test.ts +289 -0
  609. package/src/web/http/followMediaRedirect.ts +253 -0
  610. package/src/web/player/NowPlayingManager.ts +4 -3
  611. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  612. package/src/web/util/BrowserPathHelper.ts +61 -13
  613. package/website/guide/android-auto.md +114 -0
  614. package/website/guide/android-certificates.md +409 -0
  615. package/website/guide/artwork.md +255 -0
  616. package/website/guide/audio-output.md +135 -0
  617. package/website/guide/automotive.md +86 -0
  618. package/website/guide/basic-usage.md +151 -0
  619. package/website/guide/battery.md +115 -0
  620. package/website/guide/browser.md +581 -0
  621. package/website/guide/carplay.md +620 -0
  622. package/website/guide/configuration.md +173 -0
  623. package/website/guide/equalizer.md +231 -0
  624. package/website/guide/errors.md +310 -0
  625. package/website/guide/favorites.md +154 -0
  626. package/website/guide/gate.md +143 -0
  627. package/website/guide/getting-started.md +46 -0
  628. package/website/guide/hooks.md +238 -0
  629. package/website/guide/live-streams.md +125 -0
  630. package/website/guide/metadata.md +239 -0
  631. package/website/guide/migrating-from-track-player.md +244 -0
  632. package/website/guide/native-callback-fetch.md +127 -0
  633. package/website/guide/network.md +86 -0
  634. package/website/guide/now-playing.md +175 -0
  635. package/website/guide/playback.md +328 -0
  636. package/website/guide/queue.md +300 -0
  637. package/website/guide/remote-controls.md +256 -0
  638. package/website/guide/search.md +255 -0
  639. package/website/guide/sleep-timer.md +268 -0
  640. package/website/guide/track.md +247 -0
  641. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  642. package/ios/Model/MediaURL.swift +0 -67
  643. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  644. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  645. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
  646. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  647. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  648. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
  649. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  653. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  654. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  655. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  656. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
  657. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  658. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  659. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
  660. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
@@ -16,8 +16,8 @@ final class CarPlayNowPlayingManager {
16
16
 
17
17
  private let interfaceController: CPInterfaceController
18
18
  var listItemFactory: (Track, ((CPSelectableListItem, @escaping () -> Void) -> Void)?) -> CPListItem
19
- /// Pushes a browse destination (wired to the controller's navigateToUrl).
20
- var navigateToUrl: ((_ url: String, _ title: String) -> Void)?
19
+ /// Pushes a browse destination (wired to the controller's navigateToPath).
20
+ var navigateToPath: ((_ path: String, _ title: String) -> Void)?
21
21
 
22
22
  private weak var audioBrowser: HybridAudioBrowser?
23
23
  private var nowPlayingObserver: NowPlayingObserver?
@@ -46,11 +46,11 @@ final class CarPlayNowPlayingManager {
46
46
  private var builtRow: BuiltButtonRow?
47
47
 
48
48
  /// Browse path the album line navigates to for the active track —
49
- /// `track.albumUrl`, or the pre-resolved `resolveAlbumUrl` result. Resolved
49
+ /// `track.albumPath`, or the pre-resolved `resolveAlbumPath` result. Resolved
50
50
  /// at track-change time (not at tap) so the album line only surfaces when a
51
51
  /// destination actually exists.
52
52
  private var albumArtistDestination: String?
53
- /// Drops a stale async `resolveAlbumUrl` result when the track changed while
53
+ /// Drops a stale async `resolveAlbumPath` result when the track changed while
54
54
  /// the resolver was in flight (latest-update-wins).
55
55
  private var albumArtistGeneration: UInt = 0
56
56
  /// The just-tapped track whose load is still in flight. CarPlay reads
@@ -214,7 +214,7 @@ final class CarPlayNowPlayingManager {
214
214
 
215
215
  /// Re-resolves the album line's destination for the active track and
216
216
  /// enables the (tappable) album/artist button only when one exists:
217
- /// `track.albumUrl`, or the app's `resolveAlbumUrl` result.
217
+ /// `track.albumPath`, or the app's `resolveAlbumPath` result.
218
218
  /// Marks `track` as the just-tapped (still loading) track and derives the
219
219
  /// album line's destination from it. Must run before the Now Playing
220
220
  /// template is pushed — CarPlay reads `isAlbumArtistButtonEnabled` at display
@@ -242,12 +242,12 @@ final class CarPlayNowPlayingManager {
242
242
  setAlbumArtistDestination(nil)
243
243
  return
244
244
  }
245
- logger.info("AlbumArtist: track=\(track.title) albumUrl=\(track.albumUrl ?? "nil") resolver=\(self.config.resolveAlbumUrl != nil)")
246
- if let albumUrl = track.albumUrl {
247
- setAlbumArtistDestination(albumUrl)
245
+ logger.info("AlbumArtist: track=\(track.title) albumPath=\(track.albumPath ?? "nil") resolver=\(self.config.resolveAlbumPath != nil)")
246
+ if let albumPath = track.albumPath {
247
+ setAlbumArtistDestination(albumPath)
248
248
  return
249
249
  }
250
- guard let resolver = config.resolveAlbumUrl else {
250
+ guard let resolver = config.resolveAlbumPath else {
251
251
  setAlbumArtistDestination(nil)
252
252
  return
253
253
  }
@@ -256,7 +256,7 @@ final class CarPlayNowPlayingManager {
256
256
  do {
257
257
  path = try await resolver(track).await()
258
258
  } catch {
259
- self?.logger.error("resolveAlbumUrl failed: \(error.localizedDescription)")
259
+ self?.logger.error("resolveAlbumPath failed: \(error.localizedDescription)")
260
260
  path = nil
261
261
  }
262
262
  guard let self, self.albumArtistGeneration == generation else { return }
@@ -295,7 +295,10 @@ final class CarPlayNowPlayingManager {
295
295
  /// Returns the appropriate image for the favorite button based on state
296
296
  private func favoriteButtonImage(isFavorited: Bool) -> UIImage {
297
297
  let symbolName = isFavorited ? "heart.fill" : "heart"
298
- guard let image = UIImage(systemName: symbolName)?.resized(to: CPNowPlayingButtonMaximumImageSize) else {
298
+ guard let image = UIImage(systemName: symbolName)?.resized(
299
+ to: CPNowPlayingButtonMaximumImageSize,
300
+ scale: interfaceController.carTraitCollection.displayScale,
301
+ ) else {
299
302
  return UIImage()
300
303
  }
301
304
  return image
@@ -360,7 +363,7 @@ final class CarPlayNowPlayingManager {
360
363
  // the pushed destination (artist may be unset, or the live song).
361
364
  let title = track?.album ?? track?.artist ?? track?.title ?? ""
362
365
  logger.info("Album/Artist button tapped, navigating to \(destination)")
363
- navigateToUrl?(destination, title)
366
+ navigateToPath?(destination, title)
364
367
  }
365
368
 
366
369
  private func updateNowPlayingUpNextButton() {
@@ -22,8 +22,8 @@ class RNABMediaAddHandler: NSObject, INAddMediaIntentHandling {
22
22
  private static let logger = Logger(subsystem: "com.audiobrowser", category: "MediaAddHandler")
23
23
 
24
24
  func handle(
25
- intent: INAddMediaIntent,
26
- completion: @escaping @Sendable (INAddMediaIntentResponse) -> Void
25
+ intent _: INAddMediaIntent,
26
+ completion: @escaping @Sendable (INAddMediaIntentResponse) -> Void,
27
27
  ) {
28
28
  Self.logger.info("Add media — favorite current track")
29
29
 
@@ -57,7 +57,7 @@ class RNABMediaAddHandler: NSObject, INAddMediaIntentHandling {
57
57
  @objc(resolveMediaItemsForAddMedia:withCompletion:)
58
58
  func resolveMediaItems(
59
59
  for intent: INAddMediaIntent,
60
- with completion: @escaping ([INAddMediaMediaItemResolutionResult]) -> Void
60
+ with completion: @escaping ([INAddMediaMediaItemResolutionResult]) -> Void,
61
61
  ) {
62
62
  let name = intent.mediaSearch?.mediaName ?? ""
63
63
  let item = INMediaItem(identifier: name, title: name, type: .unknown, artwork: nil)
@@ -66,10 +66,10 @@ class RNABMediaAddHandler: NSObject, INAddMediaIntentHandling {
66
66
 
67
67
  @objc(resolveMediaDestinationForAddMedia:withCompletion:)
68
68
  func resolveMediaDestination(
69
- for intent: INAddMediaIntent,
70
- with completion: @escaping (INAddMediaMediaDestinationResolutionResult) -> Void
69
+ for _: INAddMediaIntent,
70
+ with completion: @escaping (INAddMediaMediaDestinationResolutionResult) -> Void,
71
71
  ) {
72
72
  // One flat favorites collection — always the user's library, no playlists.
73
- completion(.success(with: INMediaDestination.library))
73
+ completion(.success(with: INMediaDestination.library))
74
74
  }
75
75
  }
@@ -21,7 +21,7 @@ class RNABMediaAffinityHandler: NSObject, INUpdateMediaAffinityIntentHandling {
21
21
 
22
22
  func handle(
23
23
  intent: INUpdateMediaAffinityIntent,
24
- completion: @escaping @Sendable (INUpdateMediaAffinityIntentResponse) -> Void
24
+ completion: @escaping @Sendable (INUpdateMediaAffinityIntentResponse) -> Void,
25
25
  ) {
26
26
  let affinity = intent.affinityType
27
27
  Self.logger.info("Update media affinity — type=\(affinity.rawValue)")
@@ -60,7 +60,7 @@ class RNABMediaAffinityHandler: NSObject, INUpdateMediaAffinityIntentHandling {
60
60
  @objc(resolveMediaItemsForUpdateMediaAffinity:withCompletion:)
61
61
  func resolveMediaItems(
62
62
  for intent: INUpdateMediaAffinityIntent,
63
- with completion: @escaping ([INUpdateMediaAffinityMediaItemResolutionResult]) -> Void
63
+ with completion: @escaping ([INUpdateMediaAffinityMediaItemResolutionResult]) -> Void,
64
64
  ) {
65
65
  let name = intent.mediaSearch?.mediaName ?? ""
66
66
  let item = INMediaItem(identifier: name, title: name, type: .unknown, artwork: nil)
@@ -21,7 +21,7 @@ class RNABMediaIntentHandler: NSObject, INPlayMediaIntentHandling {
21
21
  mediaTypeMode: Self.mediaTypeMode(s?.mediaType ?? .unknown),
22
22
  reference: Self.reference(s?.reference ?? .unknown),
23
23
  hasMediaType: (s?.mediaType ?? .unknown) != .unknown,
24
- appName: Self.hostAppName()
24
+ appName: Self.hostAppName(),
25
25
  )
26
26
  Self.logger.info("Play media intent — query=\(criteria.query) matchesApp=\(criteria.matchesAppName) resume=\(criteria.isResume)")
27
27
 
@@ -37,18 +37,18 @@ class RNABMediaIntentHandler: NSObject, INPlayMediaIntentHandling {
37
37
  /// `album` are filters, not verticals — they yield no mode.
38
38
  private static func mediaTypeMode(_ type: INMediaItemType) -> String? {
39
39
  switch type {
40
- case .station, .radioStation, .algorithmicRadioStation, .musicStation: return "station"
41
- case .podcastShow, .podcastEpisode, .podcastPlaylist, .podcastStation: return "podcast"
42
- case .audioBook: return "audiobook"
43
- case .news: return "news"
44
- case .music: return "music"
45
- case .song: return "song"
46
- case .playlist: return "playlist"
47
- case .musicVideo: return "music-video"
48
- case .movie: return "movie"
49
- case .tvShow: return "tv-show"
50
- case .tvShowEpisode: return "tv-show-episode"
51
- default: return nil // album/artist/genre/unknown
40
+ case .station, .radioStation, .algorithmicRadioStation, .musicStation: "station"
41
+ case .podcastShow, .podcastEpisode, .podcastPlaylist, .podcastStation: "podcast"
42
+ case .audioBook: "audiobook"
43
+ case .news: "news"
44
+ case .music: "music"
45
+ case .song: "song"
46
+ case .playlist: "playlist"
47
+ case .musicVideo: "music-video"
48
+ case .movie: "movie"
49
+ case .tvShow: "tv-show"
50
+ case .tvShowEpisode: "tv-show-episode"
51
+ default: nil // album/artist/genre/unknown
52
52
  }
53
53
  }
54
54
 
@@ -56,9 +56,9 @@ class RNABMediaIntentHandler: NSObject, INPlayMediaIntentHandling {
56
56
  /// routes to native resume; `.my` to the consumer; everything else is unknown.
57
57
  private static func reference(_ ref: INMediaReference) -> MediaIntentCriteria.Reference {
58
58
  switch ref {
59
- case .currentlyPlaying: return .currentlyPlaying
60
- case .my: return .my
61
- default: return .unknown
59
+ case .currentlyPlaying: .currentlyPlaying
60
+ case .my: .my
61
+ default: .unknown
62
62
  }
63
63
  }
64
64
 
@@ -54,7 +54,7 @@ extension PlayerCapabilities {
54
54
  // Apple silently removed. On the iOS 16+ target it can never be invoked: CarPlay
55
55
  // uses its own CPNowPlayingButton heart, and Siri "I like this" routes through
56
56
  // INUpdateMediaAffinityIntent. The `favorite` capability still drives the
57
- // favorites-match row indicators via setFavoriteMatch. See issues #67 / #71.
57
+ // favorite row indicators via setFavoriteEnabled. See issues #67 / #71.
58
58
 
59
59
  // Mode controls
60
60
  if shuffleMode != false {
@@ -1,20 +1,6 @@
1
1
  import Foundation
2
2
 
3
3
  extension PlayerCapabilities {
4
- /// Resolved favorite match mode, or `nil` when favoriting is disabled.
5
- ///
6
- /// `false`/unset → `nil`; `true` → `.exact`; `{ match }` → that match.
7
- var favoriteMatch: FavoritesMatchMode? {
8
- switch favorite {
9
- case .none:
10
- nil
11
- case let .some(.first(enabled)):
12
- enabled ? .exact : nil
13
- case let .some(.second(config)):
14
- config.match
15
- }
16
- }
17
-
18
4
  /// Whether the favorite/like control is enabled.
19
- var favoriteEnabled: Bool { favoriteMatch != nil }
5
+ var favoriteEnabled: Bool { favorite ?? false }
20
6
  }
@@ -10,9 +10,11 @@ extension ResolvedTrack {
10
10
  /// - `.some(nil)` → set field to nil
11
11
  /// - `.some(value)` → set field to value
12
12
  ///
13
- /// `url` and `title` use single-optional since they are non-optional on ResolvedTrack.
13
+ /// `path` and `title` use single-optional since they are non-optional on ResolvedTrack.
14
14
  func copying(
15
- url: String? = nil,
15
+ path: String? = nil,
16
+ style: SectionStyle?? = nil,
17
+ sections: [Section]?? = nil,
16
18
  children: [Track]?? = nil,
17
19
  carPlaySiriListButton: CarPlaySiriListButtonPosition?? = nil,
18
20
  id: String?? = nil,
@@ -20,24 +22,23 @@ extension ResolvedTrack {
20
22
  artwork: Variant_String_ArtworkVariants?? = nil,
21
23
  artworkSource: ImageSource?? = nil,
22
24
  request: TrackRequest?? = nil,
23
- artworkCarPlayTinted: Bool?? = nil,
24
25
  title: String? = nil,
25
26
  subtitle: String?? = nil,
26
27
  artist: String?? = nil,
27
- albumUrl: String?? = nil,
28
+ albumPath: String?? = nil,
28
29
  album: String?? = nil,
29
30
  description: String?? = nil,
30
31
  genre: String?? = nil,
31
32
  duration: Double?? = nil,
32
- style: TrackStyle?? = nil,
33
- childrenStyle: TrackStyle?? = nil,
33
+ disabled: Bool?? = nil,
34
34
  favorited: Bool?? = nil,
35
- groupTitle: String?? = nil,
36
35
  live: Bool?? = nil,
37
- imageRow: [ImageRowItem]?? = nil,
36
+ icyCharset: String?? = nil,
38
37
  ) -> ResolvedTrack {
39
38
  ResolvedTrack(
40
- url: url ?? self.url,
39
+ path: path ?? self.path,
40
+ style: style ?? self.style,
41
+ sections: sections ?? self.sections,
41
42
  children: children ?? self.children,
42
43
  carPlaySiriListButton: carPlaySiriListButton ?? self.carPlaySiriListButton,
43
44
  id: id ?? self.id,
@@ -45,21 +46,18 @@ extension ResolvedTrack {
45
46
  artwork: artwork ?? self.artwork,
46
47
  artworkSource: artworkSource ?? self.artworkSource,
47
48
  request: request ?? self.request,
48
- artworkCarPlayTinted: artworkCarPlayTinted ?? self.artworkCarPlayTinted,
49
49
  title: title ?? self.title,
50
50
  subtitle: subtitle ?? self.subtitle,
51
51
  artist: artist ?? self.artist,
52
- albumUrl: albumUrl ?? self.albumUrl,
52
+ albumPath: albumPath ?? self.albumPath,
53
53
  album: album ?? self.album,
54
54
  description: description ?? self.description,
55
55
  genre: genre ?? self.genre,
56
56
  duration: duration ?? self.duration,
57
- style: style ?? self.style,
58
- childrenStyle: childrenStyle ?? self.childrenStyle,
57
+ disabled: disabled ?? self.disabled,
59
58
  favorited: favorited ?? self.favorited,
60
- groupTitle: groupTitle ?? self.groupTitle,
61
59
  live: live ?? self.live,
62
- imageRow: imageRow ?? self.imageRow,
60
+ icyCharset: icyCharset ?? self.icyCharset,
63
61
  )
64
62
  }
65
63
  }
@@ -0,0 +1,29 @@
1
+ #if canImport(NitroModules)
2
+ import NitroModules
3
+ #endif
4
+
5
+ extension Section {
6
+ /// Returns a copy of this Section with only the specified fields changed.
7
+ ///
8
+ /// Same double-optional (`T??`) convention as the other copying helpers;
9
+ /// `children` is single-optional since it is non-optional on Section.
10
+ func copying(
11
+ title: String?? = nil,
12
+ style: SectionStyle?? = nil,
13
+ path: String?? = nil,
14
+ children: [Track]? = nil,
15
+ ) -> Section {
16
+ Section(
17
+ title: title ?? self.title,
18
+ style: style ?? self.style,
19
+ path: path ?? self.path,
20
+ children: children ?? self.children,
21
+ )
22
+ }
23
+
24
+ /// One untitled section — the canonical wrap of a flat track list
25
+ /// (`children` authoring sugar, search results — ADR 0010).
26
+ static func untitled(_ children: [Track]) -> Section {
27
+ Section(title: nil, style: nil, path: nil, children: children)
28
+ }
29
+ }
@@ -0,0 +1,21 @@
1
+ import Foundation
2
+
3
+ extension String {
4
+ /// Re-decodes an ICY `StreamTitle` that AVFoundation decoded as Latin-1 with the
5
+ /// charset the track declares. AVFoundation keeps no raw bytes, so the tell is the
6
+ /// shape: every scalar at or below U+00FF with at least one at or above U+0080. A
7
+ /// title already decoded to another script contains higher scalars and is returned
8
+ /// as is, as is pure ASCII, an unknown charset, or bytes the charset cannot decode.
9
+ func redecodingIcyTitle(charset: String) -> String {
10
+ var high = false
11
+ for scalar in unicodeScalars {
12
+ if scalar.value > 0xFF { return self }
13
+ if scalar.value >= 0x80 { high = true }
14
+ }
15
+ guard high, let data = data(using: .isoLatin1) else { return self }
16
+ let cfEncoding = CFStringConvertIANACharSetNameToEncoding(charset as CFString)
17
+ guard cfEncoding != kCFStringEncodingInvalidId else { return self }
18
+ let encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(cfEncoding))
19
+ return String(data: data, encoding: encoding) ?? self
20
+ }
21
+ }
@@ -6,11 +6,13 @@ import AVFoundation
6
6
 
7
7
  extension TimedMetadata {
8
8
  /// Creates TimedMetadata from AVMetadataItem array, detecting the metadata format.
9
- static func from(items: [AVMetadataItem]) -> TimedMetadata? {
9
+ /// `icyCharset` is the track's declared charset for ICY titles AVFoundation
10
+ /// decoded as Latin-1; it never touches ID3 or common metadata.
11
+ static func from(items: [AVMetadataItem], icyCharset: String? = nil) -> TimedMetadata? {
10
12
  // Check for ICY metadata (Shoutcast/Icecast streams)
11
13
  let icyItems = items.filter { $0.keySpace == .icy }
12
14
  if !icyItems.isEmpty {
13
- return fromIcy(items: icyItems)
15
+ return fromIcy(items: icyItems, icyCharset: icyCharset)
14
16
  }
15
17
 
16
18
  // Check for ID3 metadata (MP3)
@@ -24,7 +26,7 @@ extension TimedMetadata {
24
26
  }
25
27
 
26
28
  /// Creates TimedMetadata from ICY (Shoutcast/Icecast) metadata.
27
- static func fromIcy(items: [AVMetadataItem]) -> TimedMetadata? {
29
+ static func fromIcy(items: [AVMetadataItem], icyCharset: String? = nil) -> TimedMetadata? {
28
30
  var title: String?
29
31
 
30
32
  for item in items {
@@ -35,7 +37,7 @@ extension TimedMetadata {
35
37
 
36
38
  guard let title else { return nil }
37
39
  return TimedMetadata(
38
- title: title,
40
+ title: icyCharset.map { title.redecodingIcyTitle(charset: $0) } ?? title,
39
41
  artist: nil,
40
42
  album: nil,
41
43
  date: nil,
@@ -11,49 +11,45 @@ extension Track {
11
11
  /// `title` uses single-optional (`String?`) since it is non-optional on Track.
12
12
  func copying(
13
13
  id: String?? = nil,
14
- url: String?? = nil,
14
+ path: String?? = nil,
15
15
  src: String?? = nil,
16
16
  artwork: Variant_String_ArtworkVariants?? = nil,
17
17
  artworkSource: ImageSource?? = nil,
18
18
  request: TrackRequest?? = nil,
19
- artworkCarPlayTinted: Bool?? = nil,
20
19
  title: String? = nil,
21
20
  subtitle: String?? = nil,
22
21
  artist: String?? = nil,
23
- albumUrl: String?? = nil,
22
+ albumPath: String?? = nil,
24
23
  album: String?? = nil,
25
24
  description: String?? = nil,
26
25
  genre: String?? = nil,
27
26
  duration: Double?? = nil,
28
27
  style: TrackStyle?? = nil,
29
- childrenStyle: TrackStyle?? = nil,
28
+ disabled: Bool?? = nil,
30
29
  favorited: Bool?? = nil,
31
- groupTitle: String?? = nil,
32
30
  live: Bool?? = nil,
33
- imageRow: [ImageRowItem]?? = nil,
31
+ icyCharset: String?? = nil,
34
32
  ) -> Track {
35
33
  Track(
36
34
  id: id ?? self.id,
37
- url: url ?? self.url,
35
+ path: path ?? self.path,
38
36
  src: src ?? self.src,
39
37
  artwork: artwork ?? self.artwork,
40
38
  artworkSource: artworkSource ?? self.artworkSource,
41
39
  request: request ?? self.request,
42
- artworkCarPlayTinted: artworkCarPlayTinted ?? self.artworkCarPlayTinted,
43
40
  title: title ?? self.title,
44
41
  subtitle: subtitle ?? self.subtitle,
45
42
  artist: artist ?? self.artist,
46
- albumUrl: albumUrl ?? self.albumUrl,
43
+ albumPath: albumPath ?? self.albumPath,
47
44
  album: album ?? self.album,
48
45
  description: description ?? self.description,
49
46
  genre: genre ?? self.genre,
50
47
  duration: duration ?? self.duration,
51
48
  style: style ?? self.style,
52
- childrenStyle: childrenStyle ?? self.childrenStyle,
49
+ disabled: disabled ?? self.disabled,
53
50
  favorited: favorited ?? self.favorited,
54
- groupTitle: groupTitle ?? self.groupTitle,
55
51
  live: live ?? self.live,
56
- imageRow: imageRow ?? self.imageRow,
52
+ icyCharset: icyCharset ?? self.icyCharset,
57
53
  )
58
54
  }
59
55
  }
@@ -0,0 +1,18 @@
1
+ #if canImport(NitroModules)
2
+ import NitroModules
3
+ #endif
4
+
5
+ /// A track's identity: the opaque `id` when present (non-blank), else the
6
+ /// playable `src`. Two tracks refer to the same item iff their identities are
7
+ /// equal. Browsable-only tracks (neither `id` nor `src`) have no identity —
8
+ /// they are addressed by `path` instead.
9
+ ///
10
+ /// This is THE comparison rule for favorites matching, section scoping,
11
+ /// skip-in-place, the car now-playing row indicator, and the contextual
12
+ /// `__trackId` — see ADR 0008. Mirrors `trackIdentity` on the TS side.
13
+ extension Track {
14
+ var identity: String? {
15
+ if let id, !id.isEmpty { return id }
16
+ return src
17
+ }
18
+ }
@@ -0,0 +1,27 @@
1
+ import UIKit
2
+
3
+ extension UIApplication {
4
+ /// The scene showing the app's own UI: the foreground-active one, else any
5
+ /// connected window scene. Nil when the app has no UI scene at all (running
6
+ /// headless for CarPlay), which callers must handle rather than assume.
7
+ ///
8
+ /// Replaces the deprecated `UIScreen.main` / `keyWindow` idioms — and exists
9
+ /// once because the walk was previously written out at each use, so the two
10
+ /// copies had already drifted apart.
11
+ var activeWindowScene: UIWindowScene? {
12
+ let scenes = connectedScenes.compactMap { $0 as? UIWindowScene }
13
+ return scenes.first { $0.activationState == .foregroundActive } ?? scenes.first
14
+ }
15
+ }
16
+
17
+ /// The scale to rasterise at for the app's own screen.
18
+ ///
19
+ /// `UITraitCollection.current` rather than the deprecated `UIScreen.main.scale`
20
+ /// (also unavailable on visionOS, which the podspec targets): on the main actor
21
+ /// it already reflects the environment being rendered for. CarPlay surfaces do
22
+ /// NOT use this — they rasterise at `carTraitCollection.displayScale`, the car
23
+ /// screen's own scale.
24
+ @MainActor
25
+ var appDisplayScale: CGFloat {
26
+ UITraitCollection.current.displayScale
27
+ }
@@ -92,8 +92,14 @@ final class HttpClient: @unchecked Sendable {
92
92
  httpRequest.headers?.forEach { name, value in
93
93
  urlRequest.setValue(value, forHTTPHeaderField: name)
94
94
  }
95
- // Set User-Agent (userAgent parameter takes precedence over headers)
96
- urlRequest.setValue(httpRequest.userAgent, forHTTPHeaderField: "User-Agent")
95
+ // An explicit `User-Agent` in `headers` wins, matching the two sibling paths
96
+ // (MediaLoader.buildAssetOptions and the artwork path in
97
+ // BrowserManager+URLResolution). `userAgent` carries a non-nil default, so
98
+ // overwriting unconditionally would replace a consumer's header with
99
+ // "react-native-audio-browser" whenever they set only the header.
100
+ if httpRequest.headers?["User-Agent"] == nil {
101
+ urlRequest.setValue(httpRequest.userAgent, forHTTPHeaderField: "User-Agent")
102
+ }
97
103
 
98
104
  // Add body for non-GET requests
99
105
  switch httpRequest.method.uppercased() {