react-native-audio-browser 0.1.1 → 0.2.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AudioBrowser.podspec +1 -1
- package/README.md +184 -2
- package/android/build.gradle +17 -5
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +18 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +473 -158
- 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/Callbacks.kt +4 -6
- package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +442 -178
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +299 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +42 -0
- package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
- package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
- package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +40 -46
- package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +172 -0
- package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
- package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
- package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +422 -134
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +248 -404
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +347 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +25 -5
- package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
- package/android/src/main/java/com/audiobrowser/player/Player.kt +475 -577
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +118 -53
- package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
- package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
- package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
- package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +85 -40
- package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +156 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +109 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +55 -0
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +265 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
- package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
- package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
- package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
- package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
- package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
- package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
- package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
- package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
- package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +112 -80
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +165 -0
- package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +203 -0
- package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
- package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +190 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
- package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
- package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
- package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +186 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +158 -0
- package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
- package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
- package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
- package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
- package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
- package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
- package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
- package/ios/Browser/BrowserConfig.swift +41 -11
- package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
- package/ios/Browser/BrowserManager.swift +303 -135
- package/ios/Browser/BrowserPathHelper.swift +32 -3
- package/ios/Browser/JsonModels.swift +287 -66
- package/ios/Browser/MediaResolveComposer.swift +114 -0
- package/ios/Browser/SearchDrillIn.swift +44 -0
- package/ios/Browser/SectionScope.swift +35 -0
- package/ios/Browser/TabBarEntries.swift +15 -0
- package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
- package/ios/CarPlay/CarPlayController.swift +739 -149
- package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
- package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
- package/ios/CarPlay/RNABAudioBrowser.h +15 -10
- package/ios/CarPlay/RNABAudioBrowser.m +15 -14
- package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
- package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
- package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
- package/ios/Extension/Capability+RemoteCommand.swift +7 -9
- package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/NavigationError+Conversion.swift +8 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
- package/ios/Extension/RequestConfig+Copying.swift +31 -0
- package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
- package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Track+AVPlayer.swift +4 -4
- package/ios/Extension/Track+Copying.swift +9 -3
- package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Variant+Artwork.swift +49 -0
- package/ios/HybridAudioBrowser.swift +805 -197
- package/ios/Model/NitroTypeStubs.swift +299 -224
- package/ios/Model/PlayerUpdateOptions.swift +23 -23
- package/ios/Model/RemoteCommand.swift +24 -6
- package/ios/Model/TrackPlayerError.swift +82 -18
- package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
- package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
- package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
- package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
- package/ios/Observer/PlayerTimeObserver.swift +3 -53
- package/ios/Option/SessionCategories.swift +34 -11
- package/ios/Player/AirPlayStallJudgement.swift +25 -0
- package/ios/Player/EndOfTrackJudgement.swift +32 -0
- package/ios/Player/LoadSeekCoordinator.swift +16 -1
- package/ios/Player/MediaIntentCriteria.swift +139 -0
- package/ios/Player/MediaLoader.swift +31 -11
- package/ios/Player/NowPlayingUpdater.swift +119 -26
- package/ios/Player/PersistedPlaybackState.swift +11 -0
- package/ios/Player/PlaybackCoordinator.swift +285 -51
- package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
- package/ios/Player/PlaybackEffectHandler.swift +19 -9
- package/ios/Player/PlaybackErrorHandler.swift +98 -14
- package/ios/Player/PlaybackStateMachine.swift +18 -13
- package/ios/Player/PlaybackStateStore.swift +27 -0
- package/ios/Player/PlaybackTimer.swift +61 -0
- package/ios/Player/PlayingStateManager.swift +3 -2
- package/ios/Player/QueueManager.swift +77 -64
- package/ios/Player/RetryManager.swift +175 -38
- package/ios/Player/ShuffleOrder.swift +10 -0
- package/ios/Player/SleepTimerHandling.swift +8 -0
- package/ios/Player/SleepTimerManager.swift +72 -11
- package/ios/Player/VolumeFader.swift +77 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +40 -21
- package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
- package/ios/Tests/BrowserPathHelperTests.swift +28 -0
- package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
- package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
- package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
- package/ios/Tests/JsonModelsTests.swift +58 -0
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +40 -7
- package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
- package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
- package/ios/Tests/MediaLoaderTests.swift +32 -20
- package/ios/Tests/MediaResolveTests.swift +126 -0
- package/ios/Tests/OnceValueTests.swift +79 -0
- package/ios/Tests/PlaybackCoordinatorTests.swift +787 -13
- package/ios/Tests/PlaybackErrorHandlerTests.swift +383 -47
- package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
- package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
- package/ios/Tests/PlaybackTimerTests.swift +55 -0
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/PlayingStateManagerTests.swift +57 -0
- package/ios/Tests/QueueManagerTests.swift +195 -18
- package/ios/Tests/RemoteCommandControllerTests.swift +156 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
- package/ios/Tests/RetryManagerTests.swift +178 -0
- package/ios/Tests/SearchDrillInTests.swift +63 -0
- package/ios/Tests/SectionScopeTests.swift +82 -0
- package/ios/Tests/SleepTimerManagerTests.swift +107 -0
- package/ios/Tests/Support/ManualSleepTimerScheduler.swift +47 -0
- package/ios/Tests/Support/TestTypes.swift +61 -20
- package/ios/Tests/TabBarEntriesTests.swift +71 -0
- package/ios/Tests/TrackSelectorTests.swift +15 -15
- package/ios/TrackPlayer.swift +385 -58
- package/ios/TrackPlayerCallbacks.swift +5 -44
- package/ios/TrackSelector.swift +5 -5
- package/ios/Util/ArtworkImageFetcher.swift +4 -2
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +7 -3
- package/ios/Util/OnceValue.swift +34 -0
- package/lib/commonjs/AudioBrowser.js +0 -11
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +227 -0
- package/lib/commonjs/features/browser-config.js.map +1 -0
- package/lib/commonjs/features/browser.js +60 -159
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +42 -0
- package/lib/commonjs/features/carConnection.js.map +1 -0
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +37 -5
- package/lib/commonjs/features/errors.js.map +1 -1
- package/lib/commonjs/features/favorites.js +1 -1
- package/lib/commonjs/features/gate.js +164 -0
- package/lib/commonjs/features/gate.js.map +1 -0
- package/lib/commonjs/features/index.js +22 -0
- package/lib/commonjs/features/index.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 +36 -1
- package/lib/commonjs/features/nowPlaying.js.map +1 -1
- package/lib/commonjs/features/output.js +37 -23
- package/lib/commonjs/features/output.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +9 -0
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/index.js +11 -0
- package/lib/commonjs/features/playback/index.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 +29 -13
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +86 -0
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/commonjs/features/playback/volume.js +2 -2
- package/lib/commonjs/features/player/options.js +79 -29
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +256 -3
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +21 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +1 -1
- package/lib/commonjs/features/queue/queue.js +12 -5
- 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 +14 -104
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +22 -5
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/native.js +5 -0
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.web.js +4 -1
- package/lib/commonjs/native.web.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 +8 -8
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/artwork.js +31 -0
- package/lib/commonjs/utils/artwork.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +302 -125
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/index.js +13 -5
- package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +176 -50
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +11 -9
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/commonjs/web/http/HttpClient.js +4 -23
- package/lib/commonjs/web/http/HttpClient.js.map +1 -1
- package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/playbackErrorKind.js +57 -0
- package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
- package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
- package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/player/OptionsManager.js +9 -17
- package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
- package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
- package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
- package/lib/module/AudioBrowser.js +1 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +221 -0
- package/lib/module/features/browser-config.js.map +1 -0
- package/lib/module/features/browser.js +54 -157
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +37 -0
- package/lib/module/features/carConnection.js.map +1 -0
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +37 -5
- package/lib/module/features/errors.js.map +1 -1
- package/lib/module/features/favorites.js +1 -1
- package/lib/module/features/gate.js +159 -0
- package/lib/module/features/gate.js.map +1 -0
- package/lib/module/features/index.js +2 -0
- package/lib/module/features/index.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 +34 -1
- package/lib/module/features/nowPlaying.js.map +1 -1
- package/lib/module/features/output.js +32 -19
- package/lib/module/features/output.js.map +1 -1
- package/lib/module/features/playback/controls.js +8 -0
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/index.js +1 -0
- package/lib/module/features/playback/index.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 +29 -13
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/state.js +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +83 -0
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/module/features/playback/volume.js +2 -2
- package/lib/module/features/player/options.js +79 -29
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +256 -3
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +17 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +1 -1
- package/lib/module/features/queue/queue.js +12 -5
- 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 -103
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +22 -5
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/native.js +5 -0
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.web.js +4 -1
- package/lib/module/native.web.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 +8 -8
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +0 -3
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/artwork.js +26 -0
- package/lib/module/utils/artwork.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +303 -126
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/module/web/TrackPlayer/Player.js +90 -26
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
- package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/module/web/TrackPlayer/index.js +2 -1
- package/lib/module/web/TrackPlayer/index.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +176 -50
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/NavigationErrorManager.js +3 -1
- package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +11 -9
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/module/web/browser/parseSearchResponse.js +16 -0
- package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/module/web/http/HttpClient.js +4 -23
- package/lib/module/web/http/HttpClient.js.map +1 -1
- package/lib/module/web/http/RequestConfigBuilder.js +107 -58
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/playbackErrorKind.js +52 -0
- package/lib/module/web/playbackErrorKind.js.map +1 -0
- package/lib/module/web/player/MediaSessionManager.js +116 -0
- package/lib/module/web/player/MediaSessionManager.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +7 -5
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/player/OptionsManager.js +9 -17
- package/lib/module/web/player/OptionsManager.js.map +1 -1
- package/lib/module/web/player/RemoteCommandController.js +92 -0
- package/lib/module/web/player/RemoteCommandController.js.map +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +0 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts +12 -0
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
- package/lib/typescript/src/features/browser.d.ts +40 -1
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +20 -0
- package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +61 -6
- package/lib/typescript/src/features/errors.d.ts.map +1 -1
- package/lib/typescript/src/features/favorites.d.ts +1 -1
- package/lib/typescript/src/features/gate.d.ts +93 -0
- package/lib/typescript/src/features/gate.d.ts.map +1 -0
- package/lib/typescript/src/features/index.d.ts +2 -0
- package/lib/typescript/src/features/index.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +26 -7
- 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 +28 -1
- package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
- package/lib/typescript/src/features/output.d.ts +28 -18
- package/lib/typescript/src/features/output.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +5 -0
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/index.d.ts +1 -0
- package/lib/typescript/src/features/playback/index.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 +4 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
- package/lib/typescript/src/features/playback/volume.d.ts +2 -2
- package/lib/typescript/src/features/player/options.d.ts +176 -127
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +216 -167
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +9 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +11 -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 -35
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +19 -4
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native.d.ts +1 -2
- package/lib/typescript/src/native.d.ts.map +1 -1
- package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
- package/lib/typescript/src/specs/audio-browser.nitro.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 +9 -3
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +349 -85
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +8 -6
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/artwork.d.ts +19 -0
- package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
- package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +4 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
- package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
- package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
- package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -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 +131 -0
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +14 -9
- package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
- package/nitrogen/generated/android/c++/JGate.hpp +62 -0
- package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
- package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
- package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +247 -223
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +26 -23
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
- package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
- package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
- package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +21 -24
- package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
- package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
- package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +19 -14
- package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
- package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
- package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
- package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
- package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +20 -20
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
- package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
- package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
- package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
- package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
- 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_.cpp +41 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → FavoriteConfig.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
- 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
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
- 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
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → Gate.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → GateReason.kt} +5 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +104 -110
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButton.kt +26 -0
- 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 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1126 -490
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +66 -45
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +178 -120
- package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -22
- package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
- package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
- package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
- package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
- package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
- 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
- package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
- package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
- package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
- package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
- 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
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
- package/nitrogen/generated/ios/swift/Gate.swift +47 -0
- package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
- package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
- package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +21 -15
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +232 -199
- package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
- package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
- package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
- package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
- package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
- package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
- package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
- package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
- package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -41
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
- package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
- package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
- package/nitrogen/generated/ios/swift/Options.swift +10 -4
- package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
- package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
- package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
- package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
- package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +10 -10
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
- package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
- package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
- package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
- package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +74 -8
- package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
- package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
- package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
- package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
- package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
- package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
- package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
- package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
- package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
- package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
- package/nitrogen/generated/shared/c++/{PercentageRating.hpp → Gate.hpp} +21 -16
- package/nitrogen/generated/shared/c++/{HeartRating.hpp → GateDecision.hpp} +23 -17
- package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateEvent.hpp} +18 -17
- package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +22 -19
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +45 -36
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
- package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
- package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
- package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
- package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
- package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
- package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
- package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
- package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
- package/nitrogen/generated/shared/c++/Options.hpp +8 -8
- package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
- package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
- package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
- package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
- package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +25 -25
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
- package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
- package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
- package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
- package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +24 -5
- package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
- package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
- package/package.json +20 -8
- package/src/AudioBrowser.ts +1 -1
- package/src/CLAUDE.md +52 -0
- package/src/codegen-no-config-variant.test.ts +70 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +110 -0
- package/src/features/browser-config.ts +293 -0
- package/src/features/browser.test.ts +19 -0
- package/src/features/browser.ts +59 -202
- package/src/features/carConnection.ts +36 -0
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +71 -5
- package/src/features/favorites.ts +1 -1
- package/src/features/gate.test.ts +72 -0
- package/src/features/gate.ts +170 -0
- package/src/features/hook-wiring.test.ts +254 -0
- package/src/features/index.ts +2 -0
- package/src/features/metadata.ts +26 -15
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +37 -1
- package/src/features/output.ts +35 -22
- package/src/features/playback/controls.ts +8 -0
- package/src/features/playback/index.ts +1 -0
- 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 +32 -13
- package/src/features/playback/state.ts +1 -1
- package/src/features/playback/trackPlaybackTime.test.ts +76 -0
- package/src/features/playback/trackPlaybackTime.ts +101 -0
- package/src/features/playback/volume.ts +2 -2
- package/src/features/player/options.test.ts +38 -0
- package/src/features/player/options.ts +217 -110
- package/src/features/player/setup.test.ts +125 -0
- package/src/features/player/setup.ts +400 -175
- package/src/features/player/validateOptions.ts +19 -0
- package/src/features/queue/activeTrack.ts +1 -1
- package/src/features/queue/queue.test.ts +36 -0
- package/src/features/queue/queue.ts +17 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -115
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +32 -5
- package/src/native.ts +5 -0
- package/src/native.web.ts +4 -1
- package/src/public-surface.test.ts +107 -0
- package/src/specs/audio-browser.nitro.ts +132 -42
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +19 -6
- package/src/types/browser-nodes.ts +160 -13
- package/src/types/browser.ts +377 -89
- package/src/utils/LazyNativeEmitter.ts +9 -8
- package/src/utils/NativeUpdatedValue.ts +0 -2
- package/src/utils/artwork.ts +30 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
- package/src/web/NativeAudioBrowser.ts +366 -159
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
- package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
- package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
- package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
- package/src/web/TrackPlayer/Player.load.test.ts +86 -0
- package/src/web/TrackPlayer/Player.ts +113 -39
- package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
- package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
- package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
- package/src/web/TrackPlayer/QueueManager.ts +302 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
- package/src/web/TrackPlayer/RepeatMode.ts +1 -1
- package/src/web/TrackPlayer/SleepTimer.ts +38 -3
- package/src/web/TrackPlayer/VolumeFader.ts +74 -0
- package/src/web/TrackPlayer/index.ts +2 -1
- package/src/web/browser/BrowserManager.ts +231 -62
- package/src/web/browser/NavigationErrorManager.ts +3 -1
- package/src/web/browser/SearchManager.ts +11 -15
- package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
- package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
- package/src/web/browser/assertBrowsePageShape.ts +27 -0
- package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
- package/src/web/browser/classifyTrackNavigation.ts +35 -0
- package/src/web/browser/parseSearchResponse.test.ts +25 -0
- package/src/web/browser/parseSearchResponse.ts +15 -0
- package/src/web/http/HttpClient.ts +5 -35
- package/src/web/http/RequestConfigBuilder.test.ts +59 -0
- package/src/web/http/RequestConfigBuilder.ts +120 -63
- package/src/web/playbackErrorKind.test.ts +55 -0
- package/src/web/playbackErrorKind.ts +56 -0
- package/src/web/player/MediaSessionManager.test.ts +220 -0
- package/src/web/player/MediaSessionManager.ts +151 -0
- package/src/web/player/NowPlayingManager.test.ts +46 -0
- package/src/web/player/NowPlayingManager.ts +6 -5
- package/src/web/player/OptionsManager.ts +12 -18
- package/src/web/player/RemoteCommandController.test.ts +171 -0
- package/src/web/player/RemoteCommandController.ts +166 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
- package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
- package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
- package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
- package/ios/CarPlay/Track+CarPlay.swift +0 -63
- package/ios/Model/MediaURL.swift +0 -67
- package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
- package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
- package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
- package/lib/commonjs/features/rating.js +0 -2
- package/lib/commonjs/features/rating.js.map +0 -1
- package/lib/commonjs/types/player.js +0 -2
- package/lib/commonjs/types/player.js.map +0 -1
- package/lib/commonjs/web/SimpleRouter.test.js +0 -174
- package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/module/features/rating.js +0 -2
- package/lib/module/features/rating.js.map +0 -1
- package/lib/module/types/player.js +0 -2
- package/lib/module/types/player.js.map +0 -1
- package/lib/module/web/SimpleRouter.test.js +0 -174
- package/lib/module/web/SimpleRouter.test.js.map +0 -1
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/typescript/src/features/rating.d.ts +0 -14
- package/lib/typescript/src/features/rating.d.ts.map +0 -1
- package/lib/typescript/src/native.web.d.ts +0 -3
- package/lib/typescript/src/native.web.d.ts.map +0 -1
- package/lib/typescript/src/types/player.d.ts +0 -64
- package/lib/typescript/src/types/player.d.ts.map +0 -1
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
- package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
- package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
- package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
- package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
- package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
- package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
- package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
- package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
- package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
- package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
- package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
- package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
- package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
- package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
- package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
- package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
- package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
- package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
- 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/AndroidUpdateOptions.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
- package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
- package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
- package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
- package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
- package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
- package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
- package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
- package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
- package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
- package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
- package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
- package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
- package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
- package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
- package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
- package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
- package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
- package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
- package/src/features/rating.ts +0 -17
- package/src/types/player.ts +0 -69
- package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
package/ios/TrackPlayer.swift
CHANGED
|
@@ -17,6 +17,12 @@ class TrackPlayer {
|
|
|
17
17
|
let nowPlayingInfoController: NowPlayingInfoController
|
|
18
18
|
let remoteCommandController: RemoteCommandController
|
|
19
19
|
private let retryManager = RetryManager()
|
|
20
|
+
private let playbackStateStore = PlaybackStateStore()
|
|
21
|
+
|
|
22
|
+
// MARK: - Periodic Persist
|
|
23
|
+
|
|
24
|
+
/// Cancelled when playback stops; recreated when it starts.
|
|
25
|
+
private var periodicSaveTask: Task<Void, Never>?
|
|
20
26
|
|
|
21
27
|
/// Retry configuration for load errors (network failures, timeouts, etc.)
|
|
22
28
|
var retryConfig: Variant_Bool_RetryConfig? {
|
|
@@ -52,23 +58,27 @@ class TrackPlayer {
|
|
|
52
58
|
)
|
|
53
59
|
|
|
54
60
|
private lazy var playerTimeObserver: PlayerTimeObserver = .init(
|
|
55
|
-
periodicObserverTimeInterval: CMTime(seconds: 1, preferredTimescale: 1000),
|
|
56
61
|
onAudioDidStart: { [weak self] in
|
|
62
|
+
// Audio rendered: this load has proven itself — retries from here on get
|
|
63
|
+
// the full recovery budget, not the short first-connect one.
|
|
64
|
+
self?.retryManager.hasPlayed = true
|
|
57
65
|
self?.coordinator.audioDidStart()
|
|
58
66
|
},
|
|
59
|
-
onSecondElapsed: { [weak self] seconds in
|
|
60
|
-
self?.nowPlayingUpdater.setCurrentTime(seconds: seconds)
|
|
61
|
-
},
|
|
62
67
|
)
|
|
63
68
|
|
|
64
69
|
private lazy var playerItemNotificationObserver: PlayerItemNotificationObserver = .init(
|
|
65
|
-
onDidPlayToEndTime: { [weak self] in
|
|
66
|
-
self?.coordinator.handleTrackDidPlayToEndTime()
|
|
67
|
-
},
|
|
70
|
+
onDidPlayToEndTime: { [weak self] in self?.handleDidPlayToEndTime() },
|
|
68
71
|
onFailedToPlayToEndTime: { [weak self] error in
|
|
69
72
|
let effectiveError = error ?? self?.avPlayer.currentItem?.error
|
|
70
|
-
self?.coordinator.errorHandler.handleError(
|
|
73
|
+
self?.coordinator.errorHandler.handleError(
|
|
74
|
+
effectiveError,
|
|
75
|
+
context: .playback,
|
|
76
|
+
httpStatusCode: self?.currentItemHttpStatusCode,
|
|
77
|
+
)
|
|
71
78
|
},
|
|
79
|
+
// Buffer emptied mid-playback: nudge the player, but don't reconnect — a
|
|
80
|
+
// genuine drop is recovered by the retry / network-restore paths.
|
|
81
|
+
onPlaybackStalled: { [weak self] in self?.recoverFromStall(reconnectIfLive: false) },
|
|
72
82
|
)
|
|
73
83
|
|
|
74
84
|
private lazy var playerItemObserver: PlayerItemPropertyObserver = .init(
|
|
@@ -86,6 +96,12 @@ class TrackPlayer {
|
|
|
86
96
|
},
|
|
87
97
|
)
|
|
88
98
|
|
|
99
|
+
// MARK: - Now-Playing Election
|
|
100
|
+
|
|
101
|
+
private let silentAudioPrimer = SilentAudioPrimer()
|
|
102
|
+
/// Latched on the first failure of each load; reset when a new track loads.
|
|
103
|
+
private var didPrimeNowPlayingElection = false
|
|
104
|
+
|
|
89
105
|
// MARK: - Callbacks
|
|
90
106
|
|
|
91
107
|
weak var callbacks: TrackPlayerCallbacks? {
|
|
@@ -101,14 +117,17 @@ class TrackPlayer {
|
|
|
101
117
|
get { coordinator.playbackError }
|
|
102
118
|
set { coordinator.playbackError = newValue }
|
|
103
119
|
}
|
|
120
|
+
|
|
104
121
|
var lastIndex: Int {
|
|
105
122
|
get { coordinator.lastIndex }
|
|
106
123
|
set { coordinator.lastIndex = newValue }
|
|
107
124
|
}
|
|
125
|
+
|
|
108
126
|
var lastTrack: Track? {
|
|
109
127
|
get { coordinator.lastTrack }
|
|
110
128
|
set { coordinator.lastTrack = newValue }
|
|
111
129
|
}
|
|
130
|
+
|
|
112
131
|
var tracks: [Track] { coordinator.tracks }
|
|
113
132
|
var currentIndex: Int { coordinator.currentIndex }
|
|
114
133
|
var currentTrack: Track? { coordinator.currentTrack }
|
|
@@ -138,6 +157,19 @@ class TrackPlayer {
|
|
|
138
157
|
set { coordinator.playWhenReady = newValue }
|
|
139
158
|
}
|
|
140
159
|
|
|
160
|
+
/// Time playback must have progressed before a buffer dip counts as a stall, so the initial
|
|
161
|
+
/// connect / a seek doesn't read as one. iOS has no native rebuffer-vs-initial signal.
|
|
162
|
+
private static let stallGraceSeconds: TimeInterval = 0.5
|
|
163
|
+
|
|
164
|
+
/// True while ongoing playback has stalled waiting for data. Approximated from
|
|
165
|
+
/// `isPlaybackLikelyToKeepUp`, gated on having actually started and the play intent (so an
|
|
166
|
+
/// initial connect / seek doesn't read as a stall). Mirrors the Android `stalled` signal, which
|
|
167
|
+
/// the load control distinguishes natively.
|
|
168
|
+
var isStalled: Bool {
|
|
169
|
+
guard let item = avPlayer.currentItem else { return false }
|
|
170
|
+
return currentTime > Self.stallGraceSeconds && playWhenReady && !item.isPlaybackLikelyToKeepUp
|
|
171
|
+
}
|
|
172
|
+
|
|
141
173
|
/**
|
|
142
174
|
Controls the time pitch algorithm applied to each track loaded into the player.
|
|
143
175
|
*/
|
|
@@ -222,7 +254,6 @@ class TrackPlayer {
|
|
|
222
254
|
set {
|
|
223
255
|
coordinator.rate = newValue
|
|
224
256
|
avPlayer.rate = newValue
|
|
225
|
-
nowPlayingUpdater.updatePlaybackValues(duration: duration, rate: newValue, currentTime: currentTime)
|
|
226
257
|
}
|
|
227
258
|
}
|
|
228
259
|
|
|
@@ -249,7 +280,9 @@ class TrackPlayer {
|
|
|
249
280
|
self?.playWhenReady ?? false
|
|
250
281
|
}
|
|
251
282
|
retryManager.onRetry = { [weak self] startFromCurrentTime in
|
|
252
|
-
|
|
283
|
+
// Re-resolve rather than replay the cached URL: a retry may be recovering
|
|
284
|
+
// from an expired short-lived URL/token that only a fresh resolve fixes.
|
|
285
|
+
self?.reloadResolving(startFromCurrentTime: startFromCurrentTime)
|
|
253
286
|
}
|
|
254
287
|
|
|
255
288
|
// Handle command center changes when MPNowPlayingSession is created/destroyed (iOS 16+)
|
|
@@ -270,6 +303,7 @@ class TrackPlayer {
|
|
|
270
303
|
func handlePlayWhenReady(_ playWhenReady: Bool?, action: () throws -> Void) rethrows {
|
|
271
304
|
try coordinator.handlePlayWhenReady(playWhenReady, action: action)
|
|
272
305
|
}
|
|
306
|
+
|
|
273
307
|
func transition(_ event: PlaybackEvent) { coordinator.transition(event) }
|
|
274
308
|
|
|
275
309
|
// MARK: - Player Actions
|
|
@@ -293,6 +327,13 @@ class TrackPlayer {
|
|
|
293
327
|
func pause() { coordinator.pause() }
|
|
294
328
|
func togglePlayback() { coordinator.togglePlayback() }
|
|
295
329
|
|
|
330
|
+
func handleInterruptionBegan() { coordinator.handleInterruptionBegan() }
|
|
331
|
+
func handleRouteDisconnected() { coordinator.handleRouteDisconnected() }
|
|
332
|
+
var willResumeAfterInterruption: Bool { coordinator.willResumeAfterInterruption }
|
|
333
|
+
func handleInterruptionEnded(shouldResume: Bool) {
|
|
334
|
+
coordinator.handleInterruptionEnded(shouldResume: shouldResume)
|
|
335
|
+
}
|
|
336
|
+
|
|
296
337
|
func stop() {
|
|
297
338
|
coordinator.stop()
|
|
298
339
|
if currentTrack?.live != true {
|
|
@@ -301,20 +342,62 @@ class TrackPlayer {
|
|
|
301
342
|
}
|
|
302
343
|
|
|
303
344
|
func reload(startFromCurrentTime: Bool) {
|
|
304
|
-
|
|
305
|
-
if startFromCurrentTime {
|
|
306
|
-
if let currentItem = avPlayer.currentItem {
|
|
307
|
-
if !currentItem.duration.isIndefinite {
|
|
308
|
-
time = currentItem.currentTime().seconds
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
345
|
+
let time = startFromCurrentTime ? resumeTime() : nil
|
|
312
346
|
loadAVPlayer()
|
|
313
347
|
if let time {
|
|
314
348
|
seekTo(time)
|
|
315
349
|
}
|
|
316
350
|
}
|
|
317
351
|
|
|
352
|
+
/// Playback position to resume from on reload, or nil when there is nothing
|
|
353
|
+
/// seekable to resume (no item, or an indefinite/live duration).
|
|
354
|
+
private func resumeTime() -> Double? {
|
|
355
|
+
guard let currentItem = avPlayer.currentItem, !currentItem.duration.isIndefinite else {
|
|
356
|
+
return nil
|
|
357
|
+
}
|
|
358
|
+
return currentItem.currentTime().seconds
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/// Re-establish a stalled stream when connectivity is restored.
|
|
362
|
+
///
|
|
363
|
+
/// AVPlayer doesn't reliably surface a mid-stream connectivity loss on a live stream as an item
|
|
364
|
+
/// failure — it sits in `.waitingToPlayAtSpecifiedRate` (a buffering stall) with no error, so the
|
|
365
|
+
/// error-driven `RetryManager` never engages. Without this, the stream stays "reconnecting"
|
|
366
|
+
/// indefinitely after the network returns. Mirrors Android, where ExoPlayer surfaces the drop as a
|
|
367
|
+
/// retryable load error and re-prepares on restore.
|
|
368
|
+
///
|
|
369
|
+
/// Guards: reload only while actually stalled (`.buffering`) with play intent — so a user-initiated
|
|
370
|
+
/// pause, or a retry that already gave up (`.error`, past the max retry duration) isn't surprisingly
|
|
371
|
+
/// resumed. Skipped when `RetryManager` is already parked waiting for the network: an error *did*
|
|
372
|
+
/// schedule a retry that owns its own reload, so we'd otherwise load twice.
|
|
373
|
+
func handleNetworkRestored() {
|
|
374
|
+
guard playWhenReady,
|
|
375
|
+
coordinator.state == .buffering,
|
|
376
|
+
!retryManager.isWaitingForNetwork
|
|
377
|
+
else { return }
|
|
378
|
+
logger.info("Connectivity restored while stalled — re-resolving to reconnect")
|
|
379
|
+
// Re-resolve rather than replay the cached URL: a resolver that mints a short-lived URL/token
|
|
380
|
+
// may have had it expire during the offline gap, so reconnecting with the stale URL would just
|
|
381
|
+
// fail (then recover only via the error path). Matches the error-retry path here, and Android,
|
|
382
|
+
// which re-resolves on its network-error reconnect. `reloadResolving` falls back to a plain
|
|
383
|
+
// reload when there's no track `src` to resolve.
|
|
384
|
+
reloadResolving(startFromCurrentTime: true)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/// Jump to the live edge. No-op for non-live tracks. Live with a seekable
|
|
388
|
+
/// window (HLS) seeks to the window end; live without one (non-seekable, e.g.
|
|
389
|
+
/// ICY) has no window to seek within, so reconnect to rejoin live.
|
|
390
|
+
func seekToLiveEdge() {
|
|
391
|
+
guard currentTrack?.live == true, let item = avPlayer.currentItem else { return }
|
|
392
|
+
if let range = item.seekableTimeRanges.last?.timeRangeValue, range.duration.seconds > 0 {
|
|
393
|
+
avPlayer.seek(to: range.end, toleranceBefore: .zero, toleranceAfter: .zero)
|
|
394
|
+
} else {
|
|
395
|
+
// Re-resolve on reconnect: this is the stall-recovery path for non-seekable live
|
|
396
|
+
// streams, where a short-lived stream URL may have expired during the outage.
|
|
397
|
+
reloadResolving(startFromCurrentTime: false)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
318
401
|
func seekTo(_ seconds: TimeInterval) {
|
|
319
402
|
seekTo(seconds, completion: { _ in })
|
|
320
403
|
}
|
|
@@ -326,10 +409,22 @@ class TrackPlayer {
|
|
|
326
409
|
} else if avPlayer.currentItem != nil {
|
|
327
410
|
let time = CMTime(seconds: seconds, preferredTimescale: 1000)
|
|
328
411
|
let seekSeconds = seconds
|
|
412
|
+
let generation = loadSeekCoordinator.generation
|
|
329
413
|
avPlayer
|
|
330
414
|
.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { [weak self] finished in
|
|
331
415
|
Task { @MainActor in
|
|
332
|
-
self
|
|
416
|
+
guard let self else {
|
|
417
|
+
completion(false)
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
// A completion from before a track change (reset() bumped the
|
|
421
|
+
// generation) belongs to the old item — delivering it would emit a
|
|
422
|
+
// bogus seek-completed and consume the new track's pending start seek.
|
|
423
|
+
guard self.loadSeekCoordinator.isCurrentGeneration(generation) else {
|
|
424
|
+
completion(false)
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
self.handleSeekCompleted(to: Double(seekSeconds), didFinish: finished)
|
|
333
428
|
completion(finished)
|
|
334
429
|
}
|
|
335
430
|
}
|
|
@@ -354,6 +449,11 @@ class TrackPlayer {
|
|
|
354
449
|
|
|
355
450
|
func enableRemoteCommands(_ commands: [RemoteCommand]) {
|
|
356
451
|
remoteCommandController.enable(commands: commands)
|
|
452
|
+
// Apply current next/previous availability so re-/late-configured commands
|
|
453
|
+
// start in the correct enabled state (not unconditionally enabled).
|
|
454
|
+
remoteCommandController.setSkipAvailability(
|
|
455
|
+
canNext: coordinator.canNext, canPrevious: coordinator.canPrevious,
|
|
456
|
+
)
|
|
357
457
|
}
|
|
358
458
|
|
|
359
459
|
func clear() {
|
|
@@ -363,6 +463,11 @@ class TrackPlayer {
|
|
|
363
463
|
|
|
364
464
|
func destroy() {
|
|
365
465
|
clear()
|
|
466
|
+
// An AVPlayer retains its time observers: one left registered is still attached when the
|
|
467
|
+
// player is released, and outlives this object.
|
|
468
|
+
playerTimeObserver.unregisterForBoundaryTimeEvents()
|
|
469
|
+
playerObserver.stopObserving()
|
|
470
|
+
stopObservingAVPlayerItem()
|
|
366
471
|
remoteCommandController.disableAll()
|
|
367
472
|
}
|
|
368
473
|
|
|
@@ -372,6 +477,10 @@ class TrackPlayer {
|
|
|
372
477
|
coordinator.setProgressUpdateInterval(interval)
|
|
373
478
|
}
|
|
374
479
|
|
|
480
|
+
func setPlaybackIntervalEnabled(_ enabled: Bool) {
|
|
481
|
+
coordinator.setPlaybackIntervalEnabled(enabled)
|
|
482
|
+
}
|
|
483
|
+
|
|
375
484
|
// MARK: - AVPlayer Management
|
|
376
485
|
|
|
377
486
|
func setTimePitchingAlgorithmForCurrentItem() {
|
|
@@ -409,7 +518,6 @@ class TrackPlayer {
|
|
|
409
518
|
private func recreateAVPlayer() {
|
|
410
519
|
coordinator.playbackError = nil
|
|
411
520
|
playerTimeObserver.unregisterForBoundaryTimeEvents()
|
|
412
|
-
playerTimeObserver.unregisterForPeriodicEvents()
|
|
413
521
|
playerObserver.stopObserving()
|
|
414
522
|
stopObservingAVPlayerItem()
|
|
415
523
|
clearCurrentAVItem()
|
|
@@ -420,6 +528,17 @@ class TrackPlayer {
|
|
|
420
528
|
setupAVPlayer()
|
|
421
529
|
}
|
|
422
530
|
|
|
531
|
+
/// Recover from a media-services reset (`mediaserverd` crashed/reset): every
|
|
532
|
+
/// AVPlayer/session handle is invalid, so recreate the player and reload the
|
|
533
|
+
/// current track, preserving play intent, so a live stream reconnects instead
|
|
534
|
+
/// of going permanently silent. No-op when nothing is loaded.
|
|
535
|
+
func handleMediaServicesReset() {
|
|
536
|
+
guard currentTrack != nil else { return }
|
|
537
|
+
logger.info("Media services were reset — recreating player and reloading current track")
|
|
538
|
+
recreateAVPlayer()
|
|
539
|
+
reloadResolving(startFromCurrentTime: false)
|
|
540
|
+
}
|
|
541
|
+
|
|
423
542
|
private func setupAVPlayer() {
|
|
424
543
|
avPlayer.allowsExternalPlayback = false
|
|
425
544
|
|
|
@@ -428,21 +547,31 @@ class TrackPlayer {
|
|
|
428
547
|
|
|
429
548
|
playerTimeObserver.avPlayer = avPlayer
|
|
430
549
|
playerTimeObserver.registerForBoundaryTimeEvents()
|
|
431
|
-
playerTimeObserver.registerForPeriodicTimeEvents()
|
|
432
550
|
|
|
433
551
|
nowPlayingInfoController.linkPlayer(avPlayer)
|
|
434
552
|
|
|
435
553
|
if playWhenReady {
|
|
436
554
|
startPlayback()
|
|
437
555
|
} else {
|
|
438
|
-
|
|
439
|
-
avPlayer.defaultRate = rate
|
|
440
|
-
}
|
|
556
|
+
avPlayer.defaultRate = rate
|
|
441
557
|
}
|
|
442
558
|
}
|
|
443
559
|
|
|
444
560
|
func loadAVPlayer() {
|
|
561
|
+
prepareForReload()
|
|
562
|
+
mediaLoader.loadAsset()
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/// Teardown shared by the two reload paths: recreate the player after a
|
|
566
|
+
/// terminal error, otherwise cancel in-flight loading and drop the current
|
|
567
|
+
/// asset, then enter the loading state.
|
|
568
|
+
private func prepareForReload() {
|
|
445
569
|
if state == .error {
|
|
570
|
+
// A restart from terminal error begins a new load of the same track:
|
|
571
|
+
// fresh budgets, unproven playback (see resetForNewLoad). Automatic
|
|
572
|
+
// retries never enter this branch — the retry loop keeps the state
|
|
573
|
+
// non-terminal — so only retry() / play-from-error land here.
|
|
574
|
+
coordinator.errorHandler.resetForNewLoad()
|
|
446
575
|
recreateAVPlayer()
|
|
447
576
|
} else {
|
|
448
577
|
mediaLoader.cancelAll()
|
|
@@ -451,7 +580,25 @@ class TrackPlayer {
|
|
|
451
580
|
mediaLoader.clearAsset()
|
|
452
581
|
}
|
|
453
582
|
transition(.trackLoading)
|
|
454
|
-
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/// Reload by re-running the resolver (fresh URL / headers / user-agent)
|
|
586
|
+
/// instead of recreating the asset from the already-resolved URL. Used by the
|
|
587
|
+
/// retry path so consumers whose resolver mints short-lived URLs or auth
|
|
588
|
+
/// tokens recover once those expire — a plain `reload()` would replay the
|
|
589
|
+
/// stale URL and keep failing. Falls back to `reload()` when there's no track
|
|
590
|
+
/// `src` to resolve.
|
|
591
|
+
func reloadResolving(startFromCurrentTime: Bool) {
|
|
592
|
+
guard let track = currentTrack, let src = track.src else {
|
|
593
|
+
reload(startFromCurrentTime: startFromCurrentTime)
|
|
594
|
+
return
|
|
595
|
+
}
|
|
596
|
+
let time = startFromCurrentTime ? resumeTime() : nil
|
|
597
|
+
prepareForReload()
|
|
598
|
+
mediaLoader.resolveAndLoad(src: src, track: track)
|
|
599
|
+
if let time {
|
|
600
|
+
seekTo(time)
|
|
601
|
+
}
|
|
455
602
|
}
|
|
456
603
|
|
|
457
604
|
func unloadAVPlayer() {
|
|
@@ -461,6 +608,47 @@ class TrackPlayer {
|
|
|
461
608
|
|
|
462
609
|
// MARK: - Observer Callbacks (map AVFoundation → coordinator)
|
|
463
610
|
|
|
611
|
+
/// Resolve an end-of-item notification: a genuine end advances the queue; a
|
|
612
|
+
/// dropped live stream or a mid-stream underrun reported as EOF is recovered
|
|
613
|
+
/// instead of advancing/stopping (see `EndOfTrackJudgement`).
|
|
614
|
+
private func handleDidPlayToEndTime() {
|
|
615
|
+
let judgement = EndOfTrackJudgement(
|
|
616
|
+
isLive: currentTrack?.live == true,
|
|
617
|
+
currentTime: currentTime,
|
|
618
|
+
duration: duration,
|
|
619
|
+
)
|
|
620
|
+
switch judgement.outcome {
|
|
621
|
+
case .ended:
|
|
622
|
+
coordinator.handleTrackDidPlayToEndTime()
|
|
623
|
+
case .stalled:
|
|
624
|
+
recoverFromStall(reconnectIfLive: true)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/// Recover from a stall while the play intent still holds. A live stream that
|
|
629
|
+
/// ran out of data (`reconnectIfLive`) rejoins the edge — reloading when there
|
|
630
|
+
/// is no seekable window; otherwise just re-issue play() to un-park AVPlayer
|
|
631
|
+
/// from `.waitingToPlayAtSpecifiedRate` (data may resume on the same
|
|
632
|
+
/// connection; a genuine drop is handled by the retry / network-restore paths).
|
|
633
|
+
private func recoverFromStall(reconnectIfLive: Bool) {
|
|
634
|
+
guard playWhenReady else { return }
|
|
635
|
+
if reconnectIfLive, currentTrack?.live == true {
|
|
636
|
+
seekToLiveEdge()
|
|
637
|
+
} else {
|
|
638
|
+
startPlayback()
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/// Previous time-control transition + whether its wait reason was
|
|
643
|
+
/// `.noItemToPlay`, retained to detect the AirPlay waiting→paused stall.
|
|
644
|
+
private var previousTimeControlStatus: PlayerTimeControlStatus?
|
|
645
|
+
private var previousWaitingReasonWasNoItemToPlay = false
|
|
646
|
+
|
|
647
|
+
/// True when the active audio route is AirPlay.
|
|
648
|
+
private var isAirPlayRoute: Bool {
|
|
649
|
+
AVAudioSession.sharedInstance().currentRoute.outputs.contains { $0.portType == .airPlay }
|
|
650
|
+
}
|
|
651
|
+
|
|
464
652
|
private func avPlayerDidChangeTimeControlStatus(_ status: AVPlayer.TimeControlStatus) {
|
|
465
653
|
let mapped: PlayerTimeControlStatus
|
|
466
654
|
switch status {
|
|
@@ -469,12 +657,36 @@ class TrackPlayer {
|
|
|
469
657
|
case .playing: mapped = .playing
|
|
470
658
|
@unknown default: return
|
|
471
659
|
}
|
|
660
|
+
recoverIfAirPlayStalled(transitioningTo: mapped)
|
|
472
661
|
coordinator.avPlayerDidChangeTimeControlStatus(mapped)
|
|
473
662
|
}
|
|
474
663
|
|
|
664
|
+
/// AVPlayer can silently strand us in .paused after a `.noItemToPlay` wait
|
|
665
|
+
/// over AirPlay while we still intend to play — re-issue play() on exactly
|
|
666
|
+
/// that transition. Also records this transition for the next comparison.
|
|
667
|
+
private func recoverIfAirPlayStalled(transitioningTo current: PlayerTimeControlStatus) {
|
|
668
|
+
let stalled = AirPlayStallJudgement(
|
|
669
|
+
previous: previousTimeControlStatus,
|
|
670
|
+
current: current,
|
|
671
|
+
previousWaitingReasonWasNoItemToPlay: previousWaitingReasonWasNoItemToPlay,
|
|
672
|
+
isAirPlay: isAirPlayRoute,
|
|
673
|
+
playWhenReady: playWhenReady,
|
|
674
|
+
).shouldNudge
|
|
675
|
+
previousTimeControlStatus = current
|
|
676
|
+
previousWaitingReasonWasNoItemToPlay =
|
|
677
|
+
current == .waitingToPlayAtSpecifiedRate && avPlayer.reasonForWaitingToPlay == .noItemToPlay
|
|
678
|
+
if stalled {
|
|
679
|
+
logger.info("AirPlay waiting→paused stall detected — nudging play()")
|
|
680
|
+
startPlayback()
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
475
684
|
private func avPlayerStatusDidChange(_ status: AVPlayer.Status) {
|
|
476
685
|
if status == .failed {
|
|
477
|
-
coordinator.avPlayerStatusDidFail(
|
|
686
|
+
coordinator.avPlayerStatusDidFail(
|
|
687
|
+
error: avPlayer.currentItem?.error,
|
|
688
|
+
httpStatusCode: currentItemHttpStatusCode,
|
|
689
|
+
)
|
|
478
690
|
}
|
|
479
691
|
}
|
|
480
692
|
|
|
@@ -486,7 +698,25 @@ class TrackPlayer {
|
|
|
486
698
|
case .failed: mapped = .failed
|
|
487
699
|
@unknown default: return
|
|
488
700
|
}
|
|
489
|
-
coordinator.avItemStatusDidChange(
|
|
701
|
+
coordinator.avItemStatusDidChange(
|
|
702
|
+
mapped,
|
|
703
|
+
error: error ?? avPlayer.currentItem?.error,
|
|
704
|
+
httpStatusCode: currentItemHttpStatusCode,
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/// The HTTP status the server last answered the current item with, if any.
|
|
709
|
+
///
|
|
710
|
+
/// `AVPlayerItem.error` never carries it — a 404 arrives as an opaque
|
|
711
|
+
/// CoreMedia error — so the error log is the only place it surfaces. The log
|
|
712
|
+
/// is per-item, so its entries belong to the track that just failed; picking
|
|
713
|
+
/// among them is `PlaybackErrorHandler`'s job, where it is testable
|
|
714
|
+
/// (`AVPlayerItemErrorLogEvent` cannot be constructed).
|
|
715
|
+
private var currentItemHttpStatusCode: Int? {
|
|
716
|
+
guard let events = avPlayer.currentItem?.errorLog()?.events else { return nil }
|
|
717
|
+
return PlaybackErrorHandler.httpStatusCode(
|
|
718
|
+
fromErrorStatusCodes: events.map(\.errorStatusCode),
|
|
719
|
+
)
|
|
490
720
|
}
|
|
491
721
|
|
|
492
722
|
private func avItemDidUpdatePlaybackLikelyToKeepUp(_ playbackLikelyToKeepUp: Bool) {
|
|
@@ -504,14 +734,64 @@ class TrackPlayer {
|
|
|
504
734
|
func handleSeekCompleted(to seconds: Double, didFinish: Bool) {
|
|
505
735
|
if loadSeekCoordinator.seekDidComplete(on: avPlayer, delegate: self), state == .loading {
|
|
506
736
|
coordinator.handleSeekCompleted(to: seconds, didFinish: didFinish)
|
|
507
|
-
} else {
|
|
508
|
-
// Not a load-seek, just update now playing
|
|
509
|
-
nowPlayingUpdater.setCurrentTime(seconds: seconds)
|
|
510
737
|
}
|
|
511
738
|
callbacks?.playerDidCompleteSeek(position: seconds, didFinish: didFinish)
|
|
512
739
|
}
|
|
513
740
|
}
|
|
514
741
|
|
|
742
|
+
// MARK: - Playback State Persistence
|
|
743
|
+
|
|
744
|
+
extension TrackPlayer {
|
|
745
|
+
/// Snapshot the current player state to UserDefaults so a cold-start resume
|
|
746
|
+
/// can restore it without the JS runtime. Live streams persist `positionMs = nil`.
|
|
747
|
+
private func persistPlaybackState() {
|
|
748
|
+
guard let track = currentTrack else { return }
|
|
749
|
+
// At .ended the position equals the duration — persisting it would make a
|
|
750
|
+
// cold-start resume seek to the end and instantly re-end (Android twin:
|
|
751
|
+
// savePositionZero).
|
|
752
|
+
let positionMs: Double? =
|
|
753
|
+
(track.live == true) ? nil : (state == .ended ? 0 : currentTime * 1000)
|
|
754
|
+
playbackStateStore.save(
|
|
755
|
+
PersistedPlaybackState(
|
|
756
|
+
track: JsonTrack(from: track),
|
|
757
|
+
positionMs: positionMs,
|
|
758
|
+
repeatMode: repeatMode.persistedString,
|
|
759
|
+
shuffleEnabled: shuffleEnabled,
|
|
760
|
+
playbackSpeed: rate,
|
|
761
|
+
),
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/// Start a 5 s repeating save while playback is active. A previous task is
|
|
766
|
+
/// cancelled first so there is never more than one running at a time.
|
|
767
|
+
private func startPeriodicSave() {
|
|
768
|
+
periodicSaveTask?.cancel()
|
|
769
|
+
periodicSaveTask = Task { @MainActor [weak self] in
|
|
770
|
+
while !Task.isCancelled {
|
|
771
|
+
try? await Task.sleep(nanoseconds: 5_000_000_000)
|
|
772
|
+
guard !Task.isCancelled else { return }
|
|
773
|
+
self?.persistPlaybackState()
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
private func stopPeriodicSave() {
|
|
779
|
+
periodicSaveTask?.cancel()
|
|
780
|
+
periodicSaveTask = nil
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
private extension RepeatMode {
|
|
785
|
+
/// String representation stored in `PersistedPlaybackState.repeatMode`.
|
|
786
|
+
var persistedString: String {
|
|
787
|
+
switch self {
|
|
788
|
+
case .off: "off"
|
|
789
|
+
case .track: "track"
|
|
790
|
+
case .queue: "queue"
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
515
795
|
// MARK: - PlaybackEffectHandler
|
|
516
796
|
|
|
517
797
|
extension TrackPlayer: PlaybackEffectHandler {
|
|
@@ -527,12 +807,20 @@ extension TrackPlayer: PlaybackEffectHandler {
|
|
|
527
807
|
stopObservingAVPlayerItem()
|
|
528
808
|
}
|
|
529
809
|
|
|
530
|
-
func loadTrack(src: String) {
|
|
531
|
-
|
|
810
|
+
func loadTrack(src: String, track: Track) {
|
|
811
|
+
didPrimeNowPlayingElection = false
|
|
812
|
+
// A new track starts unproven: its failures get the short first-connect
|
|
813
|
+
// budget until it renders audio. Reloads of the current track (retry,
|
|
814
|
+
// play-from-error) deliberately keep the flag.
|
|
815
|
+
retryManager.hasPlayed = false
|
|
816
|
+
mediaLoader.resolveAndLoad(src: src, track: track)
|
|
532
817
|
}
|
|
533
818
|
|
|
534
819
|
func reloadTrack(startFromCurrentTime: Bool) {
|
|
535
|
-
|
|
820
|
+
// Re-resolve rather than replay the cached URL: play-from-.error typically happens long
|
|
821
|
+
// after the failure (an expired signed URL would just re-fail, often non-retryably),
|
|
822
|
+
// matching every other retry path (retry, network restore, media-services reset).
|
|
823
|
+
reloadResolving(startFromCurrentTime: startFromCurrentTime)
|
|
536
824
|
}
|
|
537
825
|
|
|
538
826
|
func unloadTrack() {
|
|
@@ -553,37 +841,25 @@ extension TrackPlayer: PlaybackEffectHandler {
|
|
|
553
841
|
}
|
|
554
842
|
}
|
|
555
843
|
|
|
556
|
-
func updateNowPlayingValues(duration: Double, rate: Float, currentTime: Double) {
|
|
557
|
-
nowPlayingUpdater.updatePlaybackValues(duration: duration, rate: rate, currentTime: currentTime)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
844
|
func updateNowPlayingState(playWhenReady: Bool) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
MediaItemProperty.duration(nil),
|
|
569
|
-
NowPlayingInfoProperty.playbackRate(nil),
|
|
570
|
-
NowPlayingInfoProperty.elapsedPlaybackTime(nil),
|
|
571
|
-
])
|
|
572
|
-
nowPlayingUpdater.loadMetaValues(for: track, rate: rate)
|
|
845
|
+
nowPlayingInfoController.setPlaybackState(playing: playWhenReady)
|
|
846
|
+
if playWhenReady {
|
|
847
|
+
startPeriodicSave()
|
|
848
|
+
} else {
|
|
849
|
+
stopPeriodicSave()
|
|
850
|
+
persistPlaybackState()
|
|
851
|
+
}
|
|
573
852
|
}
|
|
574
853
|
|
|
575
|
-
func
|
|
576
|
-
|
|
854
|
+
func loadNowPlayingMetadata(for track: Track) {
|
|
855
|
+
nowPlayingUpdater.loadMetaValues(for: track)
|
|
856
|
+
persistPlaybackState()
|
|
577
857
|
}
|
|
578
858
|
|
|
579
859
|
func clearNowPlaying() {
|
|
580
860
|
nowPlayingInfoController.clear()
|
|
581
861
|
}
|
|
582
862
|
|
|
583
|
-
func setNowPlayingCurrentTime(seconds: Double) {
|
|
584
|
-
nowPlayingUpdater.setCurrentTime(seconds: seconds)
|
|
585
|
-
}
|
|
586
|
-
|
|
587
863
|
func updateRemoteRepeatMode(_ mode: RepeatMode) {
|
|
588
864
|
remoteCommandController.updateRepeatMode(mode)
|
|
589
865
|
}
|
|
@@ -591,6 +867,10 @@ extension TrackPlayer: PlaybackEffectHandler {
|
|
|
591
867
|
func updateRemoteShuffleMode(_ enabled: Bool) {
|
|
592
868
|
remoteCommandController.updateShuffleMode(enabled)
|
|
593
869
|
}
|
|
870
|
+
|
|
871
|
+
func updateSkipAvailability(canNext: Bool, canPrevious: Bool) {
|
|
872
|
+
remoteCommandController.setSkipAvailability(canNext: canNext, canPrevious: canPrevious)
|
|
873
|
+
}
|
|
594
874
|
}
|
|
595
875
|
|
|
596
876
|
// MARK: - SeekCompletionHandler
|
|
@@ -608,8 +888,20 @@ extension TrackPlayer {
|
|
|
608
888
|
coordinator.replace(index, track)
|
|
609
889
|
}
|
|
610
890
|
|
|
611
|
-
func setQueue(
|
|
612
|
-
|
|
891
|
+
func setQueue(
|
|
892
|
+
_ newTracks: [Track],
|
|
893
|
+
initialIndex: Int = 0,
|
|
894
|
+
startPositionMs: Double? = nil,
|
|
895
|
+
playWhenReady: Bool? = nil,
|
|
896
|
+
sourcePath: String? = nil,
|
|
897
|
+
) {
|
|
898
|
+
coordinator.setQueue(
|
|
899
|
+
newTracks,
|
|
900
|
+
initialIndex: initialIndex,
|
|
901
|
+
startPositionMs: startPositionMs,
|
|
902
|
+
playWhenReady: playWhenReady,
|
|
903
|
+
sourcePath: sourcePath,
|
|
904
|
+
)
|
|
613
905
|
}
|
|
614
906
|
|
|
615
907
|
func add(_ tracks: [Track], initialIndex: Int? = nil, playWhenReady: Bool? = nil) {
|
|
@@ -666,14 +958,50 @@ extension TrackPlayer: MediaLoaderDelegate {
|
|
|
666
958
|
}
|
|
667
959
|
|
|
668
960
|
func mediaLoaderDidFailWithRetryableError(_ error: Error) {
|
|
961
|
+
attachPublishingCarrierItemIfNeeded()
|
|
962
|
+
primeNowPlayingElectionIfNeeded()
|
|
669
963
|
coordinator.errorHandler.handleError(error, context: .mediaLoad)
|
|
670
964
|
}
|
|
671
965
|
|
|
672
966
|
func mediaLoaderDidFailWithUnplayableTrack() {
|
|
967
|
+
attachPublishingCarrierItemIfNeeded()
|
|
968
|
+
primeNowPlayingElectionIfNeeded()
|
|
673
969
|
transition(.errorOccurred(.trackWasUnplayable))
|
|
674
970
|
}
|
|
675
971
|
|
|
972
|
+
/// `MPNowPlayingSession` publishes only through the linked player's current item, so
|
|
973
|
+
/// a load that fails before item creation (a dead host on a cold start) leaves the
|
|
974
|
+
/// player item-less: the system never surfaces the app on the lock screen / CarPlay
|
|
975
|
+
/// — no metadata, no transport buttons — and the failure line has nowhere to appear.
|
|
976
|
+
/// Attach the failed asset's item purely as a publishing carrier. Deliberately not
|
|
977
|
+
/// observed: its `.failed` status must not re-report the error the loader already
|
|
978
|
+
/// delivered. A successful load (retry, or the next track) replaces it with the
|
|
979
|
+
/// real, observed item via `mediaLoaderDidPrepareItem`; retry reloads leave it in
|
|
980
|
+
/// place (`prepareForReload` drops only the asset), so the surface doesn't flicker.
|
|
981
|
+
private func attachPublishingCarrierItemIfNeeded() {
|
|
982
|
+
guard avPlayer.currentItem == nil, let asset = mediaLoader.asset else { return }
|
|
983
|
+
logger.notice("Attaching publishing-carrier item for failed load")
|
|
984
|
+
let item = AVPlayerItem(asset: asset)
|
|
985
|
+
nowPlayingInfoController.prepareItem(item)
|
|
986
|
+
avPlayer.replaceCurrentItem(with: item)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/// A failed load renders no audio, and without rendered audio iOS never elects the
|
|
990
|
+
/// app onto the system now-playing surfaces — the published failure metadata and
|
|
991
|
+
/// transport controls stay invisible (see `SilentAudioPrimer`). On the first
|
|
992
|
+
/// failure of a play-intended load, render a silence burst to claim the surface.
|
|
993
|
+
/// Once per load: the retry loop re-fails every few seconds, and one burst either
|
|
994
|
+
/// elected us or never will. Does not touch the retry loop itself.
|
|
995
|
+
private func primeNowPlayingElectionIfNeeded() {
|
|
996
|
+
guard playWhenReady, !didPrimeNowPlayingElection else { return }
|
|
997
|
+
didPrimeNowPlayingElection = true
|
|
998
|
+
silentAudioPrimer.prime()
|
|
999
|
+
}
|
|
1000
|
+
|
|
676
1001
|
func mediaLoaderDidFailWithError(_ error: TrackPlayerError.PlaybackError) {
|
|
1002
|
+
// No carrier here: an invalid/missing src never produced an asset. The
|
|
1003
|
+
// direct-center publish still carries the metadata once elected.
|
|
1004
|
+
primeNowPlayingElectionIfNeeded()
|
|
677
1005
|
transition(.errorOccurred(error))
|
|
678
1006
|
}
|
|
679
1007
|
|
|
@@ -689,4 +1017,3 @@ extension TrackPlayer: MediaLoaderDelegate {
|
|
|
689
1017
|
callbacks?.playerDidReceiveTimedMetadata(groups)
|
|
690
1018
|
}
|
|
691
1019
|
}
|
|
692
|
-
|