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
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.util.url
|
|
4
|
+
import com.margelo.nitro.audiobrowser.FormatNowPlayingParams
|
|
5
|
+
import com.margelo.nitro.audiobrowser.NowPlayingMetadata
|
|
6
|
+
import com.margelo.nitro.audiobrowser.NowPlayingUpdate
|
|
7
|
+
import com.margelo.nitro.audiobrowser.PlaybackError
|
|
8
|
+
import com.margelo.nitro.audiobrowser.PlaybackState
|
|
9
|
+
import com.margelo.nitro.audiobrowser.StallReason
|
|
10
|
+
import com.margelo.nitro.audiobrowser.TimedMetadata
|
|
11
|
+
import com.margelo.nitro.audiobrowser.Track
|
|
12
|
+
import kotlinx.coroutines.CoroutineScope
|
|
13
|
+
import kotlinx.coroutines.Job
|
|
14
|
+
import kotlinx.coroutines.cancel
|
|
15
|
+
import kotlinx.coroutines.delay
|
|
16
|
+
import kotlinx.coroutines.launch
|
|
17
|
+
import timber.log.Timber
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What the now-playing logic needs from the platform — implemented by [Player] (current playback
|
|
21
|
+
* reads, MediaItem stamping via `replaceMediaItem`, artwork resolution through the browser).
|
|
22
|
+
* Mirrors iOS's NowPlayingUpdater / NowPlayingInfoController split: this seam is what makes the
|
|
23
|
+
* precedence/guard/dedupe logic testable with a fake.
|
|
24
|
+
*/
|
|
25
|
+
interface NowPlayingSurface {
|
|
26
|
+
val currentIndex: Int?
|
|
27
|
+
val currentTrack: Track?
|
|
28
|
+
val playbackState: PlaybackState
|
|
29
|
+
val playbackError: PlaybackError?
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* MUST be the real play/pause intent read from the underlying ExoPlayer — not the
|
|
33
|
+
* InterceptingPlayer-masked value, which can report false through a masked terminal error to keep
|
|
34
|
+
* the session paused-but-alive. The formatter receives this; feeding it the masked value breaks
|
|
35
|
+
* its output exactly in the error case the mask exists for.
|
|
36
|
+
*/
|
|
37
|
+
val playWhenReady: Boolean
|
|
38
|
+
val isRebuffering: Boolean
|
|
39
|
+
|
|
40
|
+
/** Device connectivity, used to classify a stall as offline vs a plain rebuffer. */
|
|
41
|
+
val isOnline: Boolean
|
|
42
|
+
|
|
43
|
+
val hasNowPlayingArtworkConfig: Boolean
|
|
44
|
+
|
|
45
|
+
/** Stamp title/secondary/album onto the playing item. */
|
|
46
|
+
fun stampFields(index: Int, track: Track, title: String?, secondaryLine: String?, album: String?)
|
|
47
|
+
|
|
48
|
+
/** Stamp a resolved artwork uri onto the playing item. */
|
|
49
|
+
fun stampArtwork(index: Int, track: Track, uri: String)
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the now-playing artwork for [track] via the `nowPlayingArtwork` config kind. Returns
|
|
53
|
+
* the final uri, or null when resolution produced nothing (errors are logged inside).
|
|
54
|
+
*/
|
|
55
|
+
suspend fun resolveNowPlayingArtwork(track: Track, sizePx: Double): String?
|
|
56
|
+
|
|
57
|
+
fun emitNowPlayingChanged(metadata: NowPlayingMetadata)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Owns the Now Playing rendering pipeline: the flash > override > formatter > track precedence, the
|
|
62
|
+
* publish dedupe, the stale-result guards (render generation + track-id keying), timed (ICY)
|
|
63
|
+
* metadata, and the once-per-track artwork keying. Mirrors iOS's NowPlayingUpdater. The platform is
|
|
64
|
+
* reached only through [NowPlayingSurface]; the JS formatter arrives as a plain suspend lambda
|
|
65
|
+
* (wrapped at the Nitro boundary in Player.setup), so every guard here runs under JVM tests.
|
|
66
|
+
*/
|
|
67
|
+
class NowPlayingUpdater(private val surface: NowPlayingSurface, private val scope: CoroutineScope) {
|
|
68
|
+
|
|
69
|
+
/** Whether now-playing rendering (override/flash/formatter) is enabled (PlayerSetupOptions). */
|
|
70
|
+
var enabled = true
|
|
71
|
+
|
|
72
|
+
/** The JS formatter, wrapped to a plain suspend call at the Nitro boundary (see Player.setup). */
|
|
73
|
+
var formatter: (suspend (FormatNowPlayingParams) -> NowPlayingUpdate?)? = null
|
|
74
|
+
|
|
75
|
+
/** Latest live timed (ICY/ID3) metadata; cleared on track change. */
|
|
76
|
+
@Volatile private var latestTimedMetadata: TimedMetadata? = null
|
|
77
|
+
|
|
78
|
+
/** Imperative override (from `updateNowPlaying()`); cleared on track change. */
|
|
79
|
+
private var override: NowPlayingUpdate? = null
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Transient now-playing fields (e.g. feedback for a refused remote command). Outranks the
|
|
83
|
+
* formatter and the override while active, so live metadata can't stomp it mid-flash. Reverted by
|
|
84
|
+
* a coroutine delay on [scope] — NOT a JS timer, which pauses with a backgrounded host (and the
|
|
85
|
+
* lock screen is exactly the backgrounded case) — and cleared early on track change.
|
|
86
|
+
*/
|
|
87
|
+
private var flash: NowPlayingUpdate? = null
|
|
88
|
+
private var flashRevert: Job? = null
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Monotonic stamp for renders. An async formatter result applies only when no newer render —
|
|
92
|
+
* notably a flash — has superseded the one that launched it.
|
|
93
|
+
*/
|
|
94
|
+
private var renderGeneration = 0L
|
|
95
|
+
|
|
96
|
+
/** The last fields actually published, to dedupe redundant stamps. */
|
|
97
|
+
private data class Published(
|
|
98
|
+
val index: Int,
|
|
99
|
+
val trackId: String?,
|
|
100
|
+
val title: String?,
|
|
101
|
+
val secondaryLine: String?,
|
|
102
|
+
val album: String?,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
private var lastPublished: Published? = null
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The track id whose now-playing artwork has been resolved (or is being resolved), so a
|
|
109
|
+
* `nowPlayingArtwork` resolve runs once per track instead of on every render.
|
|
110
|
+
*/
|
|
111
|
+
private var artworkResolvedForTrackId: String? = null
|
|
112
|
+
|
|
113
|
+
/** Size hint (px, square) for now-playing artwork. Mirrors iOS (screen-width-capped to 1200). */
|
|
114
|
+
private val artworkSizePx = 1200.0
|
|
115
|
+
|
|
116
|
+
/** Cancels in-flight work. Called from Player.destroy. */
|
|
117
|
+
fun destroy() {
|
|
118
|
+
scope.cancel()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Sets/clears the imperative override (null reverts to track metadata) and re-renders. */
|
|
122
|
+
fun updateNowPlaying(update: NowPlayingUpdate?) {
|
|
123
|
+
override = update
|
|
124
|
+
render()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun flashNowPlaying(update: NowPlayingUpdate, durationMs: Double) {
|
|
128
|
+
flashRevert?.cancel()
|
|
129
|
+
flash = update
|
|
130
|
+
flashRevert =
|
|
131
|
+
scope.launch {
|
|
132
|
+
delay(durationMs.toLong())
|
|
133
|
+
flash = null
|
|
134
|
+
flashRevert = null
|
|
135
|
+
render()
|
|
136
|
+
}
|
|
137
|
+
render()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Clears an active flash immediately, reverting to the live metadata. No-op when none. */
|
|
141
|
+
fun clearNowPlayingFlash() {
|
|
142
|
+
if (flash == null) return
|
|
143
|
+
cancelFlash()
|
|
144
|
+
render()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private fun cancelFlash() {
|
|
148
|
+
flashRevert?.cancel()
|
|
149
|
+
flashRevert = null
|
|
150
|
+
flash = null
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Gets the current now playing metadata (flash/override if set, else track metadata). */
|
|
154
|
+
fun getNowPlaying(): NowPlayingMetadata? {
|
|
155
|
+
val track = surface.currentTrack ?: return null
|
|
156
|
+
val flash = flash
|
|
157
|
+
val override = override
|
|
158
|
+
|
|
159
|
+
return NowPlayingMetadata(
|
|
160
|
+
elapsedTime = null,
|
|
161
|
+
title = flash?.title ?: override?.title ?: track.title,
|
|
162
|
+
album = flash?.album ?: override?.album ?: track.album,
|
|
163
|
+
artist = flash?.artist ?: override?.artist ?: track.artist,
|
|
164
|
+
duration = track.duration,
|
|
165
|
+
artwork = track.artwork?.url,
|
|
166
|
+
description = track.description,
|
|
167
|
+
mediaId = track.src ?: track.url,
|
|
168
|
+
genre = track.genre,
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Clears override, flash, and timed metadata on track change (PlayerListener transition). */
|
|
173
|
+
fun clearOverride() {
|
|
174
|
+
override = null
|
|
175
|
+
cancelFlash()
|
|
176
|
+
latestTimedMetadata = null
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Records the latest live timed (ICY/ID3) metadata and, when a formatter is configured, re-runs
|
|
181
|
+
* it so the live song is reflected on the now-playing surface.
|
|
182
|
+
*/
|
|
183
|
+
fun onTimedMetadataReceived(timed: TimedMetadata) {
|
|
184
|
+
latestTimedMetadata = timed
|
|
185
|
+
if (enabled && formatter != null) {
|
|
186
|
+
render()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Renders the current now playing metadata to the surface. Flash wins while active (formatter
|
|
192
|
+
* skipped entirely so its async result can't land on top); otherwise the override-or-track
|
|
193
|
+
* default is stamped immediately and a configured formatter customizes it asynchronously, guarded
|
|
194
|
+
* by track identity and [renderGeneration].
|
|
195
|
+
*/
|
|
196
|
+
fun render() {
|
|
197
|
+
val index = surface.currentIndex ?: return
|
|
198
|
+
val track = surface.currentTrack ?: return
|
|
199
|
+
renderGeneration += 1
|
|
200
|
+
val generation = renderGeneration
|
|
201
|
+
|
|
202
|
+
// Metadata source. When metadata is disabled, use the raw track (ignore override + formatter);
|
|
203
|
+
// otherwise the imperative `updateNowPlaying` override wins over the track's own fields.
|
|
204
|
+
val activeOverride = if (enabled) override else null
|
|
205
|
+
val defaultTitle = activeOverride?.title ?: track.title
|
|
206
|
+
val defaultSecondary = activeOverride?.artist ?: track.artist
|
|
207
|
+
val defaultAlbum = activeOverride?.album ?: track.album
|
|
208
|
+
|
|
209
|
+
// A flash outranks both the formatter and the override; while one is active the formatter pass
|
|
210
|
+
// is skipped entirely so its async result can't land on top.
|
|
211
|
+
val activeFlash = if (enabled) flash else null
|
|
212
|
+
if (activeFlash != null) {
|
|
213
|
+
applyFields(
|
|
214
|
+
index,
|
|
215
|
+
track,
|
|
216
|
+
activeFlash.title ?: defaultTitle,
|
|
217
|
+
activeFlash.artist ?: defaultSecondary,
|
|
218
|
+
activeFlash.album ?: defaultAlbum,
|
|
219
|
+
)
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Apply the default immediately so the now-playing never lags a track/status change.
|
|
224
|
+
applyFields(index, track, defaultTitle, defaultSecondary, defaultAlbum)
|
|
225
|
+
|
|
226
|
+
// If a formatter is configured, let it customize the fields asynchronously. Falls back to the
|
|
227
|
+
// default on null/throw.
|
|
228
|
+
val activeFormatter = if (enabled) formatter else null
|
|
229
|
+
if (activeFormatter != null) {
|
|
230
|
+
val capturedId = track.src ?: track.url
|
|
231
|
+
// Gate the raw load-control signal to the buffering state so `stalled` is correct on its
|
|
232
|
+
// own: ExoPlayer's rebuffering flag is polled on a different cadence than state transitions
|
|
233
|
+
// and can linger true into the PLAYING transition as a rebuffer recovers. Classify by
|
|
234
|
+
// connectivity so the formatter can show "offline" vs a plain rebuffer; null when not
|
|
235
|
+
// stalled.
|
|
236
|
+
val isStalled = surface.playbackState == PlaybackState.BUFFERING && surface.isRebuffering
|
|
237
|
+
val stalled =
|
|
238
|
+
if (isStalled) {
|
|
239
|
+
if (surface.isOnline) StallReason.BUFFERING else StallReason.OFFLINE
|
|
240
|
+
} else {
|
|
241
|
+
null
|
|
242
|
+
}
|
|
243
|
+
val params =
|
|
244
|
+
FormatNowPlayingParams(
|
|
245
|
+
track,
|
|
246
|
+
latestTimedMetadata,
|
|
247
|
+
surface.playWhenReady,
|
|
248
|
+
stalled,
|
|
249
|
+
surface.playbackError,
|
|
250
|
+
)
|
|
251
|
+
scope.launch {
|
|
252
|
+
val formatted =
|
|
253
|
+
try {
|
|
254
|
+
activeFormatter(params)
|
|
255
|
+
} catch (e: Exception) {
|
|
256
|
+
Timber.e(e, "NowPlaying formatter threw; using default")
|
|
257
|
+
null
|
|
258
|
+
}
|
|
259
|
+
// Apply only if still the same track (a fast skip must not be overwritten by a stale
|
|
260
|
+
// result) AND no newer render superseded this one — without the generation check, a
|
|
261
|
+
// formatter round-trip in flight when a flash starts lands a beat later and overwrites
|
|
262
|
+
// the flash (mirrors the iOS renderGeneration guard).
|
|
263
|
+
val current = surface.currentTrack
|
|
264
|
+
val currentIdx = surface.currentIndex
|
|
265
|
+
if (
|
|
266
|
+
formatted != null &&
|
|
267
|
+
current != null &&
|
|
268
|
+
currentIdx != null &&
|
|
269
|
+
(current.src ?: current.url) == capturedId &&
|
|
270
|
+
renderGeneration == generation
|
|
271
|
+
) {
|
|
272
|
+
applyFields(
|
|
273
|
+
currentIdx,
|
|
274
|
+
current,
|
|
275
|
+
formatted.title ?: defaultTitle,
|
|
276
|
+
formatted.artist ?: defaultSecondary,
|
|
277
|
+
formatted.album ?: defaultAlbum,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Stamps the fields via the surface, deduped on (index, track identity, fields). */
|
|
285
|
+
private fun applyFields(
|
|
286
|
+
index: Int,
|
|
287
|
+
track: Track,
|
|
288
|
+
title: String?,
|
|
289
|
+
secondaryLine: String?,
|
|
290
|
+
album: String?,
|
|
291
|
+
) {
|
|
292
|
+
// Skip republishing identical fields. The formatter is re-invoked on every state change, so
|
|
293
|
+
// the same fields are recomputed often; an unconditional stamp would churn the MediaSession
|
|
294
|
+
// (and flicker Android Auto / now-playing) for no visible change. Keyed on the track identity
|
|
295
|
+
// so a new track with a coincidentally identical line still publishes.
|
|
296
|
+
val published = Published(index, track.src ?: track.url, title, secondaryLine, album)
|
|
297
|
+
if (published == lastPublished) return
|
|
298
|
+
lastPublished = published
|
|
299
|
+
|
|
300
|
+
surface.stampFields(index, track, title, secondaryLine, album)
|
|
301
|
+
getNowPlaying()?.let { surface.emitNowPlayingChanged(it) }
|
|
302
|
+
|
|
303
|
+
// Resolve the now-playing-only artwork (lock screen / notification / Android Auto
|
|
304
|
+
// now-playing), keyed on track id so it runs once per track and not on every render.
|
|
305
|
+
maybeResolveArtwork(track)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Resolves the playing track's now-playing artwork and stamps it onto the playing media item.
|
|
310
|
+
* Guarded exactly like iOS: only when `nowPlayingArtwork` is configured AND the track has a
|
|
311
|
+
* non-empty id (so the `{id}` token never resolves to an empty string). Otherwise the existing
|
|
312
|
+
* artworkUri — which came from `artwork` (browse list path) — is left in place.
|
|
313
|
+
*/
|
|
314
|
+
private fun maybeResolveArtwork(track: Track) {
|
|
315
|
+
val trackId =
|
|
316
|
+
track.id?.takeIf { it.isNotEmpty() }
|
|
317
|
+
?: run {
|
|
318
|
+
// No id → skip nowPlayingArtwork entirely; keep the existing (browse) artworkUri.
|
|
319
|
+
artworkResolvedForTrackId = null
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!surface.hasNowPlayingArtworkConfig) {
|
|
324
|
+
// No now-playing artwork config (or no browser) → fall back to the existing artworkUri.
|
|
325
|
+
artworkResolvedForTrackId = null
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Already resolved (or resolving) for this track id — avoid a redundant resolve per render.
|
|
330
|
+
if (artworkResolvedForTrackId == trackId) return
|
|
331
|
+
artworkResolvedForTrackId = trackId
|
|
332
|
+
|
|
333
|
+
scope.launch {
|
|
334
|
+
val uri =
|
|
335
|
+
surface.resolveNowPlayingArtwork(track, artworkSizePx)?.takeIf { it.isNotEmpty() }
|
|
336
|
+
?: return@launch
|
|
337
|
+
|
|
338
|
+
// Apply only if still the same track (a fast skip must not be overwritten by a stale
|
|
339
|
+
// result).
|
|
340
|
+
val currentIdx = surface.currentIndex ?: return@launch
|
|
341
|
+
val current = surface.currentTrack ?: return@launch
|
|
342
|
+
if (current.id != trackId) return@launch
|
|
343
|
+
|
|
344
|
+
surface.stampArtwork(currentIdx, current, uri)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.PlaybackException
|
|
4
|
+
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException
|
|
5
|
+
import com.margelo.nitro.audiobrowser.PlaybackError
|
|
6
|
+
import com.margelo.nitro.audiobrowser.PlaybackErrorKind
|
|
7
|
+
import java.io.IOException
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Maps ExoPlayer's failure codes onto [PlaybackErrorKind], the cross-platform contract iOS
|
|
11
|
+
* populates from AVFoundation. Consumers branch on the kind; the raw ExoPlayer code still travels
|
|
12
|
+
* alongside it as `PlaybackError.code` for telemetry.
|
|
13
|
+
*
|
|
14
|
+
* Codes that carry no real evidence of a cause (`ERROR_CODE_UNSPECIFIED`,
|
|
15
|
+
* `ERROR_CODE_IO_UNSPECIFIED`, a bad HTTP status whose response code we could not recover) stay
|
|
16
|
+
* [PlaybackErrorKind.UNKNOWN] rather than being guessed into a nicer-sounding bucket — a wrong
|
|
17
|
+
* classification is worse than an honest one, both for the listener and for the aggregates.
|
|
18
|
+
*/
|
|
19
|
+
object PlaybackErrorClassifier {
|
|
20
|
+
/**
|
|
21
|
+
* Classifies a player failure.
|
|
22
|
+
*
|
|
23
|
+
* @param online whether the device had connectivity at the moment of failure. ExoPlayer's codes
|
|
24
|
+
* cannot distinguish "the device is offline" from "this stream is unreachable"; only the
|
|
25
|
+
* connectivity monitor can, which is why it is passed in.
|
|
26
|
+
*/
|
|
27
|
+
fun classify(error: PlaybackException, online: Boolean): PlaybackErrorKind {
|
|
28
|
+
if (!online) return PlaybackErrorKind.OFFLINE
|
|
29
|
+
val responseCode = responseCode(error)
|
|
30
|
+
if (responseCode != null) return kindForHttpStatus(responseCode)
|
|
31
|
+
return kindForErrorCode(error.errorCode)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The HTTP status the server answered with, if this failure came from an HTTP response. */
|
|
35
|
+
fun responseCode(error: PlaybackException): Int? =
|
|
36
|
+
generateSequence(error.cause) { it.cause }
|
|
37
|
+
.filterIsInstance<InvalidResponseCodeException>()
|
|
38
|
+
.firstOrNull()
|
|
39
|
+
?.responseCode
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Classifies a load error the retry policy is still working on, as an advisory (`retrying`)
|
|
43
|
+
* [PlaybackError]. Load errors arrive as raw [IOException]s (see
|
|
44
|
+
* [RetryLoadErrorHandlingPolicy.classifyError]), never as [PlaybackException] — that shape only
|
|
45
|
+
* exists once retries are exhausted. The policy only retries HTTP statuses and transport
|
|
46
|
+
* failures, so a non-HTTP retryable load error is by construction a network one.
|
|
47
|
+
*/
|
|
48
|
+
fun retryingLoadError(exception: IOException, online: Boolean): PlaybackError {
|
|
49
|
+
val responseCode =
|
|
50
|
+
generateSequence(exception as Throwable) { it.cause }
|
|
51
|
+
.filterIsInstance<InvalidResponseCodeException>()
|
|
52
|
+
.firstOrNull()
|
|
53
|
+
?.responseCode
|
|
54
|
+
// Fixed message per classification (mirrors iOS's localized descriptions): raw
|
|
55
|
+
// IOException messages vary between attempts of the same outage, which would defeat
|
|
56
|
+
// the caller's identical-repeat dedupe and churn the wire payload.
|
|
57
|
+
val (kind, code, message) =
|
|
58
|
+
when {
|
|
59
|
+
!online ->
|
|
60
|
+
Triple(PlaybackErrorKind.OFFLINE, "not-connected-to-internet", "No internet connection")
|
|
61
|
+
responseCode != null ->
|
|
62
|
+
Triple(
|
|
63
|
+
kindForHttpStatus(responseCode),
|
|
64
|
+
"io-bad-http-status",
|
|
65
|
+
"Server responded with HTTP $responseCode",
|
|
66
|
+
)
|
|
67
|
+
else ->
|
|
68
|
+
Triple(
|
|
69
|
+
PlaybackErrorKind.UNREACHABLE,
|
|
70
|
+
"io-network-connection-failed",
|
|
71
|
+
"Could not reach the stream host",
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
return PlaybackError(kind, code, message, responseCode?.toDouble(), retrying = true)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fun kindForHttpStatus(status: Int): PlaybackErrorKind =
|
|
78
|
+
when (status) {
|
|
79
|
+
404,
|
|
80
|
+
410 -> PlaybackErrorKind.NOT_FOUND
|
|
81
|
+
in 500..599 -> PlaybackErrorKind.SERVER_ERROR
|
|
82
|
+
// Every other 4xx is the server refusing us — auth, geo-blocking, a rate limit. All of them
|
|
83
|
+
// mean "you can't have this stream", not "retry".
|
|
84
|
+
in 400..499 -> PlaybackErrorKind.REJECTED
|
|
85
|
+
else -> PlaybackErrorKind.UNKNOWN
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private fun kindForErrorCode(errorCode: Int): PlaybackErrorKind =
|
|
89
|
+
when (errorCode) {
|
|
90
|
+
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
|
|
91
|
+
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT,
|
|
92
|
+
PlaybackException.ERROR_CODE_TIMEOUT -> PlaybackErrorKind.UNREACHABLE
|
|
93
|
+
|
|
94
|
+
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND -> PlaybackErrorKind.NOT_FOUND
|
|
95
|
+
|
|
96
|
+
// The server, the licence server, or the session refused us.
|
|
97
|
+
PlaybackException.ERROR_CODE_IO_NO_PERMISSION,
|
|
98
|
+
PlaybackException.ERROR_CODE_AUTHENTICATION_EXPIRED,
|
|
99
|
+
PlaybackException.ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
|
|
100
|
+
PlaybackException.ERROR_CODE_CONCURRENT_STREAM_LIMIT,
|
|
101
|
+
PlaybackException.ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
|
|
102
|
+
PlaybackException.ERROR_CODE_NOT_AVAILABLE_IN_REGION,
|
|
103
|
+
in DRM_CODES -> PlaybackErrorKind.REJECTED
|
|
104
|
+
|
|
105
|
+
// Fetched, but nothing we can turn into audio: wrong content type, malformed or unsupported
|
|
106
|
+
// container, a decoder that refused it, or an output track we could not open.
|
|
107
|
+
PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE,
|
|
108
|
+
PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED,
|
|
109
|
+
PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE,
|
|
110
|
+
PlaybackException.ERROR_CODE_NOT_SUPPORTED,
|
|
111
|
+
in PARSING_CODES,
|
|
112
|
+
in DECODING_CODES,
|
|
113
|
+
in AUDIO_TRACK_CODES -> PlaybackErrorKind.UNPLAYABLE
|
|
114
|
+
|
|
115
|
+
else -> PlaybackErrorKind.UNKNOWN
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private val PARSING_CODES =
|
|
119
|
+
PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED..PlaybackException
|
|
120
|
+
.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED
|
|
121
|
+
private val DECODING_CODES =
|
|
122
|
+
PlaybackException.ERROR_CODE_DECODER_INIT_FAILED..PlaybackException
|
|
123
|
+
.ERROR_CODE_DECODING_RESOURCES_RECLAIMED
|
|
124
|
+
private val AUDIO_TRACK_CODES =
|
|
125
|
+
PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED..PlaybackException
|
|
126
|
+
.ERROR_CODE_AUDIO_TRACK_OFFLOAD_INIT_FAILED
|
|
127
|
+
private val DRM_CODES =
|
|
128
|
+
PlaybackException.ERROR_CODE_DRM_UNSPECIFIED..PlaybackException.ERROR_CODE_DRM_LICENSE_EXPIRED
|
|
129
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.Player as MediaPlayer
|
|
4
|
+
import com.margelo.nitro.audiobrowser.PlaybackState
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An observation that may move the playback state machine. Context (does a track exist, is the
|
|
8
|
+
* engine audibly playing, the queue size) is captured into the event at the call site —
|
|
9
|
+
* [PlaybackStateMachine] itself stays pure.
|
|
10
|
+
*/
|
|
11
|
+
sealed interface PlaybackEvent {
|
|
12
|
+
/**
|
|
13
|
+
* ExoPlayer's playback state changed. [exoState] MUST be the real ExoPlayer state, not the
|
|
14
|
+
* InterceptingPlayer-masked value (which reports READY through a masked terminal error to keep
|
|
15
|
+
* the session alive — feeding that back in would clear the ERROR state).
|
|
16
|
+
*/
|
|
17
|
+
data class ExoPlaybackStateChanged(
|
|
18
|
+
@get:MediaPlayer.State val exoState: Int,
|
|
19
|
+
val mediaItemCount: Int,
|
|
20
|
+
val playWhenReady: Boolean,
|
|
21
|
+
) : PlaybackEvent
|
|
22
|
+
|
|
23
|
+
/** The active media item changed (auto-advance, skip, queue swap). */
|
|
24
|
+
data class MediaItemTransition(val hasTrack: Boolean, val isPlaying: Boolean) : PlaybackEvent
|
|
25
|
+
|
|
26
|
+
data class PlayWhenReadyChanged(val playWhenReady: Boolean) : PlaybackEvent
|
|
27
|
+
|
|
28
|
+
data class IsPlayingChanged(val isPlaying: Boolean) : PlaybackEvent
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The playback state transitions: the next states (in order) for an event from the current state,
|
|
33
|
+
* empty to suppress. The Android analog of iOS's `nextPlaybackState(from:on:)` — guards here are
|
|
34
|
+
* state-related ("not from ERROR/STOPPED"); context-related guards live in the event fields.
|
|
35
|
+
* Effects (error clearing, event emission, timers) stay with the caller: the machine only decides.
|
|
36
|
+
*/
|
|
37
|
+
object PlaybackStateMachine {
|
|
38
|
+
|
|
39
|
+
fun transitions(current: PlaybackState, event: PlaybackEvent): List<PlaybackState> =
|
|
40
|
+
when (event) {
|
|
41
|
+
is PlaybackEvent.ExoPlaybackStateChanged ->
|
|
42
|
+
when (event.exoState) {
|
|
43
|
+
MediaPlayer.STATE_BUFFERING -> listOf(PlaybackState.BUFFERING)
|
|
44
|
+
MediaPlayer.STATE_READY ->
|
|
45
|
+
// While playing (playWhenReady), READY is a transient before PLAYING —
|
|
46
|
+
// suppress it so consumers don't flash a settled/non-loading state
|
|
47
|
+
// mid-startup (none→loading→buffering→playing). When paused it IS the
|
|
48
|
+
// settled signal a buffer-that-finished-while-paused needs, so consumers
|
|
49
|
+
// can clear their loading spinner (mono#3325).
|
|
50
|
+
if (event.playWhenReady) emptyList() else listOf(PlaybackState.READY)
|
|
51
|
+
MediaPlayer.STATE_IDLE ->
|
|
52
|
+
// A terminal error (or an explicit stop) idles ExoPlayer; transitioning to NONE
|
|
53
|
+
// would clear the ERROR/STOPPED state the session is rendering.
|
|
54
|
+
if (current == PlaybackState.ERROR || current == PlaybackState.STOPPED) emptyList()
|
|
55
|
+
else listOf(PlaybackState.NONE)
|
|
56
|
+
MediaPlayer.STATE_ENDED ->
|
|
57
|
+
// An emptied queue also reports STATE_ENDED; without items that is "nothing
|
|
58
|
+
// loaded", not "played to end".
|
|
59
|
+
listOf(if (event.mediaItemCount > 0) PlaybackState.ENDED else PlaybackState.NONE)
|
|
60
|
+
else -> emptyList()
|
|
61
|
+
}
|
|
62
|
+
is PlaybackEvent.MediaItemTransition ->
|
|
63
|
+
when {
|
|
64
|
+
!event.hasTrack -> emptyList()
|
|
65
|
+
// Already audibly playing (gapless auto-advance): run the full burst so consumers
|
|
66
|
+
// see the canonical loading→ready→playing sequence rather than sticking on LOADING.
|
|
67
|
+
event.isPlaying ->
|
|
68
|
+
listOf(PlaybackState.LOADING, PlaybackState.READY, PlaybackState.PLAYING)
|
|
69
|
+
else -> listOf(PlaybackState.LOADING)
|
|
70
|
+
}
|
|
71
|
+
is PlaybackEvent.PlayWhenReadyChanged ->
|
|
72
|
+
// STOPPED and ENDED are terminal: dropping the intent there (stop()'s own
|
|
73
|
+
// clear, the clear on natural end, or a redundant pause press) must not
|
|
74
|
+
// re-report the state as PAUSED.
|
|
75
|
+
if (
|
|
76
|
+
!event.playWhenReady && current != PlaybackState.STOPPED && current != PlaybackState.ENDED
|
|
77
|
+
) {
|
|
78
|
+
listOf(PlaybackState.PAUSED)
|
|
79
|
+
} else {
|
|
80
|
+
emptyList()
|
|
81
|
+
}
|
|
82
|
+
is PlaybackEvent.IsPlayingChanged ->
|
|
83
|
+
if (event.isPlaying) listOf(PlaybackState.PLAYING) else emptyList()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -4,6 +4,9 @@ import android.content.Context
|
|
|
4
4
|
import android.content.SharedPreferences
|
|
5
5
|
import androidx.core.content.edit
|
|
6
6
|
import androidx.media3.common.C
|
|
7
|
+
import com.audiobrowser.util.artworkOf
|
|
8
|
+
import com.audiobrowser.util.url
|
|
9
|
+
import com.margelo.nitro.audiobrowser.PlaybackState
|
|
7
10
|
import com.margelo.nitro.audiobrowser.RepeatMode
|
|
8
11
|
import com.margelo.nitro.audiobrowser.Track
|
|
9
12
|
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
@@ -96,11 +99,20 @@ class PlaybackStateStore(private val player: Player) {
|
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
/**
|
|
100
|
-
fun
|
|
102
|
+
/** Cancels the periodic save without a final save. Call on engine teardown. */
|
|
103
|
+
fun cancelPeriodicSave() {
|
|
101
104
|
periodicSaveJob?.cancel()
|
|
102
105
|
periodicSaveJob = null
|
|
103
|
-
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Stops periodic position saving and saves final position. Call when playback stops. */
|
|
109
|
+
fun stopPeriodicSave() {
|
|
110
|
+
cancelPeriodicSave()
|
|
111
|
+
// Skip the final save after a natural end: setPlaybackState(ENDED) wrote
|
|
112
|
+
// position 0 for resumption, and the intent-drop's listener echo runs this
|
|
113
|
+
// strictly afterwards — a save here would overwrite that 0 with the
|
|
114
|
+
// end-of-track position, making cold-start resume restart at the very end.
|
|
115
|
+
if (!player.isCurrentItemLive && player.playbackState != PlaybackState.ENDED) {
|
|
104
116
|
savePosition()
|
|
105
117
|
}
|
|
106
118
|
}
|
|
@@ -139,13 +151,15 @@ class PlaybackStateStore(private val player: Player) {
|
|
|
139
151
|
private fun trackToJson(track: Track): String =
|
|
140
152
|
JSONObject()
|
|
141
153
|
.apply {
|
|
154
|
+
put("id", track.id)
|
|
142
155
|
put("url", track.url)
|
|
143
156
|
put("src", track.src)
|
|
144
157
|
put("title", track.title)
|
|
145
158
|
put("subtitle", track.subtitle)
|
|
146
159
|
put("artist", track.artist)
|
|
160
|
+
put("albumUrl", track.albumUrl)
|
|
147
161
|
put("album", track.album)
|
|
148
|
-
put("artwork", track.artwork)
|
|
162
|
+
put("artwork", track.artwork?.url)
|
|
149
163
|
put("description", track.description)
|
|
150
164
|
put("genre", track.genre)
|
|
151
165
|
put("duration", track.duration)
|
|
@@ -161,13 +175,15 @@ class PlaybackStateStore(private val player: Player) {
|
|
|
161
175
|
runCatching {
|
|
162
176
|
val obj = JSONObject(json)
|
|
163
177
|
Track(
|
|
178
|
+
id = obj.optString("id").takeIf { it.isNotEmpty() },
|
|
164
179
|
url = obj.optString("url").takeIf { it.isNotEmpty() },
|
|
165
180
|
src = obj.optString("src").takeIf { it.isNotEmpty() },
|
|
166
181
|
title = obj.getString("title"),
|
|
167
182
|
subtitle = obj.optString("subtitle").takeIf { it.isNotEmpty() },
|
|
168
183
|
artist = obj.optString("artist").takeIf { it.isNotEmpty() },
|
|
184
|
+
albumUrl = obj.optString("albumUrl").takeIf { it.isNotEmpty() },
|
|
169
185
|
album = obj.optString("album").takeIf { it.isNotEmpty() },
|
|
170
|
-
artwork = obj.optString("artwork").takeIf { it.isNotEmpty() },
|
|
186
|
+
artwork = artworkOf(obj.optString("artwork").takeIf { it.isNotEmpty() }),
|
|
171
187
|
artworkSource = null, // Not persisted - will be re-transformed on browse
|
|
172
188
|
artworkCarPlayTinted = null, // iOS-only, not persisted
|
|
173
189
|
description = obj.optString("description").takeIf { it.isNotEmpty() },
|
|
@@ -189,6 +205,10 @@ class PlaybackStateStore(private val player: Player) {
|
|
|
189
205
|
else null,
|
|
190
206
|
groupTitle = obj.optString("groupTitle").takeIf { it.isNotEmpty() },
|
|
191
207
|
live = if (obj.has("live") && !obj.isNull("live")) obj.getBoolean("live") else null,
|
|
208
|
+
// Not persisted: on resumption the contextual URL is re-browsed
|
|
209
|
+
// (expandQueueFromContextualUrl), which re-parses each track's request
|
|
210
|
+
// from the API's current response and re-caches it.
|
|
211
|
+
request = null,
|
|
192
212
|
imageRow = null, // Not persisted
|
|
193
213
|
)
|
|
194
214
|
}
|