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

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 (1000) hide show
  1. package/AudioBrowser.podspec +1 -1
  2. package/README.md +184 -2
  3. package/android/build.gradle +17 -5
  4. package/android/gradle.properties +2 -2
  5. package/android/src/main/AndroidManifest.xml +18 -1
  6. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +473 -158
  7. package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
  8. package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
  9. package/android/src/main/java/com/audiobrowser/Callbacks.kt +4 -6
  10. package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
  11. package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
  12. package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
  13. package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
  14. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +442 -178
  15. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +299 -0
  16. package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
  17. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
  18. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +42 -0
  19. package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
  20. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
  21. package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
  22. package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
  23. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
  24. package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
  25. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +40 -46
  26. package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
  27. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +172 -0
  28. package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
  29. package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
  30. package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
  31. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
  32. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +422 -134
  33. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +248 -404
  34. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +347 -0
  35. package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
  36. package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
  37. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +25 -5
  38. package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
  39. package/android/src/main/java/com/audiobrowser/player/Player.kt +475 -577
  40. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
  41. package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
  42. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
  43. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +118 -53
  44. package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
  45. package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
  46. package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
  47. package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +85 -40
  48. package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
  49. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +156 -0
  50. package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +109 -0
  51. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
  52. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +55 -0
  53. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +265 -0
  54. package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
  55. package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
  56. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +24 -0
  57. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
  58. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
  59. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
  60. package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
  61. package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
  62. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
  63. package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
  64. package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
  65. package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
  66. package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
  67. package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
  68. package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
  69. package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
  70. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +112 -80
  71. package/android/src/main/res/values/strings.xml +4 -0
  72. package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
  73. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +165 -0
  74. package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
  75. package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
  76. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
  77. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
  78. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
  79. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
  80. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +55 -0
  81. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +94 -0
  82. package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
  83. package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
  84. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +203 -0
  85. package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
  86. package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
  87. package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
  88. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
  89. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +190 -0
  90. package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
  91. package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
  92. package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
  93. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
  94. package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
  95. package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
  96. package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
  97. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +153 -0
  98. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +186 -0
  99. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +158 -0
  100. package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
  101. package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
  102. package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
  103. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
  104. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
  105. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
  106. package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
  107. package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
  108. package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
  109. package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
  110. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
  111. package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
  112. package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
  113. package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
  114. package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
  115. package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
  116. package/ios/Browser/BrowserConfig.swift +41 -11
  117. package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
  118. package/ios/Browser/BrowserManager.swift +303 -135
  119. package/ios/Browser/BrowserPathHelper.swift +32 -3
  120. package/ios/Browser/JsonModels.swift +287 -66
  121. package/ios/Browser/MediaResolveComposer.swift +114 -0
  122. package/ios/Browser/SearchDrillIn.swift +44 -0
  123. package/ios/Browser/SectionScope.swift +35 -0
  124. package/ios/Browser/TabBarEntries.swift +15 -0
  125. package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
  126. package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
  127. package/ios/CarPlay/CarPlayController.swift +739 -149
  128. package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
  129. package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
  130. package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
  131. package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
  132. package/ios/CarPlay/RNABAudioBrowser.h +15 -10
  133. package/ios/CarPlay/RNABAudioBrowser.m +15 -14
  134. package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
  135. package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
  136. package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
  137. package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
  138. package/ios/Extension/Capability+RemoteCommand.swift +7 -9
  139. package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
  140. package/ios/Extension/NavigationError+Conversion.swift +8 -1
  141. package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
  142. package/ios/Extension/RequestConfig+Copying.swift +31 -0
  143. package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
  144. package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
  145. package/ios/Extension/Track+AVPlayer.swift +4 -4
  146. package/ios/Extension/Track+Copying.swift +9 -3
  147. package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
  148. package/ios/Extension/Variant+Artwork.swift +49 -0
  149. package/ios/HybridAudioBrowser.swift +805 -197
  150. package/ios/Model/NitroTypeStubs.swift +299 -224
  151. package/ios/Model/PlayerUpdateOptions.swift +23 -23
  152. package/ios/Model/RemoteCommand.swift +24 -6
  153. package/ios/Model/TrackPlayerError.swift +82 -18
  154. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
  155. package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
  156. package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
  157. package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
  158. package/ios/Observer/PlayerTimeObserver.swift +3 -53
  159. package/ios/Option/SessionCategories.swift +34 -11
  160. package/ios/Player/AirPlayStallJudgement.swift +25 -0
  161. package/ios/Player/EndOfTrackJudgement.swift +32 -0
  162. package/ios/Player/LoadSeekCoordinator.swift +16 -1
  163. package/ios/Player/MediaIntentCriteria.swift +139 -0
  164. package/ios/Player/MediaLoader.swift +31 -11
  165. package/ios/Player/NowPlayingUpdater.swift +119 -26
  166. package/ios/Player/PersistedPlaybackState.swift +11 -0
  167. package/ios/Player/PlaybackCoordinator.swift +285 -51
  168. package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
  169. package/ios/Player/PlaybackEffectHandler.swift +19 -9
  170. package/ios/Player/PlaybackErrorHandler.swift +98 -14
  171. package/ios/Player/PlaybackStateMachine.swift +18 -13
  172. package/ios/Player/PlaybackStateStore.swift +27 -0
  173. package/ios/Player/PlaybackTimer.swift +61 -0
  174. package/ios/Player/PlayingStateManager.swift +3 -2
  175. package/ios/Player/QueueManager.swift +77 -64
  176. package/ios/Player/RetryManager.swift +175 -38
  177. package/ios/Player/ShuffleOrder.swift +10 -0
  178. package/ios/Player/SleepTimerHandling.swift +8 -0
  179. package/ios/Player/SleepTimerManager.swift +72 -11
  180. package/ios/Player/VolumeFader.swift +77 -0
  181. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
  182. package/ios/RemoteCommand/RemoteCommandController.swift +40 -21
  183. package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
  184. package/ios/Tests/BrowserPathHelperTests.swift +28 -0
  185. package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
  186. package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
  187. package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
  188. package/ios/Tests/JsonModelsTests.swift +58 -0
  189. package/ios/Tests/LRUCacheTests.swift +111 -0
  190. package/ios/Tests/LoadSeekCoordinatorTests.swift +40 -7
  191. package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
  192. package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
  193. package/ios/Tests/MediaLoaderTests.swift +32 -20
  194. package/ios/Tests/MediaResolveTests.swift +126 -0
  195. package/ios/Tests/OnceValueTests.swift +79 -0
  196. package/ios/Tests/PlaybackCoordinatorTests.swift +787 -13
  197. package/ios/Tests/PlaybackErrorHandlerTests.swift +383 -47
  198. package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
  199. package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
  200. package/ios/Tests/PlaybackTimerTests.swift +55 -0
  201. package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
  202. package/ios/Tests/PlayingStateManagerTests.swift +57 -0
  203. package/ios/Tests/QueueManagerTests.swift +195 -18
  204. package/ios/Tests/RemoteCommandControllerTests.swift +156 -0
  205. package/ios/Tests/RemoteCommandTests.swift +67 -0
  206. package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
  207. package/ios/Tests/RetryManagerTests.swift +178 -0
  208. package/ios/Tests/SearchDrillInTests.swift +63 -0
  209. package/ios/Tests/SectionScopeTests.swift +82 -0
  210. package/ios/Tests/SleepTimerManagerTests.swift +107 -0
  211. package/ios/Tests/Support/ManualSleepTimerScheduler.swift +47 -0
  212. package/ios/Tests/Support/TestTypes.swift +61 -20
  213. package/ios/Tests/TabBarEntriesTests.swift +71 -0
  214. package/ios/Tests/TrackSelectorTests.swift +15 -15
  215. package/ios/TrackPlayer.swift +385 -58
  216. package/ios/TrackPlayerCallbacks.swift +5 -44
  217. package/ios/TrackSelector.swift +5 -5
  218. package/ios/Util/ArtworkImageFetcher.swift +4 -2
  219. package/ios/Util/LRUCache.swift +4 -1
  220. package/ios/Util/NetworkMonitor.swift +7 -3
  221. package/ios/Util/OnceValue.swift +34 -0
  222. package/lib/commonjs/AudioBrowser.js +0 -11
  223. package/lib/commonjs/AudioBrowser.js.map +1 -1
  224. package/lib/commonjs/CLAUDE.md +52 -0
  225. package/lib/commonjs/features/browser-config.js +227 -0
  226. package/lib/commonjs/features/browser-config.js.map +1 -0
  227. package/lib/commonjs/features/browser.js +60 -159
  228. package/lib/commonjs/features/browser.js.map +1 -1
  229. package/lib/commonjs/features/carConnection.js +42 -0
  230. package/lib/commonjs/features/carConnection.js.map +1 -0
  231. package/lib/commonjs/features/equalizer.js +1 -1
  232. package/lib/commonjs/features/errors.js +37 -5
  233. package/lib/commonjs/features/errors.js.map +1 -1
  234. package/lib/commonjs/features/favorites.js +1 -1
  235. package/lib/commonjs/features/gate.js +164 -0
  236. package/lib/commonjs/features/gate.js.map +1 -0
  237. package/lib/commonjs/features/index.js +22 -0
  238. package/lib/commonjs/features/index.js.map +1 -1
  239. package/lib/commonjs/features/metadata.js +20 -3
  240. package/lib/commonjs/features/metadata.js.map +1 -1
  241. package/lib/commonjs/features/network.js +1 -1
  242. package/lib/commonjs/features/nowPlaying.js +36 -1
  243. package/lib/commonjs/features/nowPlaying.js.map +1 -1
  244. package/lib/commonjs/features/output.js +37 -23
  245. package/lib/commonjs/features/output.js.map +1 -1
  246. package/lib/commonjs/features/playback/controls.js +9 -0
  247. package/lib/commonjs/features/playback/controls.js.map +1 -1
  248. package/lib/commonjs/features/playback/index.js +11 -0
  249. package/lib/commonjs/features/playback/index.js.map +1 -1
  250. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  251. package/lib/commonjs/features/playback/playing.js +1 -1
  252. package/lib/commonjs/features/playback/progress.js +29 -13
  253. package/lib/commonjs/features/playback/progress.js.map +1 -1
  254. package/lib/commonjs/features/playback/state.js +1 -1
  255. package/lib/commonjs/features/playback/trackPlaybackTime.js +86 -0
  256. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
  257. package/lib/commonjs/features/playback/volume.js +2 -2
  258. package/lib/commonjs/features/player/options.js +79 -29
  259. package/lib/commonjs/features/player/options.js.map +1 -1
  260. package/lib/commonjs/features/player/setup.js +256 -3
  261. package/lib/commonjs/features/player/setup.js.map +1 -1
  262. package/lib/commonjs/features/player/validateOptions.js +21 -0
  263. package/lib/commonjs/features/player/validateOptions.js.map +1 -0
  264. package/lib/commonjs/features/queue/activeTrack.js +1 -1
  265. package/lib/commonjs/features/queue/queue.js +12 -5
  266. package/lib/commonjs/features/queue/queue.js.map +1 -1
  267. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  268. package/lib/commonjs/features/queue/shuffle.js +1 -1
  269. package/lib/commonjs/features/remoteControls.js +14 -104
  270. package/lib/commonjs/features/remoteControls.js.map +1 -1
  271. package/lib/commonjs/features/sleepTimer.js +22 -5
  272. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  273. package/lib/commonjs/native.js +5 -0
  274. package/lib/commonjs/native.js.map +1 -1
  275. package/lib/commonjs/native.web.js +4 -1
  276. package/lib/commonjs/native.web.js.map +1 -1
  277. package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
  278. package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
  279. package/lib/commonjs/utils/LazyNativeEmitter.js +8 -8
  280. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  281. package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
  282. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  283. package/lib/commonjs/utils/artwork.js +31 -0
  284. package/lib/commonjs/utils/artwork.js.map +1 -0
  285. package/lib/commonjs/web/NativeAudioBrowser.js +302 -125
  286. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  287. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
  288. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  289. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
  290. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  291. package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
  292. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  293. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
  294. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  295. package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
  296. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
  297. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
  298. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
  299. package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
  300. package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
  301. package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
  302. package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
  303. package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
  304. package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
  305. package/lib/commonjs/web/TrackPlayer/index.js +13 -5
  306. package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
  307. package/lib/commonjs/web/browser/BrowserManager.js +176 -50
  308. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  309. package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
  310. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
  311. package/lib/commonjs/web/browser/SearchManager.js +11 -9
  312. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  313. package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
  314. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
  315. package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
  316. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
  317. package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
  318. package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
  319. package/lib/commonjs/web/http/HttpClient.js +4 -23
  320. package/lib/commonjs/web/http/HttpClient.js.map +1 -1
  321. package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
  322. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  323. package/lib/commonjs/web/playbackErrorKind.js +57 -0
  324. package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
  325. package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
  326. package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
  327. package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
  328. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  329. package/lib/commonjs/web/player/OptionsManager.js +9 -17
  330. package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
  331. package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
  332. package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
  333. package/lib/module/AudioBrowser.js +1 -1
  334. package/lib/module/AudioBrowser.js.map +1 -1
  335. package/lib/module/CLAUDE.md +52 -0
  336. package/lib/module/features/browser-config.js +221 -0
  337. package/lib/module/features/browser-config.js.map +1 -0
  338. package/lib/module/features/browser.js +54 -157
  339. package/lib/module/features/browser.js.map +1 -1
  340. package/lib/module/features/carConnection.js +37 -0
  341. package/lib/module/features/carConnection.js.map +1 -0
  342. package/lib/module/features/equalizer.js +1 -1
  343. package/lib/module/features/errors.js +37 -5
  344. package/lib/module/features/errors.js.map +1 -1
  345. package/lib/module/features/favorites.js +1 -1
  346. package/lib/module/features/gate.js +159 -0
  347. package/lib/module/features/gate.js.map +1 -0
  348. package/lib/module/features/index.js +2 -0
  349. package/lib/module/features/index.js.map +1 -1
  350. package/lib/module/features/metadata.js +20 -3
  351. package/lib/module/features/metadata.js.map +1 -1
  352. package/lib/module/features/network.js +1 -1
  353. package/lib/module/features/nowPlaying.js +34 -1
  354. package/lib/module/features/nowPlaying.js.map +1 -1
  355. package/lib/module/features/output.js +32 -19
  356. package/lib/module/features/output.js.map +1 -1
  357. package/lib/module/features/playback/controls.js +8 -0
  358. package/lib/module/features/playback/controls.js.map +1 -1
  359. package/lib/module/features/playback/index.js +1 -0
  360. package/lib/module/features/playback/index.js.map +1 -1
  361. package/lib/module/features/playback/playWhenReady.js +1 -1
  362. package/lib/module/features/playback/playing.js +1 -1
  363. package/lib/module/features/playback/progress.js +29 -13
  364. package/lib/module/features/playback/progress.js.map +1 -1
  365. package/lib/module/features/playback/state.js +1 -1
  366. package/lib/module/features/playback/trackPlaybackTime.js +83 -0
  367. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
  368. package/lib/module/features/playback/volume.js +2 -2
  369. package/lib/module/features/player/options.js +79 -29
  370. package/lib/module/features/player/options.js.map +1 -1
  371. package/lib/module/features/player/setup.js +256 -3
  372. package/lib/module/features/player/setup.js.map +1 -1
  373. package/lib/module/features/player/validateOptions.js +17 -0
  374. package/lib/module/features/player/validateOptions.js.map +1 -0
  375. package/lib/module/features/queue/activeTrack.js +1 -1
  376. package/lib/module/features/queue/queue.js +12 -5
  377. package/lib/module/features/queue/queue.js.map +1 -1
  378. package/lib/module/features/queue/repeatMode.js +1 -1
  379. package/lib/module/features/queue/shuffle.js +1 -1
  380. package/lib/module/features/remoteControls.js +13 -103
  381. package/lib/module/features/remoteControls.js.map +1 -1
  382. package/lib/module/features/sleepTimer.js +22 -5
  383. package/lib/module/features/sleepTimer.js.map +1 -1
  384. package/lib/module/native.js +5 -0
  385. package/lib/module/native.js.map +1 -1
  386. package/lib/module/native.web.js +4 -1
  387. package/lib/module/native.web.js.map +1 -1
  388. package/lib/module/test-utils/reactNativeStub.js +58 -0
  389. package/lib/module/test-utils/reactNativeStub.js.map +1 -0
  390. package/lib/module/utils/LazyNativeEmitter.js +8 -8
  391. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  392. package/lib/module/utils/NativeUpdatedValue.js +0 -3
  393. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  394. package/lib/module/utils/artwork.js +26 -0
  395. package/lib/module/utils/artwork.js.map +1 -0
  396. package/lib/module/web/NativeAudioBrowser.js +303 -126
  397. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  398. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
  399. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  400. package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
  401. package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  402. package/lib/module/web/TrackPlayer/Player.js +90 -26
  403. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  404. package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
  405. package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  406. package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
  407. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
  408. package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
  409. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
  410. package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
  411. package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
  412. package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
  413. package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
  414. package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
  415. package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
  416. package/lib/module/web/TrackPlayer/index.js +2 -1
  417. package/lib/module/web/TrackPlayer/index.js.map +1 -1
  418. package/lib/module/web/browser/BrowserManager.js +176 -50
  419. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  420. package/lib/module/web/browser/NavigationErrorManager.js +3 -1
  421. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
  422. package/lib/module/web/browser/SearchManager.js +11 -9
  423. package/lib/module/web/browser/SearchManager.js.map +1 -1
  424. package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
  425. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
  426. package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
  427. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
  428. package/lib/module/web/browser/parseSearchResponse.js +16 -0
  429. package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
  430. package/lib/module/web/http/HttpClient.js +4 -23
  431. package/lib/module/web/http/HttpClient.js.map +1 -1
  432. package/lib/module/web/http/RequestConfigBuilder.js +107 -58
  433. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  434. package/lib/module/web/playbackErrorKind.js +52 -0
  435. package/lib/module/web/playbackErrorKind.js.map +1 -0
  436. package/lib/module/web/player/MediaSessionManager.js +116 -0
  437. package/lib/module/web/player/MediaSessionManager.js.map +1 -0
  438. package/lib/module/web/player/NowPlayingManager.js +7 -5
  439. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  440. package/lib/module/web/player/OptionsManager.js +9 -17
  441. package/lib/module/web/player/OptionsManager.js.map +1 -1
  442. package/lib/module/web/player/RemoteCommandController.js +92 -0
  443. package/lib/module/web/player/RemoteCommandController.js.map +1 -0
  444. package/lib/typescript/src/AudioBrowser.d.ts +0 -1
  445. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  446. package/lib/typescript/src/features/browser-config.d.ts +12 -0
  447. package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
  448. package/lib/typescript/src/features/browser.d.ts +40 -1
  449. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  450. package/lib/typescript/src/features/carConnection.d.ts +20 -0
  451. package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
  452. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  453. package/lib/typescript/src/features/errors.d.ts +61 -6
  454. package/lib/typescript/src/features/errors.d.ts.map +1 -1
  455. package/lib/typescript/src/features/favorites.d.ts +1 -1
  456. package/lib/typescript/src/features/gate.d.ts +93 -0
  457. package/lib/typescript/src/features/gate.d.ts.map +1 -0
  458. package/lib/typescript/src/features/index.d.ts +2 -0
  459. package/lib/typescript/src/features/index.d.ts.map +1 -1
  460. package/lib/typescript/src/features/metadata.d.ts +26 -7
  461. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  462. package/lib/typescript/src/features/network.d.ts +1 -1
  463. package/lib/typescript/src/features/nowPlaying.d.ts +28 -1
  464. package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
  465. package/lib/typescript/src/features/output.d.ts +28 -18
  466. package/lib/typescript/src/features/output.d.ts.map +1 -1
  467. package/lib/typescript/src/features/playback/controls.d.ts +5 -0
  468. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  469. package/lib/typescript/src/features/playback/index.d.ts +1 -0
  470. package/lib/typescript/src/features/playback/index.d.ts.map +1 -1
  471. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  472. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  473. package/lib/typescript/src/features/playback/progress.d.ts +4 -1
  474. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  475. package/lib/typescript/src/features/playback/state.d.ts +1 -1
  476. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
  477. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
  478. package/lib/typescript/src/features/playback/volume.d.ts +2 -2
  479. package/lib/typescript/src/features/player/options.d.ts +176 -127
  480. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  481. package/lib/typescript/src/features/player/setup.d.ts +216 -167
  482. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  483. package/lib/typescript/src/features/player/validateOptions.d.ts +9 -0
  484. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
  485. package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
  486. package/lib/typescript/src/features/queue/queue.d.ts +11 -4
  487. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  488. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  489. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  490. package/lib/typescript/src/features/remoteControls.d.ts +23 -35
  491. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  492. package/lib/typescript/src/features/sleepTimer.d.ts +19 -4
  493. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  494. package/lib/typescript/src/index.d.ts.map +1 -1
  495. package/lib/typescript/src/native.d.ts +1 -2
  496. package/lib/typescript/src/native.d.ts.map +1 -1
  497. package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
  498. package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
  499. package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
  500. package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
  501. package/lib/typescript/src/types/browser-native.d.ts +9 -3
  502. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  503. package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
  504. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  505. package/lib/typescript/src/types/browser.d.ts +349 -85
  506. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  507. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +8 -6
  508. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  509. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
  510. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  511. package/lib/typescript/src/utils/artwork.d.ts +19 -0
  512. package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
  513. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
  514. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +4 -2
  515. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
  516. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
  517. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -20
  518. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
  519. package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
  520. package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
  521. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
  522. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
  523. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
  524. package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
  525. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
  526. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
  527. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
  528. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
  529. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
  530. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
  531. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
  532. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
  533. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
  534. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
  535. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
  536. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -0
  537. 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
  538. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
  539. package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
  540. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
  541. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +14 -9
  542. package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
  543. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
  544. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
  545. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
  546. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
  547. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
  548. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
  549. package/nitrogen/generated/android/c++/JGate.hpp +62 -0
  550. package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
  551. package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
  552. package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
  553. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +247 -223
  554. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +26 -23
  555. package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
  556. package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
  557. package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
  558. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
  559. package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
  560. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
  561. package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
  562. package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
  563. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
  564. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
  565. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +21 -24
  566. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
  567. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -20
  568. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
  569. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
  570. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
  571. package/nitrogen/generated/android/c++/JOptions.hpp +19 -14
  572. package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
  573. package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
  574. package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
  575. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
  576. package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
  577. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
  578. package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
  579. package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
  580. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
  581. package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +20 -20
  582. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
  583. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
  584. package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
  585. package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
  586. package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
  587. package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
  588. package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
  589. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
  590. package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
  591. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
  592. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
  593. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
  594. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
  595. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
  596. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.cpp +41 -0
  597. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
  598. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
  599. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
  600. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
  601. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -9
  602. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
  603. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
  604. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
  605. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → FavoriteConfig.kt} +6 -6
  606. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
  607. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
  608. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
  609. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
  610. 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
  611. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
  612. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
  613. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
  614. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
  615. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
  616. 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
  617. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
  618. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
  619. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
  620. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → Gate.kt} +9 -6
  621. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
  622. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
  623. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → GateReason.kt} +5 -8
  624. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +104 -110
  625. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
  626. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
  627. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
  628. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
  629. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
  630. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
  631. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
  632. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
  633. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
  634. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
  635. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
  636. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
  637. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -9
  638. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
  639. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
  640. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
  641. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
  642. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
  643. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
  644. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
  645. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
  646. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
  647. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  648. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButton.kt +26 -0
  649. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -3
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
  653. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
  654. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
  655. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
  656. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
  657. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
  658. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
  659. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
  660. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
  661. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
  662. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
  663. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1126 -490
  664. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +66 -45
  665. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +178 -120
  666. package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
  667. package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -22
  668. package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
  669. package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
  670. package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
  671. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
  672. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
  673. package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
  674. package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
  675. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
  676. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
  677. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
  678. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
  679. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
  680. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
  681. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
  682. 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
  683. package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
  684. package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
  685. package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
  686. package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
  687. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  688. package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
  689. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
  690. 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
  691. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
  692. package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
  693. package/nitrogen/generated/ios/swift/Gate.swift +47 -0
  694. package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
  695. package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
  696. package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
  697. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +21 -15
  698. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +232 -199
  699. package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
  700. package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
  701. package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
  702. package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
  703. package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
  704. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
  705. package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
  706. package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
  707. package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
  708. package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
  709. package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
  710. package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
  711. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -41
  712. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
  713. package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
  714. package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
  715. package/nitrogen/generated/ios/swift/Options.swift +10 -4
  716. package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
  717. package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
  718. package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
  719. package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
  720. package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
  721. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
  722. package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +10 -10
  723. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
  724. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
  725. package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
  726. package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
  727. package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
  728. package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
  729. package/nitrogen/generated/ios/swift/Track.swift +74 -8
  730. package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
  731. package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
  732. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
  733. package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
  734. package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
  735. package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
  736. package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
  737. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -19
  738. package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
  739. package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
  740. package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
  741. package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
  742. package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
  743. package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
  744. package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
  745. package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
  746. package/nitrogen/generated/shared/c++/{PercentageRating.hpp → Gate.hpp} +21 -16
  747. package/nitrogen/generated/shared/c++/{HeartRating.hpp → GateDecision.hpp} +23 -17
  748. package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateEvent.hpp} +18 -17
  749. package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
  750. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +22 -19
  751. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +45 -36
  752. package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
  753. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
  754. package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
  755. package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
  756. package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
  757. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
  758. package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
  759. package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
  760. package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
  761. package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
  762. package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
  763. package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
  764. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -19
  765. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
  766. package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
  767. package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
  768. package/nitrogen/generated/shared/c++/Options.hpp +8 -8
  769. package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
  770. package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
  771. package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
  772. package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
  773. package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
  774. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
  775. package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +25 -25
  776. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
  777. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
  778. package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
  779. package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
  780. package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
  781. package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
  782. package/nitrogen/generated/shared/c++/Track.hpp +24 -5
  783. package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
  784. package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
  785. package/package.json +20 -8
  786. package/src/AudioBrowser.ts +1 -1
  787. package/src/CLAUDE.md +52 -0
  788. package/src/codegen-no-config-variant.test.ts +70 -0
  789. package/src/features/battery.test.ts +79 -0
  790. package/src/features/browser-config.test.ts +110 -0
  791. package/src/features/browser-config.ts +293 -0
  792. package/src/features/browser.test.ts +19 -0
  793. package/src/features/browser.ts +59 -202
  794. package/src/features/carConnection.ts +36 -0
  795. package/src/features/equalizer.ts +1 -1
  796. package/src/features/errors.ts +71 -5
  797. package/src/features/favorites.ts +1 -1
  798. package/src/features/gate.test.ts +72 -0
  799. package/src/features/gate.ts +170 -0
  800. package/src/features/hook-wiring.test.ts +254 -0
  801. package/src/features/index.ts +2 -0
  802. package/src/features/metadata.ts +26 -15
  803. package/src/features/network.ts +1 -1
  804. package/src/features/nowPlaying.ts +37 -1
  805. package/src/features/output.ts +35 -22
  806. package/src/features/playback/controls.ts +8 -0
  807. package/src/features/playback/index.ts +1 -0
  808. package/src/features/playback/playWhenReady.ts +1 -1
  809. package/src/features/playback/playing.ts +1 -1
  810. package/src/features/playback/progress.test.ts +169 -0
  811. package/src/features/playback/progress.ts +32 -13
  812. package/src/features/playback/state.ts +1 -1
  813. package/src/features/playback/trackPlaybackTime.test.ts +76 -0
  814. package/src/features/playback/trackPlaybackTime.ts +101 -0
  815. package/src/features/playback/volume.ts +2 -2
  816. package/src/features/player/options.test.ts +38 -0
  817. package/src/features/player/options.ts +217 -110
  818. package/src/features/player/setup.test.ts +125 -0
  819. package/src/features/player/setup.ts +400 -175
  820. package/src/features/player/validateOptions.ts +19 -0
  821. package/src/features/queue/activeTrack.ts +1 -1
  822. package/src/features/queue/queue.test.ts +36 -0
  823. package/src/features/queue/queue.ts +17 -6
  824. package/src/features/queue/repeatMode.ts +1 -1
  825. package/src/features/queue/shuffle.ts +1 -1
  826. package/src/features/remoteControls.ts +13 -115
  827. package/src/features/sleepTimer.test.ts +172 -0
  828. package/src/features/sleepTimer.ts +32 -5
  829. package/src/native.ts +5 -0
  830. package/src/native.web.ts +4 -1
  831. package/src/public-surface.test.ts +107 -0
  832. package/src/specs/audio-browser.nitro.ts +132 -42
  833. package/src/test-utils/reactNativeStub.ts +60 -0
  834. package/src/types/browser-native.ts +19 -6
  835. package/src/types/browser-nodes.ts +160 -13
  836. package/src/types/browser.ts +377 -89
  837. package/src/utils/LazyNativeEmitter.ts +9 -8
  838. package/src/utils/NativeUpdatedValue.ts +0 -2
  839. package/src/utils/artwork.ts +30 -0
  840. package/src/utils/useDebug.test.ts +182 -0
  841. package/src/utils/useNativeUpdatedValue.test.ts +103 -0
  842. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
  843. package/src/web/NativeAudioBrowser.ts +366 -159
  844. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
  845. package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
  846. package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
  847. package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
  848. package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
  849. package/src/web/TrackPlayer/Player.load.test.ts +86 -0
  850. package/src/web/TrackPlayer/Player.ts +113 -39
  851. package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
  852. package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
  853. package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
  854. package/src/web/TrackPlayer/QueueManager.ts +302 -0
  855. package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
  856. package/src/web/TrackPlayer/RepeatMode.ts +1 -1
  857. package/src/web/TrackPlayer/SleepTimer.ts +38 -3
  858. package/src/web/TrackPlayer/VolumeFader.ts +74 -0
  859. package/src/web/TrackPlayer/index.ts +2 -1
  860. package/src/web/browser/BrowserManager.ts +231 -62
  861. package/src/web/browser/NavigationErrorManager.ts +3 -1
  862. package/src/web/browser/SearchManager.ts +11 -15
  863. package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
  864. package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
  865. package/src/web/browser/assertBrowsePageShape.ts +27 -0
  866. package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
  867. package/src/web/browser/classifyTrackNavigation.ts +35 -0
  868. package/src/web/browser/parseSearchResponse.test.ts +25 -0
  869. package/src/web/browser/parseSearchResponse.ts +15 -0
  870. package/src/web/http/HttpClient.ts +5 -35
  871. package/src/web/http/RequestConfigBuilder.test.ts +59 -0
  872. package/src/web/http/RequestConfigBuilder.ts +120 -63
  873. package/src/web/playbackErrorKind.test.ts +55 -0
  874. package/src/web/playbackErrorKind.ts +56 -0
  875. package/src/web/player/MediaSessionManager.test.ts +220 -0
  876. package/src/web/player/MediaSessionManager.ts +151 -0
  877. package/src/web/player/NowPlayingManager.test.ts +46 -0
  878. package/src/web/player/NowPlayingManager.ts +6 -5
  879. package/src/web/player/OptionsManager.ts +12 -18
  880. package/src/web/player/RemoteCommandController.test.ts +171 -0
  881. package/src/web/player/RemoteCommandController.ts +166 -0
  882. package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
  883. package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
  884. package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
  885. package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
  886. package/ios/CarPlay/Track+CarPlay.swift +0 -63
  887. package/ios/Model/MediaURL.swift +0 -67
  888. package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
  889. package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
  890. package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
  891. package/lib/commonjs/features/rating.js +0 -2
  892. package/lib/commonjs/features/rating.js.map +0 -1
  893. package/lib/commonjs/types/player.js +0 -2
  894. package/lib/commonjs/types/player.js.map +0 -1
  895. package/lib/commonjs/web/SimpleRouter.test.js +0 -174
  896. package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
  897. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
  898. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  899. package/lib/module/features/rating.js +0 -2
  900. package/lib/module/features/rating.js.map +0 -1
  901. package/lib/module/types/player.js +0 -2
  902. package/lib/module/types/player.js.map +0 -1
  903. package/lib/module/web/SimpleRouter.test.js +0 -174
  904. package/lib/module/web/SimpleRouter.test.js.map +0 -1
  905. package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
  906. package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  907. package/lib/typescript/src/features/rating.d.ts +0 -14
  908. package/lib/typescript/src/features/rating.d.ts.map +0 -1
  909. package/lib/typescript/src/native.web.d.ts +0 -3
  910. package/lib/typescript/src/native.web.d.ts.map +0 -1
  911. package/lib/typescript/src/types/player.d.ts +0 -64
  912. package/lib/typescript/src/types/player.d.ts.map +0 -1
  913. package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
  914. package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
  915. package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
  916. package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
  917. package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
  918. package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
  919. package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
  920. package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
  921. package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
  922. package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
  923. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
  924. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
  925. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
  926. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
  927. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
  928. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
  929. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
  930. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
  931. package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
  932. package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
  933. package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
  934. package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
  935. package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
  936. package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
  937. package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
  938. package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
  939. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
  940. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
  941. package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
  942. package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
  943. package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
  944. package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
  945. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
  946. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
  947. package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
  948. package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
  949. package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
  950. package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
  951. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
  952. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
  953. package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
  954. package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
  955. package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
  956. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
  957. package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
  958. package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
  959. package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
  960. package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
  961. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
  962. package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
  963. package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
  964. package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
  965. package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
  966. package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
  967. package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
  968. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
  969. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
  970. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
  971. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
  972. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +0 -50
  973. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
  974. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
  975. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
  976. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
  977. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
  978. package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
  979. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
  980. package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
  981. package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
  982. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
  983. package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
  984. package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
  985. package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
  986. package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
  987. package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
  988. package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
  989. package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
  990. package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
  991. package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
  992. package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
  993. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
  994. package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
  995. package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
  996. package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
  997. package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
  998. package/src/features/rating.ts +0 -17
  999. package/src/types/player.ts +0 -69
  1000. package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
