react-native-audio-browser 0.1.1 → 0.2.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AudioBrowser.podspec +1 -1
- package/README.md +184 -2
- package/android/build.gradle +17 -5
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +18 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +473 -158
- package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
- package/android/src/main/java/com/audiobrowser/Callbacks.kt +4 -6
- package/android/src/main/java/com/audiobrowser/Service.kt +67 -18
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +37 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +34 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +442 -178
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +299 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +79 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +41 -4
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +42 -0
- package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +35 -0
- package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +109 -97
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +6 -10
- package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +40 -8
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +40 -46
- package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +4 -1
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +172 -0
- package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +18 -1
- package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +153 -0
- package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +141 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +15 -3
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +422 -134
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +248 -404
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +347 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +129 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +85 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +25 -5
- package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +56 -0
- package/android/src/main/java/com/audiobrowser/player/Player.kt +475 -577
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +20 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +130 -0
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +190 -67
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +118 -53
- package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +37 -3
- package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +80 -0
- package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +50 -0
- package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +85 -40
- package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +81 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +156 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +109 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +37 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +55 -0
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +265 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +161 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +28 -0
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +24 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +49 -0
- package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +36 -0
- package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +23 -300
- package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +13 -23
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
- package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +4 -3
- package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +211 -0
- package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +58 -0
- package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +2 -1
- package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +14 -0
- package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +6 -111
- package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +22 -8
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +112 -80
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +165 -0
- package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +46 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +39 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +114 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +141 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +4 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +177 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +72 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +203 -0
- package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +56 -0
- package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +216 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +442 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +190 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +198 -0
- package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +137 -0
- package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +36 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +268 -0
- package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +68 -0
- package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +193 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +186 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +158 -0
- package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +18 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +163 -0
- package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +28 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +53 -0
- package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +58 -0
- package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +57 -0
- package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +40 -0
- package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +34 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +50 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +63 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +35 -0
- package/android/src/test/resources/certs/isrg-root-x1.pem +31 -0
- package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
- package/android/src/test/resources/certs/letsencrypt-r13.pem +29 -0
- package/android/src/test/resources/certs/stationplaylist-leaf.pem +34 -0
- package/ios/Browser/BrowserConfig.swift +41 -11
- package/ios/Browser/BrowserManager+URLResolution.swift +213 -93
- package/ios/Browser/BrowserManager.swift +303 -135
- package/ios/Browser/BrowserPathHelper.swift +32 -3
- package/ios/Browser/JsonModels.swift +287 -66
- package/ios/Browser/MediaResolveComposer.swift +114 -0
- package/ios/Browser/SearchDrillIn.swift +44 -0
- package/ios/Browser/SectionScope.swift +35 -0
- package/ios/Browser/TabBarEntries.swift +15 -0
- package/ios/CarPlay/CPInterfaceController+Safe.swift +126 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
- package/ios/CarPlay/CarPlayController.swift +739 -149
- package/ios/CarPlay/CarPlayImageLoader.swift +57 -8
- package/ios/CarPlay/CarPlayListItemFactory.swift +44 -35
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +180 -36
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +19 -0
- package/ios/CarPlay/RNABAudioBrowser.h +15 -10
- package/ios/CarPlay/RNABAudioBrowser.m +15 -14
- package/ios/CarPlay/RNABCarPlaySceneDelegate.m +2 -0
- package/ios/CarPlay/RNABMediaAddHandler.swift +75 -0
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +69 -0
- package/ios/CarPlay/RNABMediaIntentHandler.swift +53 -43
- package/ios/Extension/Capability+RemoteCommand.swift +7 -9
- package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/NavigationError+Conversion.swift +8 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +20 -0
- package/ios/Extension/RequestConfig+Copying.swift +31 -0
- package/ios/Extension/ResolvedTrack+Copying.swift +10 -4
- package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Track+AVPlayer.swift +4 -4
- package/ios/Extension/Track+Copying.swift +9 -3
- package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
- package/ios/Extension/Variant+Artwork.swift +49 -0
- package/ios/HybridAudioBrowser.swift +805 -197
- package/ios/Model/NitroTypeStubs.swift +299 -224
- package/ios/Model/PlayerUpdateOptions.swift +23 -23
- package/ios/Model/RemoteCommand.swift +24 -6
- package/ios/Model/TrackPlayerError.swift +82 -18
- package/ios/NowPlayingInfo/NowPlayingInfoController.swift +99 -121
- package/ios/NowPlayingInfo/SilentAudioPrimer.swift +73 -0
- package/ios/Observer/PlayerItemNotificationObserver.swift +45 -5
- package/ios/Observer/PlayerItemPropertyObserver.swift +27 -4
- package/ios/Observer/PlayerTimeObserver.swift +3 -53
- package/ios/Option/SessionCategories.swift +34 -11
- package/ios/Player/AirPlayStallJudgement.swift +25 -0
- package/ios/Player/EndOfTrackJudgement.swift +32 -0
- package/ios/Player/LoadSeekCoordinator.swift +16 -1
- package/ios/Player/MediaIntentCriteria.swift +139 -0
- package/ios/Player/MediaLoader.swift +31 -11
- package/ios/Player/NowPlayingUpdater.swift +119 -26
- package/ios/Player/PersistedPlaybackState.swift +11 -0
- package/ios/Player/PlaybackCoordinator.swift +285 -51
- package/ios/Player/PlaybackCoordinatorCallbacks.swift +6 -1
- package/ios/Player/PlaybackEffectHandler.swift +19 -9
- package/ios/Player/PlaybackErrorHandler.swift +98 -14
- package/ios/Player/PlaybackStateMachine.swift +18 -13
- package/ios/Player/PlaybackStateStore.swift +27 -0
- package/ios/Player/PlaybackTimer.swift +61 -0
- package/ios/Player/PlayingStateManager.swift +3 -2
- package/ios/Player/QueueManager.swift +77 -64
- package/ios/Player/RetryManager.swift +175 -38
- package/ios/Player/ShuffleOrder.swift +10 -0
- package/ios/Player/SleepTimerHandling.swift +8 -0
- package/ios/Player/SleepTimerManager.swift +72 -11
- package/ios/Player/VolumeFader.swift +77 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +40 -21
- package/ios/Tests/AirPlayStallJudgementTests.swift +52 -0
- package/ios/Tests/BrowserPathHelperTests.swift +28 -0
- package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
- package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
- package/ios/Tests/EndOfTrackJudgementTests.swift +35 -0
- package/ios/Tests/JsonModelsTests.swift +58 -0
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +40 -7
- package/ios/Tests/MediaIntentCriteriaTests.swift +170 -0
- package/ios/Tests/MediaLoaderAssetOptionsTests.swift +46 -0
- package/ios/Tests/MediaLoaderTests.swift +32 -20
- package/ios/Tests/MediaResolveTests.swift +126 -0
- package/ios/Tests/OnceValueTests.swift +79 -0
- package/ios/Tests/PlaybackCoordinatorTests.swift +787 -13
- package/ios/Tests/PlaybackErrorHandlerTests.swift +383 -47
- package/ios/Tests/PlaybackStateMachineTests.swift +33 -8
- package/ios/Tests/PlaybackStateStoreTests.swift +48 -0
- package/ios/Tests/PlaybackTimerTests.swift +55 -0
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/PlayingStateManagerTests.swift +57 -0
- package/ios/Tests/QueueManagerTests.swift +195 -18
- package/ios/Tests/RemoteCommandControllerTests.swift +156 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +15 -8
- package/ios/Tests/RetryManagerTests.swift +178 -0
- package/ios/Tests/SearchDrillInTests.swift +63 -0
- package/ios/Tests/SectionScopeTests.swift +82 -0
- package/ios/Tests/SleepTimerManagerTests.swift +107 -0
- package/ios/Tests/Support/ManualSleepTimerScheduler.swift +47 -0
- package/ios/Tests/Support/TestTypes.swift +61 -20
- package/ios/Tests/TabBarEntriesTests.swift +71 -0
- package/ios/Tests/TrackSelectorTests.swift +15 -15
- package/ios/TrackPlayer.swift +385 -58
- package/ios/TrackPlayerCallbacks.swift +5 -44
- package/ios/TrackSelector.swift +5 -5
- package/ios/Util/ArtworkImageFetcher.swift +4 -2
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +7 -3
- package/ios/Util/OnceValue.swift +34 -0
- package/lib/commonjs/AudioBrowser.js +0 -11
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +227 -0
- package/lib/commonjs/features/browser-config.js.map +1 -0
- package/lib/commonjs/features/browser.js +60 -159
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +42 -0
- package/lib/commonjs/features/carConnection.js.map +1 -0
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +37 -5
- package/lib/commonjs/features/errors.js.map +1 -1
- package/lib/commonjs/features/favorites.js +1 -1
- package/lib/commonjs/features/gate.js +164 -0
- package/lib/commonjs/features/gate.js.map +1 -0
- package/lib/commonjs/features/index.js +22 -0
- package/lib/commonjs/features/index.js.map +1 -1
- package/lib/commonjs/features/metadata.js +20 -3
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +36 -1
- package/lib/commonjs/features/nowPlaying.js.map +1 -1
- package/lib/commonjs/features/output.js +37 -23
- package/lib/commonjs/features/output.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +9 -0
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/index.js +11 -0
- package/lib/commonjs/features/playback/index.js.map +1 -1
- package/lib/commonjs/features/playback/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +29 -13
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +86 -0
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/commonjs/features/playback/volume.js +2 -2
- package/lib/commonjs/features/player/options.js +79 -29
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +256 -3
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +21 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +1 -1
- package/lib/commonjs/features/queue/queue.js +12 -5
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +14 -104
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +22 -5
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/native.js +5 -0
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.web.js +4 -1
- package/lib/commonjs/native.web.js.map +1 -1
- package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
- package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
- package/lib/commonjs/utils/LazyNativeEmitter.js +8 -8
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +0 -3
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/artwork.js +31 -0
- package/lib/commonjs/utils/artwork.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +302 -125
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +71 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +34 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/Player.js +90 -26
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +21 -0
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +273 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +158 -0
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js +75 -0
- package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/index.js +13 -5
- package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +176 -50
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js +3 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +11 -9
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +22 -0
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +41 -0
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js +20 -0
- package/lib/commonjs/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/commonjs/web/http/HttpClient.js +4 -23
- package/lib/commonjs/web/http/HttpClient.js.map +1 -1
- package/lib/commonjs/web/http/RequestConfigBuilder.js +107 -58
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/playbackErrorKind.js +57 -0
- package/lib/commonjs/web/playbackErrorKind.js.map +1 -0
- package/lib/commonjs/web/player/MediaSessionManager.js +121 -0
- package/lib/commonjs/web/player/MediaSessionManager.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +6 -5
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/player/OptionsManager.js +9 -17
- package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
- package/lib/commonjs/web/player/RemoteCommandController.js +96 -0
- package/lib/commonjs/web/player/RemoteCommandController.js.map +1 -0
- package/lib/module/AudioBrowser.js +1 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +221 -0
- package/lib/module/features/browser-config.js.map +1 -0
- package/lib/module/features/browser.js +54 -157
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +37 -0
- package/lib/module/features/carConnection.js.map +1 -0
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +37 -5
- package/lib/module/features/errors.js.map +1 -1
- package/lib/module/features/favorites.js +1 -1
- package/lib/module/features/gate.js +159 -0
- package/lib/module/features/gate.js.map +1 -0
- package/lib/module/features/index.js +2 -0
- package/lib/module/features/index.js.map +1 -1
- package/lib/module/features/metadata.js +20 -3
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +34 -1
- package/lib/module/features/nowPlaying.js.map +1 -1
- package/lib/module/features/output.js +32 -19
- package/lib/module/features/output.js.map +1 -1
- package/lib/module/features/playback/controls.js +8 -0
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/index.js +1 -0
- package/lib/module/features/playback/index.js.map +1 -1
- package/lib/module/features/playback/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +29 -13
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/state.js +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +83 -0
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -0
- package/lib/module/features/playback/volume.js +2 -2
- package/lib/module/features/player/options.js +79 -29
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +256 -3
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +17 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +1 -1
- package/lib/module/features/queue/queue.js +12 -5
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -103
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +22 -5
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/native.js +5 -0
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.web.js +4 -1
- package/lib/module/native.web.js.map +1 -1
- package/lib/module/test-utils/reactNativeStub.js +58 -0
- package/lib/module/test-utils/reactNativeStub.js.map +1 -0
- package/lib/module/utils/LazyNativeEmitter.js +8 -8
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +0 -3
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/artwork.js +26 -0
- package/lib/module/utils/artwork.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +303 -126
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +67 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js +29 -0
- package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +1 -0
- package/lib/module/web/TrackPlayer/Player.js +90 -26
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js +17 -0
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueueManager.js +269 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js +154 -0
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -0
- package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
- package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
- package/lib/module/web/TrackPlayer/SleepTimer.js +34 -3
- package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
- package/lib/module/web/TrackPlayer/VolumeFader.js +70 -0
- package/lib/module/web/TrackPlayer/VolumeFader.js.map +1 -0
- package/lib/module/web/TrackPlayer/index.js +2 -1
- package/lib/module/web/TrackPlayer/index.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +176 -50
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/NavigationErrorManager.js +3 -1
- package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +11 -9
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +18 -0
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -0
- package/lib/module/web/browser/classifyTrackNavigation.js +38 -0
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -0
- package/lib/module/web/browser/parseSearchResponse.js +16 -0
- package/lib/module/web/browser/parseSearchResponse.js.map +1 -0
- package/lib/module/web/http/HttpClient.js +4 -23
- package/lib/module/web/http/HttpClient.js.map +1 -1
- package/lib/module/web/http/RequestConfigBuilder.js +107 -58
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/playbackErrorKind.js +52 -0
- package/lib/module/web/playbackErrorKind.js.map +1 -0
- package/lib/module/web/player/MediaSessionManager.js +116 -0
- package/lib/module/web/player/MediaSessionManager.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +7 -5
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/player/OptionsManager.js +9 -17
- package/lib/module/web/player/OptionsManager.js.map +1 -1
- package/lib/module/web/player/RemoteCommandController.js +92 -0
- package/lib/module/web/player/RemoteCommandController.js.map +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +0 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts +12 -0
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -0
- package/lib/typescript/src/features/browser.d.ts +40 -1
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +20 -0
- package/lib/typescript/src/features/carConnection.d.ts.map +1 -0
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +61 -6
- package/lib/typescript/src/features/errors.d.ts.map +1 -1
- package/lib/typescript/src/features/favorites.d.ts +1 -1
- package/lib/typescript/src/features/gate.d.ts +93 -0
- package/lib/typescript/src/features/gate.d.ts.map +1 -0
- package/lib/typescript/src/features/index.d.ts +2 -0
- package/lib/typescript/src/features/index.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +26 -7
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +28 -1
- package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
- package/lib/typescript/src/features/output.d.ts +28 -18
- package/lib/typescript/src/features/output.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +5 -0
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/index.d.ts +1 -0
- package/lib/typescript/src/features/playback/index.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +4 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +34 -0
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -0
- package/lib/typescript/src/features/playback/volume.d.ts +2 -2
- package/lib/typescript/src/features/player/options.d.ts +176 -127
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +216 -167
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +9 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +11 -4
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -35
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +19 -4
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native.d.ts +1 -2
- package/lib/typescript/src/native.d.ts.map +1 -1
- package/lib/typescript/src/specs/audio-browser.nitro.d.ts +17 -225
- package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
- package/lib/typescript/src/types/browser-native.d.ts +9 -3
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +145 -13
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +349 -85
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +8 -6
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +0 -2
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/artwork.d.ts +19 -0
- package/lib/typescript/src/utils/artwork.d.ts.map +1 -0
- package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +4 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +24 -10
- package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/JArtworkVariants.hpp +61 -0
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +5 -0
- package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +58 -0
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +100 -0
- package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +3 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +112 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +123 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +133 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +121 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +139 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +134 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +131 -0
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +0 -10
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +14 -9
- package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +6 -0
- package/nitrogen/generated/android/c++/JGate.hpp +62 -0
- package/nitrogen/generated/android/c++/JGateDecision.hpp +64 -0
- package/nitrogen/generated/android/c++/{JThumbsRating.hpp → JGateEvent.hpp} +16 -15
- package/nitrogen/generated/android/c++/JGateReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +247 -223
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +26 -23
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +95 -0
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +33 -3
- package/nitrogen/generated/android/c++/JMediaReference.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +33 -1
- package/nitrogen/generated/android/c++/{JPartialAndroidSetupPlayerOptions.hpp → JNativeAndroidSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +61 -28
- package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +74 -0
- package/nitrogen/generated/android/c++/{JPartialIOSSetupPlayerOptions.hpp → JNativeIOSSetupOptions.hpp} +10 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +20 -9
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +182 -0
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +21 -24
- package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +6 -0
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -20
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +96 -0
- package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +3 -17
- package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +7 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +19 -14
- package/nitrogen/generated/android/c++/{JIosOutput.hpp → JOutput.hpp} +15 -15
- package/nitrogen/generated/android/c++/JOutputType.hpp +82 -0
- package/nitrogen/generated/android/c++/JPlayback.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JPlaybackError.hpp +18 -3
- package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -0
- package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +76 -0
- package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +11 -5
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +20 -20
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JRetryConfig.hpp +11 -7
- package/nitrogen/generated/android/c++/JSearchMode.hpp +27 -9
- package/nitrogen/generated/android/c++/JSearchParams.hpp +9 -3
- package/nitrogen/generated/android/c++/JStallReason.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +23 -5
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +6 -0
- package/nitrogen/generated/android/c++/JTrackRequest.hpp +93 -0
- package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +14 -1
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +71 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.cpp +41 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +121 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +70 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{HeartRating.kt → FavoriteConfig.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RatingType.kt → FavoritesMatchMode.kt} +5 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.kt → Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt} +12 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_RemoteSetRatingEvent.kt → Func_void_GateEvent.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_IosOutput.kt → Func_void_Output.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ThumbsRating.kt → Gate.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PercentageRating.kt → GateDecision.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{StarRating.kt → GateEvent.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → GateReason.kt} +5 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +104 -110
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +24 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialAndroidSetupPlayerOptions.kt → NativeAndroidSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +18 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{RemoteSetRatingEvent.kt → NativeGateRequest.kt} +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{PartialIOSSetupPlayerOptions.kt → NativeIOSSetupOptions.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +3 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{IosOutput.kt → Output.kt} +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButton.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +53 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +139 -28
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +1126 -490
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +66 -45
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +178 -120
- package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -22
- package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +95 -1
- package/nitrogen/generated/ios/swift/ArtworkVariants.swift +34 -0
- package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +29 -0
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +40 -0
- package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +67 -0
- package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +17 -4
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +69 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +60 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +61 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +61 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.swift +77 -0
- package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +46 -0
- package/nitrogen/generated/ios/swift/{Func_void_RemoteSetRatingEvent.swift → Func_void_GateEvent.swift} +10 -10
- package/nitrogen/generated/ios/swift/{Func_void_IosOutput.swift → Func_void_Output.swift} +10 -10
- package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +53 -0
- package/nitrogen/generated/ios/swift/{Func_void_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError___.swift → Func_void_std__shared_ptr_Promise_GateDecision__.swift} +17 -17
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +66 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +81 -0
- package/nitrogen/generated/ios/swift/Gate.swift +47 -0
- package/nitrogen/generated/ios/swift/GateDecision.swift +40 -0
- package/nitrogen/generated/ios/swift/GateEvent.swift +29 -0
- package/nitrogen/generated/ios/swift/GateReason.swift +40 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +21 -15
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +232 -199
- package/nitrogen/generated/ios/swift/IOSOptions.swift +51 -0
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +121 -2
- package/nitrogen/generated/ios/swift/MediaReference.swift +40 -0
- package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +107 -1
- package/nitrogen/generated/ios/swift/{PartialAndroidSetupPlayerOptions.swift → NativeAndroidSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +161 -22
- package/nitrogen/generated/ios/swift/NativeGateRequest.swift +58 -0
- package/nitrogen/generated/ios/swift/{PartialIOSSetupPlayerOptions.swift → NativeIOSSetupOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
- package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +206 -0
- package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +12 -25
- package/nitrogen/generated/ios/swift/NavigationErrorType.swift +8 -0
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -41
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +90 -0
- package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +1 -48
- package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +19 -1
- package/nitrogen/generated/ios/swift/Options.swift +10 -4
- package/nitrogen/generated/ios/swift/{IosOutput.swift → Output.swift} +7 -7
- package/nitrogen/generated/ios/swift/OutputType.swift +72 -0
- package/nitrogen/generated/ios/swift/PlaybackError.swift +43 -2
- package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +64 -0
- package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +27 -8
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +10 -10
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +74 -8
- package/nitrogen/generated/ios/swift/RetryConfig.swift +35 -4
- package/nitrogen/generated/ios/swift/SearchMode.swift +36 -12
- package/nitrogen/generated/ios/swift/SearchParams.swift +7 -2
- package/nitrogen/generated/ios/swift/StallReason.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +74 -8
- package/nitrogen/generated/ios/swift/TrackRequest.swift +106 -0
- package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +60 -1
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +18 -0
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +19 -0
- package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +18 -0
- package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +87 -0
- package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
- package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +0 -2
- package/nitrogen/generated/shared/c++/{StarRating.hpp → FavoriteConfig.hpp} +18 -17
- package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +76 -0
- package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +110 -0
- package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +6 -5
- package/nitrogen/generated/shared/c++/{PercentageRating.hpp → Gate.hpp} +21 -16
- package/nitrogen/generated/shared/c++/{HeartRating.hpp → GateDecision.hpp} +23 -17
- package/nitrogen/generated/shared/c++/{ThumbsRating.hpp → GateEvent.hpp} +18 -17
- package/nitrogen/generated/shared/c++/GateReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +22 -19
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +45 -36
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +91 -0
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +33 -2
- package/nitrogen/generated/shared/c++/MediaReference.hpp +76 -0
- package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +10 -2
- package/nitrogen/generated/shared/c++/{PartialAndroidSetupPlayerOptions.hpp → NativeAndroidSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +31 -15
- package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +97 -0
- package/nitrogen/generated/shared/c++/{PartialIOSSetupPlayerOptions.hpp → NativeIOSSetupOptions.hpp} +11 -11
- package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
- package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +138 -0
- package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +9 -7
- package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +8 -0
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -19
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +3 -19
- package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +6 -2
- package/nitrogen/generated/shared/c++/Options.hpp +8 -8
- package/nitrogen/generated/shared/c++/{IosOutput.hpp → Output.hpp} +18 -18
- package/nitrogen/generated/shared/c++/OutputType.hpp +108 -0
- package/nitrogen/generated/shared/c++/PlaybackError.hpp +18 -3
- package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +100 -0
- package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +9 -6
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +25 -25
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +24 -5
- package/nitrogen/generated/shared/c++/RetryConfig.hpp +10 -6
- package/nitrogen/generated/shared/c++/SearchMode.hpp +39 -15
- package/nitrogen/generated/shared/c++/SearchParams.hpp +9 -2
- package/nitrogen/generated/shared/c++/StallReason.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +24 -5
- package/nitrogen/generated/shared/c++/TrackRequest.hpp +93 -0
- package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +6 -2
- package/package.json +20 -8
- package/src/AudioBrowser.ts +1 -1
- package/src/CLAUDE.md +52 -0
- package/src/codegen-no-config-variant.test.ts +70 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +110 -0
- package/src/features/browser-config.ts +293 -0
- package/src/features/browser.test.ts +19 -0
- package/src/features/browser.ts +59 -202
- package/src/features/carConnection.ts +36 -0
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +71 -5
- package/src/features/favorites.ts +1 -1
- package/src/features/gate.test.ts +72 -0
- package/src/features/gate.ts +170 -0
- package/src/features/hook-wiring.test.ts +254 -0
- package/src/features/index.ts +2 -0
- package/src/features/metadata.ts +26 -15
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +37 -1
- package/src/features/output.ts +35 -22
- package/src/features/playback/controls.ts +8 -0
- package/src/features/playback/index.ts +1 -0
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.test.ts +169 -0
- package/src/features/playback/progress.ts +32 -13
- package/src/features/playback/state.ts +1 -1
- package/src/features/playback/trackPlaybackTime.test.ts +76 -0
- package/src/features/playback/trackPlaybackTime.ts +101 -0
- package/src/features/playback/volume.ts +2 -2
- package/src/features/player/options.test.ts +38 -0
- package/src/features/player/options.ts +217 -110
- package/src/features/player/setup.test.ts +125 -0
- package/src/features/player/setup.ts +400 -175
- package/src/features/player/validateOptions.ts +19 -0
- package/src/features/queue/activeTrack.ts +1 -1
- package/src/features/queue/queue.test.ts +36 -0
- package/src/features/queue/queue.ts +17 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -115
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +32 -5
- package/src/native.ts +5 -0
- package/src/native.web.ts +4 -1
- package/src/public-surface.test.ts +107 -0
- package/src/specs/audio-browser.nitro.ts +132 -42
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +19 -6
- package/src/types/browser-nodes.ts +160 -13
- package/src/types/browser.ts +377 -89
- package/src/utils/LazyNativeEmitter.ts +9 -8
- package/src/utils/NativeUpdatedValue.ts +0 -2
- package/src/utils/artwork.ts +30 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +301 -0
- package/src/web/NativeAudioBrowser.ts +366 -159
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +139 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +82 -0
- package/src/web/TrackPlayer/PlaybackTimer.test.ts +67 -0
- package/src/web/TrackPlayer/PlaybackTimer.ts +33 -0
- package/src/web/TrackPlayer/Player.dispatch.test.ts +229 -0
- package/src/web/TrackPlayer/Player.load.test.ts +86 -0
- package/src/web/TrackPlayer/Player.ts +113 -39
- package/src/web/TrackPlayer/PlayingStateFactory.test.ts +51 -0
- package/src/web/TrackPlayer/PlayingStateFactory.ts +24 -0
- package/src/web/TrackPlayer/QueueManager.test.ts +289 -0
- package/src/web/TrackPlayer/QueueManager.ts +302 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +188 -0
- package/src/web/TrackPlayer/RepeatMode.ts +1 -1
- package/src/web/TrackPlayer/SleepTimer.ts +38 -3
- package/src/web/TrackPlayer/VolumeFader.ts +74 -0
- package/src/web/TrackPlayer/index.ts +2 -1
- package/src/web/browser/BrowserManager.ts +231 -62
- package/src/web/browser/NavigationErrorManager.ts +3 -1
- package/src/web/browser/SearchManager.ts +11 -15
- package/src/web/browser/__tests__/layer-resolver.test.ts +69 -0
- package/src/web/browser/assertBrowsePageShape.test.ts +27 -0
- package/src/web/browser/assertBrowsePageShape.ts +27 -0
- package/src/web/browser/classifyTrackNavigation.test.ts +41 -0
- package/src/web/browser/classifyTrackNavigation.ts +35 -0
- package/src/web/browser/parseSearchResponse.test.ts +25 -0
- package/src/web/browser/parseSearchResponse.ts +15 -0
- package/src/web/http/HttpClient.ts +5 -35
- package/src/web/http/RequestConfigBuilder.test.ts +59 -0
- package/src/web/http/RequestConfigBuilder.ts +120 -63
- package/src/web/playbackErrorKind.test.ts +55 -0
- package/src/web/playbackErrorKind.ts +56 -0
- package/src/web/player/MediaSessionManager.test.ts +220 -0
- package/src/web/player/MediaSessionManager.ts +151 -0
- package/src/web/player/NowPlayingManager.test.ts +46 -0
- package/src/web/player/NowPlayingManager.ts +6 -5
- package/src/web/player/OptionsManager.ts +12 -18
- package/src/web/player/RemoteCommandController.test.ts +171 -0
- package/src/web/player/RemoteCommandController.ts +166 -0
- package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +0 -59
- package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +0 -72
- package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +0 -36
- package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +0 -146
- package/ios/CarPlay/Track+CarPlay.swift +0 -63
- package/ios/Model/MediaURL.swift +0 -67
- package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +0 -9
- package/ios/Player/PlaybackProgressUpdateManager.swift +0 -68
- package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +0 -166
- package/lib/commonjs/features/rating.js +0 -2
- package/lib/commonjs/features/rating.js.map +0 -1
- package/lib/commonjs/types/player.js +0 -2
- package/lib/commonjs/types/player.js.map +0 -1
- package/lib/commonjs/web/SimpleRouter.test.js +0 -174
- package/lib/commonjs/web/SimpleRouter.test.js.map +0 -1
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +0 -293
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/module/features/rating.js +0 -2
- package/lib/module/features/rating.js.map +0 -1
- package/lib/module/types/player.js +0 -2
- package/lib/module/types/player.js.map +0 -1
- package/lib/module/web/SimpleRouter.test.js +0 -174
- package/lib/module/web/SimpleRouter.test.js.map +0 -1
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js +0 -288
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +0 -1
- package/lib/typescript/src/features/rating.d.ts +0 -14
- package/lib/typescript/src/features/rating.d.ts.map +0 -1
- package/lib/typescript/src/native.web.d.ts +0 -3
- package/lib/typescript/src/native.web.d.ts.map +0 -1
- package/lib/typescript/src/types/player.d.ts +0 -64
- package/lib/typescript/src/types/player.d.ts.map +0 -1
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts +0 -188
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.d.ts +0 -40
- package/lib/typescript/src/web/SimpleRouter.d.ts.map +0 -1
- package/lib/typescript/src/web/SimpleRouter.test.d.ts +0 -2
- package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts +0 -11
- package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts +0 -59
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +0 -39
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +0 -6
- package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +0 -4
- package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +0 -34
- package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/State.d.ts +0 -13
- package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +0 -1
- package/lib/typescript/src/web/TrackPlayer/index.d.ts +0 -5
- package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/BrowserManager.d.ts +0 -154
- package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts +0 -42
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +0 -40
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +0 -1
- package/lib/typescript/src/web/browser/SearchManager.d.ts +0 -22
- package/lib/typescript/src/web/browser/SearchManager.d.ts.map +0 -1
- package/lib/typescript/src/web/http/HttpClient.d.ts +0 -25
- package/lib/typescript/src/web/http/HttpClient.d.ts.map +0 -1
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +0 -98
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +0 -1
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts +0 -34
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +0 -1
- package/lib/typescript/src/web/player/OptionsManager.d.ts +0 -32
- package/lib/typescript/src/web/player/OptionsManager.d.ts.map +0 -1
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +0 -69
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +0 -1
- package/lib/typescript/src/web/util/shuffle.d.ts +0 -8
- package/lib/typescript/src/web/util/shuffle.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +0 -86
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +0 -147
- package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +0 -80
- package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +0 -87
- package/nitrogen/generated/android/c++/JHeartRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JIosOutputType.hpp +0 -85
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +0 -96
- package/nitrogen/generated/android/c++/JPercentageRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JRatingType.hpp +0 -73
- package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +0 -66
- package/nitrogen/generated/android/c++/JStarRating.hpp +0 -57
- package/nitrogen/generated/android/c++/JUpdateOptions.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +0 -34
- package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +0 -32
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +0 -47
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +0 -53
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +0 -77
- package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +0 -119
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +0 -68
- package/nitrogen/generated/ios/swift/HeartRating.swift +0 -29
- package/nitrogen/generated/ios/swift/IosOutputType.swift +0 -76
- package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
- package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +0 -101
- package/nitrogen/generated/ios/swift/PercentageRating.swift +0 -29
- package/nitrogen/generated/ios/swift/RatingType.swift +0 -60
- package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +0 -58
- package/nitrogen/generated/ios/swift/StarRating.swift +0 -29
- package/nitrogen/generated/ios/swift/ThumbsRating.swift +0 -29
- package/nitrogen/generated/ios/swift/UpdateOptions.swift +0 -143
- package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +0 -20
- package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +0 -109
- package/nitrogen/generated/shared/c++/IosOutputType.hpp +0 -112
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
- package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +0 -104
- package/nitrogen/generated/shared/c++/RatingType.hpp +0 -96
- package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +0 -94
- package/nitrogen/generated/shared/c++/UpdateOptions.hpp +0 -111
- package/src/features/rating.ts +0 -17
- package/src/types/player.ts +0 -69
- package/src/web/TrackPlayer/PlaylistPlayer.ts +0 -346
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
package com.audiobrowser.player
|
|
2
2
|
|
|
3
3
|
import android.os.Bundle
|
|
4
|
-
import androidx.media3.common.HeartRating
|
|
5
4
|
import androidx.media3.common.MediaItem
|
|
6
5
|
import androidx.media3.common.MediaMetadata
|
|
7
6
|
import androidx.media3.common.Rating
|
|
8
7
|
import androidx.media3.session.LibraryResult
|
|
8
|
+
import androidx.media3.session.MediaConstants
|
|
9
9
|
import androidx.media3.session.MediaLibraryService
|
|
10
10
|
import androidx.media3.session.MediaSession
|
|
11
|
-
import androidx.media3.session.MediaConstants
|
|
12
11
|
import androidx.media3.session.SessionCommand
|
|
13
12
|
import androidx.media3.session.SessionError
|
|
14
13
|
import androidx.media3.session.SessionResult
|
|
15
|
-
import com.audiobrowser.AudioBrowser
|
|
16
14
|
import com.audiobrowser.browser.handleTrackLoad
|
|
15
|
+
import com.audiobrowser.extension.toTrack
|
|
17
16
|
import com.audiobrowser.util.BrowserPathHelper
|
|
18
|
-
import com.audiobrowser.util.
|
|
19
|
-
import com.audiobrowser.util.ResolvedTrackFactory
|
|
17
|
+
import com.audiobrowser.util.RatingFavorites
|
|
20
18
|
import com.audiobrowser.util.TrackFactory
|
|
21
|
-
import com.margelo.nitro.audiobrowser.Track
|
|
22
19
|
import com.google.common.collect.ImmutableList
|
|
23
20
|
import com.google.common.util.concurrent.Futures
|
|
24
21
|
import com.google.common.util.concurrent.ListenableFuture
|
|
25
|
-
import com.margelo.nitro.audiobrowser.
|
|
26
|
-
import com.margelo.nitro.audiobrowser.
|
|
22
|
+
import com.margelo.nitro.audiobrowser.Gate
|
|
23
|
+
import com.margelo.nitro.audiobrowser.GateEvent
|
|
24
|
+
import com.margelo.nitro.audiobrowser.GateReason
|
|
25
|
+
import com.margelo.nitro.audiobrowser.MediaReference
|
|
26
|
+
import com.margelo.nitro.audiobrowser.NativeGateRequest
|
|
27
27
|
import com.margelo.nitro.audiobrowser.PlayerCapabilities
|
|
28
|
-
import com.margelo.nitro.audiobrowser.
|
|
28
|
+
import com.margelo.nitro.audiobrowser.RemoteButtonLayout
|
|
29
|
+
import com.margelo.nitro.audiobrowser.SearchParams
|
|
30
|
+
import com.margelo.nitro.audiobrowser.Track
|
|
31
|
+
import kotlinx.coroutines.CancellationException
|
|
29
32
|
import kotlinx.coroutines.CoroutineScope
|
|
30
33
|
import kotlinx.coroutines.Dispatchers
|
|
31
34
|
import kotlinx.coroutines.SupervisorJob
|
|
35
|
+
import kotlinx.coroutines.TimeoutCancellationException
|
|
36
|
+
import kotlinx.coroutines.cancel
|
|
32
37
|
import kotlinx.coroutines.guava.future
|
|
38
|
+
import kotlinx.coroutines.launch
|
|
33
39
|
import kotlinx.coroutines.withContext
|
|
34
40
|
import timber.log.Timber
|
|
35
41
|
|
|
@@ -42,7 +48,9 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
42
48
|
internal val commandManager = MediaSessionCommandManager()
|
|
43
49
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
44
50
|
|
|
45
|
-
// Track which controllers are subscribed to which media IDs
|
|
51
|
+
// Track which controllers are subscribed to which media IDs.
|
|
52
|
+
// Mutated on the Media3 application thread (onSubscribe/onUnsubscribe) but read from other
|
|
53
|
+
// threads (network observer, JS-triggered notifies) — guard all access with synchronized(this).
|
|
46
54
|
private val parentIdSubscriptions =
|
|
47
55
|
mutableMapOf<String, MutableSet<MediaSession.ControllerInfo>>()
|
|
48
56
|
private var mediaLibrarySession: MediaLibraryService.MediaLibrarySession? = null
|
|
@@ -51,7 +59,8 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
51
59
|
* Recommended artwork size in pixels from the connected media browser (e.g., Android Auto).
|
|
52
60
|
* Updated when onGetLibraryRoot is called with EXTRAS_KEY_MEDIA_ART_SIZE_PIXELS hint.
|
|
53
61
|
*/
|
|
54
|
-
@Volatile
|
|
62
|
+
@Volatile
|
|
63
|
+
var artworkSizeHintPixels: Int? = null
|
|
55
64
|
private set
|
|
56
65
|
|
|
57
66
|
init {
|
|
@@ -72,36 +81,99 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
/** Creates an offline error MediaItem. */
|
|
75
|
-
private fun createOfflineMediaItem(): MediaItem
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
player.context.getString(com.audiobrowser.R.string.
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
private fun createOfflineMediaItem(): MediaItem =
|
|
85
|
+
createErrorMediaItem(
|
|
86
|
+
mediaId = BrowserPathHelper.OFFLINE_PATH,
|
|
87
|
+
title = player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error),
|
|
88
|
+
subtitle =
|
|
89
|
+
player.context.getString(com.audiobrowser.R.string.audio_browser_offline_error_subtitle),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Builds a non-browsable, non-playable [MediaItem] used to surface an error inside an Android
|
|
94
|
+
* Auto / AAOS browse list. Rendered as a greyed-out, non-interactive tile, which is the only
|
|
95
|
+
* side-effect-free way to communicate a browse failure to legacy controllers (Media3 drops
|
|
96
|
+
* [LibraryResult.ofError] on the legacy browse bridge, leaving an empty "No items" screen).
|
|
97
|
+
*/
|
|
98
|
+
private fun createErrorMediaItem(mediaId: String, title: String, subtitle: String): MediaItem =
|
|
99
|
+
MediaItem.Builder()
|
|
100
|
+
.setMediaId(mediaId)
|
|
81
101
|
.setMediaMetadata(
|
|
82
102
|
MediaMetadata.Builder()
|
|
83
|
-
.setTitle(
|
|
84
|
-
.setSubtitle(
|
|
103
|
+
.setTitle(title)
|
|
104
|
+
.setSubtitle(subtitle)
|
|
105
|
+
// Non-browsable, non-playable. Android Auto ignores these flags and still drills into a
|
|
106
|
+
// tapped tile, but onGetChildren returns an empty list for the sentinel paths so it's a
|
|
107
|
+
// harmless "No Items" dead-end rather than an endless stack of error pages.
|
|
85
108
|
.setIsBrowsable(false)
|
|
86
109
|
.setIsPlayable(false)
|
|
87
110
|
.build()
|
|
88
111
|
)
|
|
89
112
|
.build()
|
|
90
|
-
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Builds the gate tile from a per-request chrome: while a gate is active, tabs stay visible but a
|
|
116
|
+
* gated browse/search level serves this single tile (see the gate checks in [onGetChildren] /
|
|
117
|
+
* [onGetSearchResult]). Same shape as the error tiles — non-browsable, non-playable — and
|
|
118
|
+
* deliberately NOT accompanied by a SessionError: a gate is deliberate app state, not a failure.
|
|
119
|
+
* The message renders as the tile's subtitle (newlines collapse to spaces — list rows are
|
|
120
|
+
* single-line).
|
|
121
|
+
*/
|
|
122
|
+
/**
|
|
123
|
+
* Wraps a raw external-search query string into the structured [SearchParams] the gate request
|
|
124
|
+
* carries. The car search surfaces only give a free-text query, so the other fields stay null —
|
|
125
|
+
* mirrors BrowserManager's query→SearchParams construction.
|
|
126
|
+
*/
|
|
127
|
+
private fun searchParams(query: String): SearchParams =
|
|
128
|
+
SearchParams(
|
|
129
|
+
mode = null,
|
|
130
|
+
query = query,
|
|
131
|
+
genre = null,
|
|
132
|
+
artist = null,
|
|
133
|
+
album = null,
|
|
134
|
+
title = null,
|
|
135
|
+
playlist = null,
|
|
136
|
+
reference = MediaReference.UNKNOWN,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
private fun createGateMediaItem(gate: Gate): MediaItem =
|
|
140
|
+
createErrorMediaItem(
|
|
141
|
+
mediaId = BrowserPathHelper.GATE_PATH,
|
|
142
|
+
title = gate.title,
|
|
143
|
+
subtitle = gate.message?.replace('\n', ' ') ?: "",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Builds a generic "something went wrong" error tile for a browse failure. The true offline case
|
|
148
|
+
* is handled separately by the [networkMonitor] guard in [onGetChildren]; anything reaching the
|
|
149
|
+
* catch block is an online-but-failed request (e.g. server down, bad status), so it must NOT be
|
|
150
|
+
* labelled "no internet connection".
|
|
151
|
+
*/
|
|
152
|
+
private fun createBrowseErrorMediaItem(): MediaItem =
|
|
153
|
+
createErrorMediaItem(
|
|
154
|
+
mediaId = BrowserPathHelper.ERROR_PATH,
|
|
155
|
+
title = player.context.getString(com.audiobrowser.R.string.audio_browser_browse_error),
|
|
156
|
+
subtitle =
|
|
157
|
+
player.context.getString(com.audiobrowser.R.string.audio_browser_browse_error_subtitle),
|
|
158
|
+
)
|
|
91
159
|
|
|
92
160
|
fun updateMediaSession(
|
|
93
161
|
mediaSession: MediaSession,
|
|
94
162
|
capabilities: PlayerCapabilities,
|
|
95
|
-
|
|
163
|
+
remoteButtonLayout: RemoteButtonLayout?,
|
|
96
164
|
searchAvailable: Boolean,
|
|
165
|
+
forwardJumpInterval: Double,
|
|
166
|
+
backwardJumpInterval: Double,
|
|
97
167
|
) {
|
|
98
168
|
// Store as MediaLibrarySession for notifyChildrenChanged support
|
|
99
169
|
this.mediaLibrarySession = mediaSession as? MediaLibraryService.MediaLibrarySession
|
|
100
170
|
commandManager.updateMediaSession(
|
|
101
171
|
mediaSession,
|
|
102
172
|
capabilities,
|
|
103
|
-
|
|
173
|
+
remoteButtonLayout,
|
|
104
174
|
searchAvailable,
|
|
175
|
+
forwardJumpInterval,
|
|
176
|
+
backwardJumpInterval,
|
|
105
177
|
)
|
|
106
178
|
}
|
|
107
179
|
|
|
@@ -113,18 +185,6 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
113
185
|
return commandManager.buildConnectionResult(session)
|
|
114
186
|
}
|
|
115
187
|
|
|
116
|
-
/** Updates the favorite state of the current track and emits onFavoriteChanged. */
|
|
117
|
-
private fun setFavorited(favorited: Boolean) {
|
|
118
|
-
val currentTrack = player.currentTrack ?: return
|
|
119
|
-
|
|
120
|
-
// Update native favorites cache (only tracks with src can be favorited)
|
|
121
|
-
currentTrack.src?.let { src -> player.browser?.browserManager?.updateFavorite(src, favorited) }
|
|
122
|
-
|
|
123
|
-
player.setActiveTrackFavorited(favorited)
|
|
124
|
-
val event = FavoriteChangedEvent(player.currentTrack ?: currentTrack, favorited)
|
|
125
|
-
player.callbacks?.onFavoriteChanged(event)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
188
|
override fun onCustomCommand(
|
|
129
189
|
session: MediaSession,
|
|
130
190
|
controller: MediaSession.ControllerInfo,
|
|
@@ -135,7 +195,7 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
135
195
|
if (command.customAction == MediaSessionCommandManager.CUSTOM_ACTION_FAVORITE) {
|
|
136
196
|
val currentFavorited = player.currentTrack?.favorited ?: false
|
|
137
197
|
Timber.d("Favorite button tapped - toggling from $currentFavorited to ${!currentFavorited}")
|
|
138
|
-
|
|
198
|
+
player.setActiveTrackFavorited(!currentFavorited)
|
|
139
199
|
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
|
|
140
200
|
}
|
|
141
201
|
|
|
@@ -150,15 +210,10 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
150
210
|
controller: MediaSession.ControllerInfo,
|
|
151
211
|
rating: Rating,
|
|
152
212
|
): ListenableFuture<SessionResult> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// Also emit onRemoteSetRating for listeners
|
|
158
|
-
RatingFactory.media3ToBridge(rating)?.let {
|
|
159
|
-
val event = RemoteSetRatingEvent(it)
|
|
160
|
-
player.callbacks?.onRemoteSetRating(event)
|
|
161
|
-
}
|
|
213
|
+
// A heart rating from a controller (e.g. Google Assistant "I like this") toggles the
|
|
214
|
+
// now-playing favorite. setActiveTrackFavorited fires onFavoriteChanged so the consumer
|
|
215
|
+
// persists it — the same path as the notification / CarPlay heart button.
|
|
216
|
+
RatingFavorites.favoritedFor(rating)?.let { player.setActiveTrackFavorited(it) }
|
|
162
217
|
return super.onSetRating(session, controller, rating)
|
|
163
218
|
}
|
|
164
219
|
|
|
@@ -220,25 +275,55 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
220
275
|
)
|
|
221
276
|
return scope.future {
|
|
222
277
|
// Wait for browser to be registered if it's not available yet
|
|
223
|
-
val
|
|
224
|
-
player
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
278
|
+
val audioBrowser =
|
|
279
|
+
player.awaitBrowser().also { Timber.d("Browser ready, proceeding with onGetChildren") }
|
|
280
|
+
val browserManager = audioBrowser.browserManager
|
|
281
|
+
|
|
282
|
+
// While a gate is active, tabs stay visible (the root keeps serving them below) but each
|
|
283
|
+
// non-root level is resolved per request: a gated path — including re-queries of the gate
|
|
284
|
+
// tile's own sentinel path — serves the single gate tile, so drilling into it re-shows the
|
|
285
|
+
// message instead of dead-ending in "No items"; an allowed path falls through to real
|
|
286
|
+
// children. Checked before the offline guard: gated content isn't coming back with
|
|
287
|
+
// connectivity, so the gate copy is the truer message.
|
|
288
|
+
if (parentId != BrowserPathHelper.ROOT_PATH) {
|
|
289
|
+
val outcome =
|
|
290
|
+
audioBrowser.gateDecision(
|
|
291
|
+
NativeGateRequest(reason = GateReason.BROWSE, path = parentId, search = null)
|
|
292
|
+
)
|
|
293
|
+
if (outcome.gated) {
|
|
294
|
+
audioBrowser.onGate(GateEvent(GateReason.BROWSE))
|
|
295
|
+
// gateDecision guarantees non-null chrome on a gated decision (override → default →
|
|
296
|
+
// built-in).
|
|
297
|
+
return@future LibraryResult.ofItemList(
|
|
298
|
+
ImmutableList.of(createGateMediaItem(outcome.chrome!!)),
|
|
299
|
+
params,
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// The error / offline tiles are dead-ends. Some controllers (e.g. Android Auto when online)
|
|
305
|
+
// treat a non-browsable tile as tappable and subscribe to its mediaId anyway; returning the
|
|
306
|
+
// error tile again here would push an endless stack of error pages. Return nothing instead.
|
|
307
|
+
// For the offline tile, re-send the alert when still offline so a tap surfaces the
|
|
308
|
+
// explanation again — but only then, since these paths are also re-queried by
|
|
309
|
+
// notifyChildrenChanged (e.g. when connectivity returns) without any user action.
|
|
310
|
+
if (parentId == BrowserPathHelper.OFFLINE_PATH || parentId == BrowserPathHelper.ERROR_PATH) {
|
|
311
|
+
if (parentId == BrowserPathHelper.OFFLINE_PATH && !player.networkMonitor.isOnline.value) {
|
|
312
|
+
sendBrowseError(session, browser, offline = true)
|
|
313
|
+
}
|
|
314
|
+
return@future LibraryResult.ofItemList(ImmutableList.of<MediaItem>(), params)
|
|
315
|
+
}
|
|
228
316
|
|
|
229
317
|
// Show offline error when offline:
|
|
230
318
|
if (
|
|
231
319
|
!player.networkMonitor.isOnline.value && browserManager.config.androidControllerOfflineError
|
|
232
320
|
) {
|
|
233
321
|
Timber.w("Network offline - returning error message for: $parentId")
|
|
322
|
+
sendBrowseError(session, browser, offline = true)
|
|
234
323
|
return@future LibraryResult.ofItemList(ImmutableList.of(createOfflineMediaItem()), params)
|
|
235
324
|
}
|
|
236
325
|
|
|
237
326
|
try {
|
|
238
|
-
// Get context and imageLoader for SVG pre-rendering
|
|
239
|
-
val context = player.context
|
|
240
|
-
val imageLoader = player.imageLoader
|
|
241
|
-
|
|
242
327
|
val children =
|
|
243
328
|
if (parentId == BrowserPathHelper.RECENT_PATH) {
|
|
244
329
|
// TODO: implement recent media items
|
|
@@ -246,36 +331,90 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
246
331
|
} else if (parentId == BrowserPathHelper.ROOT_PATH) {
|
|
247
332
|
// Return tabs as root children (limited to 4 for automotive platform compatibility)
|
|
248
333
|
// TODO: Check what Android Auto does with empty tabs list - may need to return error?
|
|
249
|
-
val tabs = browserManager.queryTabs()
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
334
|
+
val tabs = browserManager.queryTabs()
|
|
335
|
+
if (tabs.size > 4) {
|
|
336
|
+
Timber.w(
|
|
337
|
+
"Root has ${tabs.size} tabs; dropping ${tabs.size - 4} (Android Auto root limit)"
|
|
338
|
+
)
|
|
254
339
|
}
|
|
340
|
+
toMediaItems(tabs.take(4))
|
|
255
341
|
} else {
|
|
256
342
|
// Resolve the specific path and get its children
|
|
257
343
|
val resolvedTrack = browserManager.resolve(parentId)
|
|
258
344
|
|
|
259
345
|
// Convert children to MediaItems (url is already set to contextual URL)
|
|
260
|
-
val trackChildren =
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} else {
|
|
267
|
-
trackChildren.map { track -> TrackFactory.toMedia3(track) }
|
|
268
|
-
}
|
|
346
|
+
val trackChildren =
|
|
347
|
+
resolvedTrack.children
|
|
348
|
+
?: throw IllegalStateException(
|
|
349
|
+
"Expected browsed ResolvedTrack to have a children array"
|
|
350
|
+
)
|
|
351
|
+
toMediaItems(trackChildren.toList())
|
|
269
352
|
}
|
|
270
353
|
|
|
271
354
|
LibraryResult.ofItemList(ImmutableList.copyOf(children.paginate(page, pageSize)), params)
|
|
272
355
|
} catch (e: Exception) {
|
|
356
|
+
// A cancelled browse (controller disconnected) is not a failure — don't render it as an
|
|
357
|
+
// error tile. awaitBrowser's TimeoutCancellationException IS a real failure, keep that.
|
|
358
|
+
if (e is CancellationException && e !is TimeoutCancellationException) throw e
|
|
273
359
|
Timber.e(e, "Error getting children for parentId: $parentId")
|
|
274
|
-
|
|
360
|
+
// Surface an error tile instead of a bare ofError(): Media3 drops the error message on the
|
|
361
|
+
// legacy browse bridge, which would otherwise leave an empty "No items" screen in Android
|
|
362
|
+
// Auto. See https://github.com/androidx/media/issues/2901
|
|
363
|
+
//
|
|
364
|
+
// Verified on a head unit (2026-06): the AA browse list NEVER renders error text — not for
|
|
365
|
+
// transient sendError, sticky non-fatal replication, or fatal replication
|
|
366
|
+
// (setLibraryErrorReplicationMode). Fatal replication does show the message on the
|
|
367
|
+
// playback screen, but presents the session as STATE_ERROR with no actions, hiding the
|
|
368
|
+
// now-playing item and transport controls — not worth it. Tiles remain the only in-browse
|
|
369
|
+
// signal we control.
|
|
370
|
+
sendBrowseError(session, browser, offline = false)
|
|
371
|
+
LibraryResult.ofItemList(ImmutableList.of(createBrowseErrorMediaItem()), params)
|
|
275
372
|
}
|
|
276
373
|
}
|
|
277
374
|
}
|
|
278
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Sends a non-fatal [SessionError] to the controller whose browse failed, complementing the error
|
|
378
|
+
* tile. Media3 browsers receive it via MediaController.Listener.onError; for legacy controllers
|
|
379
|
+
* Media3 transiently attaches the error code/message to the platform session's playback state
|
|
380
|
+
* without entering STATE_ERROR.
|
|
381
|
+
*
|
|
382
|
+
* NOTE: current Android Auto renders NOTHING for this (verified on a head unit — see
|
|
383
|
+
* androidx/media#2901; the transient state is cleared microseconds after being set). It is kept
|
|
384
|
+
* because it is the correct Media3-API error signal, costs nothing, and becomes user-visible if
|
|
385
|
+
* Android Auto moves to consuming SessionError via the Media3 controller API.
|
|
386
|
+
*
|
|
387
|
+
* Dispatched to the main thread: the session and its legacy stub are application-thread bound.
|
|
388
|
+
*/
|
|
389
|
+
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
|
390
|
+
private fun sendBrowseError(
|
|
391
|
+
session: MediaLibraryService.MediaLibrarySession,
|
|
392
|
+
browser: MediaSession.ControllerInfo,
|
|
393
|
+
offline: Boolean,
|
|
394
|
+
) {
|
|
395
|
+
val message =
|
|
396
|
+
player.context.getString(
|
|
397
|
+
if (offline) com.audiobrowser.R.string.audio_browser_offline_error
|
|
398
|
+
else com.audiobrowser.R.string.audio_browser_browse_error
|
|
399
|
+
)
|
|
400
|
+
val code = if (offline) SessionError.ERROR_IO else SessionError.ERROR_UNKNOWN
|
|
401
|
+
scope.launch(Dispatchers.Main) { session.sendError(browser, SessionError(code, message)) }
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Converts tracks to MediaItems for browse delivery, routing http(s) artwork through the
|
|
406
|
+
* content:// provider so Android Auto can load it via the ArtworkContentProvider. Image-row
|
|
407
|
+
* tracks (a CarPlay-only rendering) are expanded into their items as regular grouped rows first —
|
|
408
|
+
* see [TrackFactory.expandImageRows].
|
|
409
|
+
*/
|
|
410
|
+
private fun toMediaItems(tracks: List<Track>): List<MediaItem> {
|
|
411
|
+
val registry = player.browseArtworkRegistry
|
|
412
|
+
val authority = com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
|
|
413
|
+
return TrackFactory.expandImageRows(tracks).map {
|
|
414
|
+
TrackFactory.toBrowseMediaItem(it, registry, authority)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
279
418
|
override fun onGetItem(
|
|
280
419
|
session: MediaLibraryService.MediaLibrarySession,
|
|
281
420
|
browser: MediaSession.ControllerInfo,
|
|
@@ -288,6 +427,30 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
288
427
|
return@future LibraryResult.ofItem(createOfflineMediaItem(), null)
|
|
289
428
|
}
|
|
290
429
|
|
|
430
|
+
// Return the error tile as a non-browsable item so tapping it can't re-browse into a
|
|
431
|
+
// failing path (which would push another error page onto the stack).
|
|
432
|
+
if (mediaId == BrowserPathHelper.ERROR_PATH) {
|
|
433
|
+
return@future LibraryResult.ofItem(createBrowseErrorMediaItem(), null)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (mediaId == BrowserPathHelper.GATE_PATH) {
|
|
437
|
+
// A direct fetch of the gate tile's own sentinel (a controller re-reading the item it was
|
|
438
|
+
// served). Resolve the chrome for the gate path but do NOT emit onGate — that fires at the
|
|
439
|
+
// browse/search serve sites, not on an item lookup.
|
|
440
|
+
val outcome =
|
|
441
|
+
player
|
|
442
|
+
.awaitBrowser()
|
|
443
|
+
.gateDecision(
|
|
444
|
+
NativeGateRequest(
|
|
445
|
+
reason = GateReason.BROWSE,
|
|
446
|
+
path = BrowserPathHelper.GATE_PATH,
|
|
447
|
+
search = null,
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
if (!outcome.gated) return@future LibraryResult.ofError(SessionError.ERROR_BAD_VALUE)
|
|
451
|
+
return@future LibraryResult.ofItem(createGateMediaItem(outcome.chrome!!), null)
|
|
452
|
+
}
|
|
453
|
+
|
|
291
454
|
if (mediaId == BrowserPathHelper.ROOT_PATH || mediaId == BrowserPathHelper.RECENT_PATH) {
|
|
292
455
|
return@future LibraryResult.ofItem(
|
|
293
456
|
MediaItem.Builder()
|
|
@@ -303,11 +466,33 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
303
466
|
// Wait for browser to be registered if it's not available yet
|
|
304
467
|
val browserManager = player.awaitBrowser().browserManager
|
|
305
468
|
|
|
469
|
+
// Serve tracks from the track cache first (keyed by url and src). Besides avoiding an HTTP
|
|
470
|
+
// resolve, this keeps item identity correct for contextual mediaIds: resolve() strips
|
|
471
|
+
// __trackId and would return the *parent container's* metadata as the item.
|
|
472
|
+
val browseAuthority =
|
|
473
|
+
com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
|
|
474
|
+
browserManager.getCachedTrack(mediaId)?.let { track ->
|
|
475
|
+
return@future LibraryResult.ofItem(
|
|
476
|
+
TrackFactory.toBrowseMediaItem(track, player.browseArtworkRegistry, browseAuthority),
|
|
477
|
+
null,
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
|
|
306
481
|
try {
|
|
307
482
|
val resolvedTrack = browserManager.resolve(mediaId)
|
|
308
|
-
|
|
309
|
-
|
|
483
|
+
// Through the one Track conversion, so the resolve path renders identically
|
|
484
|
+
// to the cached-track path above (list line from subtitle, favorited heart)
|
|
485
|
+
// and the item's tag is a Track, as fromMedia3 expects.
|
|
486
|
+
LibraryResult.ofItem(
|
|
487
|
+
TrackFactory.toBrowseMediaItem(
|
|
488
|
+
resolvedTrack.toTrack(),
|
|
489
|
+
player.browseArtworkRegistry,
|
|
490
|
+
browseAuthority,
|
|
491
|
+
),
|
|
492
|
+
null,
|
|
493
|
+
)
|
|
310
494
|
} catch (e: Exception) {
|
|
495
|
+
if (e is CancellationException && e !is TimeoutCancellationException) throw e
|
|
311
496
|
Timber.e(e, "Error getting item for mediaId: $mediaId")
|
|
312
497
|
LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
|
|
313
498
|
}
|
|
@@ -322,8 +507,17 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
322
507
|
): ListenableFuture<LibraryResult<Void>> {
|
|
323
508
|
Timber.d("onSubscribe: ${browser.packageName}, parentId = $parentId")
|
|
324
509
|
|
|
325
|
-
parentIdSubscriptions.getOrPut(parentId) { mutableSetOf() }.add(browser)
|
|
326
|
-
|
|
510
|
+
synchronized(this) { parentIdSubscriptions.getOrPut(parentId) { mutableSetOf() }.add(browser) }
|
|
511
|
+
|
|
512
|
+
// Don't call super: Media3's default onSubscribe validates the parent by calling
|
|
513
|
+
// onGetItem(parentId), which for us is a full resolve() — an HTTP fetch whose result the
|
|
514
|
+
// legacy stub discards for Android Auto controllers. onGetChildren already surfaces browse
|
|
515
|
+
// failures. Keep the default's other behavior: notify Media3 (non-legacy) browsers so they
|
|
516
|
+
// fetch the children; legacy browsers get onLoadChildren from the framework after subscribing.
|
|
517
|
+
if (browser.controllerVersion != MediaSession.ControllerInfo.LEGACY_CONTROLLER_VERSION) {
|
|
518
|
+
session.notifyChildrenChanged(browser, parentId, Int.MAX_VALUE, params)
|
|
519
|
+
}
|
|
520
|
+
return Futures.immediateFuture(LibraryResult.ofVoid())
|
|
327
521
|
}
|
|
328
522
|
|
|
329
523
|
override fun onUnsubscribe(
|
|
@@ -333,9 +527,11 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
333
527
|
): ListenableFuture<LibraryResult<Void>> {
|
|
334
528
|
Timber.d("onUnsubscribe: ${browser.packageName}, parentId = $parentId")
|
|
335
529
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
parentIdSubscriptions
|
|
530
|
+
synchronized(this) {
|
|
531
|
+
parentIdSubscriptions[parentId]?.remove(browser)
|
|
532
|
+
if (parentIdSubscriptions[parentId]?.isEmpty() == true) {
|
|
533
|
+
parentIdSubscriptions.remove(parentId)
|
|
534
|
+
}
|
|
339
535
|
}
|
|
340
536
|
|
|
341
537
|
return super.onUnsubscribe(session, browser, parentId)
|
|
@@ -344,22 +540,38 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
344
540
|
/**
|
|
345
541
|
* Notifies all subscribed controllers to refresh their content. Called internally when network
|
|
346
542
|
* state changes to refresh all subscribed paths.
|
|
543
|
+
*
|
|
544
|
+
* Safe to call from any thread: snapshots the subscribed paths and dispatches the session calls
|
|
545
|
+
* to the main (Media3 application) thread.
|
|
347
546
|
*/
|
|
348
547
|
private fun notifySubscribedChildrenChanged() {
|
|
349
|
-
|
|
350
|
-
|
|
548
|
+
val session = mediaLibrarySession ?: return
|
|
549
|
+
val parentIds = synchronized(this) { parentIdSubscriptions.keys.toList() }
|
|
550
|
+
scope.launch(Dispatchers.Main) {
|
|
551
|
+
parentIds.forEach { parentId -> session.notifyChildrenChanged(parentId, Int.MAX_VALUE, null) }
|
|
351
552
|
}
|
|
352
553
|
}
|
|
353
554
|
|
|
354
555
|
/**
|
|
355
556
|
* Notifies external controllers that content at the given path has changed. Controllers
|
|
356
|
-
* subscribed to this path will refresh their UI.
|
|
557
|
+
* subscribed to this path will refresh their UI. Safe to call from any thread.
|
|
357
558
|
*
|
|
358
559
|
* @param path The path where content has changed
|
|
359
560
|
*/
|
|
360
561
|
fun notifyContentChanged(path: String) {
|
|
361
562
|
Timber.d("Notifying content changed for path: $path")
|
|
362
|
-
mediaLibrarySession
|
|
563
|
+
val session = mediaLibrarySession ?: return
|
|
564
|
+
scope.launch(Dispatchers.Main) { session.notifyChildrenChanged(path, Int.MAX_VALUE, null) }
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Notifies all subscribed controllers that content everywhere has changed (e.g. on a locale
|
|
569
|
+
* switch) so they re-query their children. Pairs with AudioBrowser.invalidateAllContent(), which
|
|
570
|
+
* clears the content cache first.
|
|
571
|
+
*/
|
|
572
|
+
fun invalidateAllContent() {
|
|
573
|
+
Timber.d("Invalidating all content - notifying all subscribed paths")
|
|
574
|
+
notifySubscribedChildrenChanged()
|
|
363
575
|
}
|
|
364
576
|
|
|
365
577
|
/**
|
|
@@ -367,10 +579,15 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
367
579
|
* controllers to refresh their content.
|
|
368
580
|
*/
|
|
369
581
|
fun notifyBrowserReady() {
|
|
370
|
-
Timber.d("Browser ready - notifying
|
|
582
|
+
Timber.d("Browser ready - notifying all subscribed paths")
|
|
371
583
|
notifySubscribedChildrenChanged()
|
|
372
584
|
}
|
|
373
585
|
|
|
586
|
+
/** Cancels in-flight browse/search work. Call when the owning player is destroyed. */
|
|
587
|
+
fun destroy() {
|
|
588
|
+
scope.cancel()
|
|
589
|
+
}
|
|
590
|
+
|
|
374
591
|
override fun onSearch(
|
|
375
592
|
session: MediaLibraryService.MediaLibrarySession,
|
|
376
593
|
browser: MediaSession.ControllerInfo,
|
|
@@ -379,11 +596,27 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
379
596
|
): ListenableFuture<LibraryResult<Void>> {
|
|
380
597
|
Timber.d("onSearch: ${browser.packageName}, query = $query")
|
|
381
598
|
return scope.future {
|
|
382
|
-
|
|
599
|
+
// Wait for browser registration like onGetChildren does, so a cold-start voice search
|
|
600
|
+
// doesn't fail before JS has configured the browser.
|
|
601
|
+
val audioBrowser =
|
|
602
|
+
try {
|
|
603
|
+
player.awaitBrowser()
|
|
604
|
+
} catch (e: TimeoutCancellationException) {
|
|
605
|
+
Timber.w("Timed out waiting for browser - search not available")
|
|
606
|
+
return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
|
|
607
|
+
}
|
|
608
|
+
val browserManager = audioBrowser.browserManager
|
|
383
609
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
610
|
+
// External-surface search is gated with the rest of the browse tree — otherwise search is
|
|
611
|
+
// a way around the gate. One "result": the gate tile (see onGetSearchResult).
|
|
612
|
+
val searchOutcome =
|
|
613
|
+
audioBrowser.gateDecision(
|
|
614
|
+
NativeGateRequest(reason = GateReason.SEARCH, path = null, search = searchParams(query))
|
|
615
|
+
)
|
|
616
|
+
if (searchOutcome.gated) {
|
|
617
|
+
audioBrowser.onGate(GateEvent(GateReason.SEARCH))
|
|
618
|
+
session.notifySearchResultChanged(browser, query, 1, params)
|
|
619
|
+
return@future LibraryResult.ofVoid()
|
|
387
620
|
}
|
|
388
621
|
|
|
389
622
|
// Check if search is configured
|
|
@@ -404,6 +637,7 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
404
637
|
|
|
405
638
|
LibraryResult.ofVoid()
|
|
406
639
|
} catch (e: Exception) {
|
|
640
|
+
if (e is CancellationException && e !is TimeoutCancellationException) throw e
|
|
407
641
|
Timber.e(e, "Error during search for query: $query")
|
|
408
642
|
LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
|
|
409
643
|
}
|
|
@@ -422,21 +656,38 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
422
656
|
"onGetSearchResult: ${browser.packageName}, query = $query, page = $page, pageSize = $pageSize"
|
|
423
657
|
)
|
|
424
658
|
return scope.future {
|
|
425
|
-
val
|
|
659
|
+
val audioBrowser =
|
|
660
|
+
try {
|
|
661
|
+
player.awaitBrowser()
|
|
662
|
+
} catch (e: TimeoutCancellationException) {
|
|
663
|
+
Timber.w("Timed out waiting for browser - search not available")
|
|
664
|
+
return@future LibraryResult.ofError(SessionError.ERROR_NOT_SUPPORTED)
|
|
665
|
+
}
|
|
666
|
+
val browserManager = audioBrowser.browserManager
|
|
426
667
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
668
|
+
// Gated: the single search "result" is the gate tile (paired with onSearch's count of 1).
|
|
669
|
+
val searchOutcome =
|
|
670
|
+
audioBrowser.gateDecision(
|
|
671
|
+
NativeGateRequest(reason = GateReason.SEARCH, path = null, search = searchParams(query))
|
|
672
|
+
)
|
|
673
|
+
if (searchOutcome.gated) {
|
|
674
|
+
audioBrowser.onGate(GateEvent(GateReason.SEARCH))
|
|
675
|
+
return@future LibraryResult.ofItemList(
|
|
676
|
+
ImmutableList.of(createGateMediaItem(searchOutcome.chrome!!)),
|
|
677
|
+
params,
|
|
678
|
+
)
|
|
430
679
|
}
|
|
431
680
|
|
|
432
681
|
try {
|
|
433
682
|
// Get cached search results from BrowserManager
|
|
434
683
|
browserManager.getCachedSearchResults(query)?.let { tracks ->
|
|
435
684
|
// Convert to MediaItems
|
|
685
|
+
val searchAuthority =
|
|
686
|
+
com.audiobrowser.util.ArtworkUris.authorityFor(player.context.packageName)
|
|
436
687
|
val mediaItems =
|
|
437
688
|
tracks.map { track ->
|
|
438
689
|
Timber.d("Search result: ${track.title} (url=${track.url}, src=${track.src})")
|
|
439
|
-
TrackFactory.
|
|
690
|
+
TrackFactory.toBrowseMediaItem(track, player.browseArtworkRegistry, searchAuthority)
|
|
440
691
|
}
|
|
441
692
|
|
|
442
693
|
val paginatedItems = mediaItems.paginate(page, pageSize)
|
|
@@ -448,6 +699,7 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
448
699
|
LibraryResult.ofItemList(ImmutableList.of(), params)
|
|
449
700
|
}
|
|
450
701
|
} catch (e: Exception) {
|
|
702
|
+
if (e is CancellationException && e !is TimeoutCancellationException) throw e
|
|
451
703
|
Timber.e(e, "Error getting search results for query: $query")
|
|
452
704
|
LibraryResult.ofError(SessionError.ERROR_UNKNOWN)
|
|
453
705
|
}
|
|
@@ -462,65 +714,84 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
462
714
|
startPositionMs: Long,
|
|
463
715
|
): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
|
|
464
716
|
Timber.Forest.d(
|
|
465
|
-
"onSetMediaItems: ${controller.packageName}, count=${mediaItems.size}, mediaId=${mediaItems
|
|
717
|
+
"onSetMediaItems: ${controller.packageName}, count=${mediaItems.size}, mediaId=${mediaItems.firstOrNull()?.mediaId}, uri=${mediaItems.firstOrNull()?.localConfiguration?.uri}, title=${mediaItems.firstOrNull()?.mediaMetadata?.title}"
|
|
466
718
|
)
|
|
467
719
|
|
|
720
|
+
if (mediaItems.isEmpty()) {
|
|
721
|
+
return Futures.immediateFuture(
|
|
722
|
+
MediaSession.MediaItemsWithStartPosition(emptyList(), 0, startPositionMs)
|
|
723
|
+
)
|
|
724
|
+
}
|
|
725
|
+
|
|
468
726
|
return scope.future {
|
|
469
727
|
val audioBrowser = player.awaitBrowser()
|
|
470
728
|
|
|
471
|
-
// Helper: returns the current player state unchanged so Media3 doesn't modify playback
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
currentIndex,
|
|
478
|
-
|
|
479
|
-
)
|
|
480
|
-
}
|
|
729
|
+
// Helper: returns the current player state unchanged so Media3 doesn't modify playback.
|
|
730
|
+
// ExoPlayer is main-thread confined; this future runs on IO, so hop to Main for the reads.
|
|
731
|
+
suspend fun currentPlayerState(): MediaSession.MediaItemsWithStartPosition =
|
|
732
|
+
withContext(Dispatchers.Main) {
|
|
733
|
+
val currentItems = player.tracks.map { TrackFactory.toMedia3(it) }
|
|
734
|
+
val currentIndex = player.currentIndex ?: 0
|
|
735
|
+
MediaSession.MediaItemsWithStartPosition(currentItems, currentIndex, startPositionMs)
|
|
736
|
+
}
|
|
481
737
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
738
|
+
val browserManager = audioBrowser.browserManager
|
|
739
|
+
|
|
740
|
+
// A single tapped item resolves to the contextual url of the list it was
|
|
741
|
+
// tapped in: directly for contextual mediaIds, via the track cache for
|
|
742
|
+
// stable-id mediaIds (see BrowserManager.contextualUrlFor). A search
|
|
743
|
+
// selection is not a list tap — its queue comes from the search results
|
|
744
|
+
// (resolveMediaItemsForPlayback), never from a browsed container.
|
|
745
|
+
val singleContextualUrl =
|
|
746
|
+
mediaItems
|
|
747
|
+
.singleOrNull()
|
|
748
|
+
?.takeIf { it.requestMetadata.searchQuery == null }
|
|
749
|
+
?.let { browserManager.contextualUrlFor(it.mediaId) }
|
|
750
|
+
|
|
751
|
+
// Check if this is a single item from the current queue source
|
|
752
|
+
if (singleContextualUrl != null) {
|
|
753
|
+
val parentPath = BrowserPathHelper.stripTrackId(singleContextualUrl)
|
|
754
|
+
val trackId = BrowserPathHelper.extractTrackId(singleContextualUrl)
|
|
755
|
+
|
|
756
|
+
// Check if queue already came from this parent path - just skip to the track
|
|
757
|
+
if (trackId != null && parentPath == player.queueSourcePath) {
|
|
758
|
+
val queueTracks = withContext(Dispatchers.Main) { player.tracks }
|
|
759
|
+
val index = queueTracks.indexOfFirst { it.src == trackId }
|
|
760
|
+
if (index >= 0) {
|
|
761
|
+
Timber.d("Queue already from $parentPath, skipping to index $index")
|
|
762
|
+
val track = queueTracks[index]
|
|
763
|
+
return@future handleTrackLoad(
|
|
764
|
+
audioBrowser.configuration.handleTrackLoad,
|
|
765
|
+
track,
|
|
766
|
+
queueTracks,
|
|
767
|
+
index.toDouble(),
|
|
768
|
+
::currentPlayerState,
|
|
769
|
+
) {
|
|
770
|
+
// Return the existing queue items with the new start index
|
|
771
|
+
val existingItems = queueTracks.map { TrackFactory.toMedia3(it) }
|
|
772
|
+
MediaSession.MediaItemsWithStartPosition(existingItems, index, startPositionMs)
|
|
504
773
|
}
|
|
505
774
|
}
|
|
506
775
|
}
|
|
507
776
|
}
|
|
508
777
|
|
|
509
|
-
val browserManager = audioBrowser.browserManager
|
|
510
778
|
val result =
|
|
511
779
|
browserManager.resolveMediaItemsForPlayback(mediaItems, startIndex, startPositionMs)
|
|
512
780
|
|
|
513
781
|
val tracks = result.mediaItems.map { TrackFactory.fromMedia3(it) }.toTypedArray()
|
|
514
782
|
val selectedTrack = tracks.getOrElse(result.startIndex) { tracks.first() }
|
|
515
783
|
|
|
516
|
-
handleTrackLoad(
|
|
784
|
+
handleTrackLoad(
|
|
785
|
+
audioBrowser.configuration.handleTrackLoad,
|
|
786
|
+
selectedTrack,
|
|
787
|
+
tracks,
|
|
788
|
+
result.startIndex.toDouble(),
|
|
789
|
+
::currentPlayerState,
|
|
790
|
+
) {
|
|
517
791
|
// If this was a contextual URL expansion, track the source path (only for default behavior)
|
|
518
|
-
if (
|
|
519
|
-
val
|
|
520
|
-
|
|
521
|
-
val parentPath = BrowserPathHelper.stripTrackId(mediaId)
|
|
522
|
-
withContext(Dispatchers.Main) { player.queueSourcePath = parentPath }
|
|
523
|
-
}
|
|
792
|
+
if (singleContextualUrl != null) {
|
|
793
|
+
val parentPath = BrowserPathHelper.stripTrackId(singleContextualUrl)
|
|
794
|
+
withContext(Dispatchers.Main) { player.queueSourcePath = parentPath }
|
|
524
795
|
}
|
|
525
796
|
result
|
|
526
797
|
}
|
|
@@ -537,25 +808,42 @@ class MediaSessionCallback(private val player: Player) :
|
|
|
537
808
|
*
|
|
538
809
|
* @param mediaSession The media session
|
|
539
810
|
* @param controller The controller requesting resumption
|
|
540
|
-
* @param
|
|
811
|
+
* @param isForPlayback True if this should start playback; false if just gathering info for the
|
|
812
|
+
* boot-time resumption notification (no network; local metadata only).
|
|
541
813
|
* @see https://developer.android.com/media/media3/session/background-playback#resumption
|
|
542
814
|
*/
|
|
543
815
|
override fun onPlaybackResumption(
|
|
544
816
|
mediaSession: MediaSession,
|
|
545
817
|
controller: MediaSession.ControllerInfo,
|
|
818
|
+
isForPlayback: Boolean,
|
|
546
819
|
): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
|
|
547
820
|
Timber.d("${controller.packageName}")
|
|
548
821
|
|
|
549
822
|
return scope.future {
|
|
550
|
-
//
|
|
823
|
+
// A pure read first: restore()'s side effects (it applies repeat/shuffle/speed to the
|
|
824
|
+
// live player, emitting option-changed events to JS) belong to actual resumption only.
|
|
551
825
|
val state =
|
|
552
|
-
|
|
553
|
-
withContext(Dispatchers.Main) { player.playbackStateStore.restore() }
|
|
826
|
+
player.playbackStateStore.get()
|
|
554
827
|
?: run {
|
|
555
828
|
Timber.w("No persisted playback state found")
|
|
556
829
|
throw IllegalStateException("No playback state to resume")
|
|
557
830
|
}
|
|
558
831
|
|
|
832
|
+
// isForPlayback == false is the device-boot-time notification case: network may be
|
|
833
|
+
// unavailable and we must not start playback. Return just the locally-stored track with its
|
|
834
|
+
// already-local metadata; skip the settings restore and network queue expansion below.
|
|
835
|
+
if (!isForPlayback) {
|
|
836
|
+
Timber.d("Info-gathering resumption (boot-time); returning stored track without expansion")
|
|
837
|
+
return@future MediaSession.MediaItemsWithStartPosition(
|
|
838
|
+
ImmutableList.of(TrackFactory.toMedia3(state.track)),
|
|
839
|
+
0,
|
|
840
|
+
state.positionMs,
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// restore() sets player properties which must happen on main thread
|
|
845
|
+
withContext(Dispatchers.Main) { player.playbackStateStore.restore() }
|
|
846
|
+
|
|
559
847
|
val url = state.track.url
|
|
560
848
|
Timber.d("Resuming from url=$url, positionMs=${state.positionMs}")
|
|
561
849
|
|