react-native-audio-browser 0.1.0 → 0.2.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (902) hide show
  1. package/AudioBrowser.podspec +1 -1
  2. package/README.md +180 -2
  3. package/android/build.gradle +10 -0
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/AndroidManifest.xml +15 -0
  6. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +401 -141
  7. package/android/src/main/java/com/audiobrowser/Callbacks.kt +4 -6
  8. package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
  9. package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
  10. package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
  11. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +443 -178
  12. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +300 -0
  13. package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
  14. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
  15. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +43 -0
  16. package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
  17. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
  18. package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
  19. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
  20. package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
  21. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +31 -37
  22. package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
  23. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +119 -0
  24. package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
  25. package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
  26. package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
  27. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
  28. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +415 -131
  29. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +154 -276
  30. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +348 -0
  31. package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
  32. package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
  33. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +27 -5
  34. package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
  35. package/android/src/main/java/com/audiobrowser/player/Player.kt +452 -576
  36. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
  37. package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
  38. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
  39. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +117 -53
  40. package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
  41. package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
  42. package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
  43. package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +84 -34
  44. package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
  45. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +141 -0
  46. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
  47. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +41 -0
  48. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +209 -0
  49. package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
  50. package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
  51. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +26 -0
  52. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
  53. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
  54. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
  55. package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
  56. package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
  57. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
  58. package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
  59. package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
  60. package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
  61. package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
  62. package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
  63. package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
  64. package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
  65. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +111 -80
  66. package/android/src/main/res/values/strings.xml +4 -0
  67. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +166 -0
  68. package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
  69. package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
  70. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
  71. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
  72. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
  73. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
  74. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +54 -0
  75. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +96 -0
  76. package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
  77. package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
  78. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +159 -0
  79. package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
  80. package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
  81. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
  82. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +191 -0
  83. package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
  84. package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
  85. package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
  86. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
  87. package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
  88. package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
  89. package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
  90. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +107 -0
  91. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +73 -0
  92. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +75 -0
  93. package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
  94. package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
  95. package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
  96. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
  97. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
  98. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
  99. package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
  100. package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
  101. package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
  102. package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
  103. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
  104. package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
  105. package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
  106. package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
  107. package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
  108. package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
  109. package/ios/Browser/BrowserConfig.swift +41 -11
  110. package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
  111. package/ios/Browser/BrowserManager.swift +303 -110
  112. package/ios/Browser/BrowserPathHelper.swift +32 -3
  113. package/ios/Browser/JsonModels.swift +287 -66
  114. package/ios/Browser/MediaResolveComposer.swift +114 -0
  115. package/ios/Browser/SearchDrillIn.swift +44 -0
  116. package/ios/Browser/SectionScope.swift +35 -0
  117. package/ios/Browser/TabBarEntries.swift +15 -0
  118. package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
  119. package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
  120. package/ios/CarPlay/CarPlayController.swift +739 -149
  121. package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
  122. package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
  123. package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
  124. package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
  125. package/ios/CarPlay/RNABAudioBrowser.h +15 -10
  126. package/ios/CarPlay/RNABAudioBrowser.m +15 -14
  127. package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
  128. package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
  129. package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
  130. package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
  131. package/ios/Extension/Capability+RemoteCommand.swift +7 -9
  132. package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
  133. package/ios/Extension/NavigationError+Conversion.swift +8 -1
  134. package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
  135. package/ios/Extension/RequestConfig+Copying.swift +31 -0
  136. package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
  137. package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
  138. package/ios/Extension/Track+AVPlayer.swift +4 -4
  139. package/ios/Extension/Track+Copying.swift +9 -3
  140. package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
  141. package/ios/Extension/Variant+Artwork.swift +49 -0
  142. package/ios/HybridAudioBrowser.swift +750 -173
  143. package/ios/Model/NitroTypeStubs.swift +299 -224
  144. package/ios/Model/PlayerUpdateOptions.swift +23 -23
  145. package/ios/Model/RemoteCommand.swift +6 -6
  146. package/ios/Model/TrackPlayerError.swift +82 -18
  147. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
  148. package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
  149. package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
  150. package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
  151. package/ios/Observer/PlayerTimeObserver.swift +3 -53
  152. package/ios/Option/SessionCategories.swift +33 -11
  153. package/ios/Player/AirPlayStallJudgement.swift +25 -0
  154. package/ios/Player/EndOfTrackJudgement.swift +32 -0
  155. package/ios/Player/LoadSeekCoordinator.swift +16 -1
  156. package/ios/Player/MediaIntentCriteria.swift +139 -0
  157. package/ios/Player/MediaLoader.swift +28 -10
  158. package/ios/Player/NowPlayingUpdater.swift +119 -26
  159. package/ios/Player/PersistedPlaybackState.swift +11 -0
  160. package/ios/Player/PlaybackCoordinator.swift +282 -51
  161. package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
  162. package/ios/Player/PlaybackEffectHandler.swift +19 -9
  163. package/ios/Player/PlaybackErrorHandler.swift +97 -13
  164. package/ios/Player/PlaybackStateMachine.swift +18 -13
  165. package/ios/Player/PlaybackStateStore.swift +27 -0
  166. package/ios/Player/PlaybackTimer.swift +61 -0
  167. package/ios/Player/PlayingStateManager.swift +3 -2
  168. package/ios/Player/QueueManager.swift +67 -61
  169. package/ios/Player/RetryManager.swift +97 -21
  170. package/ios/Player/SleepTimerHandling.swift +8 -0
  171. package/ios/Player/SleepTimerManager.swift +38 -5
  172. package/ios/Player/VolumeFader.swift +77 -0
  173. package/ios/RemoteCommand/RemoteCommandController.swift +31 -15
  174. package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
  175. package/ios/Tests/BrowserPathHelperTests.swift +28 -0
  176. package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
  177. package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
  178. package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
  179. package/ios/Tests/JsonModelsTests.swift +58 -0
  180. package/ios/Tests/LoadSeekCoordinatorTests.swift +38 -7
  181. package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
  182. package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
  183. package/ios/Tests/MediaLoaderTests.swift +23 -8
  184. package/ios/Tests/MediaResolveTests.swift +126 -0
  185. package/ios/Tests/OnceValueTests.swift +79 -0
  186. package/ios/Tests/PlaybackCoordinatorTests.swift +784 -13
  187. package/ios/Tests/PlaybackErrorHandlerTests.swift +380 -46
  188. package/ios/Tests/PlaybackStateMachineTests.swift +42 -10
  189. package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
  190. package/ios/Tests/PlaybackTimerTests.swift +55 -0
  191. package/ios/Tests/PlayingStateManagerTests.swift +57 -0
  192. package/ios/Tests/QueueManagerTests.swift +149 -18
  193. package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
  194. package/ios/Tests/RetryManagerTests.swift +89 -0
  195. package/ios/Tests/SearchDrillInTests.swift +63 -0
  196. package/ios/Tests/SectionScopeTests.swift +82 -0
  197. package/ios/Tests/SleepTimerManagerTests.swift +104 -0
  198. package/ios/Tests/Support/TestTypes.swift +61 -20
  199. package/ios/Tests/TabBarEntriesTests.swift +71 -0
  200. package/ios/Tests/TrackSelectorTests.swift +15 -15
  201. package/ios/TrackPlayer.swift +380 -58
  202. package/ios/TrackPlayerCallbacks.swift +0 -6
  203. package/ios/TrackSelector.swift +5 -5
  204. package/ios/Util/ArtworkImageFetcher.swift +4 -2
  205. package/ios/Util/NetworkMonitor.swift +7 -3
  206. package/ios/Util/OnceValue.swift +34 -0
  207. package/lib/commonjs/features/browser-config.js +227 -0
  208. package/lib/commonjs/features/browser-config.js.map +1 -0
  209. package/lib/commonjs/features/browser.js +59 -158
  210. package/lib/commonjs/features/browser.js.map +1 -1
  211. package/lib/commonjs/features/carConnection.js +42 -0
  212. package/lib/commonjs/features/carConnection.js.map +1 -0
  213. package/lib/commonjs/features/errors.js +34 -2
  214. package/lib/commonjs/features/errors.js.map +1 -1
  215. package/lib/commonjs/features/gate.js +149 -0
  216. package/lib/commonjs/features/gate.js.map +1 -0
  217. package/lib/commonjs/features/index.js +22 -0
  218. package/lib/commonjs/features/index.js.map +1 -1
  219. package/lib/commonjs/features/metadata.js.map +1 -1
  220. package/lib/commonjs/features/nowPlaying.js +35 -0
  221. package/lib/commonjs/features/nowPlaying.js.map +1 -1
  222. package/lib/commonjs/features/output.js +37 -23
  223. package/lib/commonjs/features/output.js.map +1 -1
  224. package/lib/commonjs/features/playback/controls.js +9 -0
  225. package/lib/commonjs/features/playback/controls.js.map +1 -1
  226. package/lib/commonjs/features/playback/index.js +11 -0
  227. package/lib/commonjs/features/playback/index.js.map +1 -1
  228. package/lib/commonjs/features/playback/progress.js +28 -12
  229. package/lib/commonjs/features/playback/progress.js.map +1 -1
  230. package/lib/commonjs/features/playback/trackPlaybackTime.js +92 -0
  231. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
  232. package/lib/commonjs/features/playback/volume.js +1 -1
  233. package/lib/commonjs/features/player/options.js +49 -13
  234. package/lib/commonjs/features/player/options.js.map +1 -1
  235. package/lib/commonjs/features/player/setup.js +249 -3
  236. package/lib/commonjs/features/player/setup.js.map +1 -1
  237. package/lib/commonjs/features/queue/queue.js +10 -3
  238. package/lib/commonjs/features/queue/queue.js.map +1 -1
  239. package/lib/commonjs/features/remoteControls.js +1 -12
  240. package/lib/commonjs/features/remoteControls.js.map +1 -1
  241. package/lib/commonjs/features/sleepTimer.js +9 -4
  242. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  243. package/lib/commonjs/native.js +5 -0
  244. package/lib/commonjs/native.js.map +1 -1
  245. package/lib/commonjs/native.web.js +4 -1
  246. package/lib/commonjs/native.web.js.map +1 -1
  247. package/lib/commonjs/utils/LazyNativeEmitter.js +0 -3
  248. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  249. package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
  250. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  251. package/lib/commonjs/utils/artwork.js +31 -0
  252. package/lib/commonjs/utils/artwork.js.map +1 -0
  253. package/lib/commonjs/web/NativeAudioBrowser.js +301 -124
  254. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  255. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
  256. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  257. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
  258. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  259. package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
  260. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  261. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
  262. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  263. package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
  264. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
  265. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
  266. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
  267. package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
  268. package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
  269. package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
  270. package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
  271. package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
  272. package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
  273. package/lib/commonjs/web/TrackPlayer/index.js +13 -5
  274. package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
  275. package/lib/commonjs/web/browser/BrowserManager.js +176 -50
  276. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  277. package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
  278. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
  279. package/lib/commonjs/web/browser/SearchManager.js +11 -9
  280. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  281. package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
  282. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
  283. package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
  284. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
  285. package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
  286. package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
  287. package/lib/commonjs/web/http/HttpClient.js +4 -23
  288. package/lib/commonjs/web/http/HttpClient.js.map +1 -1
  289. package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
  290. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  291. package/lib/commonjs/web/playbackErrorKind.js +57 -0
  292. package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
  293. package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
  294. package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
  295. package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
  296. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  297. package/lib/commonjs/web/player/OptionsManager.js +9 -17
  298. package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
  299. package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
  300. package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
  301. package/lib/module/features/browser-config.js +221 -0
  302. package/lib/module/features/browser-config.js.map +1 -0
  303. package/lib/module/features/browser.js +53 -156
  304. package/lib/module/features/browser.js.map +1 -1
  305. package/lib/module/features/carConnection.js +37 -0
  306. package/lib/module/features/carConnection.js.map +1 -0
  307. package/lib/module/features/errors.js +34 -2
  308. package/lib/module/features/errors.js.map +1 -1
  309. package/lib/module/features/gate.js +144 -0
  310. package/lib/module/features/gate.js.map +1 -0
  311. package/lib/module/features/index.js +2 -0
  312. package/lib/module/features/index.js.map +1 -1
  313. package/lib/module/features/metadata.js.map +1 -1
  314. package/lib/module/features/nowPlaying.js +33 -0
  315. package/lib/module/features/nowPlaying.js.map +1 -1
  316. package/lib/module/features/output.js +32 -19
  317. package/lib/module/features/output.js.map +1 -1
  318. package/lib/module/features/playback/controls.js +8 -0
  319. package/lib/module/features/playback/controls.js.map +1 -1
  320. package/lib/module/features/playback/index.js +1 -0
  321. package/lib/module/features/playback/index.js.map +1 -1
  322. package/lib/module/features/playback/progress.js +28 -12
  323. package/lib/module/features/playback/progress.js.map +1 -1
  324. package/lib/module/features/playback/trackPlaybackTime.js +88 -0
  325. package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
  326. package/lib/module/features/playback/volume.js +1 -1
  327. package/lib/module/features/player/options.js +48 -13
  328. package/lib/module/features/player/options.js.map +1 -1
  329. package/lib/module/features/player/setup.js +249 -3
  330. package/lib/module/features/player/setup.js.map +1 -1
  331. package/lib/module/features/queue/queue.js +10 -3
  332. package/lib/module/features/queue/queue.js.map +1 -1
  333. package/lib/module/features/remoteControls.js +0 -11
  334. package/lib/module/features/remoteControls.js.map +1 -1
  335. package/lib/module/features/sleepTimer.js +9 -4
  336. package/lib/module/features/sleepTimer.js.map +1 -1
  337. package/lib/module/native.js +5 -0
  338. package/lib/module/native.js.map +1 -1
  339. package/lib/module/native.web.js +4 -1
  340. package/lib/module/native.web.js.map +1 -1
  341. package/lib/module/utils/LazyNativeEmitter.js +0 -3
  342. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  343. package/lib/module/utils/NativeUpdatedValue.js +0 -3
  344. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  345. package/lib/module/utils/artwork.js +26 -0
  346. package/lib/module/utils/artwork.js.map +1 -0
  347. package/lib/module/web/NativeAudioBrowser.js +302 -125
  348. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  349. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
  350. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
  351. package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
  352. package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
  353. package/lib/module/web/TrackPlayer/Player.js +90 -26
  354. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  355. package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
  356. package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
  357. package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
  358. package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
  359. package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
  360. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
  361. package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
  362. package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
  363. package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
  364. package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
  365. package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
  366. package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
  367. package/lib/module/web/TrackPlayer/index.js +2 -1
  368. package/lib/module/web/TrackPlayer/index.js.map +1 -1
  369. package/lib/module/web/browser/BrowserManager.js +176 -50
  370. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  371. package/lib/module/web/browser/NavigationErrorManager.js +3 -1
  372. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
  373. package/lib/module/web/browser/SearchManager.js +11 -9
  374. package/lib/module/web/browser/SearchManager.js.map +1 -1
  375. package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
  376. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
  377. package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
  378. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
  379. package/lib/module/web/browser/parseSearchResponse.js +16 -0
  380. package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
  381. package/lib/module/web/http/HttpClient.js +4 -23
  382. package/lib/module/web/http/HttpClient.js.map +1 -1
  383. package/lib/module/web/http/RequestConfigBuilder.js +107 -58
  384. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  385. package/lib/module/web/playbackErrorKind.js +52 -0
  386. package/lib/module/web/playbackErrorKind.js.map +1 -0
  387. package/lib/module/web/player/MediaSessionManager.js +116 -0
  388. package/lib/module/web/player/MediaSessionManager.js.map +1 -0
  389. package/lib/module/web/player/NowPlayingManager.js +7 -5
  390. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  391. package/lib/module/web/player/OptionsManager.js +9 -17
  392. package/lib/module/web/player/OptionsManager.js.map +1 -1
  393. package/lib/module/web/player/RemoteCommandController.js +92 -0
  394. package/lib/module/web/player/RemoteCommandController.js.map +1 -0
  395. package/lib/typescript/src/features/browser-config.d.ts +12 -0
  396. package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
  397. package/lib/typescript/src/features/browser.d.ts +40 -1
  398. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  399. package/lib/typescript/src/features/carConnection.d.ts +20 -0
  400. package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
  401. package/lib/typescript/src/features/errors.d.ts +58 -3
  402. package/lib/typescript/src/features/errors.d.ts.map +1 -1
  403. package/lib/typescript/src/features/gate.d.ts +77 -0
  404. package/lib/typescript/src/features/gate.d.ts.map +1 -0
  405. package/lib/typescript/src/features/index.d.ts +2 -0
  406. package/lib/typescript/src/features/index.d.ts.map +1 -1
  407. package/lib/typescript/src/features/metadata.d.ts +6 -4
  408. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  409. package/lib/typescript/src/features/nowPlaying.d.ts +27 -0
  410. package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
  411. package/lib/typescript/src/features/output.d.ts +28 -18
  412. package/lib/typescript/src/features/output.d.ts.map +1 -1
  413. package/lib/typescript/src/features/playback/controls.d.ts +5 -0
  414. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  415. package/lib/typescript/src/features/playback/index.d.ts +1 -0
  416. package/lib/typescript/src/features/playback/index.d.ts.map +1 -1
  417. package/lib/typescript/src/features/playback/progress.d.ts +3 -0
  418. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  419. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
  420. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
  421. package/lib/typescript/src/features/playback/volume.d.ts +1 -1
  422. package/lib/typescript/src/features/player/options.d.ts +92 -93
  423. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  424. package/lib/typescript/src/features/player/setup.d.ts +206 -167
  425. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  426. package/lib/typescript/src/features/queue/queue.d.ts +9 -2
  427. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  428. package/lib/typescript/src/features/remoteControls.d.ts +0 -13
  429. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  430. package/lib/typescript/src/features/sleepTimer.d.ts +18 -3
  431. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  432. package/lib/typescript/src/index.d.ts.map +1 -1
  433. package/lib/typescript/src/native.d.ts +1 -2
  434. package/lib/typescript/src/native.d.ts.map +1 -1
  435. package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
  436. package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
  437. package/lib/typescript/src/types/browser-native.d.ts +9 -3
  438. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  439. package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
  440. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  441. package/lib/typescript/src/types/browser.d.ts +349 -85
  442. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  443. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +0 -2
  444. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  445. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
  446. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  447. package/lib/typescript/src/utils/artwork.d.ts +19 -0
  448. package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
  449. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
  450. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +3 -1
  451. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
  452. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
  453. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +1 -11
  454. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
  455. package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
  456. package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
  457. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
  458. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
  459. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
  460. package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
  461. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
  462. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
  463. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
  464. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
  465. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
  466. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
  467. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
  468. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
  469. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
  470. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
  471. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
  472. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -0
  473. 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
  474. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
  475. package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
  476. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
  477. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +9 -4
  478. package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
  479. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
  480. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
  481. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
  482. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
  483. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
  484. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
  485. package/nitrogen/generated/android/c++/JGate.hpp +62 -0
  486. package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
  487. package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
  488. package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
  489. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +236 -212
  490. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +25 -22
  491. package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
  492. package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
  493. package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
  494. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
  495. package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
  496. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
  497. package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
  498. package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
  499. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
  500. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
  501. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +16 -19
  502. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
  503. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +1 -11
  504. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
  505. package/nitrogen/generated/android/c++/JNotificationButton.hpp +3 -3
  506. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
  507. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
  508. package/nitrogen/generated/android/c++/JOptions.hpp +14 -9
  509. package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
  510. package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
  511. package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
  512. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
  513. package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
  514. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
  515. package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
  516. package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
  517. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
  518. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
  519. package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
  520. package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
  521. package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
  522. package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
  523. package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
  524. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
  525. package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
  526. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
  527. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
  528. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
  529. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.cpp +41 -0
  530. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
  531. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
  532. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
  533. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
  534. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +2 -8
  535. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
  536. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
  537. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
  538. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → FavoriteConfig.kt} +6 -6
  539. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
  540. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
  541. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
  542. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
  543. 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
  544. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
  545. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
  546. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
  547. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
  548. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
  549. 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
  550. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
  551. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
  552. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
  553. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → Gate.kt} +9 -6
  554. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
  555. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
  556. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateReason.kt +23 -0
  557. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +103 -109
  558. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
  559. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
  560. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
  561. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
  562. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
  563. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
  564. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
  565. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
  566. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
  567. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
  568. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
  569. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
  570. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +2 -8
  571. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
  572. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButton.kt +5 -5
  573. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
  574. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
  575. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
  576. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
  577. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
  578. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
  579. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
  580. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
  581. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  582. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -3
  583. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
  584. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
  585. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
  586. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
  587. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
  588. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
  589. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
  590. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
  591. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
  592. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
  593. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
  594. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1098 -447
  595. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +60 -39
  596. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +170 -112
  597. package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
  598. package/nitrogen/generated/ios/swift/AndroidOptions.swift +2 -12
  599. package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
  600. package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
  601. package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
  602. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
  603. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
  604. package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
  605. package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
  606. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
  607. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
  608. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
  609. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
  610. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
  611. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
  612. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
  613. 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
  614. package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
  615. package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
  616. package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
  617. package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
  618. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  619. package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
  620. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
  621. 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
  622. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
  623. package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
  624. package/nitrogen/generated/ios/swift/Gate.swift +47 -0
  625. package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
  626. package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
  627. package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
  628. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +20 -14
  629. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +230 -197
  630. package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
  631. package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
  632. package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
  633. package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
  634. package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
  635. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
  636. package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
  637. package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
  638. package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
  639. package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
  640. package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
  641. package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
  642. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +1 -30
  643. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
  644. package/nitrogen/generated/ios/swift/NotificationButton.swift +4 -4
  645. package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
  646. package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
  647. package/nitrogen/generated/ios/swift/Options.swift +10 -4
  648. package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
  649. package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
  650. package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
  651. package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
  652. package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
  653. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
  654. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
  655. package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
  656. package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
  657. package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
  658. package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
  659. package/nitrogen/generated/ios/swift/Track.swift +74 -8
  660. package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
  661. package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
  662. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
  663. package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
  664. package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
  665. package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
  666. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +1 -12
  667. package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
  668. package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
  669. package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
  670. package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
  671. package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
  672. package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
  673. package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
  674. package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
  675. package/nitrogen/generated/shared/c++/{HeartRating.hpp → Gate.hpp} +21 -16
  676. package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateDecision.hpp} +23 -17
  677. package/nitrogen/generated/shared/c++/{PercentageRating.hpp → GateEvent.hpp} +18 -17
  678. package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
  679. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +21 -18
  680. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +44 -35
  681. package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
  682. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
  683. package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
  684. package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
  685. package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
  686. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
  687. package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
  688. package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
  689. package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
  690. package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
  691. package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
  692. package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
  693. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +1 -12
  694. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
  695. package/nitrogen/generated/shared/c++/NotificationButton.hpp +8 -8
  696. package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
  697. package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
  698. package/nitrogen/generated/shared/c++/Options.hpp +8 -8
  699. package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
  700. package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
  701. package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
  702. package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
  703. package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
  704. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
  705. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
  706. package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
  707. package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
  708. package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
  709. package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
  710. package/nitrogen/generated/shared/c++/Track.hpp +24 -5
  711. package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
  712. package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
  713. package/package.json +16 -16
  714. package/src/codegen-no-config-variant.test.ts +70 -0
  715. package/src/features/browser-config.test.ts +110 -0
  716. package/src/features/browser-config.ts +293 -0
  717. package/src/features/browser.test.ts +19 -0
  718. package/src/features/browser.ts +58 -201
  719. package/src/features/carConnection.ts +36 -0
  720. package/src/features/errors.ts +68 -2
  721. package/src/features/gate.test.ts +72 -0
  722. package/src/features/gate.ts +155 -0
  723. package/src/features/index.ts +2 -0
  724. package/src/features/metadata.ts +6 -12
  725. package/src/features/nowPlaying.ts +36 -0
  726. package/src/features/output.ts +35 -22
  727. package/src/features/playback/controls.ts +8 -0
  728. package/src/features/playback/index.ts +1 -0
  729. package/src/features/playback/progress.ts +31 -12
  730. package/src/features/playback/trackPlaybackTime.test.ts +66 -0
  731. package/src/features/playback/trackPlaybackTime.ts +106 -0
  732. package/src/features/playback/volume.ts +1 -1
  733. package/src/features/player/options.test.ts +38 -0
  734. package/src/features/player/options.ts +140 -75
  735. package/src/features/player/setup.test.ts +117 -0
  736. package/src/features/player/setup.ts +390 -175
  737. package/src/features/queue/queue.test.ts +36 -0
  738. package/src/features/queue/queue.ts +15 -4
  739. package/src/features/remoteControls.ts +0 -23
  740. package/src/features/sleepTimer.ts +23 -4
  741. package/src/native.ts +5 -0
  742. package/src/native.web.ts +4 -1
  743. package/src/specs/audio-browser.nitro.ts +131 -41
  744. package/src/types/browser-native.ts +19 -6
  745. package/src/types/browser-nodes.ts +160 -13
  746. package/src/types/browser.ts +377 -89
  747. package/src/utils/LazyNativeEmitter.ts +0 -2
  748. package/src/utils/NativeUpdatedValue.ts +0 -2
  749. package/src/utils/artwork.ts +30 -0
  750. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
  751. package/src/web/NativeAudioBrowser.ts +368 -157
  752. package/src/web/SimpleRouter.test.ts +9 -9
  753. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
  754. package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
  755. package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
  756. package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
  757. package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
  758. package/src/web/TrackPlayer/Player.load.test.ts +86 -0
  759. package/src/web/TrackPlayer/Player.ts +113 -39
  760. package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
  761. package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
  762. package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
  763. package/src/web/TrackPlayer/QueueManager.ts +302 -0
  764. package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
  765. package/src/web/TrackPlayer/RepeatMode.ts +1 -1
  766. package/src/web/TrackPlayer/SleepTimer.ts +38 -3
  767. package/src/web/TrackPlayer/VolumeFader.ts +74 -0
  768. package/src/web/TrackPlayer/index.ts +2 -1
  769. package/src/web/browser/BrowserManager.ts +231 -62
  770. package/src/web/browser/NavigationErrorManager.ts +3 -1
  771. package/src/web/browser/SearchManager.ts +11 -15
  772. package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
  773. package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
  774. package/src/web/browser/assertBrowsePageShape.ts +27 -0
  775. package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
  776. package/src/web/browser/classifyTrackNavigation.ts +35 -0
  777. package/src/web/browser/parseSearchResponse.test.ts +25 -0
  778. package/src/web/browser/parseSearchResponse.ts +15 -0
  779. package/src/web/http/HttpClient.ts +5 -35
  780. package/src/web/http/RequestConfigBuilder.test.ts +59 -0
  781. package/src/web/http/RequestConfigBuilder.ts +120 -63
  782. package/src/web/playbackErrorKind.test.ts +55 -0
  783. package/src/web/playbackErrorKind.ts +56 -0
  784. package/src/web/player/MediaSessionManager.test.ts +220 -0
  785. package/src/web/player/MediaSessionManager.ts +151 -0
  786. package/src/web/player/NowPlayingManager.test.ts +46 -0
  787. package/src/web/player/NowPlayingManager.ts +6 -5
  788. package/src/web/player/OptionsManager.ts +12 -18
  789. package/src/web/player/RemoteCommandController.test.ts +171 -0
  790. package/src/web/player/RemoteCommandController.ts +166 -0
  791. package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
  792. package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
  793. package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
  794. package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
  795. package/ios/CarPlay/Track+CarPlay.swift +0 -63
  796. package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
  797. package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
  798. package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
  799. package/lib/commonjs/features/rating.js +0 -2
  800. package/lib/commonjs/features/rating.js.map +0 -1
  801. package/lib/commonjs/types/player.js +0 -2
  802. package/lib/commonjs/types/player.js.map +0 -1
  803. package/lib/commonjs/web/SimpleRouter.test.js +0 -174
  804. package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
  805. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
  806. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  807. package/lib/module/features/rating.js +0 -2
  808. package/lib/module/features/rating.js.map +0 -1
  809. package/lib/module/types/player.js +0 -2
  810. package/lib/module/types/player.js.map +0 -1
  811. package/lib/module/web/SimpleRouter.test.js +0 -174
  812. package/lib/module/web/SimpleRouter.test.js.map +0 -1
  813. package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
  814. package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
  815. package/lib/typescript/src/features/rating.d.ts +0 -14
  816. package/lib/typescript/src/features/rating.d.ts.map +0 -1
  817. package/lib/typescript/src/native.web.d.ts +0 -3
  818. package/lib/typescript/src/native.web.d.ts.map +0 -1
  819. package/lib/typescript/src/types/player.d.ts +0 -64
  820. package/lib/typescript/src/types/player.d.ts.map +0 -1
  821. package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
  822. package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
  823. package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
  824. package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
  825. package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
  826. package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
  827. package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
  828. package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
  829. package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
  830. package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
  831. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
  832. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
  833. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
  834. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
  835. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
  836. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
  837. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
  838. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
  839. package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
  840. package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
  841. package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
  842. package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
  843. package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
  844. package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
  845. package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
  846. package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
  847. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
  848. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
  849. package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
  850. package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
  851. package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
  852. package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
  853. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
  854. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
  855. package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
  856. package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
  857. package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
  858. package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
  859. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
  860. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
  861. package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
  862. package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
  863. package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
  864. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
  865. package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
  866. package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
  867. package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
  868. package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
  869. package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
  870. package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
  871. package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
  872. package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
  873. package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
  874. package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
  875. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
  876. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
  877. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +0 -50
  878. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
  879. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
  880. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
  881. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
  882. package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
  883. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
  884. package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
  885. package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
  886. package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
  887. package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
  888. package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
  889. package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
  890. package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
  891. package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
  892. package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
  893. package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
  894. package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
  895. package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
  896. package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
  897. package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
  898. package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
  899. package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
  900. package/src/features/rating.ts +0 -17
  901. package/src/types/player.ts +0 -69
  902. package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