@@ -1,10 +1,27 @@
1
+ import AVFoundation
1
2
  import Foundation
2
- import NitroModules
3
+ #if canImport(NitroModules)
4
+ import NitroModules
5
+ #endif
3
6
  import os.log
4
7
 
8
+ /// Abstracts `NetworkMonitor` so tests can fake connectivity. Sendable because
9
+ /// the network-restore race reads it from a task-group child.
10
+ protocol NetworkStatusProviding: AnyObject, Sendable {
11
+ var isOnline: Bool { get }
12
+ }
13
+
5
14
  /// Manages retry logic for media load errors with exponential backoff.
6
15
  /// Similar to Android's RetryLoadErrorHandlingPolicy.
7
16
  ///
17
+ /// Two duration budgets apply, chosen by whether the current load has ever
18
+ /// produced audio (`hasPlayed`): a short first-connect budget — a stream that
19
+ /// fails before ever playing is usually dead, and the listener is actively
20
+ /// waiting for a verdict — and the full recovery budget for a stream that
21
+ /// played and then dropped (tunnels, handovers, encoder restarts). The short
22
+ /// budget counts only online time, so a station tapped in a tunnel still gets
23
+ /// its online seconds once connectivity returns.
24
+ ///
8
25
  /// When a network monitor is provided and the device is offline, the retry will
