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
|
@@ -7,10 +7,13 @@ import com.audiobrowser.http.HttpClient
|
|
|
7
7
|
import com.audiobrowser.http.RequestConfigBuilder
|
|
8
8
|
import com.audiobrowser.util.BrowserPathHelper
|
|
9
9
|
import com.audiobrowser.util.TrackFactory
|
|
10
|
+
import com.audiobrowser.util.artworkOf
|
|
10
11
|
import com.margelo.nitro.audiobrowser.ArtworkRequestConfig
|
|
11
12
|
import com.margelo.nitro.audiobrowser.BrowserSourceCallbackParam
|
|
13
|
+
import com.margelo.nitro.audiobrowser.FavoritesMatchMode
|
|
14
|
+
import com.margelo.nitro.audiobrowser.Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____
|
|
12
15
|
import com.margelo.nitro.audiobrowser.ImageContext
|
|
13
|
-
import com.margelo.nitro.audiobrowser.
|
|
16
|
+
import com.margelo.nitro.audiobrowser.MediaReference
|
|
14
17
|
import com.margelo.nitro.audiobrowser.MediaRequestConfig
|
|
15
18
|
import com.margelo.nitro.audiobrowser.NativeRouteEntry
|
|
16
19
|
import com.margelo.nitro.audiobrowser.RequestConfig
|
|
@@ -46,6 +49,7 @@ class BrowserManager {
|
|
|
46
49
|
private var onPathChanged: ((String) -> Unit)? = null
|
|
47
50
|
private var onContentChanged: ((ResolvedTrack?) -> Unit)? = null
|
|
48
51
|
private var onTabsChanged: ((Array<Track>) -> Unit)? = null
|
|
52
|
+
private var onArtworkRegistriesCleared: (() -> Unit)? = null
|
|
49
53
|
|
|
50
54
|
private var path: String = "/"
|
|
51
55
|
set(value) {
|
|
@@ -86,28 +90,64 @@ class BrowserManager {
|
|
|
86
90
|
// Invalidated via invalidateContentCache() when content changes
|
|
87
91
|
private val contentCache = LruCache<String, ResolvedTrack>(20)
|
|
88
92
|
|
|
89
|
-
// Cache for search results
|
|
90
|
-
|
|
91
|
-
private var
|
|
93
|
+
// Cache for the most recent search - the query string and its results stored as one pair, so a
|
|
94
|
+
// concurrent reader never sees one query matched with another query's results.
|
|
95
|
+
@Volatile private var lastSearch: Pair<String, Array<Track>>? = null
|
|
92
96
|
|
|
93
97
|
// Set of favorited track identifiers (src)
|
|
94
98
|
private var favoriteIds = setOf<String>()
|
|
95
99
|
|
|
100
|
+
// Favorite match mode, propagated from the player's `favorite` capability.
|
|
101
|
+
// null = favoriting disabled (no row hearts). Set via setFavoriteMatch.
|
|
102
|
+
private var favoriteMatch: FavoritesMatchMode? = null
|
|
103
|
+
|
|
96
104
|
// Navigation tracking to prevent race conditions
|
|
97
105
|
@Volatile private var currentNavigationId = 0
|
|
98
106
|
|
|
99
107
|
/**
|
|
100
108
|
* Browser configuration containing routes, search, tabs, and request settings. This can be
|
|
101
109
|
* updated dynamically when the configuration changes.
|
|
110
|
+
*
|
|
111
|
+
* Setting a new config bumps [layerGeneration] so the cached request/browse resolver layers are
|
|
112
|
+
* re-resolved on the next request (the resolvers may close over config-derived state).
|
|
102
113
|
*/
|
|
103
114
|
var config: BrowserConfig = BrowserConfig()
|
|
115
|
+
set(value) {
|
|
116
|
+
field = value
|
|
117
|
+
layerGeneration += 1
|
|
118
|
+
// Registered artwork resolutions pin configs (and their JS callback handles)
|
|
119
|
+
// from the previous configuration — never resolve through them again.
|
|
120
|
+
artworkResolutions.clear()
|
|
121
|
+
onArtworkRegistriesCleared?.invoke()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Resolver-layer caching. The request/browse layers may be resolver thunks
|
|
125
|
+
// (config.requestResolver / config.browseResolver) resolved once per *content generation*:
|
|
126
|
+
// re-resolved when content is invalidated (clearContentCache, from invalidateAllContent) or when
|
|
127
|
+
// a new config is set, cached, and merged per request.
|
|
128
|
+
private var layerGeneration = 0
|
|
129
|
+
private var resolvedLayerGeneration = -1
|
|
130
|
+
private var resolvedRequestLayer: TransformableRequestConfig? = null
|
|
131
|
+
private var resolvedBrowseLayer: TransformableRequestConfig? = null
|
|
132
|
+
|
|
133
|
+
/** Test-only accessors for the resolver-layer cache state (see ensureLayersResolved). */
|
|
134
|
+
internal val layerGenerationForTest: Int
|
|
135
|
+
get() = layerGeneration
|
|
136
|
+
|
|
137
|
+
internal val resolvedLayerGenerationForTest: Int
|
|
138
|
+
get() = resolvedLayerGeneration
|
|
139
|
+
|
|
140
|
+
internal val resolvedRequestLayerForTest: TransformableRequestConfig?
|
|
141
|
+
get() = resolvedRequestLayer
|
|
142
|
+
|
|
143
|
+
internal val resolvedBrowseLayerForTest: TransformableRequestConfig?
|
|
144
|
+
get() = resolvedBrowseLayer
|
|
104
145
|
|
|
105
146
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* The ImageContext parameter provides size hints for CDN URL generation.
|
|
147
|
+
* Maps produced artwork URIs back to (Track, artwork-config kind) so display-time bitmap loading
|
|
148
|
+
* can re-resolve Track-first. See [ArtworkResolutionRegistry].
|
|
109
149
|
*/
|
|
110
|
-
|
|
150
|
+
val artworkResolutions = ArtworkResolutionRegistry()
|
|
111
151
|
|
|
112
152
|
/**
|
|
113
153
|
* Sets the favorited track identifiers. Tracks will have their favorited field hydrated based on
|
|
@@ -118,6 +158,14 @@ class BrowserManager {
|
|
|
118
158
|
Timber.d("Set ${favoriteIds.size} favorite IDs")
|
|
119
159
|
}
|
|
120
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Sets the favorite match mode (propagated from the `favorite` capability). null disables
|
|
163
|
+
* row-heart hydration.
|
|
164
|
+
*/
|
|
165
|
+
fun setFavoriteMatch(match: FavoritesMatchMode?) {
|
|
166
|
+
favoriteMatch = match
|
|
167
|
+
}
|
|
168
|
+
|
|
121
169
|
/**
|
|
122
170
|
* Updates the favorite state for a single track identifier. Called when the heart button is
|
|
123
171
|
* tapped in media controllers.
|
|
@@ -133,40 +181,30 @@ class BrowserManager {
|
|
|
133
181
|
}
|
|
134
182
|
|
|
135
183
|
/**
|
|
136
|
-
* Hydrates the favorited field on a track based on the favoriteIds set.
|
|
137
|
-
*
|
|
138
|
-
* favorited.
|
|
184
|
+
* Hydrates the favorited field on a track based on the favoriteIds set. No-op unless favoriting
|
|
185
|
+
* is enabled (the `favorite` capability). Only playable (src-bearing) tracks are favoritable; the
|
|
186
|
+
* flag is set to true OR false so non-favorited tracks still show an (empty) heart. Doesn't
|
|
187
|
+
* overwrite API-provided values.
|
|
139
188
|
*/
|
|
140
189
|
private fun hydrateFavorite(track: Track): Track {
|
|
190
|
+
val match = favoriteMatch ?: return track
|
|
141
191
|
// Don't overwrite API-provided favorites
|
|
142
192
|
if (track.favorited != null) return track
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return
|
|
149
|
-
url = track.url,
|
|
150
|
-
src = track.src,
|
|
151
|
-
artwork = track.artwork,
|
|
152
|
-
artworkSource = track.artworkSource,
|
|
153
|
-
artworkCarPlayTinted = track.artworkCarPlayTinted,
|
|
154
|
-
title = track.title,
|
|
155
|
-
subtitle = track.subtitle,
|
|
156
|
-
artist = track.artist,
|
|
157
|
-
album = track.album,
|
|
158
|
-
description = track.description,
|
|
159
|
-
genre = track.genre,
|
|
160
|
-
duration = track.duration,
|
|
161
|
-
style = track.style,
|
|
162
|
-
childrenStyle = track.childrenStyle,
|
|
163
|
-
favorited = true,
|
|
164
|
-
groupTitle = track.groupTitle,
|
|
165
|
-
live = track.live,
|
|
166
|
-
imageRow = track.imageRow,
|
|
167
|
-
)
|
|
193
|
+
// Only playable tracks are favoritable
|
|
194
|
+
val src = track.src ?: return track
|
|
195
|
+
|
|
196
|
+
val isFavorited = isFavorite(src, match)
|
|
197
|
+
|
|
198
|
+
return track.copy(favorited = isFavorited)
|
|
168
199
|
}
|
|
169
200
|
|
|
201
|
+
/** Whether [src] is favorited under the given match mode. */
|
|
202
|
+
private fun isFavorite(src: String, match: FavoritesMatchMode): Boolean =
|
|
203
|
+
when (match) {
|
|
204
|
+
FavoritesMatchMode.EXACT -> favoriteIds.contains(src)
|
|
205
|
+
FavoritesMatchMode.PARTIAL -> favoriteIds.any { BrowserPathHelper.containsSegment(src, it) }
|
|
206
|
+
}
|
|
207
|
+
|
|
170
208
|
/** Hydrates favorites on all children of a ResolvedTrack. */
|
|
171
209
|
private fun hydrateChildren(resolvedTrack: ResolvedTrack): ResolvedTrack {
|
|
172
210
|
val children = resolvedTrack.children ?: return resolvedTrack
|
|
@@ -174,20 +212,29 @@ class BrowserManager {
|
|
|
174
212
|
return resolvedTrack.copy(children = hydratedChildren)
|
|
175
213
|
}
|
|
176
214
|
|
|
177
|
-
/** Cache a track by
|
|
215
|
+
/** Cache a track by id, url, and src for O(1) lookup from any mediaId form. */
|
|
178
216
|
private fun cacheTrack(track: Track) {
|
|
217
|
+
track.id?.takeUnless { it.isBlank() }?.let { trackCache.put(it, track) }
|
|
179
218
|
track.url?.let { trackCache.put(it, track) }
|
|
180
219
|
track.src?.let { trackCache.put(it, track) }
|
|
181
220
|
}
|
|
182
221
|
|
|
183
222
|
private fun cacheChildren(resolvedTrack: ResolvedTrack) {
|
|
184
|
-
resolvedTrack.children?.forEach { track ->
|
|
223
|
+
resolvedTrack.children?.forEach { track ->
|
|
224
|
+
cacheTrack(track)
|
|
225
|
+
// Image-row items are playable surfaces of their own: cache them (with
|
|
226
|
+
// their stamped contextual urls) so an id-keyed mediaId from an
|
|
227
|
+
// expanded Android Auto tile can find its way back to queue expansion.
|
|
228
|
+
track.imageRow?.forEach { item ->
|
|
229
|
+
cacheTrack(with(TrackFactory) { item.toTrack(groupTitle = track.title) })
|
|
230
|
+
}
|
|
231
|
+
}
|
|
185
232
|
}
|
|
186
233
|
|
|
187
234
|
/**
|
|
188
|
-
* Get a cached Track by mediaId (url or src), or null if not cached. Used by Media3
|
|
189
|
-
* MediaItem shells with full track metadata. Re-hydrates favorites in case
|
|
190
|
-
* called after caching.
|
|
235
|
+
* Get a cached Track by mediaId (stable id, url, or src), or null if not cached. Used by Media3
|
|
236
|
+
* to rehydrate MediaItem shells with full track metadata. Re-hydrates favorites in case
|
|
237
|
+
* setFavoriteStates was called after caching.
|
|
191
238
|
*/
|
|
192
239
|
fun getCachedTrack(mediaId: String): Track? {
|
|
193
240
|
// Try direct lookup first (matches url or src)
|
|
@@ -212,27 +259,76 @@ class BrowserManager {
|
|
|
212
259
|
}
|
|
213
260
|
|
|
214
261
|
/**
|
|
215
|
-
* Resolves
|
|
216
|
-
*
|
|
262
|
+
* Resolves a tapped mediaId to the contextual url to expand a queue from, or null when there is
|
|
263
|
+
* none. A contextual mediaId is its own; a stable-id mediaId (see TrackFactory.buildMediaItem)
|
|
264
|
+
* resolves through the track cache to the contextual url of the container it was most recently
|
|
265
|
+
* browsed in — a legacy car controller round-trips only the mediaId, so the cache is what
|
|
266
|
+
* remembers which list the row came from.
|
|
267
|
+
*/
|
|
268
|
+
fun contextualUrlFor(mediaId: String): String? {
|
|
269
|
+
if (BrowserPathHelper.isContextual(mediaId)) return mediaId
|
|
270
|
+
return getCachedTrack(mediaId)?.url?.takeIf { BrowserPathHelper.isContextual(it) }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Resolves a single Media3 MediaItem to a Track. Prefers the track cache (keyed by id, url, and
|
|
275
|
+
* src).
|
|
217
276
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
277
|
+
* A cache miss is legitimately reachable: a controller can replay a mediaId this process never
|
|
278
|
+
* browsed — e.g. a search-result track after process death. When the mediaId is a playable URL,
|
|
279
|
+
* or the item's requestMetadata carries the playable uri (stamped by TrackFactory for stable-id
|
|
280
|
+
* mediaIds, and round-tripped by Media3 controllers), fall back to a minimal track built from the
|
|
281
|
+
* item's own metadata instead of failing playback.
|
|
282
|
+
*
|
|
283
|
+
* @throws IllegalStateException if the mediaId is not cached and no playable URL is available
|
|
220
284
|
*/
|
|
221
|
-
fun
|
|
222
|
-
|
|
223
|
-
Timber.d("=== Resolving from cache: mediaId='$mediaId' ===")
|
|
285
|
+
private fun resolveMediaItemToTrack(mediaItem: MediaItem): Track {
|
|
286
|
+
val mediaId = mediaItem.mediaId
|
|
224
287
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
288
|
+
getCachedTrack(mediaId)?.let { cachedTrack ->
|
|
289
|
+
Timber.d("Resolved mediaId='$mediaId' from cache: '${cachedTrack.title}'")
|
|
290
|
+
return cachedTrack
|
|
291
|
+
}
|
|
229
292
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
293
|
+
val fallbackSrc =
|
|
294
|
+
if (mediaId.startsWith("http://") || mediaId.startsWith("https://")) {
|
|
295
|
+
mediaId
|
|
296
|
+
} else {
|
|
297
|
+
mediaItem.requestMetadata.mediaUri?.toString()
|
|
298
|
+
}
|
|
299
|
+
if (fallbackSrc != null) {
|
|
300
|
+
Timber.w("Cache MISS for mediaId='$mediaId' - building minimal track from media item")
|
|
301
|
+
val metadata = mediaItem.mediaMetadata
|
|
302
|
+
return hydrateFavorite(
|
|
303
|
+
Track(
|
|
304
|
+
// A mediaId distinct from the playable uri is the track's stable id —
|
|
305
|
+
// keep it so the item's identity (car now-playing row match) survives.
|
|
306
|
+
id = mediaId.takeIf { it != fallbackSrc },
|
|
307
|
+
url = null,
|
|
308
|
+
src = fallbackSrc,
|
|
309
|
+
artwork = artworkOf(metadata.artworkUri?.toString()),
|
|
310
|
+
artworkSource = null,
|
|
311
|
+
request = null,
|
|
312
|
+
artworkCarPlayTinted = null,
|
|
313
|
+
title = metadata.title?.toString() ?: mediaId,
|
|
314
|
+
subtitle = metadata.artist?.toString(),
|
|
315
|
+
artist = null,
|
|
316
|
+
albumUrl = null,
|
|
317
|
+
album = metadata.albumTitle?.toString(),
|
|
318
|
+
description = metadata.description?.toString(),
|
|
319
|
+
genre = metadata.genre?.toString(),
|
|
320
|
+
duration = null,
|
|
321
|
+
style = null,
|
|
322
|
+
childrenStyle = null,
|
|
323
|
+
favorited = null,
|
|
324
|
+
groupTitle = null,
|
|
325
|
+
live = null,
|
|
326
|
+
imageRow = null,
|
|
327
|
+
)
|
|
234
328
|
)
|
|
235
329
|
}
|
|
330
|
+
|
|
331
|
+
throw IllegalStateException("MediaItem not found in cache: $mediaId")
|
|
236
332
|
}
|
|
237
333
|
|
|
238
334
|
/**
|
|
@@ -249,7 +345,7 @@ class BrowserManager {
|
|
|
249
345
|
* @param startIndex Index of the item to start playing
|
|
250
346
|
* @param startPositionMs Position within the start item to begin playback
|
|
251
347
|
* @return MediaSession.MediaItemsWithStartPosition ready for Media3
|
|
252
|
-
* @throws IllegalStateException if
|
|
348
|
+
* @throws IllegalStateException if a mediaId is neither cached nor a playable URL
|
|
253
349
|
*/
|
|
254
350
|
suspend fun resolveMediaItemsForPlayback(
|
|
255
351
|
mediaItems: List<MediaItem>,
|
|
@@ -270,10 +366,13 @@ class BrowserManager {
|
|
|
270
366
|
val searchTracks = searchResults.children
|
|
271
367
|
|
|
272
368
|
if (searchTracks != null && searchTracks.isNotEmpty()) {
|
|
273
|
-
// Find the selected track in search results
|
|
369
|
+
// Find the selected track in search results (mediaId is the stable id
|
|
370
|
+
// when the track has one, else url/src — see TrackFactory)
|
|
274
371
|
val mediaId = mediaItem.mediaId
|
|
275
372
|
val selectedIndex =
|
|
276
|
-
searchTracks.indexOfFirst { track ->
|
|
373
|
+
searchTracks.indexOfFirst { track ->
|
|
374
|
+
track.id == mediaId || track.url == mediaId || track.src == mediaId
|
|
375
|
+
}
|
|
277
376
|
|
|
278
377
|
if (selectedIndex >= 0) {
|
|
279
378
|
Timber.d(
|
|
@@ -297,11 +396,14 @@ class BrowserManager {
|
|
|
297
396
|
}
|
|
298
397
|
|
|
299
398
|
val mediaId = mediaItems[0].mediaId
|
|
399
|
+
// A failed search match falls through to plain resolution, never to browse
|
|
400
|
+
// expansion — the user asked for that one result, not a browsed list.
|
|
401
|
+
val contextualUrl = if (searchQuery == null) contextualUrlFor(mediaId) else null
|
|
300
402
|
|
|
301
|
-
if (
|
|
302
|
-
Timber.d("Attempting queue expansion for mediaId='$mediaId'")
|
|
403
|
+
if (contextualUrl != null) {
|
|
404
|
+
Timber.d("Attempting queue expansion for mediaId='$mediaId' via '$contextualUrl'")
|
|
303
405
|
|
|
304
|
-
val expanded = expandQueueFromContextualUrl(
|
|
406
|
+
val expanded = expandQueueFromContextualUrl(contextualUrl)
|
|
305
407
|
|
|
306
408
|
if (expanded != null) {
|
|
307
409
|
val (tracks, selectedIndex) = expanded
|
|
@@ -318,12 +420,11 @@ class BrowserManager {
|
|
|
318
420
|
}
|
|
319
421
|
}
|
|
320
422
|
|
|
321
|
-
// No expansion - resolve from cache
|
|
322
|
-
val
|
|
323
|
-
val cachedTracks = resolveMediaIdsFromCache(mediaIds)
|
|
423
|
+
// No expansion - resolve from cache (with a minimal-track fallback for replayed mediaIds)
|
|
424
|
+
val resolvedTracks = mediaItems.map { resolveMediaItemToTrack(it) }
|
|
324
425
|
|
|
325
426
|
// Convert to Media3 MediaItems
|
|
326
|
-
val resolvedMediaItems =
|
|
427
|
+
val resolvedMediaItems = resolvedTracks.map { track -> TrackFactory.toMedia3(track) }
|
|
327
428
|
|
|
328
429
|
return MediaSession.MediaItemsWithStartPosition(resolvedMediaItems, startIndex, startPositionMs)
|
|
329
430
|
}
|
|
@@ -355,6 +456,11 @@ class BrowserManager {
|
|
|
355
456
|
if (useCache) {
|
|
356
457
|
contentCache.get(normalizedPath)?.let { cached ->
|
|
357
458
|
Timber.d("Content cache HIT for path='$normalizedPath'")
|
|
459
|
+
// Re-key the track cache even on a hit: an id-keyed lookup (stable-id
|
|
460
|
+
// mediaId → contextual url, see contextualUrlFor) must reflect the
|
|
461
|
+
// most recently *browsed* container, which a cached re-display
|
|
462
|
+
// otherwise wouldn't re-register.
|
|
463
|
+
cacheChildren(cached)
|
|
358
464
|
// Re-hydrate favorites in case they changed since caching
|
|
359
465
|
return hydrateChildren(cached)
|
|
360
466
|
}
|
|
@@ -387,35 +493,37 @@ class BrowserManager {
|
|
|
387
493
|
Timber.d("Invalidated content cache for path='$path'")
|
|
388
494
|
}
|
|
389
495
|
|
|
390
|
-
/** Clears all cached content.
|
|
496
|
+
/** Clears all cached content. */
|
|
391
497
|
fun clearContentCache() {
|
|
392
498
|
contentCache.evictAll()
|
|
499
|
+
// Bump the layer generation so request/browse resolver thunks are re-resolved on the next
|
|
500
|
+
// request (invalidateAllContent → clearContentCache is the documented re-resolve trigger).
|
|
501
|
+
layerGeneration += 1
|
|
502
|
+
// Invalidated content's artwork resolutions go with it (same staleness rule as the
|
|
503
|
+
// config setter).
|
|
504
|
+
artworkResolutions.clear()
|
|
505
|
+
onArtworkRegistriesCleared?.invoke()
|
|
393
506
|
Timber.d("Cleared all content cache")
|
|
394
507
|
}
|
|
395
508
|
|
|
396
509
|
private suspend fun resolveUncached(path: String): ResolvedTrack {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
510
|
+
// Match an explicit route (or the '*' default). With no match, fall back to
|
|
511
|
+
// the implicit default: fetch the path via the request + browse config.
|
|
512
|
+
val match = config.routes?.let { findBestRouteMatch(path, it) }
|
|
513
|
+
|
|
514
|
+
val resolvedTrack: ResolvedTrack
|
|
515
|
+
val effectiveArtworkConfig: ArtworkRequestConfig?
|
|
516
|
+
if (match != null) {
|
|
517
|
+
val (routeEntry, routeParams) = match
|
|
518
|
+
Timber.d("Matched route: ${routeEntry.path} with params: $routeParams")
|
|
519
|
+
resolvedTrack = resolveRouteEntry(routeEntry, path, routeParams)
|
|
520
|
+
effectiveArtworkConfig = routeEntry.artwork ?: config.artwork
|
|
521
|
+
} else {
|
|
522
|
+
Timber.d("No route matched for path: $path — using implicit default")
|
|
523
|
+
resolvedTrack = executeApiRequest(null, path, mapOf("path" to path))
|
|
524
|
+
effectiveArtworkConfig = config.artwork
|
|
401
525
|
}
|
|
402
526
|
|
|
403
|
-
// Find best matching route
|
|
404
|
-
val (routeEntry, routeParams) =
|
|
405
|
-
findBestRouteMatch(path, routes)
|
|
406
|
-
?: run {
|
|
407
|
-
Timber.e("No route matched for path: $path")
|
|
408
|
-
throw ContentNotFoundException(path)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
Timber.d("Matched route: ${routeEntry.path} with params: $routeParams")
|
|
412
|
-
|
|
413
|
-
// Resolve the track from the route
|
|
414
|
-
val resolvedTrack = resolveRouteEntry(routeEntry, path, routeParams)
|
|
415
|
-
|
|
416
|
-
// Get effective artwork config: per-route overrides global
|
|
417
|
-
val effectiveArtworkConfig = routeEntry.artwork ?: config.artwork
|
|
418
|
-
|
|
419
527
|
// Transform children: generate contextual URLs and transform artwork URLs
|
|
420
528
|
val transformedChildren =
|
|
421
529
|
resolvedTrack.children?.let { children ->
|
|
@@ -423,8 +531,11 @@ class BrowserManager {
|
|
|
423
531
|
children
|
|
424
532
|
.mapIndexed { index, track ->
|
|
425
533
|
async {
|
|
426
|
-
// Validate that track has stable identifier
|
|
427
|
-
|
|
534
|
+
// Validate that track has stable identifier. A track carrying an
|
|
535
|
+
// imageRow is exempt: a url-less row is a pure preview — never
|
|
536
|
+
// selected, navigated to, or cached (its items carry their own
|
|
537
|
+
// identity; on Android Auto it expands into them).
|
|
538
|
+
if (track.imageRow.isNullOrEmpty()) validateTrack(track, "Child track")
|
|
428
539
|
|
|
429
540
|
var transformedTrack = track
|
|
430
541
|
|
|
@@ -446,22 +557,35 @@ class BrowserManager {
|
|
|
446
557
|
)
|
|
447
558
|
}
|
|
448
559
|
|
|
449
|
-
//
|
|
450
|
-
//
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
val browseContext = ImageContext(null, null)
|
|
560
|
+
// Playable image-row items get contextual URLs like any list
|
|
561
|
+
// row, so a tile tap expands into its section's queue instead
|
|
562
|
+
// of a queue of one (ADR 0006).
|
|
563
|
+
transformedTrack.imageRow?.let { items ->
|
|
454
564
|
transformedTrack =
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
565
|
+
transformedTrack.copy(
|
|
566
|
+
imageRow =
|
|
567
|
+
items
|
|
568
|
+
.map { item ->
|
|
569
|
+
if (item.url == null && item.src != null) {
|
|
570
|
+
item.copy(url = BrowserPathHelper.build(path, item.src))
|
|
571
|
+
} else {
|
|
572
|
+
item
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
.toTypedArray()
|
|
462
576
|
)
|
|
463
577
|
}
|
|
464
578
|
|
|
579
|
+
// Transform artwork URL. At browse-time there is no display size info.
|
|
580
|
+
transformedTrack =
|
|
581
|
+
transformArtworkUrl(
|
|
582
|
+
transformedTrack,
|
|
583
|
+
effectiveArtworkConfig,
|
|
584
|
+
path,
|
|
585
|
+
index,
|
|
586
|
+
ImageContext(null, null),
|
|
587
|
+
)
|
|
588
|
+
|
|
465
589
|
transformedTrack
|
|
466
590
|
}
|
|
467
591
|
}
|
|
@@ -478,7 +602,7 @@ class BrowserManager {
|
|
|
478
602
|
}
|
|
479
603
|
|
|
480
604
|
/**
|
|
481
|
-
* Transforms a track's artwork
|
|
605
|
+
* Transforms a track's artwork via [resolveArtworkUrl]. Populates artworkSource with the
|
|
482
606
|
* transformed ImageSource, keeping artwork unchanged. Handles all edge cases: undefined returns,
|
|
483
607
|
* errors, missing artwork.
|
|
484
608
|
*
|
|
@@ -487,7 +611,6 @@ class BrowserManager {
|
|
|
487
611
|
private suspend fun transformArtworkUrl(
|
|
488
612
|
track: Track,
|
|
489
613
|
artworkConfig: ArtworkRequestConfig?,
|
|
490
|
-
resolver: suspend (Track, ArtworkRequestConfig?, ImageContext?) -> ImageSource?,
|
|
491
614
|
path: String,
|
|
492
615
|
index: Int,
|
|
493
616
|
imageContext: ImageContext? = null,
|
|
@@ -498,7 +621,7 @@ class BrowserManager {
|
|
|
498
621
|
}
|
|
499
622
|
|
|
500
623
|
return try {
|
|
501
|
-
val imageSource =
|
|
624
|
+
val imageSource = resolveArtworkUrl(track, artworkConfig, imageContext)
|
|
502
625
|
|
|
503
626
|
when {
|
|
504
627
|
// resolve returned null → no artwork source
|
|
@@ -511,6 +634,15 @@ class BrowserManager {
|
|
|
511
634
|
// resolve returned ImageSource → set artworkSource
|
|
512
635
|
else -> {
|
|
513
636
|
Timber.d("[$path] Child[$index] '${track.title}': artworkSource set: ${imageSource.uri}")
|
|
637
|
+
// Remember how this URI was produced so display-time loading (which only
|
|
638
|
+
// gets a URI from Media3) can re-resolve Track-first with a size hint.
|
|
639
|
+
// Register the per-route config only when it isn't the global fallback,
|
|
640
|
+
// so display-time resolution reads the *current* config.artwork.
|
|
641
|
+
artworkResolutions.register(
|
|
642
|
+
imageSource.uri,
|
|
643
|
+
track,
|
|
644
|
+
artworkConfig?.takeIf { it !== config.artwork },
|
|
645
|
+
)
|
|
514
646
|
track.copy(artworkSource = imageSource)
|
|
515
647
|
}
|
|
516
648
|
}
|
|
@@ -549,8 +681,20 @@ class BrowserManager {
|
|
|
549
681
|
return null
|
|
550
682
|
}
|
|
551
683
|
|
|
684
|
+
// Queue scope is the tapped section, not the whole page (ADR 0006). An
|
|
685
|
+
// id that no longer appears on the page aborts the expansion — the
|
|
686
|
+
// caller falls back to the stored single track; silently queueing the
|
|
687
|
+
// changed list would resume the wrong station.
|
|
688
|
+
val sectionTracks =
|
|
689
|
+
when (val section = SectionScope.section(children.toList(), trackId)) {
|
|
690
|
+
is SectionScope.Section.ImageRow ->
|
|
691
|
+
with(TrackFactory) { section.items.map { it.toTrack(groupTitle = null) } }
|
|
692
|
+
is SectionScope.Section.Run -> section.tracks
|
|
693
|
+
null -> return null
|
|
694
|
+
}
|
|
695
|
+
|
|
552
696
|
// Filter to only playable tracks (tracks with src)
|
|
553
|
-
val playableTracks =
|
|
697
|
+
val playableTracks = sectionTracks.filter { track -> track.src != null }
|
|
554
698
|
|
|
555
699
|
if (playableTracks.isEmpty()) {
|
|
556
700
|
Timber.w("Parent has no playable tracks, cannot expand queue")
|
|
@@ -642,8 +786,9 @@ class BrowserManager {
|
|
|
642
786
|
* @return Array of Track results, or null if not found
|
|
643
787
|
*/
|
|
644
788
|
fun getCachedSearchResults(query: String): Array<Track>? {
|
|
645
|
-
|
|
646
|
-
|
|
789
|
+
val (cachedQuery, cachedResults) = lastSearch ?: return null
|
|
790
|
+
if (query != cachedQuery) return null
|
|
791
|
+
return cachedResults.map { hydrateFavorite(it) }.toTypedArray()
|
|
647
792
|
}
|
|
648
793
|
|
|
649
794
|
/**
|
|
@@ -662,6 +807,7 @@ class BrowserManager {
|
|
|
662
807
|
album = null,
|
|
663
808
|
title = null,
|
|
664
809
|
playlist = null,
|
|
810
|
+
reference = MediaReference.UNKNOWN,
|
|
665
811
|
)
|
|
666
812
|
)
|
|
667
813
|
}
|
|
@@ -686,10 +832,11 @@ class BrowserManager {
|
|
|
686
832
|
|
|
687
833
|
// Check if result is browsable-only (container/route) vs playable
|
|
688
834
|
// If it's browsable but also playable (has src or playable=true), treat it as playable
|
|
835
|
+
val firstResultUrl = firstResult.url
|
|
689
836
|
val tracksToFilter =
|
|
690
|
-
if (firstResult.src == null) {
|
|
691
|
-
Timber.d("First search result is browsable-only, resolving: $
|
|
692
|
-
val resolvedTrack = resolve(
|
|
837
|
+
if (firstResult.src == null && firstResultUrl != null) {
|
|
838
|
+
Timber.d("First search result is browsable-only, resolving: $firstResultUrl")
|
|
839
|
+
val resolvedTrack = resolve(firstResultUrl)
|
|
693
840
|
resolvedTrack.children
|
|
694
841
|
?.filter { it.src != null }
|
|
695
842
|
?.takeIf { it.isNotEmpty() }
|
|
@@ -717,6 +864,7 @@ class BrowserManager {
|
|
|
717
864
|
album = null,
|
|
718
865
|
title = null,
|
|
719
866
|
playlist = null,
|
|
867
|
+
reference = MediaReference.UNKNOWN,
|
|
720
868
|
)
|
|
721
869
|
)
|
|
722
870
|
}
|
|
@@ -735,20 +883,34 @@ class BrowserManager {
|
|
|
735
883
|
val searchPath = BrowserPathHelper.createSearchPath(params.query)
|
|
736
884
|
|
|
737
885
|
try {
|
|
738
|
-
// Execute search
|
|
739
|
-
|
|
886
|
+
// Execute search. Drop results without a stable identifier (url or src): search results come
|
|
887
|
+
// from server/JS data that doesn't pass through validateTrack like browse children do, and
|
|
888
|
+
// downstream conversion (TrackFactory.toMedia3) and browsable-result resolution require one.
|
|
889
|
+
val searchResults =
|
|
890
|
+
resolveSearch(params)
|
|
891
|
+
.filter { track ->
|
|
892
|
+
val valid = track.url != null || track.src != null
|
|
893
|
+
if (!valid) {
|
|
894
|
+
Timber.w("Dropping search result without url or src: '${track.title}'")
|
|
895
|
+
}
|
|
896
|
+
valid
|
|
897
|
+
}
|
|
898
|
+
.toTypedArray()
|
|
740
899
|
|
|
741
900
|
// Create ResolvedTrack
|
|
742
901
|
val searchResolvedTrack =
|
|
743
902
|
ResolvedTrack(
|
|
903
|
+
id = null,
|
|
744
904
|
url = searchPath,
|
|
745
905
|
title = "Search: ${params.query}",
|
|
746
906
|
children = searchResults,
|
|
747
907
|
carPlaySiriListButton = null,
|
|
748
908
|
artwork = null,
|
|
749
909
|
artworkSource = null,
|
|
910
|
+
request = null,
|
|
750
911
|
artworkCarPlayTinted = null,
|
|
751
912
|
artist = null,
|
|
913
|
+
albumUrl = null,
|
|
752
914
|
description = null,
|
|
753
915
|
subtitle = null,
|
|
754
916
|
album = null,
|
|
@@ -764,8 +926,7 @@ class BrowserManager {
|
|
|
764
926
|
)
|
|
765
927
|
|
|
766
928
|
// Cache search results for getCachedSearchResults()
|
|
767
|
-
|
|
768
|
-
lastSearchResults = searchResults
|
|
929
|
+
lastSearch = params.query to searchResults
|
|
769
930
|
|
|
770
931
|
// Cache individual tracks for Media3 lookups
|
|
771
932
|
cacheChildren(searchResolvedTrack)
|
|
@@ -780,14 +941,17 @@ class BrowserManager {
|
|
|
780
941
|
// Return empty search result on error
|
|
781
942
|
val emptySearchResult =
|
|
782
943
|
ResolvedTrack(
|
|
944
|
+
id = null,
|
|
783
945
|
url = searchPath,
|
|
784
946
|
title = "Search: ${params.query}",
|
|
785
947
|
children = emptyArray(),
|
|
786
948
|
carPlaySiriListButton = null,
|
|
787
949
|
artwork = null,
|
|
788
950
|
artworkSource = null,
|
|
951
|
+
request = null,
|
|
789
952
|
artworkCarPlayTinted = null,
|
|
790
953
|
artist = null,
|
|
954
|
+
albumUrl = null,
|
|
791
955
|
description = null,
|
|
792
956
|
subtitle = null,
|
|
793
957
|
album = null,
|
|
@@ -848,6 +1012,15 @@ class BrowserManager {
|
|
|
848
1012
|
onTabsChanged = callback
|
|
849
1013
|
}
|
|
850
1014
|
|
|
1015
|
+
/**
|
|
1016
|
+
* Set callback invoked whenever both [artworkResolutions] and the browse-artwork registry are
|
|
1017
|
+
* cleared (config swap or content invalidation). The caller should clear any parallel
|
|
1018
|
+
* [com.audiobrowser.browser.BrowseArtworkRegistry] instance it owns.
|
|
1019
|
+
*/
|
|
1020
|
+
fun setOnArtworkRegistriesCleared(callback: () -> Unit) {
|
|
1021
|
+
onArtworkRegistriesCleared = callback
|
|
1022
|
+
}
|
|
1023
|
+
|
|
851
1024
|
/**
|
|
852
1025
|
* Query navigation tabs from the configured tabs source. This is an async operation that resolves
|
|
853
1026
|
* the tabs configuration.
|
|
@@ -925,15 +1098,25 @@ class BrowserManager {
|
|
|
925
1098
|
entry.browseCallback?.let { callback ->
|
|
926
1099
|
Timber.d("Resolving route via callback")
|
|
927
1100
|
val param = BrowserSourceCallbackParam(path, routeParams)
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1101
|
+
// BrowserSourceCallback may return a BrowseResult synchronously or via a
|
|
1102
|
+
// Promise. Nitro flattens (ResolvedTrack | BrowseError) | Promise<BrowseResult>
|
|
1103
|
+
// into a 3-arm variant: sync track, sync error, or a Promise resolving to a
|
|
1104
|
+
// BrowseResult (which is itself a ResolvedTrack | BrowseError variant).
|
|
1105
|
+
return callback
|
|
1106
|
+
.invoke(param)
|
|
1107
|
+
.await()
|
|
1108
|
+
.match(
|
|
1109
|
+
first = { resolvedTrack -> resolvedTrack },
|
|
1110
|
+
second = { browseError -> throw CallbackException(browseError.error) },
|
|
1111
|
+
third = { promise ->
|
|
1112
|
+
promise
|
|
1113
|
+
.await()
|
|
1114
|
+
.match(
|
|
1115
|
+
first = { resolvedTrack -> resolvedTrack },
|
|
1116
|
+
second = { browseError -> throw CallbackException(browseError.error) },
|
|
1117
|
+
)
|
|
1118
|
+
},
|
|
1119
|
+
)
|
|
937
1120
|
}
|
|
938
1121
|
|
|
939
1122
|
entry.browseConfig?.let { apiConfig ->
|
|
@@ -999,34 +1182,119 @@ class BrowserManager {
|
|
|
999
1182
|
}
|
|
1000
1183
|
|
|
1001
1184
|
/**
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1185
|
+
* Resolves a single request/browse layer. When a resolver thunk is present it is invoked and its
|
|
1186
|
+
* result awaited. The resolver is Promise-only (the TS layer normalizes a sync-or-async thunk via
|
|
1187
|
+
* Promise.resolve), so its native shape is `Promise<Promise<TransformableRequestConfig>>` — a
|
|
1188
|
+
* double await. When there is no resolver the static layer config is returned as-is.
|
|
1189
|
+
*/
|
|
1190
|
+
private suspend fun resolveLayer(
|
|
1191
|
+
staticConfig: TransformableRequestConfig?,
|
|
1192
|
+
resolver: Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____?,
|
|
1193
|
+
): TransformableRequestConfig? {
|
|
1194
|
+
if (resolver == null) return staticConfig
|
|
1195
|
+
// Promise-only resolver (the TS layer wraps a sync-or-async thunk in
|
|
1196
|
+
// Promise.resolve) → double await: the bridge promise, then the JS promise.
|
|
1197
|
+
return resolver.invoke().await().await()
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Ensures the request/browse resolver layers are resolved for the current [layerGeneration],
|
|
1202
|
+
* caching the result. Re-resolves when the generation changes (config set or content
|
|
1203
|
+
* invalidation). Uses a simple generation guard with no in-flight cache: a benign idempotent
|
|
1204
|
+
* double-resolve under concurrent first-requests is acceptable, and on a thrown resolver nothing
|
|
1205
|
+
* is cached so the next request retries naturally.
|
|
1206
|
+
*/
|
|
1207
|
+
internal suspend fun ensureLayersResolved() {
|
|
1208
|
+
if (resolvedLayerGeneration == layerGeneration) return
|
|
1209
|
+
val generation = layerGeneration
|
|
1210
|
+
val req = resolveLayer(config.request, config.requestResolver)
|
|
1211
|
+
val brw = resolveLayer(config.browse, config.browseResolver)
|
|
1212
|
+
// A newer generation started while we were awaiting — drop this stale result.
|
|
1213
|
+
if (generation != layerGeneration) return
|
|
1214
|
+
resolvedRequestLayer = req
|
|
1215
|
+
resolvedBrowseLayer = brw
|
|
1216
|
+
resolvedLayerGeneration = generation
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Ensures the request layer is resolved for the current generation and returns it (the resolver
|
|
1221
|
+
* result when a [BrowserConfig.requestResolver] is configured, else the static
|
|
1222
|
+
* [BrowserConfig.request]).
|
|
1223
|
+
*
|
|
1224
|
+
* Consumers outside the browse path (media URL building, artwork) must obtain the request layer
|
|
1225
|
+
* through this accessor rather than reading the static `config.request`, so a resolver-only
|
|
1226
|
+
* consumer still gets a baseUrl/headers/transform for media, artwork, and now-playing artwork.
|
|
1227
|
+
*/
|
|
1228
|
+
internal suspend fun resolvedRequestConfig(): TransformableRequestConfig? {
|
|
1229
|
+
ensureLayersResolved()
|
|
1230
|
+
return resolvedRequestLayer
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* Builds the HTTP request for an API-backed path by layering request (shared) → kind
|
|
1235
|
+
* (browse/search) → route configs. Each layer's transform receives the previous layer's output; a
|
|
1236
|
+
* layer with no transform merges its static fields.
|
|
1237
|
+
*
|
|
1238
|
+
* `initialQuery` seeds query params onto the BASE the kind layer receives (e.g. search q/mode/…):
|
|
1239
|
+
* a layer with a transform "wins completely" and is handed only the base, so params placed on a
|
|
1240
|
+
* layer's own static query would be dropped before the transform runs. The same goes for `path`:
|
|
1241
|
+
* it is carried from the base through every layer (only a transform may change it), so a kind
|
|
1242
|
+
* whose config supplies the path (search) must seed it via the `path` parameter. Mirrors iOS
|
|
1243
|
+
* `buildApiRequest`.
|
|
1244
|
+
*
|
|
1245
|
+
* @throws ContentNotFoundException when no layer supplies a baseUrl — there is nothing to fetch,
|
|
1246
|
+
* so the path is genuinely "not found" rather than a network error (mirrors iOS's `guard let
|
|
1247
|
+
* baseUrl`).
|
|
1248
|
+
*/
|
|
1249
|
+
internal suspend fun buildApiRequest(
|
|
1250
|
+
kindConfig: TransformableRequestConfig?,
|
|
1251
|
+
routeConfig: TransformableRequestConfig?,
|
|
1252
|
+
path: String?,
|
|
1253
|
+
params: Map<String, String>,
|
|
1254
|
+
initialQuery: Map<String, String>? = null,
|
|
1255
|
+
): HttpClient.HttpRequest {
|
|
1256
|
+
// Resolve the request/browse resolver thunks once per content generation (cached).
|
|
1257
|
+
ensureLayersResolved()
|
|
1258
|
+
|
|
1259
|
+
var merged =
|
|
1260
|
+
RequestConfig(
|
|
1261
|
+
method = null,
|
|
1262
|
+
path = path,
|
|
1263
|
+
baseUrl = null,
|
|
1264
|
+
headers = null,
|
|
1265
|
+
query = null,
|
|
1266
|
+
body = null,
|
|
1267
|
+
contentType = null,
|
|
1268
|
+
userAgent = null,
|
|
1269
|
+
)
|
|
1270
|
+
resolvedRequestLayer?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
|
|
1271
|
+
if (!initialQuery.isNullOrEmpty()) {
|
|
1272
|
+
merged = merged.copy(query = (merged.query ?: emptyMap()) + initialQuery)
|
|
1273
|
+
}
|
|
1274
|
+
kindConfig?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
|
|
1275
|
+
routeConfig?.let { merged = RequestConfigBuilder.mergeConfig(merged, it, params) }
|
|
1276
|
+
|
|
1277
|
+
if (merged.baseUrl.isNullOrBlank()) {
|
|
1278
|
+
throw ContentNotFoundException(path ?: "")
|
|
1279
|
+
}
|
|
1280
|
+
return RequestConfigBuilder.buildHttpRequest(merged)
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Execute an API request for browser content. Request building (layering + transforms + baseUrl
|
|
1285
|
+
* guard) lives in [buildApiRequest]; this adds the browse-specific response shape (a
|
|
1286
|
+
* ResolvedTrack page object).
|
|
1004
1287
|
*/
|
|
1005
1288
|
private suspend fun executeApiRequest(
|
|
1006
|
-
apiConfig: TransformableRequestConfig
|
|
1289
|
+
apiConfig: TransformableRequestConfig?,
|
|
1007
1290
|
path: String,
|
|
1008
1291
|
routeParams: Map<String, String>,
|
|
1009
1292
|
): ResolvedTrack {
|
|
1010
1293
|
return withContext(Dispatchers.IO) {
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
?: RequestConfig(
|
|
1017
|
-
path = path,
|
|
1018
|
-
method = null,
|
|
1019
|
-
baseUrl = null,
|
|
1020
|
-
headers = null,
|
|
1021
|
-
query = null,
|
|
1022
|
-
body = null,
|
|
1023
|
-
contentType = null,
|
|
1024
|
-
userAgent = null,
|
|
1025
|
-
)
|
|
1026
|
-
val mergedConfig = RequestConfigBuilder.mergeConfig(baseConfig, apiConfig, routeParams)
|
|
1027
|
-
|
|
1028
|
-
// 2. Build and execute HTTP request
|
|
1029
|
-
val httpRequest = RequestConfigBuilder.buildHttpRequest(mergedConfig)
|
|
1294
|
+
// request (shared) → browse (kind) → route. apiConfig is null for the
|
|
1295
|
+
// implicit default (an unmatched browse path → fetch via request + browse + path).
|
|
1296
|
+
ensureLayersResolved()
|
|
1297
|
+
val httpRequest = buildApiRequest(resolvedBrowseLayer, apiConfig, path, routeParams)
|
|
1030
1298
|
val response = httpClient.request(httpRequest)
|
|
1031
1299
|
|
|
1032
1300
|
response.fold(
|
|
@@ -1066,24 +1334,10 @@ class BrowserManager {
|
|
|
1066
1334
|
): Array<Track> {
|
|
1067
1335
|
return withContext(Dispatchers.IO) {
|
|
1068
1336
|
try {
|
|
1069
|
-
// 1. Start with base config
|
|
1070
|
-
val baseConfig =
|
|
1071
|
-
config.request?.let { RequestConfigBuilder.toRequestConfig(it) }
|
|
1072
|
-
?: RequestConfig(
|
|
1073
|
-
method = null,
|
|
1074
|
-
path = null,
|
|
1075
|
-
baseUrl = null,
|
|
1076
|
-
headers = null,
|
|
1077
|
-
query = null,
|
|
1078
|
-
body = null,
|
|
1079
|
-
contentType = null,
|
|
1080
|
-
userAgent = null,
|
|
1081
|
-
)
|
|
1082
|
-
|
|
1083
|
-
// 2. Build query parameters from SearchParams
|
|
1084
1337
|
val searchQueryParams = buildMap {
|
|
1085
1338
|
put("q", params.query)
|
|
1086
1339
|
params.mode?.let { put("mode", it.toString().lowercase()) }
|
|
1340
|
+
if (params.reference == MediaReference.MY) put("reference", "my")
|
|
1087
1341
|
params.genre?.let { put("genre", it) }
|
|
1088
1342
|
params.artist?.let { put("artist", it) }
|
|
1089
1343
|
params.album?.let { put("album", it) }
|
|
@@ -1091,31 +1345,26 @@ class BrowserManager {
|
|
|
1091
1345
|
params.playlist?.let { put("playlist", it) }
|
|
1092
1346
|
}
|
|
1093
1347
|
|
|
1094
|
-
//
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1348
|
+
// request (shared) → search (kind); no browse layer and no route — search
|
|
1349
|
+
// is its own kind. The search params seed the base (see buildApiRequest
|
|
1350
|
+
// docs), and so does the search config's path: a layer's static path
|
|
1351
|
+
// never applies (the path is carried from the base), so the caller
|
|
1352
|
+
// seeds it — mirrors the web stub's fetchSearchResults.
|
|
1353
|
+
val httpRequest =
|
|
1354
|
+
buildApiRequest(
|
|
1355
|
+
kindConfig = apiConfig,
|
|
1356
|
+
routeConfig = null,
|
|
1099
1357
|
path = apiConfig.path,
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
query = (apiConfig.query ?: emptyMap()) + searchQueryParams,
|
|
1103
|
-
body = apiConfig.body,
|
|
1104
|
-
contentType = apiConfig.contentType,
|
|
1105
|
-
userAgent = apiConfig.userAgent,
|
|
1358
|
+
params = emptyMap(),
|
|
1359
|
+
initialQuery = searchQueryParams,
|
|
1106
1360
|
)
|
|
1107
|
-
|
|
1108
|
-
// 3. Merge configs and apply transform if provided
|
|
1109
|
-
var mergedConfig = RequestConfigBuilder.mergeConfig(baseConfig, searchConfig, emptyMap())
|
|
1110
|
-
|
|
1111
|
-
// 4. Build and execute HTTP request
|
|
1112
|
-
val httpRequest = RequestConfigBuilder.buildHttpRequest(mergedConfig)
|
|
1113
1361
|
val response = httpClient.request(httpRequest)
|
|
1114
1362
|
|
|
1115
1363
|
response.fold(
|
|
1116
1364
|
onSuccess = { httpResponse ->
|
|
1117
1365
|
if (httpResponse.isSuccessful) {
|
|
1118
|
-
//
|
|
1366
|
+
// The search endpoint returns a bare Track array (unlike browse,
|
|
1367
|
+
// which returns a page object). iOS parses it the same way.
|
|
1119
1368
|
val jsonTracks = json.decodeFromString<List<JsonTrack>>(httpResponse.body)
|
|
1120
1369
|
jsonTracks.map { it.toNitro() }.toTypedArray()
|
|
1121
1370
|
} else {
|
|
@@ -1156,8 +1405,23 @@ class CallbackException(message: String) : Exception(message)
|
|
|
1156
1405
|
*/
|
|
1157
1406
|
data class BrowserConfig(
|
|
1158
1407
|
val request: TransformableRequestConfig? = null,
|
|
1408
|
+
// Resolver thunk for the shared request layer. When set, it is resolved once per content
|
|
1409
|
+
// generation (re-resolved after invalidateAllContent), cached, and merged per request — instead
|
|
1410
|
+
// of carrying a static `request`. `request` and `requestResolver` are mutually exclusive in
|
|
1411
|
+
// practice (the consumer sets one or the other), but both are merged if present.
|
|
1412
|
+
val requestResolver:
|
|
1413
|
+
Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____? =
|
|
1414
|
+
null,
|
|
1415
|
+
val browse: TransformableRequestConfig? = null,
|
|
1416
|
+
// Resolver thunk for the browse layer. See `requestResolver`.
|
|
1417
|
+
val browseResolver:
|
|
1418
|
+
Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____? =
|
|
1419
|
+
null,
|
|
1159
1420
|
val media: MediaRequestConfig? = null,
|
|
1160
1421
|
val artwork: ArtworkRequestConfig? = null,
|
|
1422
|
+
// Now-playing-only artwork configuration (lock screen / notification / Android Auto now-playing).
|
|
1423
|
+
// A distinct kind from `artwork`; the now-playing path falls back to `artwork` when this is null.
|
|
1424
|
+
val nowPlayingArtwork: ArtworkRequestConfig? = null,
|
|
1161
1425
|
// Routes as array with flattened entries (includes __tabs__, __search__, and __default__ special
|
|
1162
1426
|
// routes)
|
|
1163
1427
|
val routes: Array<NativeRouteEntry>? = null,
|