@@ -1,5 +1,6 @@
1
- import Testing
2
1
  @testable import AudioBrowserTestable
2
+ import Foundation
3
+ import Testing
3
4
 
4
5
  /// Helper to build a coordinator with mocks for testing.
5
6
  @MainActor
@@ -7,7 +8,7 @@ private func makeCoordinator() -> (
7
8
  coordinator: PlaybackCoordinator,
8
9
  effectHandler: MockPlaybackEffectHandler,
9
10
  callbacks: MockPlaybackCoordinatorCallbacks,
10
- sleepTimer: MockSleepTimerHandling
11
+ sleepTimer: MockSleepTimerHandling,
11
12
  ) {
12
13
  let retryHandler = MockRetryHandling()
13
14
  let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
@@ -26,7 +27,7 @@ private func loadTrack(
26
27
  _ coordinator: PlaybackCoordinator,
27
28
  id: String = "t1",
28
29
  src: String? = "https://example.com/audio.mp3",
29
- title: String = "Test Track"
30
+ title: String = "Test Track",
30
31
  ) {
31
32
  let track = Track(id: id, src: src, title: title)
32
33
  coordinator.setQueue([track])
@@ -41,7 +42,7 @@ struct TransitionTests {
41
42
  let (c, eh, _, _) = makeCoordinator()
42
43
  loadTrack(c)
43
44
  c.playWhenReady = true
44
- eh.startPlaybackCallCount = 0 // reset from playWhenReady setter
45
+ eh.startPlaybackCallCount = 0 // reset from playWhenReady setter
45
46
 
46
47
  c.transition(.bufferingSufficient)
47
48
 
@@ -106,19 +107,119 @@ struct TransitionTests {
106
107
  #expect(cb.playbackChanges.count == 1)
107
108
  #expect(cb.errorEvents.count == 1)
108
109
  }
110
+ }
111
+
112
+ // MARK: - Retrying Errors
113
+
114
+ /// Advisory errors from the retry loop: attached to the current non-terminal
115
+ /// state via `playerDidChangePlayback`, never a state transition and never a
116
+ /// `playerDidError`.
117
+ /// Builds a coordinator whose retry handler will accept the error and
118
+ /// pretend the (instant) retry succeeded, so only the advisory path runs.
119
+ /// The effect handler is returned solely to keep the weak reference alive.
120
+ @MainActor
121
+ private func makeRetrying() -> (
122
+ PlaybackCoordinator, MockRetryHandling, MockPlaybackCoordinatorCallbacks,
123
+ MockPlaybackEffectHandler
124
+ ) {
125
+ let retryHandler = MockRetryHandling()
126
+ retryHandler.isRetryableResult = true
127
+ retryHandler.attemptRetryResult = true
128
+ let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
129
+ let coordinator = PlaybackCoordinator(
130
+ errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
131
+ )
132
+ let effectHandler = MockPlaybackEffectHandler()
133
+ coordinator.effectHandler = effectHandler
134
+ let callbacks = MockPlaybackCoordinatorCallbacks()
135
+ coordinator.callbacks = callbacks
136
+ return (coordinator, retryHandler, callbacks, effectHandler)
137
+ }
109
138
 
139
+ @Suite("PlaybackCoordinator - retrying errors")
140
+ struct RetryingErrorTests {
110
141
  @Test @MainActor
111
- func activeStates_updateNowPlayingValues() {
112
- let (c, eh, _, _) = makeCoordinator()
113
- eh.hasLoadedAsset = true
114
- eh.duration = 120
115
- eh.currentTime = 30
142
+ func retryingError_attachesToCurrentState() async {
143
+ let (c, _, cb, _) = makeRetrying()
144
+ c.transition(.trackLoading)
145
+ cb.playbackChanges.removeAll()
146
+
147
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
116
148
 
149
+ #expect(c.state == .loading)
150
+ #expect(c.playbackError == .hostUnreachable)
151
+ #expect(cb.playbackChanges.count == 1)
152
+ #expect(cb.playbackChanges.last?.state == .loading)
153
+ #expect(cb.playbackChanges.last?.error?.retrying == true)
154
+ // Advisory, not terminal: the error event is reserved for .error.
155
+ #expect(cb.errorEvents.isEmpty)
156
+ await c.errorHandler.pendingRetryTask?.value
157
+ }
158
+
159
+ /// Every failed attempt re-reports through `handleError`; identical repeats
160
+ /// must not re-emit (they'd re-render now-playing every backoff tick).
161
+ @Test @MainActor
162
+ func repeatedIdenticalFailure_emitsOnce() async {
163
+ let (c, _, cb, _) = makeRetrying()
117
164
  c.transition(.trackLoading)
118
- #expect(eh.updateNowPlayingValuesCalls.count == 1)
165
+ cb.playbackChanges.removeAll()
119
166
 
120
- c.transition(.bufferingSufficient)
121
- #expect(eh.updateNowPlayingValuesCalls.count == 2)
167
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
168
+ await c.errorHandler.pendingRetryTask?.value
169
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
170
+ await c.errorHandler.pendingRetryTask?.value
171
+
172
+ #expect(cb.playbackChanges.count == 1)
173
+ }
174
+
175
+ /// While .playing, buffered audio is still audibly fine — the advisory must
176
+ /// not replace the song line over working sound (matches Android's guard).
177
+ @Test @MainActor
178
+ func retryingErrorWhilePlaying_isSuppressed() async {
179
+ let (c, _, cb, _) = makeRetrying()
180
+ c.transition(.avPlayerPlaying)
181
+ cb.playbackChanges.removeAll()
182
+
183
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .playback)
184
+
185
+ #expect(c.playbackError == nil)
186
+ #expect(cb.playbackChanges.isEmpty)
187
+ await c.errorHandler.pendingRetryTask?.value
188
+ }
189
+
190
+ /// Reaching .ready proves the retry recovered: the advisory error must not
191
+ /// survive into the clean state's emission.
192
+ @Test @MainActor
193
+ func recovery_clearsTheRetryingError() async {
194
+ let (c, _, cb, _) = makeRetrying()
195
+ c.transition(.trackLoading)
196
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
197
+ await c.errorHandler.pendingRetryTask?.value
198
+
199
+ c.transition(.loadSeekCompleted)
200
+
201
+ #expect(c.state == .ready)
202
+ #expect(c.playbackError == nil)
203
+ #expect(cb.playbackChanges.last?.error == nil)
204
+ }
205
+
206
+ /// Giving up hardens the advisory into a terminal error: same kind, state
207
+ /// .error, `retrying` gone.
208
+ @Test @MainActor
209
+ func exhaustion_hardensIntoTerminalError() async {
210
+ let (c, retryHandler, cb, _) = makeRetrying()
211
+ c.transition(.trackLoading)
212
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
213
+ await c.errorHandler.pendingRetryTask?.value
214
+
215
+ retryHandler.attemptRetryResult = false
216
+ c.errorHandler.handleError(URLError(.cannotConnectToHost), context: .mediaLoad)
217
+ await c.errorHandler.pendingRetryTask?.value
218
+
219
+ #expect(c.state == .error)
220
+ #expect(cb.playbackChanges.last?.state == .error)
221
+ #expect(cb.playbackChanges.last?.error?.retrying == nil)
222
+ #expect(cb.errorEvents.count == 1)
122
223
  }
123
224
  }
124
225
 
@@ -253,7 +354,7 @@ struct ObserverGuardTests {
253
354
 
254
355
  c.avPlayerDidChangeTimeControlStatus(.waitingToPlayAtSpecifiedRate)
255
356
 
256
- #expect(c.state == .playing) // unchanged
357
+ #expect(c.state == .playing) // unchanged
257
358
  }
258
359
 
259
360
  @Test @MainActor
@@ -384,6 +485,60 @@ struct PlayWhenReadyTests {
384
485
  }
385
486
  }
386
487
 
488
+ // MARK: - Skip availability (next/previous greying)
489
+
490
+ @Suite("PlaybackCoordinator - skip availability")
491
+ struct SkipAvailabilityCoordinatorTests {
492
+ private static func multiTrack() -> [Track] {
493
+ [
494
+ Track(id: "t1", src: "https://example.com/1.mp3", title: "Track 1"),
495
+ Track(id: "t2", src: "https://example.com/2.mp3", title: "Track 2"),
496
+ ]
497
+ }
498
+
499
+ @Test @MainActor
500
+ func singleTrack_disablesBoth() {
501
+ let (c, eh, _, _) = makeCoordinator()
502
+ c.setQueue([Track(id: "t1", src: "https://example.com/1.mp3", title: "Track 1")])
503
+
504
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
505
+ #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == false)
506
+ }
507
+
508
+ @Test @MainActor
509
+ func firstOfMany_nextOnly() {
510
+ let (c, eh, _, _) = makeCoordinator()
511
+ c.setQueue(Self.multiTrack())
512
+
513
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == true)
514
+ #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == false)
515
+ }
516
+
517
+ @Test @MainActor
518
+ func lastOfMany_previousOnly() {
519
+ let (c, eh, _, _) = makeCoordinator()
520
+ c.setQueue(Self.multiTrack())
521
+ try? c.skipTo(1)
522
+
523
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
524
+ #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == true)
525
+ }
526
+
527
+ @Test @MainActor
528
+ func repeatAll_repushesAvailability_atEnd() {
529
+ let (c, eh, _, _) = makeCoordinator()
530
+ c.setQueue(Self.multiTrack())
531
+ try? c.skipTo(1) // last track
532
+ eh.updateSkipAvailabilityCalls.removeAll()
533
+
534
+ c.repeatMode = .queue
535
+
536
+ // repeat-all wraps, so Next becomes available again at the last track.
537
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == true)
538
+ #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == true)
539
+ }
540
+ }
541
+
387
542
  // MARK: - handleCurrentTrackChanged