9
26
  /// trigger immediately when connectivity is restored instead of waiting for the
10
27
  /// full backoff delay.
@@ -19,28 +36,53 @@ class RetryManager {
19
36
  }
20
37
 
21
38
  private static let defaultMaxRetryDurationMs: Double = 120_000 // 2 minutes
39
+ private static let defaultFirstConnectMaxRetryDurationMs: Double = 12_000
22
40
 
23
41
  /// Prevents surprising playback resumption after long periods offline.
24
42
  private var maxRetryDuration: TimeInterval = defaultMaxRetryDurationMs / 1000
25
43
 
44
+ /// Bounds retries of a load that has never produced audio, while online.
45
+ private var firstConnectMaxRetryDuration: TimeInterval = defaultFirstConnectMaxRetryDurationMs / 1000
46
+
47
+ /// True once the current load has produced audio; selects between the
48
+ /// first-connect and recovery budgets. Set by the owner when audio starts and
49
+ /// cleared by the owner when a new track loads — deliberately NOT cleared in
50
+ /// `reset()`, which the healthy-playback budget refill also calls mid-play.
51
+ var hasPlayed = false
52
+
53
+ /// First failure observed while online for the current load — the
54
+ /// first-connect budget's clock. Never set while offline, and wiped by any
55
+ /// offline observation, so an offline stretch cannot burn the short budget:
56
+ /// the clock restarts at the next failure observed online.
57
+ private var firstOnlineRetryTime: Date?
58
+
26
59
  private var policy: Policy = .disabled
