react-native-audio-browser 0.1.1 → 0.2.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AudioBrowser.podspec +1 -1
- package/README.md +180 -2
- package/android/build.gradle +10 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +15 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +401 -141
- 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/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 +443 -178
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +300 -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 +43 -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/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 +31 -37
- package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +119 -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 +415 -131
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +154 -276
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +348 -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 +27 -5
- package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
- package/android/src/main/java/com/audiobrowser/player/Player.kt +452 -576
- 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 +117 -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 +84 -40
- package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +141 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +41 -0
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +209 -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 +26 -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 +111 -80
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +166 -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 +54 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +96 -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 +159 -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/NowPlayingUpdaterTest.kt +442 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +191 -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 +107 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +73 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +75 -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 -110
- 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 +750 -173
- package/ios/Model/NitroTypeStubs.swift +299 -224
- package/ios/Model/PlayerUpdateOptions.swift +23 -23
- package/ios/Model/RemoteCommand.swift +6 -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 +33 -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 +28 -10
- package/ios/Player/NowPlayingUpdater.swift +119 -26
- package/ios/Player/PersistedPlaybackState.swift +11 -0
- package/ios/Player/PlaybackCoordinator.swift +282 -51
- package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
- package/ios/Player/PlaybackEffectHandler.swift +19 -9
- package/ios/Player/PlaybackErrorHandler.swift +97 -13
- 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 +67 -61
- package/ios/Player/RetryManager.swift +97 -21
- package/ios/Player/SleepTimerHandling.swift +8 -0
- package/ios/Player/SleepTimerManager.swift +38 -5
- package/ios/Player/VolumeFader.swift +77 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +31 -15
- 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/LoadSeekCoordinatorTests.swift +38 -7
- package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
- package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
- package/ios/Tests/MediaLoaderTests.swift +23 -8
- package/ios/Tests/MediaResolveTests.swift +126 -0
- package/ios/Tests/OnceValueTests.swift +79 -0
- package/ios/Tests/PlaybackCoordinatorTests.swift +784 -13
- package/ios/Tests/PlaybackErrorHandlerTests.swift +380 -46
- package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
- package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
- package/ios/Tests/PlaybackTimerTests.swift +55 -0
- package/ios/Tests/PlayingStateManagerTests.swift +57 -0
- package/ios/Tests/QueueManagerTests.swift +149 -18
- package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
- package/ios/Tests/RetryManagerTests.swift +89 -0
- package/ios/Tests/SearchDrillInTests.swift +63 -0
- package/ios/Tests/SectionScopeTests.swift +82 -0
- package/ios/Tests/SleepTimerManagerTests.swift +104 -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 +380 -58
- package/ios/TrackPlayerCallbacks.swift +0 -6
- package/ios/TrackSelector.swift +5 -5
- package/ios/Util/ArtworkImageFetcher.swift +4 -2
- package/ios/Util/NetworkMonitor.swift +7 -3
- package/ios/Util/OnceValue.swift +34 -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 +59 -158
- 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/errors.js +34 -2
- package/lib/commonjs/features/errors.js.map +1 -1
- package/lib/commonjs/features/gate.js +149 -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.map +1 -1
- package/lib/commonjs/features/nowPlaying.js +35 -0
- 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/progress.js +28 -12
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +92 -0
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +49 -13
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +249 -3
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +10 -3
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/remoteControls.js +1 -12
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +9 -4
- 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/utils/LazyNativeEmitter.js +0 -3
- 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 +301 -124
- 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/features/browser-config.js +221 -0
- package/lib/module/features/browser-config.js.map +1 -0
- package/lib/module/features/browser.js +53 -156
- 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/errors.js +34 -2
- package/lib/module/features/errors.js.map +1 -1
- package/lib/module/features/gate.js +144 -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.map +1 -1
- package/lib/module/features/nowPlaying.js +33 -0
- 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/progress.js +28 -12
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +88 -0
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +48 -13
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +249 -3
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/queue/queue.js +10 -3
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/remoteControls.js +0 -11
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +9 -4
- 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/utils/LazyNativeEmitter.js +0 -3
- 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 +302 -125
- 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/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/errors.d.ts +58 -3
- package/lib/typescript/src/features/errors.d.ts.map +1 -1
- package/lib/typescript/src/features/gate.d.ts +77 -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 +6 -4
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +27 -0
- 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/progress.d.ts +3 -0
- package/lib/typescript/src/features/playback/progress.d.ts.map +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 +1 -1
- package/lib/typescript/src/features/player/options.d.ts +92 -93
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +206 -167
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +9 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +0 -13
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +18 -3
- 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/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 +0 -2
- 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 +3 -1
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
- package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +1 -11
- 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 +9 -4
- 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 +236 -212
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +25 -22
- 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 +16 -19
- package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +1 -11
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
- package/nitrogen/generated/android/c++/JNotificationButton.hpp +3 -3
- 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 +14 -9
- 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++/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_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 +2 -8
- 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/{ThumbsRating.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/{HeartRating.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/GateReason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +103 -109
- 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 +2 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButton.kt +5 -5
- 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/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_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 +1098 -447
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +60 -39
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +170 -112
- package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +2 -12
- 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 +20 -14
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +230 -197
- 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 +1 -30
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
- package/nitrogen/generated/ios/swift/NotificationButton.swift +4 -4
- 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/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_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 +1 -12
- 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++/{HeartRating.hpp → Gate.hpp} +21 -16
- package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateDecision.hpp} +23 -17
- package/nitrogen/generated/shared/c++/{PercentageRating.hpp → GateEvent.hpp} +18 -17
- package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +21 -18
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +44 -35
- 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 +1 -12
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/NotificationButton.hpp +8 -8
- 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++/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 +11 -6
- package/src/codegen-no-config-variant.test.ts +70 -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 +58 -201
- package/src/features/carConnection.ts +36 -0
- package/src/features/errors.ts +68 -2
- package/src/features/gate.test.ts +72 -0
- package/src/features/gate.ts +155 -0
- package/src/features/index.ts +2 -0
- package/src/features/metadata.ts +6 -12
- package/src/features/nowPlaying.ts +36 -0
- 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/progress.ts +31 -12
- package/src/features/playback/trackPlaybackTime.test.ts +66 -0
- package/src/features/playback/trackPlaybackTime.ts +106 -0
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.test.ts +38 -0
- package/src/features/player/options.ts +140 -75
- package/src/features/player/setup.test.ts +117 -0
- package/src/features/player/setup.ts +390 -175
- package/src/features/queue/queue.test.ts +36 -0
- package/src/features/queue/queue.ts +15 -4
- package/src/features/remoteControls.ts +0 -23
- package/src/features/sleepTimer.ts +23 -4
- package/src/native.ts +5 -0
- package/src/native.web.ts +4 -1
- package/src/specs/audio-browser.nitro.ts +131 -41
- 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 +0 -2
- package/src/utils/NativeUpdatedValue.ts +0 -2
- package/src/utils/artwork.ts +30 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
- package/src/web/NativeAudioBrowser.ts +365 -158
- 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/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++/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/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/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/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++/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
|
@@ -2,61 +2,59 @@ package com.audiobrowser.player
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
import android.os.Looper
|
|
7
|
+
import androidx.core.net.toUri
|
|
6
8
|
import androidx.media3.common.C
|
|
7
|
-
import androidx.media3.common.ForwardingPlayer
|
|
8
9
|
import androidx.media3.common.HeartRating
|
|
9
|
-
import androidx.media3.common.
|
|
10
|
-
import androidx.media3.common.TrackSelectionParameters
|
|
10
|
+
import androidx.media3.common.MediaMetadata
|
|
11
11
|
import androidx.media3.database.StandaloneDatabaseProvider
|
|
12
12
|
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
|
|
13
13
|
import androidx.media3.datasource.cache.SimpleCache
|
|
14
|
-
import androidx.media3.exoplayer.DefaultRenderersFactory
|
|
15
14
|
import androidx.media3.exoplayer.ExoPlayer
|
|
16
|
-
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
|
|
17
15
|
import androidx.media3.session.MediaLibraryService
|
|
18
16
|
import androidx.media3.session.MediaSession
|
|
19
17
|
import com.audiobrowser.AudioBrowser
|
|
20
18
|
import com.audiobrowser.Callbacks
|
|
19
|
+
import com.audiobrowser.browser.displayArtworkSource
|
|
20
|
+
import com.audiobrowser.browser.resolveArtworkUrl
|
|
21
|
+
import com.audiobrowser.browser.unattributedArtworkSource
|
|
21
22
|
import com.audiobrowser.extension.NumberExt.Companion.toSeconds
|
|
22
|
-
import com.margelo.nitro.audiobrowser.ImageContext
|
|
23
23
|
import com.audiobrowser.model.PlayerSetupOptions
|
|
24
24
|
import com.audiobrowser.model.PlayerUpdateOptions
|
|
25
|
-
import com.audiobrowser.util.AndroidAudioContentTypeFactory
|
|
26
25
|
import com.audiobrowser.util.CoilBitmapLoader
|
|
27
|
-
import com.audiobrowser.util.EqualizerManager
|
|
28
26
|
import com.audiobrowser.util.NetworkConnectivityMonitor
|
|
29
27
|
import com.audiobrowser.util.PlayingStateFactory
|
|
30
28
|
import com.audiobrowser.util.RepeatModeFactory
|
|
31
29
|
import com.audiobrowser.util.TrackFactory
|
|
32
|
-
import com.margelo.nitro.audiobrowser.AndroidPlayerWakeMode
|
|
33
30
|
import com.margelo.nitro.audiobrowser.AppKilledPlaybackBehavior
|
|
34
31
|
import com.margelo.nitro.audiobrowser.FavoriteChangedEvent
|
|
32
|
+
import com.margelo.nitro.audiobrowser.GateEvent
|
|
33
|
+
import com.margelo.nitro.audiobrowser.GateReason
|
|
34
|
+
import com.margelo.nitro.audiobrowser.ImageContext
|
|
35
|
+
import com.margelo.nitro.audiobrowser.ImageSource
|
|
36
|
+
import com.margelo.nitro.audiobrowser.NativeGateRequest
|
|
35
37
|
import com.margelo.nitro.audiobrowser.NowPlayingMetadata
|
|
36
|
-
import com.margelo.nitro.audiobrowser.NowPlayingUpdate
|
|
37
38
|
import com.margelo.nitro.audiobrowser.Playback
|
|
38
39
|
import com.margelo.nitro.audiobrowser.PlaybackActiveTrackChangedEvent
|
|
39
40
|
import com.margelo.nitro.audiobrowser.PlaybackError
|
|
41
|
+
import com.margelo.nitro.audiobrowser.PlaybackPlayWhenReadyChangedEvent
|
|
40
42
|
import com.margelo.nitro.audiobrowser.PlaybackProgressUpdatedEvent
|
|
41
43
|
import com.margelo.nitro.audiobrowser.PlaybackQueueEndedEvent
|
|
42
44
|
import com.margelo.nitro.audiobrowser.PlaybackState
|
|
43
45
|
import com.margelo.nitro.audiobrowser.PlayingState
|
|
44
|
-
import com.margelo.nitro.audiobrowser.RatingType
|
|
45
|
-
import com.margelo.nitro.audiobrowser.RemoteJumpBackwardEvent
|
|
46
|
-
import com.margelo.nitro.audiobrowser.RemoteJumpForwardEvent
|
|
47
|
-
import com.margelo.nitro.audiobrowser.RemoteSeekEvent
|
|
48
46
|
import com.margelo.nitro.audiobrowser.RepeatMode
|
|
49
47
|
import com.margelo.nitro.audiobrowser.SearchParams
|
|
50
|
-
import com.margelo.nitro.audiobrowser.SleepTimer as NitroSleepTimer
|
|
51
|
-
import com.margelo.nitro.audiobrowser.SleepTimerEndOfTrack
|
|
52
|
-
import com.margelo.nitro.audiobrowser.SleepTimerTime
|
|
53
48
|
import com.margelo.nitro.audiobrowser.Track
|
|
54
|
-
import com.margelo.nitro.core.NullType
|
|
55
49
|
import java.io.File
|
|
50
|
+
import java.io.IOException
|
|
56
51
|
import java.util.concurrent.TimeUnit
|
|
57
52
|
import kotlin.time.Duration.Companion.seconds
|
|
58
53
|
import kotlinx.coroutines.CompletableDeferred
|
|
54
|
+
import kotlinx.coroutines.Dispatchers
|
|
55
|
+
import kotlinx.coroutines.MainScope
|
|
59
56
|
import kotlinx.coroutines.TimeoutCancellationException
|
|
57
|
+
import kotlinx.coroutines.withContext
|
|
60
58
|
import kotlinx.coroutines.withTimeout
|
|
61
59
|
import timber.log.Timber
|
|
62
60
|
|
|
@@ -66,25 +64,50 @@ class Player(internal val context: Context) {
|
|
|
66
64
|
get() = options.appKilledPlaybackBehavior
|
|
67
65
|
|
|
68
66
|
private var options = PlayerUpdateOptions()
|
|
67
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
69
68
|
internal var callbacks: Callbacks? = null
|
|
70
69
|
private lateinit var mediaSession: MediaSession
|
|
71
70
|
val networkMonitor: NetworkConnectivityMonitor = NetworkConnectivityMonitor(context)
|
|
72
|
-
|
|
71
|
+
internal val equalizer =
|
|
72
|
+
EqualizerController(onSettingsChanged = { settings -> callbacks?.onEqualizerChanged(settings) })
|
|
73
73
|
private val mediaSessionCallback = MediaSessionCallback(this)
|
|
74
74
|
internal val playbackStateStore = PlaybackStateStore(this)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
75
|
+
internal val volumeFader = VolumeFader(getVolume = { volume }, setVolume = { volume = it })
|
|
76
|
+
internal val sleepTimer =
|
|
77
|
+
SleepTimerManager(
|
|
78
|
+
volumeFader,
|
|
79
|
+
pause = { pause() },
|
|
80
|
+
onChanged = { callbacks?.onSleepTimerChanged(it) },
|
|
81
|
+
)
|
|
83
82
|
|
|
84
83
|
lateinit var exoPlayer: ExoPlayer
|
|
85
84
|
lateinit var forwardingPlayer: androidx.media3.common.Player
|
|
86
85
|
/** Thread-safe cache of playWhenReady for access from non-main threads (e.g., retry policy) */
|
|
87
86
|
@Volatile internal var playWhenReadyCache = false
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether the current load has ever produced audio — selects between the retry policy's
|
|
90
|
+
* first-connect and recovery budgets. Volatile: read from ExoPlayer's loader thread. Set when
|
|
91
|
+
* playback starts, cleared on media-item transition (a new load starts unproven); retry reloads
|
|
92
|
+
* of the same load keep it.
|
|
93
|
+
*/
|
|
94
|
+
@Volatile internal var hasPlayedCache = false
|
|
95
|
+
|
|
96
|
+
/** Last playWhenReady value emitted to JS — see [emitPlayWhenReadyChanged]. */
|
|
97
|
+
private var lastEmittedPlayWhenReady: Boolean? = null
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Emits the playWhenReady change to JS exactly once per value. Two producers exist: the
|
|
101
|
+
* synchronous clear at ENDED in [setPlaybackState] (ordered before the state / queue-ended
|
|
102
|
+
* callbacks, matching iOS) and the async media3 listener (authoritative for changes ExoPlayer
|
|
103
|
+
* makes on its own, e.g. audio focus); the guard deduplicates their overlap.
|
|
104
|
+
*/
|
|
105
|
+
internal fun emitPlayWhenReadyChanged(value: Boolean) {
|
|
106
|
+
if (lastEmittedPlayWhenReady == value) return
|
|
107
|
+
lastEmittedPlayWhenReady = value
|
|
108
|
+
callbacks?.onPlaybackPlayWhenReadyChanged(PlaybackPlayWhenReadyChangedEvent(value))
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
private lateinit var mediaFactory: MediaFactory
|
|
89
112
|
private lateinit var loadControl: DynamicLoadControl
|
|
90
113
|
private var automaticBufferManager: AutomaticBufferManager? = null
|
|
@@ -95,49 +118,39 @@ class Player(internal val context: Context) {
|
|
|
95
118
|
*/
|
|
96
119
|
@Volatile private var pendingNetworkRetry = false
|
|
97
120
|
|
|
98
|
-
private var _browser: AudioBrowser? = null
|
|
99
121
|
private var browserRegistered = CompletableDeferred<AudioBrowser>()
|
|
100
122
|
private var _coilBitmapLoader: CoilBitmapLoader? = null
|
|
101
123
|
|
|
102
124
|
/**
|
|
103
|
-
* Coil ImageLoader for SVG pre-rendering in Android Auto browse items.
|
|
104
|
-
*
|
|
125
|
+
* Coil ImageLoader for SVG pre-rendering in Android Auto browse items. Set by Service after
|
|
126
|
+
* creation.
|
|
105
127
|
*/
|
|
106
128
|
var imageLoader: coil3.ImageLoader? = null
|
|
107
129
|
|
|
130
|
+
/** Registry that maps content:// tokens to artwork URLs for the browse content provider. */
|
|
131
|
+
val browseArtworkRegistry = com.audiobrowser.browser.BrowseArtworkRegistry()
|
|
132
|
+
|
|
108
133
|
/**
|
|
109
|
-
* Set the CoilBitmapLoader for
|
|
134
|
+
* Set the CoilBitmapLoader for display-time bitmap loading. Called from Service after creation.
|
|
110
135
|
*/
|
|
111
136
|
var coilBitmapLoader: CoilBitmapLoader?
|
|
112
137
|
get() = _coilBitmapLoader
|
|
113
138
|
set(value) {
|
|
114
139
|
_coilBitmapLoader = value
|
|
115
|
-
// If browser is already set, wire up the resolver
|
|
116
|
-
_browser?.let { audioBrowser -> wireUpArtworkResolver(audioBrowser, value) }
|
|
117
140
|
}
|
|
118
141
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
value?.let { audioBrowser ->
|
|
124
|
-
// Wire up artwork URL resolver
|
|
125
|
-
// This ensures the resolver is available when onGetChildren resumes
|
|
126
|
-
wireUpArtworkResolver(audioBrowser, _coilBitmapLoader)
|
|
127
|
-
|
|
128
|
-
// NOTE: We do NOT complete browserRegistered here.
|
|
129
|
-
// Configuration (routes/tabs) must be set before Android Auto can browse.
|
|
130
|
-
// Call notifyBrowserConfigurationReady() after configuration is set.
|
|
131
|
-
}
|
|
132
|
-
}
|
|
142
|
+
// NOTE: setting the browser does NOT complete browserRegistered.
|
|
143
|
+
// Configuration (routes/tabs) must be set before Android Auto can browse.
|
|
144
|
+
// Call notifyBrowserConfigurationReady() after configuration is set.
|
|
145
|
+
var browser: AudioBrowser? = null
|
|
133
146
|
|
|
134
147
|
/**
|
|
135
|
-
* Notifies that the browser configuration is ready (routes/tabs are configured).
|
|
136
|
-
*
|
|
137
|
-
*
|
|
148
|
+
* Notifies that the browser configuration is ready (routes/tabs are configured). This should be
|
|
149
|
+
* called after setting the browser AND its configuration. Only then will Android Auto be able to
|
|
150
|
+
* browse content.
|
|
138
151
|
*/
|
|
139
152
|
fun notifyBrowserConfigurationReady() {
|
|
140
|
-
val audioBrowser =
|
|
153
|
+
val audioBrowser = browser ?: return
|
|
141
154
|
|
|
142
155
|
if (!browserRegistered.isCompleted) {
|
|
143
156
|
Timber.d("Browser configuration ready - completing deferred")
|
|
@@ -162,27 +175,6 @@ class Player(internal val context: Context) {
|
|
|
162
175
|
}
|
|
163
176
|
}
|
|
164
177
|
|
|
165
|
-
/**
|
|
166
|
-
* Wires up the artwork URL resolver between BrowserManager and CoilBitmapLoader. This enables
|
|
167
|
-
* artwork URL transformation during browse-time (Phase 2).
|
|
168
|
-
*/
|
|
169
|
-
private fun wireUpArtworkResolver(audioBrowser: AudioBrowser, loader: CoilBitmapLoader?) {
|
|
170
|
-
val browserManager = audioBrowser.browserManager
|
|
171
|
-
if (loader != null) {
|
|
172
|
-
Timber.d("Wiring up artwork URL resolver")
|
|
173
|
-
browserManager.artworkUrlResolver = { track, perRouteConfig, imageContext ->
|
|
174
|
-
loader.transformArtworkUrlForTrack(track, perRouteConfig, imageContext)
|
|
175
|
-
}
|
|
176
|
-
// Clear content cache so that content resolved before the resolver was wired up
|
|
177
|
-
// will be re-fetched with artwork transformation applied
|
|
178
|
-
browserManager.clearContentCache()
|
|
179
|
-
Timber.d("Cleared content cache after wiring artwork resolver")
|
|
180
|
-
} else {
|
|
181
|
-
Timber.d("CoilBitmapLoader not available - artwork URL transformation disabled")
|
|
182
|
-
browserManager.artworkUrlResolver = null
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
178
|
/**
|
|
187
179
|
* Suspends until the browser is registered, with a timeout.
|
|
188
180
|
*
|
|
@@ -200,133 +192,16 @@ class Player(internal val context: Context) {
|
|
|
200
192
|
throw e
|
|
201
193
|
}
|
|
202
194
|
|
|
203
|
-
/**
|
|
204
|
-
* ForwardingPlayer that intercepts external player actions and dispatches them to callbacks.
|
|
205
|
-
*
|
|
206
|
-
* This class blocks all external media item modifications to delegate control to RNAB. These
|
|
207
|
-
* overrides prevent media controllers (like Android Auto, notifications) from directly modifying
|
|
208
|
-
* the queue, ensuring all queue changes go through the RNAB API for proper state management and
|
|
209
|
-
* event handling.
|
|
210
|
-
*/
|
|
211
|
-
private inner class InterceptingPlayer(player: ExoPlayer) : ForwardingPlayer(player) {
|
|
212
|
-
|
|
213
|
-
override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
|
|
214
|
-
return super.setMediaItems(mediaItems, resetPosition)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
override fun addMediaItems(mediaItems: MutableList<MediaItem>) {
|
|
218
|
-
return super.addMediaItems(mediaItems)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
override fun addMediaItems(index: Int, mediaItems: MutableList<MediaItem>) {
|
|
222
|
-
return super.addMediaItems(index, mediaItems)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
override fun setMediaItems(
|
|
226
|
-
mediaItems: MutableList<MediaItem>,
|
|
227
|
-
startIndex: Int,
|
|
228
|
-
startPositionMs: Long,
|
|
229
|
-
) {
|
|
230
|
-
return super.setMediaItems(mediaItems, startIndex, startPositionMs)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
|
|
234
|
-
return super.setMediaItems(mediaItems)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Intercept playback controls and dispatch to callbacks or fall back to default behavior
|
|
238
|
-
override fun play() {
|
|
239
|
-
if (callbacks?.handleRemotePlay() != true) {
|
|
240
|
-
super.play()
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
override fun pause() {
|
|
245
|
-
if (callbacks?.handleRemotePause() != true) {
|
|
246
|
-
super.pause()
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
override fun seekToNext() {
|
|
251
|
-
Timber.Forest.d("InterceptingPlayer.seekToNext() called")
|
|
252
|
-
if (callbacks?.handleRemoteNext() != true) {
|
|
253
|
-
super.seekToNext()
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
override fun seekToNextMediaItem() {
|
|
258
|
-
Timber.Forest.d("InterceptingPlayer.seekToNextMediaItem() called")
|
|
259
|
-
if (callbacks?.handleRemoteNext() != true) {
|
|
260
|
-
super.seekToNextMediaItem()
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
override fun seekToPrevious() {
|
|
265
|
-
Timber.Forest.d("InterceptingPlayer.seekToPrevious() called")
|
|
266
|
-
if (callbacks?.handleRemotePrevious() != true) {
|
|
267
|
-
super.seekToPrevious()
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
override fun seekToPreviousMediaItem() {
|
|
272
|
-
Timber.Forest.d("InterceptingPlayer.seekToPreviousMediaItem() called")
|
|
273
|
-
if (callbacks?.handleRemotePrevious() != true) {
|
|
274
|
-
super.seekToPreviousMediaItem()
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
override fun seekForward() {
|
|
279
|
-
Timber.Forest.d("InterceptingPlayer.seekForward() called")
|
|
280
|
-
if (
|
|
281
|
-
callbacks?.handleRemoteJumpForward(
|
|
282
|
-
RemoteJumpForwardEvent(interval = options.forwardJumpInterval)
|
|
283
|
-
) != true
|
|
284
|
-
) {
|
|
285
|
-
super.seekForward()
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
override fun seekBack() {
|
|
290
|
-
if (
|
|
291
|
-
callbacks?.handleRemoteJumpBackward(
|
|
292
|
-
RemoteJumpBackwardEvent(interval = options.backwardJumpInterval)
|
|
293
|
-
) != true
|
|
294
|
-
) {
|
|
295
|
-
super.seekBack()
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
override fun stop() {
|
|
300
|
-
if (callbacks?.handleRemoteStop() != true) {
|
|
301
|
-
super.stop()
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
override fun seekTo(mediaItemIndex: Int, positionMs: Long) {
|
|
306
|
-
if (
|
|
307
|
-
callbacks?.handleRemoteSeek(RemoteSeekEvent(position = positionMs.toDouble() / 1000.0)) !=
|
|
308
|
-
true
|
|
309
|
-
) {
|
|
310
|
-
super.seekTo(mediaItemIndex, positionMs)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
override fun seekTo(positionMs: Long) {
|
|
315
|
-
if (
|
|
316
|
-
callbacks?.handleRemoteSeek(RemoteSeekEvent(position = positionMs.toDouble() / 1000.0)) !=
|
|
317
|
-
true
|
|
318
|
-
) {
|
|
319
|
-
super.seekTo(positionMs)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
195
|
private lateinit var playerListener: PlayerListener
|
|
325
196
|
private var cache: SimpleCache? = null
|
|
326
197
|
|
|
327
|
-
private val
|
|
328
|
-
|
|
329
|
-
|
|
198
|
+
private val progressTimer: PlaybackTimer by lazy {
|
|
199
|
+
PlaybackTimer(
|
|
200
|
+
isActive = {
|
|
201
|
+
it == PlaybackState.LOADING || it == PlaybackState.BUFFERING || it == PlaybackState.PLAYING
|
|
202
|
+
}
|
|
203
|
+
) {
|
|
204
|
+
val index = currentIndex ?: return@PlaybackTimer
|
|
330
205
|
val event =
|
|
331
206
|
PlaybackProgressUpdatedEvent(
|
|
332
207
|
position = position.toSeconds(),
|
|
@@ -338,7 +213,12 @@ class Player(internal val context: Context) {
|
|
|
338
213
|
}
|
|
339
214
|
}
|
|
340
215
|
|
|
216
|
+
private val intervalTimer: PlaybackTimer by lazy {
|
|
217
|
+
PlaybackTimer(isActive = { it == PlaybackState.PLAYING }) { callbacks?.onPlaybackInterval() }
|
|
218
|
+
}
|
|
219
|
+
|
|
341
220
|
internal var playingState: PlayingState = PlayingState(false, false)
|
|
221
|
+
private set
|
|
342
222
|
|
|
343
223
|
val currentTrack: Track?
|
|
344
224
|
get() = exoPlayer.currentMediaItem?.let { TrackFactory.fromMedia3(it) }
|
|
@@ -352,9 +232,6 @@ class Player(internal val context: Context) {
|
|
|
352
232
|
internal var playbackState: PlaybackState = PlaybackState.NONE
|
|
353
233
|
private set
|
|
354
234
|
|
|
355
|
-
/** Current now playing metadata override (null = use track metadata) */
|
|
356
|
-
private var nowPlayingOverride: NowPlayingUpdate? = null
|
|
357
|
-
|
|
358
235
|
fun getPlayback(): Playback {
|
|
359
236
|
return Playback(playbackState, playbackError)
|
|
360
237
|
}
|
|
@@ -363,9 +240,33 @@ class Player(internal val context: Context) {
|
|
|
363
240
|
return playingState
|
|
364
241
|
}
|
|
365
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Re-derives [playingState] from the current `playWhenReady` + [playbackState] and emits
|
|
245
|
+
* `onPlaybackPlayingState` when it changed. The only writer of [playingState] — called from the
|
|
246
|
+
* state machine ([setPlaybackState]) and from [PlayerListener.onPlayWhenReadyChanged], the change
|
|
247
|
+
* points of its two inputs.
|
|
248
|
+
*/
|
|
249
|
+
internal fun refreshPlayingState() {
|
|
250
|
+
val newPlayingState = PlayingStateFactory.derive(playWhenReady, playbackState)
|
|
251
|
+
if (newPlayingState == playingState) return
|
|
252
|
+
Timber.d(
|
|
253
|
+
"PlayingState changed: playing=${newPlayingState.playing}, buffering=${newPlayingState.buffering}"
|
|
254
|
+
)
|
|
255
|
+
playingState = newPlayingState
|
|
256
|
+
callbacks?.onPlaybackPlayingState(playingState)
|
|
257
|
+
}
|
|
258
|
+
|
|
366
259
|
var playWhenReady: Boolean
|
|
367
260
|
get() = exoPlayer.playWhenReady
|
|
368
261
|
set(value) {
|
|
262
|
+
// Raising the intent at STATE_ENDED must replay, not silently no-op:
|
|
263
|
+
// ExoPlayer's setPlayWhenReady(true) never restarts an ended player (the
|
|
264
|
+
// ENDED→seekToDefaultPosition replay lives in media3's media-button
|
|
265
|
+
// path, not the Player API). In the setter so every writer gets it —
|
|
266
|
+
// play(), togglePlayback() and JS setPlayWhenReady alike.
|
|
267
|
+
if (value && exoPlayer.playbackState == ExoPlayer.STATE_ENDED) {
|
|
268
|
+
exoPlayer.seekToDefaultPosition()
|
|
269
|
+
}
|
|
369
270
|
playWhenReadyCache = value
|
|
370
271
|
exoPlayer.playWhenReady = value
|
|
371
272
|
}
|
|
@@ -398,8 +299,6 @@ class Player(internal val context: Context) {
|
|
|
398
299
|
val isPlaying
|
|
399
300
|
get() = exoPlayer.isPlaying
|
|
400
301
|
|
|
401
|
-
var ratingType: RatingType = RatingType.NONE
|
|
402
|
-
|
|
403
302
|
var repeatMode: RepeatMode
|
|
404
303
|
get() = RepeatModeFactory.fromMedia3(exoPlayer.repeatMode)
|
|
405
304
|
internal set(value) {
|
|
@@ -429,8 +328,14 @@ class Player(internal val context: Context) {
|
|
|
429
328
|
.map { index -> TrackFactory.fromMedia3(exoPlayer.getMediaItemAt(index)) }
|
|
430
329
|
.toTypedArray()
|
|
431
330
|
|
|
432
|
-
|
|
433
|
-
|
|
331
|
+
/**
|
|
332
|
+
* Whether nothing follows the current item in *playback order* — shuffle- and repeat-aware via
|
|
333
|
+
* media3's own order computation (the linear `currentMediaItemIndex == count - 1` check missed a
|
|
334
|
+
* shuffled queue whose playback order ends on a different linear index). Matches iOS's
|
|
335
|
+
* `isLastInPlaybackOrder`.
|
|
336
|
+
*/
|
|
337
|
+
val isLastInPlaybackOrder: Boolean
|
|
338
|
+
get() = exoPlayer.nextMediaItemIndex == C.INDEX_UNSET
|
|
434
339
|
|
|
435
340
|
/**
|
|
436
341
|
* The source path from which the current queue was expanded (e.g., from a contextual URL). Used
|
|
@@ -467,6 +372,12 @@ class Player(internal val context: Context) {
|
|
|
467
372
|
fun setup(setupOptions: PlayerSetupOptions) {
|
|
468
373
|
Timber.Forest.d("Setting up player with new options")
|
|
469
374
|
|
|
375
|
+
nowPlaying.enabled = setupOptions.autoUpdateNowPlayingMetadata
|
|
376
|
+
// Wrap the Nitro Func into a plain suspend call so the updater (and its tests) never touch
|
|
377
|
+
// bridge types; the double-await depth is unchanged (invoke -> bridge Promise -> JS promise).
|
|
378
|
+
nowPlaying.formatter =
|
|
379
|
+
setupOptions.nowPlayingMetadataFormatter?.let { f -> { params -> f.invoke(params).await() } }
|
|
380
|
+
|
|
470
381
|
val isInitialSetup = !::exoPlayer.isInitialized
|
|
471
382
|
|
|
472
383
|
if (!isInitialSetup) {
|
|
@@ -476,6 +387,9 @@ class Player(internal val context: Context) {
|
|
|
476
387
|
}
|
|
477
388
|
|
|
478
389
|
if (setupOptions.maxCacheSize > 0) {
|
|
390
|
+
// SimpleCache locks its folder — re-setup must release the previous
|
|
391
|
+
// instance first or the constructor throws.
|
|
392
|
+
cache?.release()
|
|
479
393
|
cache =
|
|
480
394
|
SimpleCache(
|
|
481
395
|
File(context.cacheDir, "RNAB"),
|
|
@@ -489,94 +403,46 @@ class Player(internal val context: Context) {
|
|
|
489
403
|
cache = null
|
|
490
404
|
}
|
|
491
405
|
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
// Create bandwidth meter for adaptive bitrate selection in HLS/DASH
|
|
497
|
-
val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build()
|
|
498
|
-
|
|
499
|
-
loadControl = run {
|
|
500
|
-
val minBuffer = setupOptions.minBuffer.toInt()
|
|
501
|
-
val maxBuffer = setupOptions.maxBuffer.toInt()
|
|
502
|
-
val playBuffer = setupOptions.playBuffer.toInt()
|
|
503
|
-
// When automatic (rebufferBuffer is null), start at playBuffer and let AutomaticBufferManager
|
|
504
|
-
// adjust
|
|
505
|
-
// When fixed (rebufferBuffer is set), use that value
|
|
506
|
-
val playAfterRebuffer = setupOptions.rebufferBuffer?.toInt() ?: playBuffer
|
|
507
|
-
val backBuffer = setupOptions.backBuffer.toInt()
|
|
508
|
-
val config =
|
|
509
|
-
BufferConfig(
|
|
510
|
-
minBufferMs = minBuffer,
|
|
511
|
-
maxBufferMs = maxBuffer,
|
|
512
|
-
bufferForPlaybackMs = playBuffer,
|
|
513
|
-
bufferForPlaybackAfterRebufferMs = playAfterRebuffer,
|
|
514
|
-
backBufferMs = backBuffer,
|
|
515
|
-
)
|
|
516
|
-
DynamicLoadControl(initialConfig = config)
|
|
517
|
-
}
|
|
518
|
-
// Create MediaFactory with reference to browser for media URL transformation
|
|
519
|
-
// shouldRetry checks playWhenReady to avoid retrying when paused (e.g., another app took audio
|
|
520
|
-
// focus). Uses thread-safe cache since this is called from ExoPlayer's playback thread.
|
|
521
|
-
// isOnline and onRetryPending enable network-aware retry acceleration.
|
|
522
|
-
mediaFactory =
|
|
523
|
-
MediaFactory(
|
|
406
|
+
// One engine generation: load control, media factory, ExoPlayer (pure construction —
|
|
407
|
+
// see buildPlayerEngine; lifecycle and wiring stay here).
|
|
408
|
+
val engine =
|
|
409
|
+
buildPlayerEngine(
|
|
524
410
|
context,
|
|
411
|
+
setupOptions,
|
|
525
412
|
cache,
|
|
526
|
-
setupOptions.retryPolicy,
|
|
527
413
|
shouldRetry = { playWhenReadyCache },
|
|
528
414
|
isOnline = { networkMonitor.getOnline() },
|
|
529
|
-
|
|
415
|
+
hasPlayed = { hasPlayedCache },
|
|
416
|
+
onRetryPending = { exception, isNetworkError ->
|
|
530
417
|
// Track pending network retries for acceleration when connectivity returns
|
|
531
418
|
pendingNetworkRetry = isNetworkError
|
|
532
419
|
if (isNetworkError) {
|
|
533
420
|
Timber.d("Network retry pending, will accelerate on connectivity restoration")
|
|
534
421
|
}
|
|
422
|
+
reportRetryingError(exception)
|
|
535
423
|
},
|
|
536
|
-
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
exoPlayer =
|
|
542
|
-
ExoPlayer.Builder(context)
|
|
543
|
-
.setRenderersFactory(renderer)
|
|
544
|
-
.setBandwidthMeter(bandwidthMeter)
|
|
545
|
-
.setHandleAudioBecomingNoisy(setupOptions.handleAudioBecomingNoisy)
|
|
546
|
-
.setMediaSourceFactory(mediaFactory)
|
|
547
|
-
.setWakeMode(
|
|
548
|
-
when (setupOptions.wakeMode) {
|
|
549
|
-
AndroidPlayerWakeMode.NONE -> C.WAKE_MODE_NONE
|
|
550
|
-
AndroidPlayerWakeMode.LOCAL -> C.WAKE_MODE_LOCAL
|
|
551
|
-
AndroidPlayerWakeMode.NETWORK -> C.WAKE_MODE_NETWORK
|
|
552
|
-
}
|
|
553
|
-
)
|
|
554
|
-
.setLoadControl(loadControl)
|
|
555
|
-
.setName("AudioBrowser")
|
|
556
|
-
.build()
|
|
557
|
-
exoPlayer.setAudioAttributes(
|
|
558
|
-
AudioAttributes.Builder()
|
|
559
|
-
.setUsage(C.USAGE_MEDIA)
|
|
560
|
-
.setContentType(AndroidAudioContentTypeFactory.toMedia3(setupOptions.audioContentType))
|
|
561
|
-
.build(),
|
|
562
|
-
true, // handle audio focus
|
|
563
|
-
)
|
|
424
|
+
resolveMediaConfig = { url -> browser?.getMediaRequestConfig(url) },
|
|
425
|
+
)
|
|
426
|
+
loadControl = engine.loadControl
|
|
427
|
+
mediaFactory = engine.mediaFactory
|
|
428
|
+
exoPlayer = engine.exoPlayer
|
|
564
429
|
|
|
565
|
-
//
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
)
|
|
572
|
-
.setIsGaplessSupportRequired(it.gaplessSupportRequired)
|
|
573
|
-
.setIsSpeedChangeSupportRequired(it.rateChangeSupportRequired)
|
|
574
|
-
.build()
|
|
575
|
-
exoPlayer.trackSelectionParameters.audioOffloadPreferences = audioOffloadPreferences
|
|
430
|
+
// A rebuilt engine starts paused and fires no change event — sync the
|
|
431
|
+
// eager cache and tell JS, or the stale true sticks (and the emit dedupe
|
|
432
|
+
// would swallow the next real rise).
|
|
433
|
+
if (!isInitialSetup) {
|
|
434
|
+
playWhenReadyCache = false
|
|
435
|
+
emitPlayWhenReadyChanged(false)
|
|
576
436
|
}
|
|
577
437
|
|
|
578
438
|
// Recreate forwarding player with new ExoPlayer
|
|
579
|
-
forwardingPlayer =
|
|
439
|
+
forwardingPlayer =
|
|
440
|
+
InterceptingPlayer(
|
|
441
|
+
exoPlayer,
|
|
442
|
+
{ callbacks },
|
|
443
|
+
{ options },
|
|
444
|
+
keepSessionAliveOnError = setupOptions.keepSessionAliveOnError,
|
|
445
|
+
)
|
|
580
446
|
|
|
581
447
|
if (isInitialSetup) {
|
|
582
448
|
// Initial setup - create player listener and emit initial state
|
|
@@ -585,7 +451,7 @@ class Player(internal val context: Context) {
|
|
|
585
451
|
callbacks?.onPlaybackChanged(Playback(PlaybackState.NONE, null))
|
|
586
452
|
|
|
587
453
|
// Initialize equalizer with audio session ID
|
|
588
|
-
|
|
454
|
+
equalizer.initialize(exoPlayer.audioSessionId)
|
|
589
455
|
} else {
|
|
590
456
|
// Re-setup - re-add listener and update MediaSession
|
|
591
457
|
forwardingPlayer.addListener(playerListener)
|
|
@@ -620,8 +486,8 @@ class Player(internal val context: Context) {
|
|
|
620
486
|
}
|
|
621
487
|
|
|
622
488
|
/**
|
|
623
|
-
* Starts observing network connectivity changes and invokes the callback when state changes.
|
|
624
|
-
*
|
|
489
|
+
* Starts observing network connectivity changes and invokes the callback when state changes. Also
|
|
490
|
+
* handles accelerating pending network retries when connectivity is restored.
|
|
625
491
|
*
|
|
626
492
|
* @param scope The coroutine scope to use for observation
|
|
627
493
|
*/
|
|
@@ -629,6 +495,10 @@ class Player(internal val context: Context) {
|
|
|
629
495
|
networkMonitor.observeOnline(scope) { isOnline ->
|
|
630
496
|
callbacks?.onOnlineChanged(isOnline)
|
|
631
497
|
|
|
498
|
+
// Re-render now-playing so the formatter's stall classification (buffering vs offline) tracks
|
|
499
|
+
// connectivity immediately, not only on the next playback transition.
|
|
500
|
+
nowPlaying.render()
|
|
501
|
+
|
|
632
502
|
// Accelerate pending network retry when connectivity is restored
|
|
633
503
|
if (isOnline && pendingNetworkRetry) {
|
|
634
504
|
Timber.d("Network restored with pending retry, triggering immediate retry")
|
|
@@ -804,36 +674,18 @@ class Player(internal val context: Context) {
|
|
|
804
674
|
* controllers without interrupting playback.
|
|
805
675
|
*/
|
|
806
676
|
fun setActiveTrackFavorited(favorited: Boolean) {
|
|
807
|
-
val index = exoPlayer.currentMediaItemIndex
|
|
808
|
-
if (index == C.INDEX_UNSET) return
|
|
809
|
-
|
|
810
677
|
val currentTrack = this.currentTrack ?: return
|
|
811
678
|
|
|
812
|
-
//
|
|
679
|
+
// Persist to the native favorites cache regardless of player state — the
|
|
680
|
+
// user's gesture is durable even when there's no active media item to
|
|
681
|
+
// update (queue tear-down, between-track gaps, etc.).
|
|
813
682
|
currentTrack.src?.let { src -> browser?.browserManager?.updateFavorite(src, favorited) }
|
|
814
683
|
|
|
684
|
+
val index = exoPlayer.currentMediaItemIndex
|
|
685
|
+
if (index == C.INDEX_UNSET) return
|
|
686
|
+
|
|
815
687
|
// Create updated Track with new favorited state
|
|
816
|
-
val updatedTrack =
|
|
817
|
-
Track(
|
|
818
|
-
url = currentTrack.url,
|
|
819
|
-
src = currentTrack.src,
|
|
820
|
-
artwork = currentTrack.artwork,
|
|
821
|
-
artworkSource = currentTrack.artworkSource,
|
|
822
|
-
artworkCarPlayTinted = currentTrack.artworkCarPlayTinted,
|
|
823
|
-
title = currentTrack.title,
|
|
824
|
-
subtitle = currentTrack.subtitle,
|
|
825
|
-
artist = currentTrack.artist,
|
|
826
|
-
album = currentTrack.album,
|
|
827
|
-
description = currentTrack.description,
|
|
828
|
-
genre = currentTrack.genre,
|
|
829
|
-
duration = currentTrack.duration,
|
|
830
|
-
style = currentTrack.style,
|
|
831
|
-
childrenStyle = currentTrack.childrenStyle,
|
|
832
|
-
favorited = favorited,
|
|
833
|
-
groupTitle = currentTrack.groupTitle,
|
|
834
|
-
live = currentTrack.live,
|
|
835
|
-
imageRow = currentTrack.imageRow,
|
|
836
|
-
)
|
|
688
|
+
val updatedTrack = currentTrack.copy(favorited = favorited)
|
|
837
689
|
|
|
838
690
|
// Use buildUpon() on the existing MediaItem to update only the metadata
|
|
839
691
|
// This preserves internal references and avoids playback interruption
|
|
@@ -881,78 +733,152 @@ class Player(internal val context: Context) {
|
|
|
881
733
|
// MARK: - Now Playing Metadata
|
|
882
734
|
|
|
883
735
|
/**
|
|
884
|
-
*
|
|
885
|
-
*
|
|
736
|
+
* Now Playing rendering (flash/override/formatter precedence, dedupe, stale guards, artwork
|
|
737
|
+
* keying) lives in [NowPlayingUpdater]; this object is its platform surface — current playback
|
|
738
|
+
* reads plus MediaItem stamping via [replaceMediaItem], and artwork resolution via the browser.
|
|
886
739
|
*/
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
740
|
+
val nowPlaying: NowPlayingUpdater =
|
|
741
|
+
NowPlayingUpdater(
|
|
742
|
+
object : NowPlayingSurface {
|
|
743
|
+
override val currentIndex
|
|
744
|
+
get() = this@Player.currentIndex
|
|
745
|
+
|
|
746
|
+
override val currentTrack
|
|
747
|
+
get() = this@Player.currentTrack
|
|
748
|
+
|
|
749
|
+
override val playbackState
|
|
750
|
+
get() = this@Player.playbackState
|
|
751
|
+
|
|
752
|
+
override val playbackError
|
|
753
|
+
get() = this@Player.playbackError
|
|
754
|
+
|
|
755
|
+
override val playWhenReady
|
|
756
|
+
get() = exoPlayer.playWhenReady
|
|
757
|
+
|
|
758
|
+
override val isRebuffering
|
|
759
|
+
get() = loadControl.isRebuffering
|
|
760
|
+
|
|
761
|
+
override val isOnline
|
|
762
|
+
get() = networkMonitor.getOnline()
|
|
763
|
+
|
|
764
|
+
override val hasNowPlayingArtworkConfig
|
|
765
|
+
get() = browser?.browserManager?.config?.nowPlayingArtwork != null
|
|
766
|
+
|
|
767
|
+
override fun stampFields(
|
|
768
|
+
index: Int,
|
|
769
|
+
track: Track,
|
|
770
|
+
title: String?,
|
|
771
|
+
secondaryLine: String?,
|
|
772
|
+
album: String?,
|
|
773
|
+
) =
|
|
774
|
+
restampMediaItem(index, track) {
|
|
775
|
+
setTitle(title)
|
|
776
|
+
.setDisplayTitle(title)
|
|
777
|
+
.setArtist(secondaryLine)
|
|
778
|
+
// Android Auto reads DISPLAY_SUBTITLE (not ARTIST) once DISPLAY_TITLE is set, so
|
|
779
|
+
// mirror the line into `subtitle`; `artist` still drives the lock screen /
|
|
780
|
+
// notification.
|
|
781
|
+
.setSubtitle(secondaryLine)
|
|
782
|
+
.setAlbumTitle(album)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
override fun stampArtwork(index: Int, track: Track, uri: String) =
|
|
786
|
+
restampMediaItem(index, track) { setArtworkUri(uri.toUri()) }
|
|
787
|
+
|
|
788
|
+
override suspend fun resolveNowPlayingArtwork(track: Track, sizePx: Double): String? {
|
|
789
|
+
val browserManager = browser?.browserManager ?: return null
|
|
790
|
+
val config = browserManager.config.nowPlayingArtwork ?: return null
|
|
791
|
+
val resolved =
|
|
792
|
+
try {
|
|
793
|
+
browserManager.resolveArtworkUrl(track, config, ImageContext(sizePx, sizePx))
|
|
794
|
+
} catch (e: Exception) {
|
|
795
|
+
Timber.e(e, "Failed to resolve now-playing artwork for track id=${track.id}")
|
|
796
|
+
null
|
|
797
|
+
}
|
|
798
|
+
val uri = resolved?.uri?.takeIf { it.isNotEmpty() } ?: return null
|
|
799
|
+
// Remember how this URI was produced: Media3 hands it back to the bitmap loader, which
|
|
800
|
+
// re-resolves Track-first (with the nowPlayingArtwork kind, not the global artwork
|
|
801
|
+
// config).
|
|
802
|
+
browserManager.artworkResolutions.register(uri, track, config)
|
|
803
|
+
return uri
|
|
804
|
+
}
|
|
891
805
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
return NowPlayingMetadata(
|
|
898
|
-
elapsedTime = null,
|
|
899
|
-
title = override?.title ?: track.title,
|
|
900
|
-
album = track.album,
|
|
901
|
-
artist = override?.artist ?: track.artist,
|
|
902
|
-
duration = track.duration,
|
|
903
|
-
artwork = track.artwork,
|
|
904
|
-
description = track.description,
|
|
905
|
-
mediaId = track.src ?: track.url,
|
|
906
|
-
genre = track.genre,
|
|
907
|
-
rating = null, // TODO: map track.favorited to rating if needed
|
|
806
|
+
override fun emitNowPlayingChanged(metadata: NowPlayingMetadata) {
|
|
807
|
+
callbacks?.onNowPlayingChanged(metadata)
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
MainScope(),
|
|
908
811
|
)
|
|
909
|
-
}
|
|
910
812
|
|
|
911
|
-
/**
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
813
|
+
/** Republishes the item at [index] with mutated metadata, preserving uri and Track tag. */
|
|
814
|
+
private fun restampMediaItem(
|
|
815
|
+
index: Int,
|
|
816
|
+
track: Track,
|
|
817
|
+
mutate: MediaMetadata.Builder.() -> MediaMetadata.Builder,
|
|
818
|
+
) {
|
|
819
|
+
val currentMediaItem = exoPlayer.getMediaItemAt(index)
|
|
820
|
+
val updatedMediaItem =
|
|
821
|
+
currentMediaItem
|
|
822
|
+
.buildUpon()
|
|
823
|
+
.setUri(currentMediaItem.localConfiguration?.uri)
|
|
824
|
+
.setMediaMetadata(currentMediaItem.mediaMetadata.buildUpon().mutate().build())
|
|
825
|
+
.setTag(track)
|
|
826
|
+
.build()
|
|
827
|
+
exoPlayer.replaceMediaItem(index, updatedMediaItem)
|
|
917
828
|
}
|
|
918
829
|
|
|
919
830
|
/**
|
|
920
831
|
* Resets the retry timer when track changes. Called from PlayerListener.onMediaItemTransition.
|
|
921
832
|
*/
|
|
833
|
+
/**
|
|
834
|
+
* A restart from a terminal error begins a new load of the same track: fresh retry budgets and
|
|
835
|
+
* unproven playback, so the user's tap yields a visible first-connect retry window instead of a
|
|
836
|
+
* single silent attempt — the same behavior as re-selecting the track (ADR 0004). Hooked off the
|
|
837
|
+
* player error clearing: every recovery path (retry(), play(), the transport controls'
|
|
838
|
+
* idle-error recovery) goes through exoPlayer.prepare(), which clears the error.
|
|
839
|
+
*/
|
|
840
|
+
internal fun resetForNewLoad() {
|
|
841
|
+
resetRetryTimer()
|
|
842
|
+
hasPlayedCache = false
|
|
843
|
+
}
|
|
844
|
+
|
|
922
845
|
internal fun resetRetryTimer() {
|
|
923
846
|
mediaFactory.resetRetryTimer()
|
|
924
847
|
}
|
|
925
848
|
|
|
926
849
|
/**
|
|
927
|
-
*
|
|
928
|
-
*
|
|
850
|
+
* Finds the queue Track whose published artwork URI matches [uri], for display-time artwork
|
|
851
|
+
* resolution of app-supplied queue tracks that never went through browse (so they are not in the
|
|
852
|
+
* resolution registry). Must run on the main thread (ExoPlayer access).
|
|
929
853
|
*/
|
|
930
|
-
private fun
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
.setTitle(override?.title ?: track.title)
|
|
940
|
-
.setDisplayTitle(override?.title ?: track.title)
|
|
941
|
-
.setArtist(override?.artist ?: track.artist)
|
|
942
|
-
.build()
|
|
943
|
-
|
|
944
|
-
val updatedMediaItem =
|
|
945
|
-
currentMediaItem
|
|
946
|
-
.buildUpon()
|
|
947
|
-
.setUri(currentMediaItem.localConfiguration?.uri)
|
|
948
|
-
.setMediaMetadata(updatedMetadata)
|
|
949
|
-
.setTag(track)
|
|
950
|
-
.build()
|
|
951
|
-
|
|
952
|
-
exoPlayer.replaceMediaItem(index, updatedMediaItem)
|
|
854
|
+
private fun findQueueTrackByArtworkUri(uri: String): Track? {
|
|
855
|
+
for (i in 0 until exoPlayer.mediaItemCount) {
|
|
856
|
+
val item = exoPlayer.getMediaItemAt(i)
|
|
857
|
+
if (item.mediaMetadata.artworkUri?.toString() == uri) {
|
|
858
|
+
return item.localConfiguration?.tag as? Track
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return null
|
|
862
|
+
}
|
|
953
863
|
|
|
954
|
-
|
|
955
|
-
|
|
864
|
+
/**
|
|
865
|
+
* Track-first display-time artwork resolution for [CoilBitmapLoader]: registry hit
|
|
866
|
+
* (browse/now-playing-resolved URIs) → queue-tag lookup (app-supplied tracks with raw artwork) →
|
|
867
|
+
* header-only fallback for unattributable URIs (registry eviction / process-death restore). Null
|
|
868
|
+
* means "fetch the URI as-is".
|
|
869
|
+
*/
|
|
870
|
+
suspend fun resolveDisplayArtwork(uri: String, sizeHintPixels: Int?): ImageSource? {
|
|
871
|
+
val browserManager = browser?.browserManager ?: return null
|
|
872
|
+
val imageContext =
|
|
873
|
+
sizeHintPixels?.takeIf { it > 0 }?.let { ImageContext(it.toDouble(), it.toDouble()) }
|
|
874
|
+
browserManager.displayArtworkSource(uri, imageContext)?.let {
|
|
875
|
+
return it
|
|
876
|
+
}
|
|
877
|
+
withContext(Dispatchers.Main) { findQueueTrackByArtworkUri(uri) }
|
|
878
|
+
?.let { track ->
|
|
879
|
+
return browserManager.resolveArtworkUrl(track, null, imageContext)
|
|
880
|
+
}
|
|
881
|
+
return browserManager.unattributedArtworkSource(uri)
|
|
956
882
|
}
|
|
957
883
|
|
|
958
884
|
/**
|
|
@@ -975,12 +901,25 @@ class Player(internal val context: Context) {
|
|
|
975
901
|
}
|
|
976
902
|
|
|
977
903
|
fun play() {
|
|
978
|
-
|
|
904
|
+
// Through the setter so the STATE_ENDED replay handling applies
|
|
905
|
+
// (ExoPlayer.play() is only setPlayWhenReady(true)).
|
|
906
|
+
playWhenReady = true
|
|
979
907
|
if (currentTrack != null) {
|
|
908
|
+
// No-op unless the player is STATE_IDLE (ExoPlayer.prepare early-returns
|
|
909
|
+
// otherwise), so this only reconnects after a stop() or error and never
|
|
910
|
+
// re-buffers a healthy stream. Reconnecting is also how live streams
|
|
911
|
+
// rejoin the live edge on resume.
|
|
980
912
|
exoPlayer.prepare()
|
|
981
913
|
}
|
|
982
914
|
}
|
|
983
915
|
|
|
916
|
+
/** Jump to the live edge (default position) of a live item; no-op otherwise. */
|
|
917
|
+
fun seekToLiveEdge() {
|
|
918
|
+
if (exoPlayer.isCurrentMediaItemLive) {
|
|
919
|
+
exoPlayer.seekToDefaultPosition()
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
984
923
|
/**
|
|
985
924
|
* Executes a search and plays the results. Used for voice commands with structured search
|
|
986
925
|
* parameters.
|
|
@@ -990,7 +929,20 @@ class Player(internal val context: Context) {
|
|
|
990
929
|
*/
|
|
991
930
|
suspend fun playFromSearch(params: SearchParams): Boolean {
|
|
992
931
|
return try {
|
|
993
|
-
val
|
|
932
|
+
val audioBrowser = awaitBrowser()
|
|
933
|
+
// A gate blocks external-surface "play" search too — otherwise voice search is a way around
|
|
934
|
+
// the gate (mirrors the iOS play-media intent guard). Resolved per request; a gated decision
|
|
935
|
+
// fires onGate(search) and refuses.
|
|
936
|
+
val gateOutcome =
|
|
937
|
+
audioBrowser.gateDecision(
|
|
938
|
+
NativeGateRequest(reason = GateReason.SEARCH, path = null, search = params)
|
|
939
|
+
)
|
|
940
|
+
if (gateOutcome.gated) {
|
|
941
|
+
Timber.i("playFromSearch refused — request is gated")
|
|
942
|
+
audioBrowser.onGate(GateEvent(GateReason.SEARCH))
|
|
943
|
+
return false
|
|
944
|
+
}
|
|
945
|
+
val browserManager = audioBrowser.browserManager
|
|
994
946
|
|
|
995
947
|
Timber.d(
|
|
996
948
|
"Executing voice search: mode=${params.mode}, query='${params.query}', artist='${params.artist}', album='${params.album}'"
|
|
@@ -1019,7 +971,10 @@ class Player(internal val context: Context) {
|
|
|
1019
971
|
}
|
|
1020
972
|
|
|
1021
973
|
fun pause() {
|
|
1022
|
-
|
|
974
|
+
// Through the setter so the eager playWhenReadyCache write holds for the
|
|
975
|
+
// drop direction too — the retry policy reads it off-main before
|
|
976
|
+
// ExoPlayer's listener round-trip syncs it.
|
|
977
|
+
playWhenReady = false
|
|
1023
978
|
}
|
|
1024
979
|
|
|
1025
980
|
fun togglePlayback() {
|
|
@@ -1036,8 +991,11 @@ class Player(internal val context: Context) {
|
|
|
1036
991
|
* required for playback.
|
|
1037
992
|
*/
|
|
1038
993
|
fun stop() {
|
|
1039
|
-
|
|
1040
|
-
|
|
994
|
+
// State first so the machine's STOPPED guard suppresses the PAUSED transition
|
|
995
|
+
// from the playWhenReady drop below; through setPlaybackState so the change
|
|
996
|
+
// is emitted (direct field assignment bypasses events — see its docstring).
|
|
997
|
+
setPlaybackState(PlaybackState.STOPPED)
|
|
998
|
+
playWhenReady = false
|
|
1041
999
|
exoPlayer.stop()
|
|
1042
1000
|
}
|
|
1043
1001
|
|
|
@@ -1051,7 +1009,15 @@ class Player(internal val context: Context) {
|
|
|
1051
1009
|
* use [pause].
|
|
1052
1010
|
*/
|
|
1053
1011
|
fun destroy() {
|
|
1012
|
+
// Cancel without a final save: the loop would tick against the released
|
|
1013
|
+
// player and persist position 0 over the real resumption position.
|
|
1014
|
+
playbackStateStore.cancelPeriodicSave()
|
|
1015
|
+
// An armed timer's Runnable would outlive the release and fire pause() into the
|
|
1016
|
+
// released player. Cleared here, while a mid-ramp fade can still restore volume.
|
|
1017
|
+
sleepTimer.clear()
|
|
1054
1018
|
stop()
|
|
1019
|
+
nowPlaying.destroy()
|
|
1020
|
+
mediaSessionCallback.destroy()
|
|
1055
1021
|
forwardingPlayer.removeListener(playerListener)
|
|
1056
1022
|
automaticBufferManager?.detach()
|
|
1057
1023
|
automaticBufferManager = null
|
|
@@ -1059,8 +1025,7 @@ class Player(internal val context: Context) {
|
|
|
1059
1025
|
cache?.release()
|
|
1060
1026
|
cache = null
|
|
1061
1027
|
networkMonitor.destroy()
|
|
1062
|
-
|
|
1063
|
-
equalizerManager = null
|
|
1028
|
+
equalizer.release()
|
|
1064
1029
|
}
|
|
1065
1030
|
|
|
1066
1031
|
fun seekTo(duration: Long, unit: TimeUnit) {
|
|
@@ -1073,6 +1038,57 @@ class Player(internal val context: Context) {
|
|
|
1073
1038
|
exoPlayer.seekTo(positionMs)
|
|
1074
1039
|
}
|
|
1075
1040
|
|
|
1041
|
+
/**
|
|
1042
|
+
* Invalidates in-flight [reportRetryingError] posts: bumped on track change so a report
|
|
1043
|
+
* classified for the outgoing track can't land on the incoming one's spinner. Written on main,
|
|
1044
|
+
* read on ExoPlayer's loader thread.
|
|
1045
|
+
*/
|
|
1046
|
+
@Volatile private var retryingErrorGeneration = 0
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Surfaces a load failure the retry policy is still working on: attached to the current
|
|
1050
|
+
* (non-terminal) state via [Callbacks.onPlaybackChanged], so UIs can show the cause over the
|
|
1051
|
+
* spinner. Not an ERROR transition, and not a [Callbacks.onPlaybackError] — that event stays
|
|
1052
|
+
* reserved for terminal errors. Called from ExoPlayer's loader thread, hence the main hop.
|
|
1053
|
+
*/
|
|
1054
|
+
private fun reportRetryingError(exception: IOException) {
|
|
1055
|
+
val error = PlaybackErrorClassifier.retryingLoadError(exception, networkMonitor.getOnline())
|
|
1056
|
+
val generation = retryingErrorGeneration
|
|
1057
|
+
mainHandler.post {
|
|
1058
|
+
// The track changed while this post was in flight — the error belongs to the old one.
|
|
1059
|
+
if (generation != retryingErrorGeneration) return@post
|
|
1060
|
+
// Only attach while the failure is what the listener is looking at. During PLAYING/READY
|
|
1061
|
+
// buffered audio is still audibly fine, and ExoPlayer's in-place retry can succeed without
|
|
1062
|
+
// any state change — an advisory attached there would never clear.
|
|
1063
|
+
if (
|
|
1064
|
+
playbackState != PlaybackState.LOADING &&
|
|
1065
|
+
playbackState != PlaybackState.BUFFERING &&
|
|
1066
|
+
playbackState != PlaybackState.PAUSED
|
|
1067
|
+
) {
|
|
1068
|
+
return@post
|
|
1069
|
+
}
|
|
1070
|
+
// Each failed attempt re-reports; identical repeats add nothing (and would
|
|
1071
|
+
// re-render now-playing every backoff tick).
|
|
1072
|
+
if (playbackError == error) return@post
|
|
1073
|
+
playbackError = error
|
|
1074
|
+
callbacks?.onPlaybackChanged(Playback(playbackState, error))
|
|
1075
|
+
nowPlaying.render()
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* Drops a retrying error whose track is gone. The new track may keep the state on
|
|
1081
|
+
* LOADING/BUFFERING — no state-change emit — so the clear is emitted here, or the old track's
|
|
1082
|
+
* error would linger over the new one's spinner.
|
|
1083
|
+
*/
|
|
1084
|
+
internal fun clearRetryingError() {
|
|
1085
|
+
retryingErrorGeneration++
|
|
1086
|
+
if (playbackError?.retrying != true) return
|
|
1087
|
+
playbackError = null
|
|
1088
|
+
callbacks?.onPlaybackChanged(Playback(playbackState, null))
|
|
1089
|
+
nowPlaying.render()
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1076
1092
|
/**
|
|
1077
1093
|
* Updates the player state and emits a state change event if the state has changed. Only emits an
|
|
1078
1094
|
* event if the new state differs from the current state.
|
|
@@ -1088,39 +1104,78 @@ class Player(internal val context: Context) {
|
|
|
1088
1104
|
val oldState = playbackState
|
|
1089
1105
|
playbackState = state
|
|
1090
1106
|
|
|
1107
|
+
// Audio rendered: this load has proven itself — retries from here on get
|
|
1108
|
+
// the retry policy's full recovery budget, not the short first-connect one.
|
|
1109
|
+
if (state == PlaybackState.PLAYING) {
|
|
1110
|
+
hasPlayedCache = true
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// A natural end exhausts the play intent — nothing is left to play. Keeping
|
|
1114
|
+
// playWhenReady true inverted the play/pause toggle, held audio focus forever
|
|
1115
|
+
// (ExoPlayer abandons it only at IDLE or on the intent dropping), and kept the
|
|
1116
|
+
// periodic position save running. Cleared and emitted before the state /
|
|
1117
|
+
// queue-ended callbacks so JS observes the native order (intent → state →
|
|
1118
|
+
// queueEnded); the media3 listener's later echo is deduplicated by
|
|
1119
|
+
// emitPlayWhenReadyChanged. The state machine's ENDED guard keeps the drop
|
|
1120
|
+
// from re-reporting the state as PAUSED.
|
|
1121
|
+
if (state == PlaybackState.ENDED && playWhenReady) {
|
|
1122
|
+
playWhenReady = false
|
|
1123
|
+
emitPlayWhenReadyChanged(false)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// The last track fires no AUTO media-item transition, so an armed
|
|
1127
|
+
// end-of-track sleep timer would stay set forever and pause the next
|
|
1128
|
+
// track played instead.
|
|
1129
|
+
if (state == PlaybackState.ENDED) {
|
|
1130
|
+
sleepTimer.onTrackEnd()
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1091
1133
|
// Clear error when transitioning away from error state
|
|
1092
1134
|
if (oldState == PlaybackState.ERROR) {
|
|
1093
1135
|
playbackError = null
|
|
1094
1136
|
callbacks?.onPlaybackError(null)
|
|
1095
1137
|
}
|
|
1096
1138
|
|
|
1139
|
+
// A retrying error rides along through LOADING/BUFFERING/PAUSED; a state that
|
|
1140
|
+
// proves recovery (READY/PLAYING — data flowed) or abandons the attempt clears
|
|
1141
|
+
// it, before the Playback below is built so the emitted event is already clean.
|
|
1142
|
+
if (
|
|
1143
|
+
playbackError?.retrying == true &&
|
|
1144
|
+
state != PlaybackState.LOADING &&
|
|
1145
|
+
state != PlaybackState.BUFFERING &&
|
|
1146
|
+
state != PlaybackState.PAUSED
|
|
1147
|
+
) {
|
|
1148
|
+
playbackError = null
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1097
1151
|
val playback = Playback(state, playbackError)
|
|
1098
1152
|
callbacks?.onPlaybackChanged(playback)
|
|
1099
1153
|
|
|
1100
|
-
//
|
|
1154
|
+
// Re-render the now-playing on every state change: the metadata formatter receives
|
|
1155
|
+
// `playbackState` (plus isRebuffering / isOnline / error), so any transition can change its
|
|
1156
|
+
// output — the live song while paused, a "Reconnecting…" line on a rebuffer, an offline/error
|
|
1157
|
+
// line. The publish-dedupe in applyNowPlayingFields drops redundant updates, so re-running
|
|
1158
|
+
// through the rapid startup sequence (none→loading→buffering→ready→playing) is cheap.
|
|
1159
|
+
nowPlaying.render()
|
|
1160
|
+
|
|
1161
|
+
// Emit queue ended event when playback ends on the last track. Repeat modes never
|
|
1162
|
+
// conceptually end the queue (matches web's endsQueue()): an ENDED that surfaces while
|
|
1163
|
+
// repeating (e.g. a seek to the end) must not read as "playlist over".
|
|
1101
1164
|
// This coupling ensures queue ended events are always triggered consistently with state
|
|
1102
1165
|
// changes
|
|
1103
|
-
if (state == PlaybackState.ENDED &&
|
|
1166
|
+
if (state == PlaybackState.ENDED && isLastInPlaybackOrder && repeatMode == RepeatMode.OFF) {
|
|
1104
1167
|
currentIndex?.let { index ->
|
|
1105
1168
|
val event =
|
|
1106
1169
|
PlaybackQueueEndedEvent(track = index.toDouble(), position = position.toSeconds())
|
|
1107
1170
|
callbacks?.onPlaybackQueueEnded(event)
|
|
1108
1171
|
}
|
|
1109
|
-
// Reset saved position to 0 so resumption starts from beginning
|
|
1110
|
-
|
|
1111
|
-
playbackStateStore.savePositionZero()
|
|
1112
|
-
}
|
|
1172
|
+
// Reset saved position to 0 so resumption starts from beginning
|
|
1173
|
+
playbackStateStore.savePositionZero()
|
|
1113
1174
|
}
|
|
1114
1175
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
Timber.d(
|
|
1119
|
-
"PlayingState changed: playing=${newPlayingState.playing}, buffering=${newPlayingState.buffering}"
|
|
1120
|
-
)
|
|
1121
|
-
playingState = newPlayingState
|
|
1122
|
-
callbacks?.onPlaybackPlayingState(playingState)
|
|
1123
|
-
}
|
|
1176
|
+
progressTimer.onPlaybackStateChanged(state)
|
|
1177
|
+
intervalTimer.onPlaybackStateChanged(state)
|
|
1178
|
+
refreshPlayingState()
|
|
1124
1179
|
}
|
|
1125
1180
|
}
|
|
1126
1181
|
|
|
@@ -1130,7 +1185,11 @@ class Player(internal val context: Context) {
|
|
|
1130
1185
|
* @param interval The interval in seconds, or null to disable progress updates
|
|
1131
1186
|
*/
|
|
1132
1187
|
fun setProgressUpdateInterval(interval: Double?) {
|
|
1133
|
-
|
|
1188
|
+
progressTimer.setInterval(interval)
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
fun setPlaybackIntervalEnabled(enabled: Boolean) {
|
|
1192
|
+
intervalTimer.setInterval(if (enabled) 1.0 else null)
|
|
1134
1193
|
}
|
|
1135
1194
|
|
|
1136
1195
|
/**
|
|
@@ -1149,8 +1208,6 @@ class Player(internal val context: Context) {
|
|
|
1149
1208
|
|
|
1150
1209
|
// Check what changed
|
|
1151
1210
|
val skipSilenceChanged = previousOptions.skipSilence != options.skipSilence
|
|
1152
|
-
val ratingTypeChanged = previousOptions.ratingType != options.ratingType
|
|
1153
|
-
val shuffleChanged = previousOptions.shuffle != options.shuffle
|
|
1154
1211
|
val progressUpdateEventIntervalChanged =
|
|
1155
1212
|
previousOptions.progressUpdateEventInterval != options.progressUpdateEventInterval
|
|
1156
1213
|
val forwardJumpIntervalChanged =
|
|
@@ -1165,8 +1222,6 @@ class Player(internal val context: Context) {
|
|
|
1165
1222
|
|
|
1166
1223
|
val hasChanged =
|
|
1167
1224
|
skipSilenceChanged ||
|
|
1168
|
-
ratingTypeChanged ||
|
|
1169
|
-
shuffleChanged ||
|
|
1170
1225
|
progressUpdateEventIntervalChanged ||
|
|
1171
1226
|
forwardJumpIntervalChanged ||
|
|
1172
1227
|
backwardJumpIntervalChanged ||
|
|
@@ -1179,12 +1234,10 @@ class Player(internal val context: Context) {
|
|
|
1179
1234
|
skipSilence = options.skipSilence
|
|
1180
1235
|
}
|
|
1181
1236
|
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
if (shuffleChanged) {
|
|
1187
|
-
shuffleMode = options.shuffle
|
|
1237
|
+
if (capabilitiesChanged) {
|
|
1238
|
+
// The `favorite` capability is the single favoriting switch — propagate
|
|
1239
|
+
// its match mode to the browser so it can hydrate row hearts.
|
|
1240
|
+
browser?.browserManager?.setFavoriteMatch(options.capabilities.favoriteMatch)
|
|
1188
1241
|
}
|
|
1189
1242
|
|
|
1190
1243
|
if (progressUpdateEventIntervalChanged) {
|
|
@@ -1241,187 +1294,6 @@ class Player(internal val context: Context) {
|
|
|
1241
1294
|
return networkMonitor.getOnline()
|
|
1242
1295
|
}
|
|
1243
1296
|
|
|
1244
|
-
/** Initializes the equalizer with the player's audio session ID. */
|
|
1245
|
-
private fun initializeEqualizer() {
|
|
1246
|
-
val audioSessionId = exoPlayer.audioSessionId
|
|
1247
|
-
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
|
1248
|
-
Timber.d("Skipping equalizer init - no audio session yet, will init on first playback")
|
|
1249
|
-
return
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
try {
|
|
1253
|
-
equalizerManager =
|
|
1254
|
-
EqualizerManager(audioSessionId).apply {
|
|
1255
|
-
setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
|
|
1256
|
-
}
|
|
1257
|
-
Timber.d("Equalizer initialized with session ID: $audioSessionId")
|
|
1258
|
-
} catch (e: Exception) {
|
|
1259
|
-
Timber.e(e, "Failed to initialize equalizer")
|
|
1260
|
-
equalizerManager = null
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* Reinitializes the equalizer when the audio session ID changes. Preserves current settings
|
|
1266
|
-
* (enabled state, preset, or custom levels). Also handles first-time initialization when the
|
|
1267
|
-
* equalizer was skipped at startup due to unset audio session ID.
|
|
1268
|
-
*/
|
|
1269
|
-
internal fun reinitializeEqualizer(newAudioSessionId: Int) {
|
|
1270
|
-
val oldManager = equalizerManager
|
|
1271
|
-
|
|
1272
|
-
// First-time initialization (skipped at startup because session was unset)
|
|
1273
|
-
if (oldManager == null) {
|
|
1274
|
-
Timber.d("First-time equalizer initialization with session ID: $newAudioSessionId")
|
|
1275
|
-
try {
|
|
1276
|
-
equalizerManager =
|
|
1277
|
-
EqualizerManager(newAudioSessionId).apply {
|
|
1278
|
-
setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
|
|
1279
|
-
}
|
|
1280
|
-
Timber.d("Equalizer initialized with session ID: $newAudioSessionId")
|
|
1281
|
-
} catch (e: Exception) {
|
|
1282
|
-
Timber.e(e, "Failed to initialize equalizer")
|
|
1283
|
-
equalizerManager = null
|
|
1284
|
-
}
|
|
1285
|
-
return
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// Same session ID, no need to reinitialize
|
|
1289
|
-
if (oldManager.audioSessionId == newAudioSessionId) {
|
|
1290
|
-
return
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
try {
|
|
1294
|
-
// Capture current settings before releasing old equalizer
|
|
1295
|
-
val currentSettings = oldManager.getSettings()
|
|
1296
|
-
|
|
1297
|
-
// Release old equalizer
|
|
1298
|
-
oldManager.release()
|
|
1299
|
-
|
|
1300
|
-
// Create new equalizer with new session ID
|
|
1301
|
-
equalizerManager =
|
|
1302
|
-
EqualizerManager(newAudioSessionId).apply {
|
|
1303
|
-
setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
// Restore previous settings if available
|
|
1307
|
-
currentSettings?.let { settings ->
|
|
1308
|
-
if (settings.activePreset != null) {
|
|
1309
|
-
// Restore preset
|
|
1310
|
-
equalizerManager?.setPreset(settings.activePreset)
|
|
1311
|
-
} else if (settings.enabled) {
|
|
1312
|
-
// Restore custom levels
|
|
1313
|
-
equalizerManager?.setLevels(settings.bandLevels)
|
|
1314
|
-
}
|
|
1315
|
-
// Restore enabled state
|
|
1316
|
-
equalizerManager?.setEnabled(settings.enabled)
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
Timber.d("Equalizer reinitialized for new session ID: $newAudioSessionId")
|
|
1320
|
-
} catch (e: Exception) {
|
|
1321
|
-
Timber.e(e, "Failed to reinitialize equalizer")
|
|
1322
|
-
equalizerManager = null
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
/**
|
|
1327
|
-
* Gets the current equalizer settings.
|
|
1328
|
-
*
|
|
1329
|
-
* @return Current equalizer settings or null if not available
|
|
1330
|
-
*/
|
|
1331
|
-
fun getEqualizerSettings(): com.margelo.nitro.audiobrowser.EqualizerSettings? {
|
|
1332
|
-
return equalizerManager?.getSettings()
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
/**
|
|
1336
|
-
* Enables or disables the equalizer.
|
|
1337
|
-
*
|
|
1338
|
-
* @param enabled true to enable, false to disable
|
|
1339
|
-
*/
|
|
1340
|
-
fun setEqualizerEnabled(enabled: Boolean) {
|
|
1341
|
-
equalizerManager?.setEnabled(enabled)
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
/**
|
|
1345
|
-
* Applies a preset to the equalizer.
|
|
1346
|
-
*
|
|
1347
|
-
* @param preset Name of the preset to apply
|
|
1348
|
-
*/
|
|
1349
|
-
fun setEqualizerPreset(preset: String) {
|
|
1350
|
-
equalizerManager?.setPreset(preset)
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
/**
|
|
1354
|
-
* Sets custom band levels for the equalizer.
|
|
1355
|
-
*
|
|
1356
|
-
* @param levels Array of level values in millibels for each band
|
|
1357
|
-
*/
|
|
1358
|
-
fun setEqualizerLevels(levels: DoubleArray) {
|
|
1359
|
-
equalizerManager?.setLevels(levels)
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
// MARK: - Sleep Timer
|
|
1363
|
-
|
|
1364
|
-
/**
|
|
1365
|
-
* Gets the current sleep timer state.
|
|
1366
|
-
*
|
|
1367
|
-
* @return Sleep timer state or null if no timer is active
|
|
1368
|
-
*/
|
|
1369
|
-
fun getSleepTimer(): NitroSleepTimer {
|
|
1370
|
-
return when {
|
|
1371
|
-
sleepTimer.time != null -> {
|
|
1372
|
-
NitroSleepTimer.create(SleepTimerTime(sleepTimer.time!!))
|
|
1373
|
-
}
|
|
1374
|
-
sleepTimer.sleepWhenPlayedToEnd -> {
|
|
1375
|
-
NitroSleepTimer.create(SleepTimerEndOfTrack(true))
|
|
1376
|
-
}
|
|
1377
|
-
else -> {
|
|
1378
|
-
NitroSleepTimer.create(NullType.NULL)
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
/**
|
|
1384
|
-
* Sets a sleep timer to stop playback after the specified duration.
|
|
1385
|
-
*
|
|
1386
|
-
* @param seconds Number of seconds until playback stops
|
|
1387
|
-
*/
|
|
1388
|
-
fun setSleepTimer(seconds: Double) {
|
|
1389
|
-
sleepTimer.sleepAfter(seconds)
|
|
1390
|
-
callbacks?.onSleepTimerChanged(getSleepTimer())
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
/** Sets a sleep timer to stop playback when the current track finishes playing. */
|
|
1394
|
-
fun setSleepTimerToEndOfTrack() {
|
|
1395
|
-
sleepTimer.sleepWhenPlayedToEnd()
|
|
1396
|
-
callbacks?.onSleepTimerChanged(getSleepTimer())
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
/**
|
|
1400
|
-
* Clears the active sleep timer.
|
|
1401
|
-
*
|
|
1402
|
-
* @return true if a timer was cleared, false if no timer was active
|
|
1403
|
-
*/
|
|
1404
|
-
fun clearSleepTimer(): Boolean {
|
|
1405
|
-
val wasRunning = sleepTimer.clear()
|
|
1406
|
-
if (wasRunning) {
|
|
1407
|
-
callbacks?.onSleepTimerChanged(NitroSleepTimer.create(NullType.NULL))
|
|
1408
|
-
}
|
|
1409
|
-
return wasRunning
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
/**
|
|
1413
|
-
* Checks if the sleep timer is set to end on track completion and stops playback if so. Called
|
|
1414
|
-
* when a track naturally finishes playing.
|
|
1415
|
-
*/
|
|
1416
|
-
internal fun checkSleepTimerOnTrackEnd() {
|
|
1417
|
-
if (sleepTimer.sleepWhenPlayedToEnd) {
|
|
1418
|
-
Timber.d("Sleep timer triggered on track end, stopping playback")
|
|
1419
|
-
sleepTimer.clear()
|
|
1420
|
-
stop()
|
|
1421
|
-
callbacks?.onSleepTimerChanged(NitroSleepTimer.create(NullType.NULL))
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
1297
|
/**
|
|
1426
1298
|
* Validates that an index is within bounds [0, trackCount).
|
|
1427
1299
|
*
|
|
@@ -1469,6 +1341,10 @@ class Player(internal val context: Context) {
|
|
|
1469
1341
|
mediaSessionCallback.notifyContentChanged(path)
|
|
1470
1342
|
}
|
|
1471
1343
|
|
|
1344
|
+
fun invalidateAllContent() {
|
|
1345
|
+
mediaSessionCallback.invalidateAllContent()
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1472
1348
|
/**
|
|
1473
1349
|
* Returns the recommended artwork size in pixels from the connected media browser (e.g., Android
|
|
1474
1350
|
* Auto), or null if not provided.
|