react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -21
- package/android/build.gradle +35 -12
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
- package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
- package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
- package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
- package/android/src/main/res/values/strings.xml +25 -5
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/model/IcyTitleTest.kt +49 -0
- package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
- package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +230 -205
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +264 -106
- package/ios/Browser/SearchDrillIn.swift +7 -7
- package/ios/Browser/SectionPresentation.swift +64 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +58 -0
- package/ios/Browser/TabBarEntries.swift +9 -3
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +227 -85
- package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
- package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
- package/ios/Extension/Section+Copying.swift +29 -0
- package/ios/Extension/String+IcyCharset.swift +21 -0
- package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
- package/ios/Extension/Track+Copying.swift +8 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +253 -100
- package/ios/Model/NitroTypeStubs.swift +125 -23
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/Option/SessionCategories.swift +3 -2
- package/ios/Player/MediaIntentCriteria.swift +17 -9
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackErrorHandler.swift +4 -4
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +46 -13
- package/ios/Player/RetryManager.swift +80 -18
- package/ios/Player/SeekCompletionHandler.swift +3 -1
- package/ios/Player/ShuffleOrder.swift +33 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +150 -1
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
- package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/QueueManagerTests.swift +122 -10
- package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
- package/ios/Tests/RetryManagerTests.swift +89 -0
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +90 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +127 -0
- package/ios/Tests/Support/TestTypes.swift +8 -1
- package/ios/Tests/TabBarEntriesTests.swift +19 -19
- package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +63 -35
- package/ios/TrackPlayerCallbacks.swift +5 -38
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/AudioBrowser.js +8 -8
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +12 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +11 -3
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +1 -1
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +3 -3
- package/lib/commonjs/features/favorites.js +14 -11
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/gate.js +16 -1
- package/lib/commonjs/features/gate.js.map +1 -1
- package/lib/commonjs/features/metadata.js +20 -3
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +1 -1
- package/lib/commonjs/features/output.js +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +3 -2
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +3 -4
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +48 -34
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +18 -9
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +23 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +15 -4
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +13 -6
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +13 -92
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +13 -1
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
- package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
- package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +200 -75
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +175 -100
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +18 -23
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
- package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +2 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +12 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +11 -3
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +1 -1
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +3 -3
- package/lib/module/features/favorites.js +14 -11
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/gate.js +16 -1
- package/lib/module/features/gate.js.map +1 -1
- package/lib/module/features/metadata.js +20 -3
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +1 -1
- package/lib/module/features/output.js +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +3 -2
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/features/playback/state.js +3 -4
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +0 -5
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +47 -32
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +17 -8
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +19 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +15 -4
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +13 -6
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -92
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +13 -1
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/test-utils/reactNativeStub.js +58 -0
- package/lib/module/test-utils/reactNativeStub.js.map +1 -0
- package/lib/module/utils/LazyNativeEmitter.js +28 -14
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +200 -75
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
- package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +254 -39
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +175 -100
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +18 -23
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/http/RequestConfigBuilder.js +110 -27
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/http/followMediaRedirect.js +215 -0
- package/lib/module/web/http/followMediaRedirect.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +10 -2
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +1 -1
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +3 -3
- package/lib/typescript/src/features/favorites.d.ts +14 -11
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/gate.d.ts +18 -2
- package/lib/typescript/src/features/gate.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +20 -3
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/output.d.ts +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +3 -4
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/volume.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts +108 -56
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +14 -4
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +7 -4
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -22
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
- package/lib/typescript/src/types/browser-native.d.ts +3 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +82 -57
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
- package/nitrogen/generated/android/c++/JSection.hpp +116 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
- package/nitrogen/generated/ios/swift/Section.swift +82 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +28 -63
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
- package/nitrogen/generated/shared/c++/Section.hpp +102 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +18 -30
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +24 -11
- package/src/AudioBrowser.ts +2 -1
- package/src/CLAUDE.md +52 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +12 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +11 -3
- package/src/features/carConnection.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +3 -3
- package/src/features/favorites.ts +14 -11
- package/src/features/gate.ts +16 -1
- package/src/features/hook-wiring.test.ts +302 -0
- package/src/features/metadata.ts +20 -3
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.test.ts +169 -0
- package/src/features/playback/progress.ts +3 -2
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +3 -4
- package/src/features/playback/trackPlaybackTime.test.ts +16 -6
- package/src/features/playback/trackPlaybackTime.ts +1 -6
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.ts +126 -76
- package/src/features/player/setup.test.ts +24 -7
- package/src/features/player/setup.ts +22 -10
- package/src/features/player/validateOptions.ts +21 -0
- package/src/features/queue/activeTrack.ts +15 -4
- package/src/features/queue/queue.ts +13 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -92
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +9 -1
- package/src/nitro-stub-drift.test.ts +277 -0
- package/src/public-surface.test.ts +147 -0
- package/src/specs/audio-browser.nitro.ts +5 -4
- package/src/style-resolution.test.ts +210 -0
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +7 -3
- package/src/types/browser-nodes.ts +396 -134
- package/src/types/browser.ts +85 -59
- package/src/utils/LazyNativeEmitter.ts +28 -15
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
- package/src/web/NativeAudioBrowser.ts +239 -82
- package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
- package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
- package/src/web/TrackPlayer/Player.load.test.ts +137 -1
- package/src/web/TrackPlayer/Player.ts +284 -37
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
- package/src/web/__tests__/lifecycle.test.ts +345 -0
- package/src/web/__tests__/lifecycleHarness.ts +351 -0
- package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
- package/src/web/__tests__/loadSupersession.test.ts +175 -0
- package/src/web/__tests__/retry.test.ts +169 -0
- package/src/web/browser/BrowserManager.ts +209 -111
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/SearchManager.test.ts +75 -0
- package/src/web/browser/SearchManager.ts +25 -28
- package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
- package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
- package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
- package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/http/RequestConfigBuilder.test.ts +241 -1
- package/src/web/http/RequestConfigBuilder.ts +140 -33
- package/src/web/http/followMediaRedirect.test.ts +289 -0
- package/src/web/http/followMediaRedirect.ts +253 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +247 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/ios/Model/MediaURL.swift +0 -67
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
|
@@ -12,15 +12,15 @@ private final class BrowserMock: TrackSelectionBrowser {
|
|
|
12
12
|
|
|
13
13
|
// Record calls for verification
|
|
14
14
|
var trackLoadEvents: [TrackLoadEvent] = []
|
|
15
|
-
var
|
|
15
|
+
var expandedPaths: [String] = []
|
|
16
16
|
|
|
17
17
|
func awaitTrackLoadHandler(event: TrackLoadEvent) async -> Bool {
|
|
18
18
|
trackLoadEvents.append(event)
|
|
19
19
|
return trackLoadHandlerResult
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
func
|
|
23
|
-
|
|
22
|
+
func expandQueueFromContextualPath(_ path: String) async throws -> (tracks: [Track], selectedIndex: Int)? {
|
|
23
|
+
expandedPaths.append(path)
|
|
24
24
|
if let error = expandQueueError {
|
|
25
25
|
throw error
|
|
26
26
|
}
|
|
@@ -53,23 +53,23 @@ private func makeSelector() -> (TrackSelector, BrowserMock, PlayerMock) {
|
|
|
53
53
|
@Suite("browsable track")
|
|
54
54
|
@MainActor
|
|
55
55
|
struct BrowsableTrackTests {
|
|
56
|
-
@Test func
|
|
56
|
+
@Test func pathOnly_returnsBrowse() async {
|
|
57
57
|
let (selector, _, player) = makeSelector()
|
|
58
|
-
let track = Track(id: "t1",
|
|
58
|
+
let track = Track(id: "t1", path: "/some/path")
|
|
59
59
|
let result = await selector.select(track: track, player: player)
|
|
60
|
-
guard case let .browse(
|
|
60
|
+
guard case let .browse(path) = result else {
|
|
61
61
|
Issue.record("expected .browse, got \(result)")
|
|
62
62
|
return
|
|
63
63
|
}
|
|
64
|
-
#expect(
|
|
64
|
+
#expect(path == "/some/path")
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// MARK: - No src or
|
|
68
|
+
// MARK: - No src or path
|
|
69
69
|
|
|
70
|
-
@Suite("no src or
|
|
70
|
+
@Suite("no src or path")
|
|
71
71
|
@MainActor
|
|
72
|
-
struct
|
|
72
|
+
struct NoSrcOrPathTests {
|
|
73
73
|
@Test func returnsNone() async {
|
|
74
74
|
let (selector, _, player) = makeSelector()
|
|
75
75
|
let track = Track(id: "t1")
|
|
@@ -115,25 +115,56 @@ struct PlayableTrackTests {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// MARK: - Contextual
|
|
118
|
+
// MARK: - Contextual path Queue Reuse
|
|
119
119
|
|
|
120
|
-
@Suite("contextual
|
|
120
|
+
@Suite("contextual path queue reuse")
|
|
121
121
|
@MainActor
|
|
122
|
-
struct
|
|
122
|
+
struct ContextualPathQueueReuseTests {
|
|
123
123
|
@Test func matchingQueueSourcePath_returnsSkipTo() async {
|
|
124
124
|
let (selector, browser, player) = makeSelector()
|
|
125
125
|
let parentPath = "/library/radio"
|
|
126
126
|
let trackSrc = "song.mp3"
|
|
127
|
-
let
|
|
127
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
128
|
+
|
|
129
|
+
// Id-less queue tracks: identity falls back to src.
|
|
130
|
+
player.queueSourcePath = parentPath
|
|
131
|
+
player.tracks = [
|
|
132
|
+
Track(src: "other.mp3"),
|
|
133
|
+
Track(src: "song.mp3"),
|
|
134
|
+
Track(src: "another.mp3"),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
let track = Track(path: contextualPath, src: trackSrc)
|
|
138
|
+
let result = await selector.select(track: track, player: player)
|
|
139
|
+
guard case let .play(intent) = result else {
|
|
140
|
+
Issue.record("expected .play, got \(result)")
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
guard case let .skipTo(index) = intent else {
|
|
144
|
+
Issue.record("expected .skipTo, got \(intent)")
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
#expect(index == 1)
|
|
148
|
+
#expect(browser.trackLoadEvents.count == 1)
|
|
149
|
+
#expect(browser.expandedPaths.isEmpty)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Skip-in-place matches by id when queue tracks carry ids: the contextual
|
|
153
|
+
// trackId is the row's identity (its id), and the queue row is found even
|
|
154
|
+
// though its src differs textually from the tapped track's.
|
|
155
|
+
@Test func matchingQueueSourcePath_skipsToTrackByIdWhenSrcDiffers() async {
|
|
156
|
+
let (selector, browser, player) = makeSelector()
|
|
157
|
+
let parentPath = "/library/radio"
|
|
158
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: "b")
|
|
128
159
|
|
|
129
160
|
player.queueSourcePath = parentPath
|
|
130
161
|
player.tracks = [
|
|
131
162
|
Track(id: "a", src: "other.mp3"),
|
|
132
|
-
Track(id: "b", src: "song.mp3"),
|
|
163
|
+
Track(id: "b", src: "https://cdn.example/song.mp3?token=queue"),
|
|
133
164
|
Track(id: "c", src: "another.mp3"),
|
|
134
165
|
]
|
|
135
166
|
|
|
136
|
-
let track = Track(id: "
|
|
167
|
+
let track = Track(id: "b", path: contextualPath, src: "song.mp3")
|
|
137
168
|
let result = await selector.select(track: track, player: player)
|
|
138
169
|
guard case let .play(intent) = result else {
|
|
139
170
|
Issue.record("expected .play, got \(result)")
|
|
@@ -144,8 +175,7 @@ struct ContextualUrlQueueReuseTests {
|
|
|
144
175
|
return
|
|
145
176
|
}
|
|
146
177
|
#expect(index == 1)
|
|
147
|
-
#expect(browser.
|
|
148
|
-
#expect(browser.expandedUrls.isEmpty)
|
|
178
|
+
#expect(browser.expandedPaths.isEmpty)
|
|
149
179
|
}
|
|
150
180
|
|
|
151
181
|
@Test func handlerIntercepts_returnsIntercepted() async {
|
|
@@ -153,32 +183,93 @@ struct ContextualUrlQueueReuseTests {
|
|
|
153
183
|
browser.trackLoadHandlerResult = true
|
|
154
184
|
let parentPath = "/library/radio"
|
|
155
185
|
let trackSrc = "song.mp3"
|
|
156
|
-
let
|
|
186
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
157
187
|
|
|
158
188
|
player.queueSourcePath = parentPath
|
|
159
189
|
player.tracks = [
|
|
160
|
-
Track(
|
|
190
|
+
Track(src: "song.mp3"),
|
|
161
191
|
]
|
|
162
192
|
|
|
163
|
-
let track = Track(
|
|
193
|
+
let track = Track(path: contextualPath, src: trackSrc)
|
|
164
194
|
let result = await selector.select(track: track, player: player)
|
|
165
195
|
guard case .intercepted = result else {
|
|
166
196
|
Issue.record("expected .intercepted, got \(result)")
|
|
167
197
|
return
|
|
168
198
|
}
|
|
199
|
+
#expect(browser.expandedPaths.isEmpty)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// A queue holding the same identity twice (normal for music playlists):
|
|
203
|
+
// exact contextual-path equality — which carries the stamped page index —
|
|
204
|
+
// pins the tapped copy instead of the first one.
|
|
205
|
+
@Test func duplicateIdentityInQueue_pathMatchPinsTheTappedCopy() async {
|
|
206
|
+
let (selector, browser, player) = makeSelector()
|
|
207
|
+
let parentPath = "/playlist"
|
|
208
|
+
func stamped(_ id: String, _ index: Int) -> String {
|
|
209
|
+
BrowserPathHelper.build(parentPath: parentPath, trackId: id, index: index)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
player.queueSourcePath = parentPath
|
|
213
|
+
player.tracks = [
|
|
214
|
+
Track(path: stamped("a", 0), src: "a"),
|
|
215
|
+
Track(path: stamped("b", 1), src: "b"),
|
|
216
|
+
Track(path: stamped("a", 2), src: "a"),
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
let track = Track(path: stamped("a", 2), src: "a")
|
|
220
|
+
let result = await selector.select(track: track, player: player)
|
|
221
|
+
guard case let .play(.skipTo(index)) = result else {
|
|
222
|
+
Issue.record("expected .play(.skipTo), got \(result)")
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
#expect(index == 2)
|
|
226
|
+
#expect(browser.expandedPaths.isEmpty)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// A tap in section B whose identity also sits in the active section-A
|
|
230
|
+
// queue: the exact surface is not in the queue, so the selection
|
|
231
|
+
// re-expands (re-scoping to the tapped section) instead of silently
|
|
232
|
+
// skipping inside the old section's queue.
|
|
233
|
+
@Test func indexStampedPathNotInQueue_reExpandsInsteadOfIdentitySkip() async {
|
|
234
|
+
let (selector, browser, player) = makeSelector()
|
|
235
|
+
let parentPath = "/city"
|
|
236
|
+
func stamped(_ id: String, _ index: Int) -> String {
|
|
237
|
+
BrowserPathHelper.build(parentPath: parentPath, trackId: id, index: index)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
player.queueSourcePath = parentPath
|
|
241
|
+
player.tracks = [
|
|
242
|
+
Track(path: stamped("dup", 0), src: "dup"),
|
|
243
|
+
Track(path: stamped("x", 1), src: "x"),
|
|
244
|
+
]
|
|
245
|
+
browser.expandQueueResult = (
|
|
246
|
+
tracks: [Track(path: stamped("y", 4), src: "y"), Track(path: stamped("dup", 5), src: "dup")],
|
|
247
|
+
selectedIndex: 1,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
let track = Track(path: stamped("dup", 5), src: "dup")
|
|
251
|
+
let result = await selector.select(track: track, player: player)
|
|
252
|
+
guard case let .play(.setQueue(tracks, startIndex, sourcePath)) = result else {
|
|
253
|
+
Issue.record("expected .play(.setQueue), got \(result)")
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
#expect(tracks.count == 2)
|
|
257
|
+
#expect(startIndex == 1)
|
|
258
|
+
#expect(sourcePath == parentPath)
|
|
259
|
+
#expect(browser.expandedPaths == [stamped("dup", 5)])
|
|
169
260
|
}
|
|
170
261
|
}
|
|
171
262
|
|
|
172
|
-
// MARK: - Contextual
|
|
263
|
+
// MARK: - Contextual path Expansion
|
|
173
264
|
|
|
174
|
-
@Suite("contextual
|
|
265
|
+
@Suite("contextual path expansion")
|
|
175
266
|
@MainActor
|
|
176
|
-
struct
|
|
267
|
+
struct ContextualPathExpansionTests {
|
|
177
268
|
@Test func expands_returnsSetQueue() async {
|
|
178
269
|
let (selector, browser, player) = makeSelector()
|
|
179
270
|
let parentPath = "/library/radio"
|
|
180
271
|
let trackSrc = "song.mp3"
|
|
181
|
-
let
|
|
272
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
182
273
|
|
|
183
274
|
let expandedTracks = [
|
|
184
275
|
Track(id: "a", src: "first.mp3"),
|
|
@@ -187,7 +278,7 @@ struct ContextualUrlExpansionTests {
|
|
|
187
278
|
]
|
|
188
279
|
browser.expandQueueResult = (tracks: expandedTracks, selectedIndex: 1)
|
|
189
280
|
|
|
190
|
-
let track = Track(id: "t1",
|
|
281
|
+
let track = Track(id: "t1", path: contextualPath, src: trackSrc)
|
|
191
282
|
let result = await selector.select(track: track, player: player)
|
|
192
283
|
guard case let .play(intent) = result else {
|
|
193
284
|
Issue.record("expected .play, got \(result)")
|
|
@@ -200,7 +291,7 @@ struct ContextualUrlExpansionTests {
|
|
|
200
291
|
#expect(tracks.count == 3)
|
|
201
292
|
#expect(startIndex == 1)
|
|
202
293
|
#expect(sourcePath == parentPath)
|
|
203
|
-
#expect(browser.
|
|
294
|
+
#expect(browser.expandedPaths == [contextualPath])
|
|
204
295
|
#expect(browser.trackLoadEvents.count == 1)
|
|
205
296
|
}
|
|
206
297
|
|
|
@@ -209,14 +300,14 @@ struct ContextualUrlExpansionTests {
|
|
|
209
300
|
browser.trackLoadHandlerResult = true
|
|
210
301
|
let parentPath = "/library/radio"
|
|
211
302
|
let trackSrc = "song.mp3"
|
|
212
|
-
let
|
|
303
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
213
304
|
|
|
214
305
|
browser.expandQueueResult = (
|
|
215
306
|
tracks: [Track(id: "a", src: "song.mp3")],
|
|
216
307
|
selectedIndex: 0,
|
|
217
308
|
)
|
|
218
309
|
|
|
219
|
-
let track = Track(id: "t1",
|
|
310
|
+
let track = Track(id: "t1", path: contextualPath, src: trackSrc)
|
|
220
311
|
let result = await selector.select(track: track, player: player)
|
|
221
312
|
guard case .intercepted = result else {
|
|
222
313
|
Issue.record("expected .intercepted, got \(result)")
|
|
@@ -228,11 +319,11 @@ struct ContextualUrlExpansionTests {
|
|
|
228
319
|
let (selector, browser, player) = makeSelector()
|
|
229
320
|
let parentPath = "/library/radio"
|
|
230
321
|
let trackSrc = "song.mp3"
|
|
231
|
-
let
|
|
322
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
232
323
|
|
|
233
324
|
browser.expandQueueResult = nil
|
|
234
325
|
|
|
235
|
-
let track = Track(id: "t1",
|
|
326
|
+
let track = Track(id: "t1", path: contextualPath, src: trackSrc)
|
|
236
327
|
let result = await selector.select(track: track, player: player)
|
|
237
328
|
guard case let .play(intent) = result else {
|
|
238
329
|
Issue.record("expected .play, got \(result)")
|
|
@@ -249,11 +340,11 @@ struct ContextualUrlExpansionTests {
|
|
|
249
340
|
let (selector, browser, player) = makeSelector()
|
|
250
341
|
let parentPath = "/library/radio"
|
|
251
342
|
let trackSrc = "song.mp3"
|
|
252
|
-
let
|
|
343
|
+
let contextualPath = BrowserPathHelper.build(parentPath: parentPath, trackId: trackSrc)
|
|
253
344
|
|
|
254
345
|
browser.expandQueueError = MockError.expansion
|
|
255
346
|
|
|
256
|
-
let track = Track(id: "t1",
|
|
347
|
+
let track = Track(id: "t1", path: contextualPath, src: trackSrc)
|
|
257
348
|
let result = await selector.select(track: track, player: player)
|
|
258
349
|
guard case let .play(intent) = result else {
|
|
259
350
|
Issue.record("expected .play, got \(result)")
|
|
@@ -266,3 +357,47 @@ struct ContextualUrlExpansionTests {
|
|
|
266
357
|
#expect(loaded.src == trackSrc)
|
|
267
358
|
}
|
|
268
359
|
}
|
|
360
|
+
|
|
361
|
+
// MARK: - Disabled Track
|
|
362
|
+
|
|
363
|
+
@Suite("disabled track")
|
|
364
|
+
@MainActor
|
|
365
|
+
struct DisabledTrackTests {
|
|
366
|
+
@Test func disabledPlayable_returnsNone() async {
|
|
367
|
+
let (selector, browser, player) = makeSelector()
|
|
368
|
+
let track = Track(id: "t1", src: "https://example.com/a.mp3", disabled: true)
|
|
369
|
+
let result = await selector.select(track: track, player: player)
|
|
370
|
+
guard case .none = result else {
|
|
371
|
+
Issue.record("Expected .none for a disabled track, got \(result)")
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
// Refused outright: not even the JS load handler hears about it.
|
|
375
|
+
#expect(browser.trackLoadEvents.isEmpty)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@Test func disabledBrowsable_returnsNone() async {
|
|
379
|
+
// Unavailable means unavailable — a disabled browsable doesn't navigate
|
|
380
|
+
// either (the ladder grays or hides it; a delivered tap stays inert).
|
|
381
|
+
let (selector, _, player) = makeSelector()
|
|
382
|
+
let track = Track(id: "t1", path: "/gone", disabled: true)
|
|
383
|
+
let result = await selector.select(track: track, player: player)
|
|
384
|
+
guard case .none = result else {
|
|
385
|
+
Issue.record("Expected .none for a disabled browsable, got \(result)")
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
@Test func disabledContextual_neverExpands() async {
|
|
391
|
+
let (selector, browser, player) = makeSelector()
|
|
392
|
+
browser.expandQueueResult = (tracks: [Track(id: "a", src: "a")], selectedIndex: 0)
|
|
393
|
+
let track = Track(
|
|
394
|
+
id: "t1", path: "/home?__trackId=t1", src: "https://example.com/a.mp3", disabled: true,
|
|
395
|
+
)
|
|
396
|
+
let result = await selector.select(track: track, player: player)
|
|
397
|
+
guard case .none = result else {
|
|
398
|
+
Issue.record("Expected .none, got \(result)")
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
#expect(browser.expandedPaths.isEmpty)
|
|
402
|
+
}
|
|
403
|
+
}
|
package/ios/TrackPlayer.swift
CHANGED
|
@@ -136,7 +136,7 @@ class TrackPlayer {
|
|
|
136
136
|
var previousTracks: [Track] { coordinator.previousTracks }
|
|
137
137
|
var isLastInPlaybackOrder: Bool { coordinator.isLastInPlaybackOrder }
|
|
138
138
|
var playbackActive: Bool { coordinator.playbackActive }
|
|
139
|
-
var sleepTimerManager:
|
|
139
|
+
var sleepTimerManager: any SleepTimerHandling { coordinator.sleepTimerManager }
|
|
140
140
|
var queue: QueueManager { coordinator.queue }
|
|
141
141
|
var loadSeekCoordinator: LoadSeekCoordinator { coordinator.loadSeekCoordinator }
|
|
142
142
|
var playingStateManager: PlayingStateManager { coordinator.playingStateManager }
|
|
@@ -195,12 +195,14 @@ class TrackPlayer {
|
|
|
195
195
|
return seconds.isNaN ? 0 : seconds
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/// The current item's duration, or 0 when it has none (a live stream).
|
|
199
|
+
///
|
|
200
|
+
/// Don't reintroduce `item.asset.duration`: it is deprecated as of the iOS 16
|
|
201
|
+
/// minimum and blocks the main actor fetching a remote asset's duration. Read
|
|
202
|
+
/// on every progress tick.
|
|
198
203
|
var duration: Double {
|
|
199
204
|
guard let item = avPlayer.currentItem else { return 0.0 }
|
|
200
205
|
|
|
201
|
-
if !item.asset.duration.seconds.isNaN {
|
|
202
|
-
return item.asset.duration.seconds
|
|
203
|
-
}
|
|
204
206
|
if !item.duration.seconds.isNaN {
|
|
205
207
|
return item.duration.seconds
|
|
206
208
|
}
|
|
@@ -252,8 +254,22 @@ class TrackPlayer {
|
|
|
252
254
|
var rate: Float {
|
|
253
255
|
get { coordinator.rate }
|
|
254
256
|
set {
|
|
257
|
+
// A non-positive rate leaves the player unable to play at all — and would
|
|
258
|
+
// poison the next play() through `defaultRate` below. Rejected here, at
|
|
259
|
+
// the one setter every source funnels through (JS, remote command, CarPlay
|
|
260
|
+
// rate cycling, a restored snapshot), rather than at each of them.
|
|
261
|
+
guard newValue > 0 else { return }
|
|
255
262
|
coordinator.rate = newValue
|
|
256
|
-
|
|
263
|
+
// Assigning a nonzero `AVPlayer.rate` IS `play()` — so setting the speed
|
|
264
|
+
// while paused would start audible playback behind the user's back, with
|
|
265
|
+
// `playWhenReady` still false (the lock screen would read "paused" over
|
|
266
|
+
// sound). On Android and web the speed control is independent of the play
|
|
267
|
+
// intent; this keeps iOS the same.
|
|
268
|
+
avPlayer.defaultRate = newValue
|
|
269
|
+
if playWhenReady { avPlayer.rate = newValue }
|
|
270
|
+
// Same reason as the repeat/shuffle edges below — a speed changed while
|
|
271
|
+
// paused would otherwise not reach the snapshot the cold resume restores.
|
|
272
|
+
persistPlaybackState()
|
|
257
273
|
}
|
|
258
274
|
}
|
|
259
275
|
|
|
@@ -312,17 +328,6 @@ class TrackPlayer {
|
|
|
312
328
|
coordinator.load(track, playWhenReady: playWhenReady)
|
|
313
329
|
}
|
|
314
330
|
|
|
315
|
-
func togglePlaying() {
|
|
316
|
-
switch avPlayer.timeControlStatus {
|
|
317
|
-
case .playing, .waitingToPlayAtSpecifiedRate:
|
|
318
|
-
pause()
|
|
319
|
-
case .paused:
|
|
320
|
-
play()
|
|
321
|
-
@unknown default:
|
|
322
|
-
fatalError("Unknown AVPlayer.timeControlStatus")
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
331
|
func play() { coordinator.play() }
|
|
327
332
|
func pause() { coordinator.pause() }
|
|
328
333
|
func togglePlayback() { coordinator.togglePlayback() }
|
|
@@ -456,13 +461,22 @@ class TrackPlayer {
|
|
|
456
461
|
)
|
|
457
462
|
}
|
|
458
463
|
|
|
464
|
+
/// Empties the queue and the published now-playing info. Unlinking the
|
|
465
|
+
/// session belongs in `destroy()`: `avPlayer` survives a clear, and only
|
|
466
|
+
/// `setupAVPlayer()` re-links.
|
|
459
467
|
func clear() {
|
|
460
468
|
coordinator.clear()
|
|
461
|
-
nowPlayingInfoController.unlinkPlayer()
|
|
462
469
|
}
|
|
463
470
|
|
|
464
471
|
func destroy() {
|
|
465
472
|
clear()
|
|
473
|
+
// The player is going away for good — now the session must go with it.
|
|
474
|
+
nowPlayingInfoController.unlinkPlayer()
|
|
475
|
+
// An AVPlayer retains its time observers: one left registered is still attached when the
|
|
476
|
+
// player is released, and outlives this object.
|
|
477
|
+
playerTimeObserver.unregisterForBoundaryTimeEvents()
|
|
478
|
+
playerObserver.stopObserving()
|
|
479
|
+
stopObservingAVPlayerItem()
|
|
466
480
|
remoteCommandController.disableAll()
|
|
467
481
|
}
|
|
468
482
|
|
|
@@ -545,10 +559,12 @@ class TrackPlayer {
|
|
|
545
559
|
|
|
546
560
|
nowPlayingInfoController.linkPlayer(avPlayer)
|
|
547
561
|
|
|
562
|
+
// Stage the rate unconditionally, so a freshly recreated player carries it
|
|
563
|
+
// before anything plays — `rate`'s setter holds the same invariant, and on
|
|
564
|
+
// the playWhenReady branch below `defaultRate` was previously left at 1.0.
|
|
565
|
+
avPlayer.defaultRate = rate
|
|
548
566
|
if playWhenReady {
|
|
549
567
|
startPlayback()
|
|
550
|
-
} else {
|
|
551
|
-
avPlayer.defaultRate = rate
|
|
552
568
|
}
|
|
553
569
|
}
|
|
554
570
|
|
|
@@ -750,13 +766,28 @@ extension TrackPlayer {
|
|
|
750
766
|
PersistedPlaybackState(
|
|
751
767
|
track: JsonTrack(from: track),
|
|
752
768
|
positionMs: positionMs,
|
|
753
|
-
repeatMode: repeatMode.
|
|
769
|
+
repeatMode: repeatMode.stringValue,
|
|
754
770
|
shuffleEnabled: shuffleEnabled,
|
|
755
771
|
playbackSpeed: rate,
|
|
756
|
-
)
|
|
772
|
+
),
|
|
757
773
|
)
|
|
758
774
|
}
|
|
759
775
|
|
|
776
|
+
/// Applies the player settings a snapshot carries — the inverse of
|
|
777
|
+
/// `persistPlaybackState()`, and the twin of the Kotlin store's `restore()`.
|
|
778
|
+
///
|
|
779
|
+
/// Call BEFORE building the queue: `setQueue` rebuilds the shuffle order
|
|
780
|
+
/// pinned to the starting track, so shuffle has to be set for that order to
|
|
781
|
+
/// be the one playback uses (and the mode events then precede the
|
|
782
|
+
/// queue-changed event, as on Android). An unrecognized repeat mode restores
|
|
783
|
+
/// as `.off` rather than failing the whole resume over one field; a
|
|
784
|
+
/// non-positive speed is rejected by `rate`'s own setter.
|
|
785
|
+
func restorePlaybackSettings(from state: PersistedPlaybackState) {
|
|
786
|
+
repeatMode = RepeatMode(fromString: state.repeatMode) ?? .off
|
|
787
|
+
shuffleEnabled = state.shuffleEnabled
|
|
788
|
+
rate = state.playbackSpeed
|
|
789
|
+
}
|
|
790
|
+
|
|
760
791
|
/// Start a 5 s repeating save while playback is active. A previous task is
|
|
761
792
|
/// cancelled first so there is never more than one running at a time.
|
|
762
793
|
private func startPeriodicSave() {
|
|
@@ -776,17 +807,6 @@ extension TrackPlayer {
|
|
|
776
807
|
}
|
|
777
808
|
}
|
|
778
809
|
|
|
779
|
-
private extension RepeatMode {
|
|
780
|
-
/// String representation stored in `PersistedPlaybackState.repeatMode`.
|
|
781
|
-
var persistedString: String {
|
|
782
|
-
switch self {
|
|
783
|
-
case .off: "off"
|
|
784
|
-
case .track: "track"
|
|
785
|
-
case .queue: "queue"
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
|
|
790
810
|
// MARK: - PlaybackEffectHandler
|
|
791
811
|
|
|
792
812
|
extension TrackPlayer: PlaybackEffectHandler {
|
|
@@ -852,15 +872,23 @@ extension TrackPlayer: PlaybackEffectHandler {
|
|
|
852
872
|
}
|
|
853
873
|
|
|
854
874
|
func clearNowPlaying() {
|
|
855
|
-
|
|
875
|
+
nowPlayingUpdater.clearNowPlaying()
|
|
856
876
|
}
|
|
857
877
|
|
|
878
|
+
// Both persist: a snapshot is otherwise only written on track load, on pause,
|
|
879
|
+
// and every 5s while playing, so a mode toggled while paused was lost — the
|
|
880
|
+
// cold-start resume would restore the value from before the toggle. Android
|
|
881
|
+
// persists on every setting change (`PlayerListener.onRepeatModeChanged` /
|
|
882
|
+
// `onShuffleModeEnabledChanged`); these are the same two edges, and the
|
|
883
|
+
// coordinator only calls them on a real change.
|
|
858
884
|
func updateRemoteRepeatMode(_ mode: RepeatMode) {
|
|
859
885
|
remoteCommandController.updateRepeatMode(mode)
|
|
886
|
+
persistPlaybackState()
|
|
860
887
|
}
|
|
861
888
|
|
|
862
889
|
func updateRemoteShuffleMode(_ enabled: Bool) {
|
|
863
890
|
remoteCommandController.updateShuffleMode(enabled)
|
|
891
|
+
persistPlaybackState()
|
|
864
892
|
}
|
|
865
893
|
|
|
866
894
|
func updateSkipAvailability(canNext: Bool, canPrevious: Bool) {
|
|
@@ -879,8 +907,8 @@ extension TrackPlayer: TrackSelectionPlayer {}
|
|
|
879
907
|
// MARK: - Queue Methods (thin forwarders)
|
|
880
908
|
|
|
881
909
|
extension TrackPlayer {
|
|
882
|
-
func replace(_ index: Int, _ track: Track) {
|
|
883
|
-
coordinator.replace(index, track)
|
|
910
|
+
func replace(_ index: Int, _ track: Track) throws {
|
|
911
|
+
try coordinator.replace(index, track)
|
|
884
912
|
}
|
|
885
913
|
|
|
886
914
|
func setQueue(
|
|
@@ -9,10 +9,11 @@ import NitroModules
|
|
|
9
9
|
/// Note: Method names are prefixed to avoid conflicts with JS callback property names.
|
|
10
10
|
/// All callbacks are called from the main actor since TrackPlayer is @MainActor.
|
|
11
11
|
///
|
|
12
|
-
/// Extends PlaybackCoordinatorCallbacks for the state machine / coordinator callbacks
|
|
13
|
-
///
|
|
12
|
+
/// Extends PlaybackCoordinatorCallbacks for the state machine / coordinator callbacks
|
|
13
|
+
/// and RemoteCommandCallbacks for the remote-command events, then adds metadata,
|
|
14
|
+
/// seek, and configuration callbacks that are TrackPlayer-specific.
|
|
14
15
|
@MainActor
|
|
15
|
-
protocol TrackPlayerCallbacks: PlaybackCoordinatorCallbacks {
|
|
16
|
+
protocol TrackPlayerCallbacks: PlaybackCoordinatorCallbacks, RemoteCommandCallbacks {
|
|
16
17
|
// MARK: - Metadata Events
|
|
17
18
|
|
|
18
19
|
/// Called when common metadata is received.
|
|
@@ -34,32 +35,7 @@ protocol TrackPlayerCallbacks: PlaybackCoordinatorCallbacks {
|
|
|
34
35
|
|
|
35
36
|
// MARK: - Remote Control Events
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
func remotePlay()
|
|
39
|
-
|
|
40
|
-
/// Called when pause is triggered remotely.
|
|
41
|
-
func remotePause()
|
|
42
|
-
|
|
43
|
-
/// Called when stop is triggered remotely.
|
|
44
|
-
func remoteStop()
|
|
45
|
-
|
|
46
|
-
/// Called when toggle play/pause is triggered remotely.
|
|
47
|
-
func remotePlayPause()
|
|
48
|
-
|
|
49
|
-
/// Called when next is triggered remotely.
|
|
50
|
-
func remoteNext()
|
|
51
|
-
|
|
52
|
-
/// Called when previous is triggered remotely.
|
|
53
|
-
func remotePrevious()
|
|
54
|
-
|
|
55
|
-
/// Called when jump forward is triggered remotely.
|
|
56
|
-
func remoteJumpForward(interval: Double)
|
|
57
|
-
|
|
58
|
-
/// Called when jump backward is triggered remotely.
|
|
59
|
-
func remoteJumpBackward(interval: Double)
|
|
60
|
-
|
|
61
|
-
/// Called when seek is triggered remotely.
|
|
62
|
-
func remoteSeek(position: Double)
|
|
38
|
+
// The command-center events live in RemoteCommandCallbacks, inherited above.
|
|
63
39
|
|
|
64
40
|
/// Called when change playback position is triggered remotely.
|
|
65
41
|
func remoteChangePlaybackPosition(position: Double)
|
|
@@ -70,15 +46,6 @@ protocol TrackPlayerCallbacks: PlaybackCoordinatorCallbacks {
|
|
|
70
46
|
/// Called when play from search is triggered remotely.
|
|
71
47
|
func remotePlaySearch(query: String)
|
|
72
48
|
|
|
73
|
-
/// Called when repeat mode change is triggered remotely (CarPlay/lock screen).
|
|
74
|
-
func remoteChangeRepeatMode(mode: RepeatMode)
|
|
75
|
-
|
|
76
|
-
/// Called when shuffle mode change is triggered remotely (CarPlay/lock screen).
|
|
77
|
-
func remoteChangeShuffleMode(enabled: Bool)
|
|
78
|
-
|
|
79
|
-
/// Called when playback rate change is triggered remotely (CarPlay/lock screen).
|
|
80
|
-
func remoteChangePlaybackRate(rate: Float)
|
|
81
|
-
|
|
82
49
|
// MARK: - Configuration Events
|
|
83
50
|
|
|
84
51
|
/// Called when options are changed.
|