388
543
 
389
544
  @Suite("PlaybackCoordinator - handleCurrentTrackChanged")
@@ -447,6 +602,7 @@ struct HandleTrackDidPlayToEndTimeTests {
447
602
  let (c, eh, _, _) = makeCoordinator()
448
603
  loadTrack(c)
449
604
  c.repeatMode = .track
605
+ c.playWhenReady = true
450
606
 
451
607
  c.handleTrackDidPlayToEndTime()
452
608
 
@@ -454,6 +610,25 @@ struct HandleTrackDidPlayToEndTimeTests {
454
610
  #expect(eh.replayCurrentTrackCallCount == 1)
455
611
  }
456
612
 
613
+ /// The end-of-track sleep timer pauses (clears intent) just before this
614
+ /// handler runs — an unguarded replay would resume seconds after the sleep
615
+ /// timer paused. Without intent the track settles in .ended, where play()
616
+ /// reloads (a bare .paused would park at the end, where play() no-ops).
617
+ /// A repeating queue never conceptually ends, so no queue-ended reaches JS.
618
+ @Test @MainActor
619
+ func repeatTrack_withoutIntent_settlesEnded() {
620
+ let (c, eh, cb, _) = makeCoordinator()
621
+ loadTrack(c)
622
+ c.repeatMode = .track
623
+ c.playWhenReady = false
624
+
625
+ c.handleTrackDidPlayToEndTime()
626
+
627
+ #expect(cb.queueEndedEvents.isEmpty)
628
+ #expect(eh.replayCurrentTrackCallCount == 0)
629
+ #expect(c.state == .ended)
630
+ }
631
+
457
632
  @Test @MainActor
458
633
  func lastTrack_noRepeat_transitionsToEnded() {
459
634
  let (c, eh, cb, _) = makeCoordinator()
@@ -484,3 +659,599 @@ struct HandleTrackDidPlayToEndTimeTests {
484
659
  #expect(cb.activeTrackChanges.first?.track?.id == "t2")
485
660
  }
486
661
  }
662
+
663
+ // MARK: - Natural end drops play intent
664
+
665
+ /// A natural queue end exhausts the play intent: nothing is left to play, so
666
+ /// `playWhenReady` must not stay true. Keeping it inverted the play/pause
667
+ /// toggle, held the audio session forever, armed interruption auto-resume into
668
+ /// silence, and made play-from-ended a silent no-op.
669
+ @Suite("PlaybackCoordinator - natural end drops play intent")
670
+ struct NaturalEndPlayIntentTests {
671
+ @Test @MainActor
672
+ func naturalEnd_clearsPlayWhenReady() {
673
+ let (c, eh, _, _) = makeCoordinator()
674
+ startPlaying(c, eh)
675
+
676
+ c.handleTrackDidPlayToEndTime()
677
+
678
+ #expect(c.state == .ended)
679
+ #expect(c.playWhenReady == false)
680
+ }
681
+
682
+ @Test @MainActor
683
+ func naturalEnd_requestsSessionRelease() {
684
+ let (c, eh, callbacks, _) = makeCoordinator()
685
+ startPlaying(c, eh)
686
+ callbacks.releaseSessionCount = 0
687
+
688
+ c.handleTrackDidPlayToEndTime()
689
+
690
+ #expect(callbacks.releaseSessionCount == 1)
691
+ }
692
+
693
+ /// play() from .ended must reload from the start — startPlayback() alone is a
694
+ /// silent no-op on a player parked at the end of its item.
695
+ @Test @MainActor
696
+ func playAfterNaturalEnd_reloadsFromStart() {
697
+ let (c, eh, _, _) = makeCoordinator()
698
+ startPlaying(c, eh)
699
+ c.handleTrackDidPlayToEndTime()
700
+ eh.reloadTrackCalls.removeAll()
701
+
702
+ c.play()
703
+
704
+ #expect(eh.reloadTrackCalls == [false])
705
+ }
706
+
707
+ /// The cleared intent opens the call-site `!playWhenReady` gate, so a stray
708
+ /// timeControlStatus pause after the end reaches the state machine — the
709
+ /// table's .ended guard must hold the state.
710
+ @Test @MainActor
711
+ func strayPauseAfterNaturalEnd_staysEnded() {
712
+ let (c, eh, _, _) = makeCoordinator()
713
+ startPlaying(c, eh)
714
+ c.handleTrackDidPlayToEndTime()
715
+ #expect(c.state == .ended)
716
+
717
+ c.avPlayerDidChangeTimeControlStatus(.paused)
718
+
719
+ #expect(c.state == .ended)
720
+ }
721
+
722
+ @Test @MainActor
723
+ func interruptionAfterNaturalEnd_doesNotArmResume() {
724
+ let (c, eh, _, _) = makeCoordinator()
725
+ startPlaying(c, eh)
726
+ c.handleTrackDidPlayToEndTime()
727
+ eh.reloadTrackCalls.removeAll()
728
+
729
+ c.handleInterruptionBegan()
730
+ c.handleInterruptionEnded(shouldResume: true)
731
+
732
+ #expect(c.playWhenReady == false) // nothing was playing — never resume
733
+ #expect(eh.reloadTrackCalls.isEmpty)
734
+ }
735
+ }
736
+
737
+ // MARK: - Healthy playback refills the retry budget
738
+
739
+ /// Sustained audible playback proves the stream recovered, so the retry
740
+ /// window/attempt counters must refill (mirrors Android's healthy-playback
741
+ /// refill) — otherwise a long-lived stream permanently loses retry after its
742
+ /// first recovered blip.
743
+ @Suite("PlaybackCoordinator - healthy playback resets retry budget")
744
+ struct HealthyPlaybackRetryResetTests {
745
+ @Test @MainActor
746
+ func sustainedPlayback_resetsRetryBudget() async throws {
747
+ let retryHandler = MockRetryHandling()
748
+ let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
749
+ let coordinator = PlaybackCoordinator(
750
+ errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
751
+ )
752
+ let effectHandler = MockPlaybackEffectHandler()
753
+ coordinator.effectHandler = effectHandler
754
+ coordinator.healthyPlaybackDuration = 0.01
755
+
756
+ coordinator.transition(.avPlayerPlaying)
757
+ try await Task.sleep(nanoseconds: 100_000_000)
758
+
759
+ #expect(retryHandler.resetCallCount == 1)
760
+ }
761
+
762
+ /// resetRetry() cancels a pending retry — firing the refill mid-episode
763
+ /// would kill an in-flight reconnect silently (stuck .playing forever).
764
+ @Test @MainActor
765
+ func refill_skipsWhileRetryIsPending() async throws {
766
+ let retryHandler = MockRetryHandling()
767
+ retryHandler.isRetryableResult = true
768
+ retryHandler.attemptRetryDelayNs = 1_000_000_000
769
+ let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
770
+ let coordinator = PlaybackCoordinator(
771
+ errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
772
+ )
773
+ let effectHandler = MockPlaybackEffectHandler()
774
+ coordinator.effectHandler = effectHandler
775
+ coordinator.healthyPlaybackDuration = 0.01
776
+
777
+ coordinator.transition(.avPlayerPlaying)
778
+ errorHandler.handleError(URLError(.timedOut), context: .playback)
779
+ try await Task.sleep(nanoseconds: 100_000_000)
780
+
781
+ #expect(retryHandler.resetCallCount == 0)
782
+ #expect(errorHandler.pendingRetryTask != nil)
783
+ }
784
+
785
+ @Test @MainActor
786
+ func playbackInterruptedBeforeThreshold_doesNotReset() async throws {
787
+ let retryHandler = MockRetryHandling()
788
+ let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
789
+ let coordinator = PlaybackCoordinator(
790
+ errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
791
+ )
792
+ let effectHandler = MockPlaybackEffectHandler()
793
+ coordinator.effectHandler = effectHandler
794
+ effectHandler.hasLoadedAsset = true
795
+ coordinator.healthyPlaybackDuration = 0.5
796
+
797
+ coordinator.transition(.avPlayerPlaying)
798
+ coordinator.transition(.avPlayerWaiting) // stalls before the threshold
799
+
800
+ try await Task.sleep(nanoseconds: 100_000_000)
801
+
802
+ #expect(retryHandler.resetCallCount == 0)
803
+ }
804
+ }
805
+
806
+ @Suite("PlaybackCoordinator - now playing state")
807
+ struct PlaybackCoordinatorNowPlayingStateTests {
808
+ @Test @MainActor
809
+ func change_updatesNowPlayingState_evenDuringLoading() {
810
+ let (c, eh, _, _) = makeCoordinator()
811
+ c.transition(.trackLoading) // mid track-load; start/pause is deferred
812
+ eh.updateNowPlayingStateCalls.removeAll()
813
+
814
+ c.playWhenReady = true
815
+
816
+ // The lock-screen / CarPlay play-pause button reflects the user's play
817
+ // *intent* immediately, even while the new item is still loading — otherwise
818
+ // it stays stuck showing "paused" until a later state transition repairs it.
819
+ #expect(eh.updateNowPlayingStateCalls.last == true)
820
+ }
821
+
822
+ @Test @MainActor
823
+ func staysPlaying_whenBufferRunsDry() {
824
+ let (c, eh, _, _) = makeCoordinator()
825
+ c.playWhenReady = true
826
+ c.transition(.avPlayerPlaying)
827
+ eh.updateNowPlayingStateCalls.removeAll()
828
+
829
+ c.transition(.avPlayerWaiting) // buffer ran dry → .buffering
830
+
831
+ // A buffer underrun doesn't change play intent, so the button stays "playing"
832
+ // (the system shows buffering) — it must NOT flip to paused like a user pause.
833
+ #expect(eh.updateNowPlayingStateCalls.last == true)
834
+ }
835
+
836
+ @Test @MainActor
837
+ func fallsBackToPaused_onError() {
838
+ let (c, eh, _, _) = makeCoordinator()
839
+ c.playWhenReady = true
840
+ c.transition(.trackLoading) // active + intent → playing
841
+ eh.updateNowPlayingStateCalls.removeAll()
842
+
843
+ c.transition(.errorOccurred(.playbackFailed))
844
+
845
+ // On a terminal error the button must fall back to paused — never stick on
846
+ // "playing" — even though playWhenReady is still true.
847
+ #expect(eh.updateNowPlayingStateCalls.last == false)
848
+ }
849
+
850
+ @Test @MainActor
851
+ func change_doesNotStampNowPlayingState_fromTerminalState() {
852
+ let (c, eh, _, _) = makeCoordinator()
853
+ c.transition(.errorOccurred(.playbackFailed)) // terminal, not playbackActive
854
+ eh.updateNowPlayingStateCalls.removeAll()
855
+
856
+ c.playWhenReady = true
857
+
858
+ // Must NOT stamp .playing from a terminal state: the reload may fail and no
859
+ // active transition would repair a premature "playing", leaving a phantom
860
+ // playing button. The reload's own .loading/.ready transition stamps it.
861
+ #expect(eh.updateNowPlayingStateCalls.isEmpty)
862
+ }
863
+
864
+ @Test @MainActor
865
+ func selectingNewTrack_whileStopped_showsPlayingInNowPlaying() {
866
+ let (c, eh, _, _) = makeCoordinator()
867
+ let tracks = [
868
+ Track(id: "t1", src: "https://example.com/1.mp3", title: "Track 1"),
869
+ Track(id: "t2", src: "https://example.com/2.mp3", title: "Track 2"),
870
+ ]
871
+ c.setQueue(tracks) // loads t1 (playWhenReady defaults to false)
872
+ c.transition(.stopped) // user had paused/stopped
873
+ eh.updateNowPlayingStateCalls.removeAll()
874
+
875
+ // User selects another track, intending to play it.
876
+ try? c.skipTo(1, playWhenReady: true)
877
+
878
+ // Audio will play, so the now-playing button must end up "playing" — not the
879
+ // stale "paused" captured before playWhenReady flipped true during loading.
880
+ #expect(eh.updateNowPlayingStateCalls.last == true)
881
+ }
882
+ }
883
+
884
+ // MARK: - Sleep Timer Fade
885
+
886
+ @Suite("PlaybackCoordinator - sleep timer fade")
887
+ struct SleepTimerFadeTests {
888
+ @Test @MainActor
889
+ func fadeStart_capturesPreFadeVolume() {
890
+ let (c, eh, _, st) = makeCoordinator()
891
+ eh.volume = 0.8
892
+
893
+ st.onFadeStart?(10)
894
+
895
+ #expect(c.volumeFader.isActive)
896
+ #expect(c.volumeFader.originalVolume == 0.8)
897
+ }
898
+
899
+ @Test @MainActor
900
+ func completion_pausesThenRestoresPreFadeVolume() {
901
+ let (c, eh, _, st) = makeCoordinator()
902
+ loadTrack(c)
903
+ c.playWhenReady = true
904
+ c.transition(.bufferingSufficient) // leave .loading so pause reaches the player
905
+ eh.pausePlaybackCallCount = 0
906
+
907
+ st.onFadeStart?(10)
908
+ eh.volume = 0.2 // mid-fade
909
+
910
+ st.onComplete?()
911
+
912
+ #expect(c.playWhenReady == false)
913
+ #expect(eh.pausePlaybackCallCount == 1)
914
+ #expect(eh.volume == 1.0)
915
+ #expect(!c.volumeFader.isActive)
916
+ }
917
+
918
+ @Test @MainActor
919
+ func completion_withoutFade_justPauses() {
920
+ let (c, eh, _, st) = makeCoordinator()
921
+ loadTrack(c)
922
+ c.playWhenReady = true
923
+ eh.volume = 0.8
924
+
925
+ st.onComplete?()
926
+
927
+ #expect(c.playWhenReady == false)
928
+ #expect(eh.volume == 0.8) // untouched — no fade ran
929
+ }
930
+
931
+ @Test @MainActor
932
+ func fadeCancel_restoresPreFadeVolume() {
933
+ let (c, eh, _, st) = makeCoordinator()
934
+ eh.volume = 0.8
935
+
936
+ st.onFadeStart?(10)
937
+ eh.volume = 0.1 // mid-fade
938
+
939
+ st.onFadeCancel?()
940
+
941
+ #expect(eh.volume == 0.8)
942
+ #expect(!c.volumeFader.isActive)
943
+ }
944
+
945
+ @Test @MainActor
946
+ func pauseDuringFade_clearsSleepTimer() {
947
+ let (c, _, _, st) = makeCoordinator()
948
+ loadTrack(c)
949
+ c.playWhenReady = true
950
+
951
+ st.onFadeStart?(10)
952
+ c.pause()
953
+
954
+ #expect(st.clearCallCount == 1)
955
+ }
956
+
957
+ @Test @MainActor
958
+ func pauseWithoutFade_leavesSleepTimerAlone() {
959
+ let (c, _, _, st) = makeCoordinator()
960
+ loadTrack(c)
961
+ c.playWhenReady = true
962
+
963
+ c.pause()
964
+
965
+ #expect(st.clearCallCount == 0)
966
+ }
967
+ }
968
+
969
+ // MARK: - Audio Session Interruptions
970
+
971
+ /// Helper to drive the coordinator into an actively-playing state.
972
+ @MainActor
973
+ private func startPlaying(_ c: PlaybackCoordinator, _ eh: MockPlaybackEffectHandler) {
974
+ loadTrack(c)
975
+ eh.hasLoadedAsset = true
976
+ c.playWhenReady = true
977
+ c.transition(.avPlayerPlaying)
978
+ }
979
+
980
+ @Suite("PlaybackCoordinator - audio session interruptions")
981
+ struct InterruptionTests {
982
+ @Test @MainActor
983
+ func began_whilePlaying_pauses() {
984
+ let (c, eh, _, _) = makeCoordinator()
985
+ startPlaying(c, eh)
986
+ #expect(c.state == .playing)
987
+
988
+ c.handleInterruptionBegan()
989
+
990
+ #expect(c.state == .paused)
991
+ #expect(c.playWhenReady == false)
992
+ }
993
+
994
+ /// The bug: iOS pauses the AVPlayer first; that `timeControlStatus → paused`
995
+ /// is swallowed because `playWhenReady` is still true, so the state stays
996
+ /// `.playing`. The interruption handler must force the pause regardless.
997
+ @Test @MainActor
998
+ func began_afterSwallowedTimeControlPause_stillPauses() {
999
+ let (c, eh, _, _) = makeCoordinator()
1000
+ startPlaying(c, eh)
1001
+
1002
+ c.avPlayerDidChangeTimeControlStatus(.paused)
1003
+ #expect(c.state == .playing) // swallowed — the symptom this fix targets
1004
+
1005
+ c.handleInterruptionBegan()
1006
+
1007
+ #expect(c.state == .paused)
1008
+ }
1009
+
1010
+ @Test @MainActor
1011
+ func ended_shouldResume_resumesWhenWasPlaying() {
1012
+ let (c, eh, _, _) = makeCoordinator()
1013
+ startPlaying(c, eh)
1014
+ c.handleInterruptionBegan()
1015
+ #expect(c.playWhenReady == false)
1016
+ eh.startPlaybackCallCount = 0
1017
+
1018
+ c.handleInterruptionEnded(shouldResume: true)
1019
+
1020
+ #expect(c.playWhenReady == true)
1021
+ #expect(eh.startPlaybackCallCount == 1)
1022
+ }
1023
+
1024
+ @Test @MainActor
1025
+ func ended_withoutShouldResume_staysPaused() {
1026
+ let (c, eh, _, _) = makeCoordinator()
1027
+ startPlaying(c, eh)
1028
+ c.handleInterruptionBegan()
1029
+
1030
+ c.handleInterruptionEnded(shouldResume: false)
1031
+
1032
+ #expect(c.state == .paused)
1033
+ #expect(c.playWhenReady == false)
1034
+ }
1035
+
1036
+ /// The host gates audio-session reactivation at interruption-end on this:
1037
+ /// paused-when-interrupted must not grab a non-mixable session for a resume
1038
+ /// that never happens.
1039
+ @Test @MainActor
1040
+ func willResumeAfterInterruption_reflectsCapturedIntent() {
1041
+ let (c, eh, _, _) = makeCoordinator()
1042
+ startPlaying(c, eh)
1043
+
1044
+ c.handleInterruptionBegan()
1045
+ #expect(c.willResumeAfterInterruption == true)
1046
+
1047
+ c.handleInterruptionEnded(shouldResume: false)
1048
+ #expect(c.willResumeAfterInterruption == false)
1049
+
1050
+ // Paused before the interruption: no resume will happen.
1051
+ c.handleInterruptionBegan()
1052
+ #expect(c.willResumeAfterInterruption == false)
1053
+ }
1054
+
1055
+ /// iOS can deliver nested interruptions (Siri, then a phone call) — a second
1056
+ /// .began must not re-capture the now-false intent and wipe the resume flag.
1057
+ @Test @MainActor
1058
+ func doubleBegan_preservesResumeIntent() {
1059
+ let (c, eh, _, _) = makeCoordinator()
1060
+ startPlaying(c, eh)
1061
+
1062
+ c.handleInterruptionBegan()
1063
+ c.handleInterruptionBegan()
1064
+ c.handleInterruptionEnded(shouldResume: true)
1065
+
1066
+ #expect(c.playWhenReady == true)
1067
+ }
1068
+
1069
+ /// Unplugging headphones during a call is a deliberate output loss — the
1070
+ /// call ending must not blast playback from the built-in speaker.
1071
+ @Test @MainActor
1072
+ func routeDisconnectDuringInterruption_cancelsResume() {
1073
+ let (c, eh, _, _) = makeCoordinator()
1074
+ startPlaying(c, eh)
1075
+ c.handleInterruptionBegan()
1076
+
1077
+ c.handleRouteDisconnected()
1078
+ c.handleInterruptionEnded(shouldResume: true)
1079
+
1080
+ #expect(c.playWhenReady == false)
1081
+ }
1082
+
1083
+ /// An explicit stop during the interruption is a terminal command — the
1084
+ /// interruption-end must not restart playback over it.
1085
+ @Test @MainActor
1086
+ func stopDuringInterruption_isNotOverriddenByResume() {
1087
+ let (c, eh, _, _) = makeCoordinator()
1088
+ startPlaying(c, eh)
1089
+ c.handleInterruptionBegan()
1090
+
1091
+ c.stop()
1092
+ c.handleInterruptionEnded(shouldResume: true)
1093
+
1094
+ #expect(c.state == .stopped)
1095
+ #expect(c.playWhenReady == false)
1096
+ #expect(eh.reloadTrackCalls.isEmpty)
1097
+ }
1098
+
1099
+ /// A user who plays then pauses during the interruption has taken ownership
1100
+ /// of the intent — interruption-end must not override their pause.
1101
+ @Test @MainActor
1102
+ func userPauseDuringInterruption_overridesResume() {
1103
+ let (c, eh, _, _) = makeCoordinator()
1104
+ startPlaying(c, eh)
1105
+ c.handleInterruptionBegan()
1106
+
1107
+ c.play()
1108
+ c.pause()
1109
+ c.handleInterruptionEnded(shouldResume: true)
1110
+
1111
+ #expect(c.playWhenReady == false)
1112
+ }
1113
+
1114
+ @Test @MainActor
1115
+ func ended_doesNotResume_whenNotPlayingBeforeInterruption() {
1116
+ let (c, eh, _, _) = makeCoordinator()
1117
+ startPlaying(c, eh)
1118
+ // Pause before the interruption (e.g. user already paused).
1119
+ c.playWhenReady = false
1120
+ c.avPlayerDidChangeTimeControlStatus(.paused)
1121
+ #expect(c.state == .paused)
1122
+
1123
+ c.handleInterruptionBegan()
1124
+ c.handleInterruptionEnded(shouldResume: true)
1125
+
1126
+ #expect(c.playWhenReady == false) // never resumes — we weren't playing
1127
+ }
1128
+ }
1129
+
1130
+ // MARK: - Skip availability freshness
1131
+
1132
+ /// tracks.didSet pushes availability mid-mutation, while currentIndex and the
1133
+ /// shuffle order are still stale — the coordinator must re-push after the
1134
+ /// mutation settles or the lock-screen buttons keep the wrong state.
1135
+ @Suite("PlaybackCoordinator - skip availability after mutations")
1136
+ struct MutationSkipAvailabilityTests {
1137
+ @Test @MainActor
1138
+ func move_pushesPostMutationAvailability() throws {
1139
+ let (c, eh, _, _) = makeCoordinator()
1140
+ c.setQueue([
1141
+ Track(id: "a", src: "https://example.com/a.mp3", title: "A"),
1142
+ Track(id: "b", src: "https://example.com/b.mp3", title: "B"),
1143
+ ])
1144
+ eh.updateSkipAvailabilityCalls.removeAll()
1145
+
1146
+ try c.move(fromIndex: 0, toIndex: 1)
1147
+
1148
+ // Current track is now last: nothing to skip to.
1149
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
1150
+ }
1151
+
1152
+ @Test @MainActor
1153
+ func removeBeforeCurrent_pushesPostMutationAvailability() throws {
1154
+ let (c, eh, _, _) = makeCoordinator()
1155
+ c.setQueue(
1156
+ [
1157
+ Track(id: "a", src: "https://example.com/a.mp3", title: "A"),
1158
+ Track(id: "b", src: "https://example.com/b.mp3", title: "B"),
1159
+ Track(id: "c", src: "https://example.com/c.mp3", title: "C"),
1160
+ ], initialIndex: 2,
1161
+ )
1162
+ eh.updateSkipAvailabilityCalls.removeAll()
1163
+
1164
+ try c.remove(0)
1165
+
1166
+ // Current settled at index 1 of 2: previous available, next not.
1167
+ #expect(eh.updateSkipAvailabilityCalls.last?.canNext == false)
1168
+ #expect(eh.updateSkipAvailabilityCalls.last?.canPrevious == true)
1169
+ }
1170
+ }
1171
+
1172
+ // MARK: - Stop cancels pending retry
1173
+
1174
+ @Suite("PlaybackCoordinator - stop cancels pending retry")
1175
+ struct StopCancelsRetryTests {
1176
+ /// A retry surviving stop() gives up later (intent dropped) and surfaced
1177
+ /// .errorOccurred over the deliberate stop — the UI showed an error seconds
1178
+ /// after the user stopped.
1179
+ @Test @MainActor
1180
+ func stop_cancelsPendingRetry_andStaysSilent() async {
1181
+ let retryHandler = MockRetryHandling()
1182
+ retryHandler.isRetryableResult = true
1183
+ retryHandler.attemptRetryResult = false
1184
+ retryHandler.attemptRetryDelayNs = 50_000_000
1185
+ let errorHandler = PlaybackErrorHandler(retryHandler: retryHandler)
1186
+ let coordinator = PlaybackCoordinator(
1187
+ errorHandler: errorHandler, sleepTimerManager: MockSleepTimerHandling(),
1188
+ )
1189
+ let effectHandler = MockPlaybackEffectHandler()
1190
+ let callbacks = MockPlaybackCoordinatorCallbacks()
1191
+ coordinator.effectHandler = effectHandler
1192
+ coordinator.callbacks = callbacks
1193
+
1194
+ errorHandler.handleError(URLError(.timedOut), context: .playback)
1195
+ let task = errorHandler.pendingRetryTask
1196
+ #expect(task != nil)
1197
+
1198
+ coordinator.stop()
1199
+ #expect(errorHandler.pendingRetryTask == nil)
1200
+
1201
+ await task?.value
1202
+ #expect(coordinator.state == .stopped)
1203
+ #expect(callbacks.errorEvents.isEmpty)
1204
+ }
1205
+ }
1206
+
1207
+ // MARK: - Audio session release (#60)
1208
+
1209
+ @Suite("PlaybackCoordinator - audio session release")
1210
+ struct SessionReleaseTests {
1211
+ @Test @MainActor
1212
+ func deliberatePause_requestsRelease() {
1213
+ let (c, eh, callbacks, _) = makeCoordinator()
1214
+ startPlaying(c, eh)
1215
+ callbacks.releaseSessionCount = 0
1216
+ c.pause()
1217
+ #expect(callbacks.releaseSessionCount == 1)
1218
+ }
1219
+
1220
+ @Test @MainActor
1221
+ func play_doesNotRequestRelease() {
1222
+ let (c, eh, callbacks, _) = makeCoordinator()
1223
+ startPlaying(c, eh)
1224
+ #expect(callbacks.releaseSessionCount == 0)
1225
+ }
1226
+
1227
+ @Test @MainActor
1228
+ func interruptionBegan_doesNotRequestRelease() {
1229
+ // A phone call pauses (playWhenReady → false) but is meant to resume, so the session must be
1230
+ // held — the exact race the intent-gating design exists to avoid.
1231
+ let (c, eh, callbacks, _) = makeCoordinator()
1232
+ startPlaying(c, eh)
1233
+ callbacks.releaseSessionCount = 0
1234
+ c.handleInterruptionBegan()
1235
+ #expect(callbacks.releaseSessionCount == 0)
1236
+ }
1237
+
1238
+ @Test @MainActor
1239
+ func interruptionEnded_withResume_doesNotRequestRelease() {
1240
+ let (c, eh, callbacks, _) = makeCoordinator()
1241
+ startPlaying(c, eh)
1242
+ c.handleInterruptionBegan()
1243
+ callbacks.releaseSessionCount = 0
1244
+ c.handleInterruptionEnded(shouldResume: true)
1245
+ #expect(callbacks.releaseSessionCount == 0)
1246
+ }
1247
+
1248
+ @Test @MainActor
1249
+ func interruptionEnded_withoutResume_requestsRelease() {
1250
+ let (c, eh, callbacks, _) = makeCoordinator()
1251
+ startPlaying(c, eh)
1252
+ c.handleInterruptionBegan()
1253
+ callbacks.releaseSessionCount = 0
1254
+ c.handleInterruptionEnded(shouldResume: false)
1255
+ #expect(callbacks.releaseSessionCount == 1)
1256
+ }
1257
+ }