27
60
  private var attemptCount = 0
28
61
  private var firstRetryTime: Date?
29
62
 
30
- weak var networkMonitor: NetworkMonitor?
31
- private var isWaitingForNetwork = false
63
+ weak var networkMonitor: (any NetworkStatusProviding)?
64
+ /// True only while a scheduled retry is parked polling for connectivity to return. Exposed so the
65
+ /// player's stall-driven reconnect can defer to an in-flight error retry (which owns its own
66
+ /// reload) and avoid a double load.
67
+ private(set) var isWaitingForNetwork = false
32
68
  /// Invalidates in-flight retries when reset() is called (e.g. track change)
33
69
  private var generation: Int = 0
34
70
 
35
71
  var shouldRetry: () -> Bool = { true }
36
72
  var onRetry: ((Bool) -> Void)?
37
73
 
74
+ var isEnabled: Bool {
75
+ if case .disabled = policy { return false }
76
+ return true
77
+ }
78
+
38
79
  // MARK: - Configuration
39
80
 
40
81
  func updatePolicy(from config: Variant_Bool_RetryConfig?) {
41
82
  guard let config else {
42
83
  policy = .disabled
43
84
  maxRetryDuration = Self.defaultMaxRetryDurationMs / 1000
85
+ firstConnectMaxRetryDuration = Self.defaultFirstConnectMaxRetryDurationMs / 1000
44
86
  logger.debug("Retry policy: disabled")
45
87
  return
46
88
  }
@@ -49,13 +91,22 @@ class RetryManager {
49
91
  case let .first(enabled):
50
92
  policy = enabled ? .infinite : .disabled
51
93
  maxRetryDuration = Self.defaultMaxRetryDurationMs / 1000
94
+ firstConnectMaxRetryDuration = Self.defaultFirstConnectMaxRetryDurationMs / 1000
52
95
  logger.debug("Retry policy: \(enabled ? "infinite" : "disabled")")
53
96
  case let .second(retryConfig):
54
- let maxRetries = Int(retryConfig.maxRetries)
55
- policy = .limited(maxRetries: maxRetries)
56
97
  let durationMs = retryConfig.maxRetryDurationMs ?? Self.defaultMaxRetryDurationMs
57
98
  maxRetryDuration = durationMs / 1000
58
- logger.debug("Retry policy: limited to \(maxRetries) retries, max duration \(self.maxRetryDuration)s")
99
+ let firstConnectMs = retryConfig.firstConnectMaxRetryDurationMs
100
+ ?? Self.defaultFirstConnectMaxRetryDurationMs
101
+ firstConnectMaxRetryDuration = firstConnectMs / 1000
102
+ // No attempt cap = retry indefinitely, bounded only by the duration.
103
+ if let maxRetries = retryConfig.maxRetries.map(Int.init) {
104
+ policy = .limited(maxRetries: maxRetries)
105
+ logger.debug("Retry policy: limited to \(maxRetries) retries, max duration \(self.maxRetryDuration)s")
106
+ } else {
107
+ policy = .infinite
108
+ logger.debug("Retry policy: infinite, max duration \(self.maxRetryDuration)s")
109
+ }
59
110
  }
60
111
  }
61
112
 
@@ -72,30 +123,89 @@ class RetryManager {
72
123
 
73
124
  // MARK: - Error Classification
74
125
 
75
- func isRetryable(_ error: Error?) -> Bool {
126
+ /// HTTP statuses worth another attempt. Same set as Android's
127
+ /// `RetryLoadErrorHandlingPolicy.RETRYABLE_HTTP_STATUS_CODES`, so a flaky
128
+ /// server behaves the same on both platforms.
129
+ private static let retryableHTTPStatusCodes: Set<Int> = [408, 429]
130
+
131
+ /// AVFoundation failures a retry cannot fix: the media is unusable rather
132
+ /// than the transport unreliable. Everything not listed falls through to
133
+ /// retry — `AVErrorUnknown` especially, which is the wrapper AVFoundation
134
+ /// puts around opaque CoreMedia transport failures.
135
+ private static let fatalAVErrorCodes: Set<Int> = [
136
+ AVError.fileFormatNotRecognized.rawValue,
137
+ AVError.failedToParse.rawValue,
138
+ AVError.decodeFailed.rawValue,
139
+ AVError.contentIsProtected.rawValue,
140
+ AVError.contentIsNotAuthorized.rawValue,
141
+ ]
142
+
143
+ /// Whether another attempt could plausibly succeed.
144
+ ///
145
+ /// Mirrors Android's `classifyError`, including its default: an unrecognized
146
+ /// failure is treated as *transient* and retried, bounded by the retry
147
+ /// budgets, rather than being terminal. Defaulting the other way made
148
+ /// `retry: true` largely inert on iOS, because AVFoundation reports most
149
+ /// stream failures as opaque `CoreMediaErrorDomain` / `AVFoundationErrorDomain`
150
+ /// errors rather than `URLError`.
151
+ ///
152
+ /// - Parameter httpStatusCode: The status from the item's error log, when the
153
+ /// caller has one. `AVPlayerItem.error` never carries it — a 404 arrives as
154
+ /// an opaque CoreMedia error — so this is the only reliable HTTP signal and
155
+ /// it outranks the error itself.
156
+ func isRetryable(_ error: Error?, httpStatusCode: Int?) -> Bool {
157
+ if let status = httpStatusCode {
158
+ let retryable = Self.retryableHTTPStatusCodes.contains(status) || (500 ... 599).contains(status)
159
+ logger.debug("HTTP \(status) \(retryable ? "is" : "not") retryable")
160
+ return retryable
161
+ }
162
+
76
163
  guard let error else { return false }
77
164
 
78
- if let urlError = error as? URLError {
79
- switch urlError.code {
80
- // Transient network errors - safe to retry
81
- case .timedOut,
82
- .networkConnectionLost,
83
- .notConnectedToInternet,
84
- .cannotConnectToHost,
85
- .cannotFindHost,
86
- .dnsLookupFailed:
87
- logger.debug("URLError is retryable: code=\(urlError.code.rawValue)")
88
- return true
89
- default:
90
- logger.debug("URLError not retryable: code=\(urlError.code.rawValue)")
165
+ // Walk the underlying-error chain: AVFoundation routinely wraps the real
166
+ // cause (a URLError, or a CoreMedia code) in an AVError.
167
+ for candidate in Self.errorChain(error) {
168
+ if let urlError = candidate as? URLError {
169
+ switch urlError.code {
170
+ // Transient network errors - safe to retry
171
+ case .timedOut,
172
+ .networkConnectionLost,
173
+ .notConnectedToInternet,
174
+ .cannotConnectToHost,
175
+ .cannotFindHost,
176
+ .dnsLookupFailed:
177
+ logger.debug("URLError is retryable: code=\(urlError.code.rawValue)")
178
+ return true
179
+ default:
180
+ logger.debug("URLError not retryable: code=\(urlError.code.rawValue)")
181
+ return false
182
+ }
183
+ }
184
+
185
+ let nsError = candidate as NSError
186
+ if nsError.domain == AVFoundationErrorDomain,
187
+ Self.fatalAVErrorCodes.contains(nsError.code)
188
+ {
189
+ logger.debug("AVError not retryable: code=\(nsError.code)")
91
190
  return false
92
191
  }
93
192
  }
94
193
 
95
- // Unknown errors - don't retry
96
194
  let nsError = error as NSError
97
- logger.debug("Error not retryable: domain=\(nsError.domain), code=\(nsError.code)")
98
- return false
195
+ logger.debug("Treating as transient: domain=\(nsError.domain), code=\(nsError.code)")
196
+ return true
197
+ }
198
+
199
+ /// The error and its `NSUnderlyingError` ancestors, outermost first.
200
+ private static func errorChain(_ error: Error) -> [Error] {
201
+ var chain: [Error] = []
202
+ var current: Error? = error
203
+ // Bounded: a malformed cycle must not spin here.
204
+ while let error = current, chain.count < 8 {
205
+ chain.append(error)
206
+ current = (error as NSError).userInfo[NSUnderlyingErrorKey] as? Error
207
+ }
208
+ return chain
99
209
  }
100
210
 
101
211
  // MARK: - Retry Management
@@ -103,11 +213,30 @@ class RetryManager {
103
213
  func reset() {
104
214
  attemptCount = 0
105
215
  firstRetryTime = nil
216
+ firstOnlineRetryTime = nil
106
217
  isWaitingForNetwork = false
107
218
  generation += 1
108
219
  logger.debug("Retry count reset (generation \(self.generation))")
109
220
  }
110
221
 
222
+ private var isOnline: Bool { networkMonitor?.isOnline ?? true }
223
+
224
+ /// A description of the exhausted budget, or nil while budget remains.
225
+ /// The recovery duration bounds everything (including offline waits); the
226
+ /// first-connect duration additionally bounds never-played loads, online only.
227
+ private func exhaustedBudget() -> String? {
228
+ let now = Date()
229
+ if let start = firstRetryTime, now.timeIntervalSince(start) >= maxRetryDuration {
230
+ return "max retry duration (\(maxRetryDuration)s)"
231
+ }
232
+ if !hasPlayed, isOnline, let start = firstOnlineRetryTime,
233
+ now.timeIntervalSince(start) >= firstConnectMaxRetryDuration
234
+ {
235
+ return "first-connect retry duration (\(firstConnectMaxRetryDuration)s)"
236
+ }
237
+ return nil
238
+ }
239
+
111
240
  func attemptRetry(startFromCurrentTime: Bool) async -> Bool {
112
241
  if case .disabled = policy {
113
242
  logger.debug("Retry disabled, not retrying")
@@ -129,14 +258,20 @@ class RetryManager {
129
258
  if firstRetryTime == nil {
130
259
  firstRetryTime = Date()
131
260
  }
261
+ if isOnline {
262
+ if firstOnlineRetryTime == nil { firstOnlineRetryTime = Date() }
263
+ } else {
264
+ // Seeing the device offline restarts the first-connect clock: a station
265
+ // that lost connectivity mid-budget gets its full online seconds again
266
+ // after restoration, instead of the offline gap counting against it.
267
+ firstOnlineRetryTime = nil
268
+ }
132
269
 
133
- // Check if we've been retrying too long (prevents surprising resumption after long offline periods)
134
- if let startTime = firstRetryTime {
135
- let elapsed = Date().timeIntervalSince(startTime)
136
- if elapsed >= maxRetryDuration {
137
- logger.info("Max retry duration (\(self.maxRetryDuration)s) exceeded after \(elapsed)s, giving up")
138
- return false
139
- }
270
+ // Check if we've been retrying too long (prevents surprising resumption
271
+ // after long offline periods, and grants dead-on-arrival streams a fast verdict)
272
+ if let budget = exhaustedBudget() {
273
+ logger.info("\(budget) exceeded, giving up")
274
+ return false
140
275
  }
141
276
 
142
277
  let delaySeconds = calculateDelaySeconds()
@@ -157,18 +292,20 @@ class RetryManager {
157
292
  return false
158
293
  }
159
294
 
295
+ // Cancellation can land after the sleep completed — without this check a
296
+ // replaced retry (new error right as the old backoff expires) still fires
297
+ // onRetry, double-reloading and burning the attempt count twice.
298
+ guard !Task.isCancelled else { return false }
299
+
160
300
  guard shouldRetry() else {
161
301
  logger.debug("shouldRetry returned false after delay, cancelling retry")
162
302
  return false
163
303
  }
164
304
 
165
- // Re-check duration — we may have waited a long time for network restoration
166
- if let startTime = firstRetryTime {
167
- let elapsed = Date().timeIntervalSince(startTime)
168
- if elapsed >= maxRetryDuration {
169
- logger.info("Max retry duration (\(self.maxRetryDuration)s) exceeded after waiting (\(elapsed)s), giving up")
170
- return false
171
- }
305
+ // Re-check the budgets — we may have waited a long time for network restoration
306
+ if let budget = exhaustedBudget() {
307
+ logger.info("\(budget) exceeded after waiting, giving up")
308
+ return false
172
309
  }
173
310
 
174
311
  attemptCount += 1
@@ -218,7 +355,7 @@ class RetryManager {
218
355
 
219
356
  /// Returns false when network is restored, true if cancelled.
220
357
  @MainActor
221
- private func waitForNetworkRestored(monitor: NetworkMonitor) async -> Bool {
358
+ private func waitForNetworkRestored(monitor: any NetworkStatusProviding) async -> Bool {
222
359
  if monitor.isOnline {
223
360
  return false
224
361
  }
@@ -39,6 +39,16 @@ struct ShuffleOrder {
39
39
  indexInShuffled = Self.buildIndexInShuffled(from: shuffled)
40
40
  }
41
41
 
42
+ /// Creates a shuffle order for the given length with `firstIndex` pinned to shuffle position 0.
43
+ ///
44
+ /// Playback starts on that track, so leaving it wherever the permutation put it would end the
45
+ /// queue as soon as it is reached — on average after half the queue, and immediately whenever it
46
+ /// lands last.
47
+ init(length: Int, firstIndex: Int) {
48
+ self.init(length: length)
49
+ reshuffle(keepingFirst: firstIndex)
50
+ }
51
+
42
52
  /// Creates a shuffle order with pre-defined indices
43
53
  init(shuffledIndices: [Int]) {
44
54
  shuffled = shuffledIndices
@@ -1,7 +1,15 @@
1
+ import Foundation
2
+
1
3
  /// Protocol abstracting sleep timer for testability.
2
4
  /// PlaybackCoordinator uses this instead of SleepTimerManager directly.
3
5
  @MainActor protocol SleepTimerHandling: AnyObject {
4
6
  var onComplete: (() -> Void)? { get set }
7
+ /// Invoked when the fade-out window of a time-based timer begins.
8
+ var onFadeStart: ((_ duration: TimeInterval) -> Void)? { get set }
9
+ /// Invoked when a timer is cancelled/replaced while its fade may be running —
10
+ /// the listener restores the pre-fade volume.
11
+ var onFadeCancel: (() -> Void)? { get set }
12
+ @discardableResult func clear() -> Bool
5
13
  func onTrackChanged()
6
14
  func onTrackPlayedToEnd()
7
15
  }
@@ -1,17 +1,57 @@
1
1
  import Foundation
2
- import NitroModules
2
+
3
+ #if canImport(NitroModules)
4
+ import NitroModules
5
+ #endif
3
6
 
4
7
  /// Type alias for the Nitro SleepTimer variant type
5
8
  typealias SleepTimerState = SleepTimer
6
9
 
10
+ /// A scheduled job that has not run yet. `cancel()` is nonisolated so `deinit`
11
+ /// — always nonisolated in Swift 6 — can tear pending work down.
12
+ protocol SleepTimerJob: AnyObject {
13
+ func cancel()
14
+ }
15
+
16
+ extension DispatchWorkItem: SleepTimerJob {}
17
+
18
+ /// Runs work after a delay. Injected into `SleepTimerManager` so tests can
19
+ /// drive the clock instead of racing it: production schedules on the main
20
+ /// queue, tests hand in a scheduler they advance by hand.
21
+ @MainActor
22
+ protocol SleepTimerScheduling {
23
+ func schedule(
24
+ after delay: TimeInterval,
25
+ _ work: @escaping @Sendable @MainActor () -> Void,
26
+ ) -> any SleepTimerJob
27
+ }
28
+
29
+ struct MainQueueSleepTimerScheduler: SleepTimerScheduling {
30
+ func schedule(
31
+ after delay: TimeInterval,
32
+ _ work: @escaping @Sendable @MainActor () -> Void,
33
+ ) -> any SleepTimerJob {
34
+ let job = DispatchWorkItem { MainActor.assumeIsolated { work() } }
35
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: job)
36
+ return job
37
+ }
38
+ }
39
+
7
40
  /// Manages sleep timer functionality for the audio player.
8
41
  /// Supports both time-based timers and end-of-track timers.
9
42
  @MainActor
10
43
  class SleepTimerManager: SleepTimerHandling {
11
44
  // MARK: - Properties
12
45
 
46
+ private let scheduler: any SleepTimerScheduling
47
+
13
48
  // nonisolated(unsafe) for deinit cleanup — deinit is always nonisolated in Swift 6.
14
- nonisolated(unsafe) private var sleepTimerJob: DispatchWorkItem?
49
+ private nonisolated(unsafe) var sleepTimerJob: (any SleepTimerJob)?
50
+ private nonisolated(unsafe) var fadeJob: (any SleepTimerJob)?
51
+
52
+ init(scheduler: any SleepTimerScheduling = MainQueueSleepTimerScheduler()) {
53
+ self.scheduler = scheduler
54
+ }
15
55
 
16
56
  /// The time when playback should stop (seconds since epoch), or -1 if inactive
17
57
  private(set) var sleepTimerTime: TimeInterval = -1 {
@@ -36,6 +76,14 @@ class SleepTimerManager: SleepTimerHandling {
36
76
  /// Callback invoked when the sleep timer fires
37
77
  var onComplete: (() -> Void)?
38
78
 
79
+ /// Callback invoked when the fade-out window of a time-based timer begins
80
+ var onFadeStart: ((_ duration: TimeInterval) -> Void)?
81
+
82
+ /// Callback invoked when a timer is cancelled/replaced while its fade may be
83
+ /// running — the listener restores the pre-fade volume. Not invoked on
84
+ /// completion: there the pause lands first, then the volume is restored.
85
+ var onFadeCancel: (() -> Void)?
86
+
39
87
  /// Callback invoked when the sleep timer state changes
40
88
  var onChanged: ((SleepTimerState) -> Void)?
41
89
 
@@ -49,6 +97,7 @@ class SleepTimerManager: SleepTimerHandling {
49
97
  if !hasSleepTimer { return false }
50
98
  sleepTimerTime = -1
51
99
  willSleepWhenCurrentItemReachesEnd = false
100
+ onFadeCancel?()
52
101
  onChanged?(.first(NullType.null))
53
102
  return true
54
103
  }
@@ -74,17 +123,23 @@ class SleepTimerManager: SleepTimerHandling {
74
123
  }
75
124
 
76
125
  /// Sets a time-based sleep timer.
77
- /// - Parameter seconds: Number of seconds until playback stops
78
- func set(seconds: TimeInterval) {
126
+ /// - Parameters:
127
+ /// - seconds: Number of seconds until playback stops
128
+ /// - fadeDuration: Seconds over which to fade the volume out before the
129
+ /// deadline (clamped to `seconds`); silence lands exactly at the deadline
130
+ func set(seconds: TimeInterval, fadeDuration: TimeInterval? = nil) {
79
131
  cancelSleepTimerJob()
80
- let job = DispatchWorkItem { [weak self] in
81
- MainActor.assumeIsolated {
82
- self?.complete()
132
+ onFadeCancel?()
133
+ sleepTimerTime = Date().timeIntervalSince1970 + seconds
134
+ sleepTimerJob = scheduler.schedule(after: seconds) { [weak self] in
135
+ self?.complete()
136
+ }
137
+ if let fadeDuration, fadeDuration > 0 {
138
+ let fade = min(fadeDuration, seconds)
139
+ fadeJob = scheduler.schedule(after: seconds - fade) { [weak self] in
140
+ self?.onFadeStart?(fade)
83
141
  }
84
142
  }
85
- sleepTimerJob = job
86
- sleepTimerTime = Date().timeIntervalSince1970 + seconds
87
- DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: job)
88
143
  if let state = get() { onChanged?(state) }
89
144
  }
90
145
 
@@ -92,7 +147,10 @@ class SleepTimerManager: SleepTimerHandling {
92
147
  func setToEndOfTrack() {
93
148
  let changed = !willSleepWhenCurrentItemReachesEnd
94
149
  willSleepWhenCurrentItemReachesEnd = true
95
- if changed, let state = get() { onChanged?(state) }
150
+ if changed {
151
+ onFadeCancel?()
152
+ if let state = get() { onChanged?(state) }
153
+ }
96
154
  }
97
155
 
98
156
  /// Called when the current track changes. Resets end-of-track timer.
@@ -116,6 +174,8 @@ class SleepTimerManager: SleepTimerHandling {
116
174
  private func cancelSleepTimerJob() {
117
175
  sleepTimerJob?.cancel()
118
176
  sleepTimerJob = nil
177
+ fadeJob?.cancel()
178
+ fadeJob = nil
119
179
  }
120
180
 
121
181
  private func complete() {
@@ -127,5 +187,6 @@ class SleepTimerManager: SleepTimerHandling {
127
187
 
128
188
  deinit {
129
189
  sleepTimerJob?.cancel()
190
+ fadeJob?.cancel()
130
191
  }
131
192
  }
@@ -0,0 +1,77 @@
1
+ import Foundation
2
+
3
+ /// Ramps the player volume down over a duration (sleep-timer fade).
4
+ /// The gain follows a squared curve: loudness perception is logarithmic, so a
5
+ /// linear gain ramp sounds like a late cutoff rather than a fade.
6
+ @MainActor
7
+ final class VolumeFader {
8
+ private static let stepInterval: TimeInterval = 0.05
9
+
10
+ private let getVolume: () -> Float
11
+ private let setVolume: (Float) -> Void
12
+ // nonisolated(unsafe) for deinit cleanup — deinit is always nonisolated in Swift 6.
13
+ private nonisolated(unsafe) var timer: Timer?
14
+
15
+ /// Volume captured when the fade started; restored after the fade resolves.
16
+ /// Non-nil from fade start until cancel/completion, even once the ramp has
17
+ /// reached silence.
18
+ private(set) var originalVolume: Float?
19
+
20
+ var isActive: Bool { originalVolume != nil }
21
+
22
+ init(getVolume: @escaping () -> Float, setVolume: @escaping (Float) -> Void) {
23
+ self.getVolume = getVolume
24
+ self.setVolume = setVolume
25
+ }
26
+
27
+ func start(duration: TimeInterval) {
28
+ // If a fade is already running, keep its captured volume as the baseline —
29
+ // the current (partially faded) volume is not the value to restore.
30
+ let baseVolume = originalVolume ?? getVolume()
31
+ cancel(restoringVolume: false)
32
+ originalVolume = baseVolume
33
+ guard duration > 0 else { return }
34
+
35
+ let startDate = Date()
36
+ let timer = Timer(timeInterval: Self.stepInterval, repeats: true) { [weak self] _ in
37
+ MainActor.assumeIsolated {
38
+ guard let self else { return }
39
+ let progress = min(1, Date().timeIntervalSince(startDate) / duration)
40
+ let remaining = Float(1 - progress)
41
+ self.setVolume(baseVolume * remaining * remaining)
42
+ if progress >= 1 {
43
+ self.timer?.invalidate()
44
+ self.timer = nil
45
+ }
46
+ }
47
+ }
48
+ self.timer = timer
49
+ RunLoop.main.add(timer, forMode: .common)
50
+ }
51
+
52
+ /// Stops the ramp. With `restoringVolume` the pre-fade volume is put back
53
+ /// immediately (cancellation); without it the caller restores after pausing
54
+ /// (completion path — restoring first would let full-volume audio slip out).
55
+ func cancel(restoringVolume: Bool) {
56
+ timer?.invalidate()
57
+ timer = nil
58
+ guard let originalVolume else { return }
59
+ self.originalVolume = nil
60
+ if restoringVolume { setVolume(originalVolume) }
61
+ }
62
+
63
+ /// Resolves the fade around a halting action: stops the ramp, runs `halt`,
64
+ /// then restores the pre-fade volume — in that order, because restoring
65
+ /// before halting would let full-volume audio slip out. The restore is
66
+ /// skipped when no fade was running.
67
+ func resolve(around halt: () -> Void) {
68
+ let preFadeVolume = originalVolume
69
+ cancel(restoringVolume: false)
70
+ halt()
71
+ if let preFadeVolume { setVolume(preFadeVolume) }
72
+ }
73
+
74
+ deinit {
75
+ timer?.invalidate()
76
+ }
77
+ }
@@ -0,0 +1,47 @@
1
+ #if canImport(NitroModules)
2
+ import NitroModules
3
+ #endif
4
+
5
+ /// The callbacks `RemoteCommandController` invokes when the system delivers a
6
+ /// remote command (lock screen, Control Center, CarPlay, headset).
7
+ ///
8
+ /// Split out of `TrackPlayerCallbacks` — which the controller does not otherwise
9
+ /// use — so the controller depends only on what it calls. `TrackPlayerCallbacks`
10
+ /// extends this protocol, so conformers implement both at once.
11
+ @MainActor protocol RemoteCommandCallbacks: AnyObject {
12
+ /// Called when play is triggered remotely.
13
+ func remotePlay()
14
+
15
+ /// Called when pause is triggered remotely.
16
+ func remotePause()
17
+
18
+ /// Called when stop is triggered remotely.
19
+ func remoteStop()
20
+
21
+ /// Called when toggle play/pause is triggered remotely.
22
+ func remotePlayPause()
23
+
24
+ /// Called when next is triggered remotely.
25
+ func remoteNext()
26
+
27
+ /// Called when previous is triggered remotely.
28
+ func remotePrevious()
29
+
30
+ /// Called when jump forward is triggered remotely.
31
+ func remoteJumpForward(interval: Double)
32
+
33
+ /// Called when jump backward is triggered remotely.
34
+ func remoteJumpBackward(interval: Double)
35
+
36
+ /// Called when seek is triggered remotely.
37
+ func remoteSeek(position: Double)
38
+
39
+ /// Called when repeat mode change is triggered remotely (CarPlay/lock screen).
40
+ func remoteChangeRepeatMode(mode: RepeatMode)
41
+
42
+ /// Called when shuffle mode change is triggered remotely (CarPlay/lock screen).
43
+ func remoteChangeShuffleMode(enabled: Bool)
44
+
45
+ /// Called when playback rate change is triggered remotely (CarPlay/lock screen).
46
+ func remoteChangePlaybackRate(rate: Float)
47
+ }