react-native-queue-player 1.2.0 → 2.0.0
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/QueuePlayer.podspec +14 -9
- package/README.md +3 -3
- package/android/consumer-rules.pro +1 -1
- package/android/src/main/cpp/airplay2_control.c +4 -4
- package/android/src/main/cpp/airplay2_control.h +4 -4
- package/android/src/main/cpp/airplay2_jni.cpp +3 -19
- package/android/src/main/cpp/airplay2_pair.c +7 -7
- package/android/src/main/cpp/airplay2_pair.h +2 -2
- package/android/src/main/cpp/airplay2_rtsp.c +8 -8
- package/android/src/main/cpp/airplay2_session.c +2 -2
- package/android/src/main/cpp/airplay_jni.cpp +8 -69
- package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +7 -16
- package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +2 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/CellularTransportMonitor.kt +77 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +187 -52
- package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +27 -7
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +33 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +31 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +45 -30
- package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +111 -15
- package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +60 -33
- package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +3 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +103 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +77 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +6 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +48 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +71 -76
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +68 -46
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +6 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +5 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +11 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +26 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +22 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +38 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +17 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/SuppliedReplayGain.kt +66 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +839 -271
- package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +4 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +4 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +5 -4
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +0 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +4 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +16 -9
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +6 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +1 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +11 -35
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +0 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +0 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +32 -33
- package/android/src/test/java/androidx/media3/session/{MediaSessionControllerRequestTestSeam.kt → MediaSessionControllerRequestTestSupport.kt} +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +12 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineFocusLossTest.kt +115 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EngineEndSignalOrderTest.kt +70 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +15 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +135 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +1 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +36 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +24 -18
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterCancelTest.kt +85 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +112 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +47 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +53 -29
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +8 -8
- package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +33 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +128 -5
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +209 -14
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +150 -10
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainMergeTest.kt +258 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowDynamicsProcessingRejectingEnable.kt +19 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizer.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizerRejectingBandWrites.kt +27 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SuppliedReplayGainTest.kt +61 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +4 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastCommandRoutingTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +88 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +205 -27
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +277 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +85 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +11 -7
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +90 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +8 -26
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +3 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +0 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitorTest.kt +173 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/ShadowConnectivityManagerRecordingRequests.kt +27 -0
- package/app.plugin.js +17 -1
- package/ios/AVPlayerItemQueueItemId.swift +26 -12
- package/ios/AVQueueBuilder.swift +120 -85
- package/ios/ArtworkLoader.swift +0 -7
- package/ios/ArtworkResolver.swift +22 -4
- package/ios/AssetReplayGainReader.swift +118 -0
- package/ios/AudioSession.swift +67 -16
- package/ios/AudioTapProvider.swift +284 -149
- package/ios/CarPlayBridge.swift +0 -12
- package/ios/CarPlayCoordinator.swift +18 -2
- package/ios/CarPlaySceneDelegate.swift +2 -2
- package/ios/Cast/AirPlayRouteState.swift +9 -8
- package/ios/Cast/Core/CastEventBridge.swift +66 -26
- package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
- package/ios/Cast/Core/CastSession.swift +2 -2
- package/ios/Cast/Core/CastTransportRouter.swift +21 -9
- package/ios/Cast/Core/LocalMediaServer.swift +41 -52
- package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +32 -11
- package/ios/Cast/Core/MediaServerHandle.swift +0 -4
- package/ios/Cast/Core/MediaTokenRegistry.swift +0 -6
- package/ios/CastManager.swift +1 -1
- package/ios/CrossfadeEngine.swift +660 -226
- package/ios/EQTap.swift +20 -17
- package/ios/Equalizer.swift +1 -1
- package/ios/FLACStreamInfo.swift +65 -0
- package/ios/GaplessEngine.swift +57 -30
- package/ios/InputGuards.swift +35 -5
- package/ios/LookaheadCache.swift +202 -58
- package/ios/LookaheadCachePrefetcher.swift +48 -36
- package/ios/MediaServer/MediaHTTPConnection.swift +691 -0
- package/ios/NetworkRecoveryPolicy.swift +32 -0
- package/ios/NowPlayingFormatExtractor.swift +25 -18
- package/ios/NowPlayingInfo.swift +84 -48
- package/ios/OutputRouteMonitor.swift +16 -3
- package/ios/PendingBrowseRequests.swift +1 -1
- package/ios/PlaceholderArtwork.swift +9 -5
- package/ios/PlaybackEngine.swift +165 -76
- package/ios/PlaybackErrorMapping.swift +3 -3
- package/ios/PlaybackModeStateMachine.swift +2 -2
- package/ios/PlaybackNetworkMonitor.swift +73 -0
- package/ios/PlayerStateDerivation.swift +6 -6
- package/ios/QueueMutationArithmetic.swift +25 -0
- package/ios/QueueSkipArithmetic.swift +5 -3
- package/ios/QueueWindowArithmetic.swift +152 -0
- package/ios/ReadThroughServer.swift +283 -0
- package/ios/RemoteCommands.swift +44 -4
- package/ios/ReplayGainData.swift +22 -3
- package/ios/ReplayGainExtractor.swift +70 -27
- package/ios/Siri/VoiceDonation.swift +14 -9
- package/ios/StreamingBitrateProbe.swift +22 -68
- package/ios/SuppliedReplayGain.swift +83 -0
- package/ios/Tests/AVQueueBuilderTests.swift +158 -221
- package/ios/Tests/ActiveItemEngineStub.swift +44 -0
- package/ios/Tests/AssetReplayGainReaderTests.swift +228 -0
- package/ios/Tests/AudioTapProviderDispatchTargetTests.swift +158 -0
- package/ios/Tests/AudioTapProviderReplayGainTests.swift +122 -10
- package/ios/Tests/BitrateReresolveBudgetTests.swift +71 -0
- package/ios/Tests/CastMediaItemTranslationTests.swift +72 -0
- package/ios/Tests/CastNowPlayingControllerTests.swift +1 -1
- package/ios/Tests/CrossfadeAdvanceWithoutFadeTests.swift +120 -0
- package/ios/Tests/CrossfadeEngineIncomingMixTests.swift +122 -0
- package/ios/Tests/CrossfadeEngineTests.swift +164 -0
- package/ios/Tests/CrossfadeMixReapplyTests.swift +66 -0
- package/ios/Tests/CrossfadePlaybackIntentTests.swift +81 -0
- package/ios/Tests/EngineSwapTests.swift +91 -0
- package/ios/Tests/EqualizerAudioMixProviderTests.swift +46 -0
- package/ios/Tests/EqualizerHybridTests.swift +5 -2
- package/ios/Tests/FLACStreamInfoTests.swift +88 -0
- package/ios/Tests/GaplessEngineLifecycleTests.swift +4 -4
- package/ios/Tests/InputGuardsTests.swift +41 -0
- package/ios/Tests/InterruptionIntentTests.swift +77 -0
- package/ios/Tests/LookaheadCacheCellularAccessTests.swift +185 -0
- package/ios/Tests/LookaheadCachePrefetcherTests.swift +53 -30
- package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +160 -1
- package/ios/Tests/LookaheadCacheTests.swift +44 -18
- package/ios/Tests/MediaHTTPConnectionTests.swift +79 -0
- package/ios/Tests/MutationDeferralTests.swift +220 -0
- package/ios/Tests/NowPlayingInfoTests.swift +86 -99
- package/ios/Tests/NowPlayingSnapshotTests.swift +127 -0
- package/ios/Tests/OriginRestartStitcherTests.swift +60 -0
- package/ios/Tests/PlaybackNetworkMonitorTests.swift +95 -0
- package/ios/Tests/PlayerFixtures.swift +48 -0
- package/ios/Tests/PlayerStateDerivationTests.swift +10 -10
- package/ios/Tests/QueueMutationGenerationTests.swift +141 -0
- package/ios/Tests/QueueRebuildPrefixTests.swift +317 -0
- package/ios/Tests/QueueStateTests.swift +3 -1
- package/ios/Tests/QueueWindowArithmeticTests.swift +56 -0
- package/ios/Tests/QueueWindowSliceTests.swift +162 -0
- package/ios/Tests/ReadThroughRoutingLifecycleTests.swift +63 -0
- package/ios/Tests/ReadThroughServerTests.swift +345 -0
- package/ios/Tests/RemoteCommandsTests.swift +28 -0
- package/ios/Tests/ReplayGainExtractorTests.swift +216 -6
- package/ios/Tests/ReplayGainMergeTests.swift +230 -0
- package/ios/Tests/RetryRecoveryTests.swift +303 -0
- package/ios/Tests/SkipCapabilityTests.swift +233 -8
- package/ios/Tests/SkipIndexTests.swift +32 -2
- package/ios/Tests/SleepTimerPauseIntentTests.swift +43 -0
- package/ios/Tests/StallRecoveryTests.swift +97 -0
- package/ios/Tests/StreamingBitrateProbeTests.swift +17 -20
- package/ios/Tests/TopUpWindowGateTests.swift +394 -0
- package/ios/Tests/TrackPlayer+TestHops.swift +14 -0
- package/ios/Tests/TrackPlayerCallOrderTests.swift +93 -0
- package/ios/Tests/TrackPlayerConfigureTeardownTests.swift +86 -0
- package/ios/Tests/TrackPlayerEndVerdictTests.swift +165 -0
- package/ios/Tests/TrackPlayerSeekTests.swift +137 -0
- package/ios/Tests/TrackPlayerThreadingTests.swift +127 -0
- package/ios/Tests/TrackSourceClassifierTests.swift +33 -7
- package/ios/Tests/VoiceVocabularyOptInTests.swift +38 -0
- package/ios/TrackPlayer+Automotive.swift +101 -0
- package/ios/TrackPlayer+Cache.swift +232 -0
- package/ios/TrackPlayer+Config.swift +291 -0
- package/ios/TrackPlayer+EngineDelegate.swift +209 -0
- package/ios/TrackPlayer+EventsAPI.swift +111 -0
- package/ios/TrackPlayer+EventsDispatch.swift +968 -0
- package/ios/TrackPlayer+EventsWiring.swift +173 -0
- package/ios/TrackPlayer+Lifecycle.swift +930 -0
- package/ios/TrackPlayer+NowPlayingFormat.swift +258 -0
- package/ios/TrackPlayer+Queue.swift +940 -0
- package/ios/TrackPlayer+Recovery.swift +196 -0
- package/ios/TrackPlayer+Skip.swift +455 -0
- package/ios/TrackPlayer+SleepTimer.swift +175 -0
- package/ios/TrackPlayer+State.swift +108 -0
- package/ios/TrackPlayer+Threading.swift +135 -0
- package/ios/TrackPlayer+Transport.swift +267 -0
- package/ios/TrackPlayer+Window.swift +195 -0
- package/ios/TrackPlayer.swift +305 -4619
- package/ios/Visualizer.swift +6 -5
- package/ios/tests-harness/Podfile +1 -1
- package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +19 -11
- package/ios/tests-harness/scripts/seed-xcodeproj.rb +2 -2
- package/lib/module/hooks/useActiveTrack.js +29 -22
- package/lib/module/hooks/useActiveTrack.js.map +1 -1
- package/lib/module/hooks/useCast.js +8 -26
- package/lib/module/hooks/useCast.js.map +1 -1
- package/lib/module/hooks/useEqualizer.js +19 -12
- package/lib/module/hooks/useEqualizer.js.map +1 -1
- package/lib/module/hooks/useLookaheadCache.js +3 -7
- package/lib/module/hooks/useLookaheadCache.js.map +1 -1
- package/lib/module/hooks/useQueue.js +66 -19
- package/lib/module/hooks/useQueue.js.map +1 -1
- package/lib/module/index.js +6 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/queueDelta.js +41 -0
- package/lib/module/queueDelta.js.map +1 -0
- package/lib/module/types.js +28 -5
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/TrackPlayer.nitro.d.ts +53 -28
- package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -1
- package/lib/typescript/hooks/useActiveTrack.d.ts +5 -7
- package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -1
- package/lib/typescript/hooks/useCast.d.ts +6 -1
- package/lib/typescript/hooks/useCast.d.ts.map +1 -1
- package/lib/typescript/hooks/useEqualizer.d.ts +2 -1
- package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -1
- package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -1
- package/lib/typescript/hooks/useQueue.d.ts +4 -4
- package/lib/typescript/hooks/useQueue.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/queueDelta.d.ts +10 -0
- package/lib/typescript/queueDelta.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +168 -12
- package/lib/typescript/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JFunc_void_QueueChangeDelta_double_QueueChangeReason.hpp +85 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +45 -19
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +3 -3
- package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +8 -4
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
- package/nitrogen/generated/android/c++/JQueueChangeDelta.hpp +128 -0
- package/nitrogen/generated/android/c++/JTrackItem.hpp +19 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/{Func_void_std__vector_TrackItem__double_QueueChangeReason.kt → Func_void_QueueChangeDelta_double_QueueChangeReason.kt} +14 -16
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +6 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +9 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +7 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/QueueChangeDelta.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +24 -4
- package/nitrogen/generated/android/queueplayerOnLoad.cpp +2 -2
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +16 -16
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +80 -65
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -3
- package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_QueueChangeDelta_double_QueueChangeReason.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +3 -3
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +32 -24
- package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +20 -2
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
- package/nitrogen/generated/ios/swift/QueueChangeDelta.swift +82 -0
- package/nitrogen/generated/ios/swift/TrackItem.swift +73 -1
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +6 -3
- package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +7 -3
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
- package/nitrogen/generated/shared/c++/QueueChangeDelta.hpp +113 -0
- package/nitrogen/generated/shared/c++/TrackItem.hpp +18 -2
- package/package.json +8 -8
- package/src/TrackPlayer.nitro.ts +53 -27
- package/src/hooks/useActiveTrack.ts +29 -22
- package/src/hooks/useCast.ts +14 -15
- package/src/hooks/useEqualizer.ts +19 -12
- package/src/hooks/useLookaheadCache.ts +3 -7
- package/src/hooks/useQueue.ts +66 -17
- package/src/index.ts +13 -3
- package/src/queueDelta.ts +41 -0
- package/src/types.ts +169 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +0 -82
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +0 -15
- package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +0 -192
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +0 -135
- package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +0 -27
- package/ios/Cast/Core/LocalAddressMonitor.swift +0 -110
- package/ios/Cast/Core/MediaHTTPConnection.swift +0 -349
- package/ios/MetadataReader.swift +0 -555
- package/ios/Tests/CrossfadeEngineStubTests.swift +0 -43
- package/ios/Tests/MetadataReaderTests.swift +0 -603
- package/ios/Tests/PlaybackEngineProtocolTests.swift +0 -92
- package/ios/Tests/VoiceDonationTests.swift +0 -26
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__double_QueueChangeReason.hpp +0 -101
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__double_QueueChangeReason.swift +0 -46
- /package/ios/{Cast/Core → MediaServer}/MimeTypes.swift +0 -0
package/src/hooks/useQueue.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { getTrackPlayer } from '../clients';
|
|
3
3
|
import type { TrackItem } from '../types';
|
|
4
|
+
import { applyQueueDelta } from '../queueDelta';
|
|
4
5
|
|
|
5
6
|
export interface QueueSnapshot {
|
|
6
7
|
/** The current queue, in play order. */
|
|
@@ -17,28 +18,75 @@ const INITIAL: QueueSnapshot = { queue: [], currentIndex: -1 };
|
|
|
17
18
|
* `onTrackChange` so `currentIndex` stays live on auto-advance / skip,
|
|
18
19
|
* which move the active position without changing the queue contents.
|
|
19
20
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* Renders empty for the first frame, then fills in: the seed reads
|
|
22
|
+
* `getQueue()`, which resolves asynchronously. A mutation landing in that gap
|
|
23
|
+
* is not lost: a delta that arrives before the seed lands cannot be applied —
|
|
24
|
+
* there is nothing correct to apply it to — so the queue is re-read instead.
|
|
24
25
|
*/
|
|
25
26
|
export function useQueue(): QueueSnapshot {
|
|
26
|
-
const [snapshot, setSnapshot] = useState<QueueSnapshot>(
|
|
27
|
-
try {
|
|
28
|
-
const player = getTrackPlayer();
|
|
29
|
-
return {
|
|
30
|
-
queue: player.getQueue(),
|
|
31
|
-
currentIndex: player.getCurrentTrackIndex(),
|
|
32
|
-
};
|
|
33
|
-
} catch {
|
|
34
|
-
return INITIAL;
|
|
35
|
-
}
|
|
36
|
-
});
|
|
27
|
+
const [snapshot, setSnapshot] = useState<QueueSnapshot>(INITIAL);
|
|
37
28
|
|
|
38
29
|
useEffect(() => {
|
|
39
30
|
const player = getTrackPlayer();
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
let disposed = false;
|
|
32
|
+
// `haveBase` is what makes a delta applicable: without a queue to apply it
|
|
33
|
+
// to, applying one silently produces a wrong mirror — and for `shuffle`, an
|
|
34
|
+
// array of holes. `seenEvent` separately stops a slow seed overwriting
|
|
35
|
+
// newer state.
|
|
36
|
+
let haveBase = false;
|
|
37
|
+
let seenEvent = false;
|
|
38
|
+
let revision = 0;
|
|
39
|
+
// Guards against two overlapping re-reads resolving out of order.
|
|
40
|
+
let readTicket = 0;
|
|
41
|
+
|
|
42
|
+
const reread = () => {
|
|
43
|
+
const ticket = ++readTicket;
|
|
44
|
+
// Until this read lands there is no base a delta can be applied to:
|
|
45
|
+
// an event arriving meanwhile re-reads too, and the ticket keeps the
|
|
46
|
+
// last read's answer.
|
|
47
|
+
haveBase = false;
|
|
48
|
+
void player
|
|
49
|
+
.getQueue()
|
|
50
|
+
.then((queue) => {
|
|
51
|
+
if (disposed || ticket !== readTicket) return;
|
|
52
|
+
haveBase = true;
|
|
53
|
+
// Re-read the index too: the queue just came from a fresh read, and
|
|
54
|
+
// the index captured when the event fired may already be stale.
|
|
55
|
+
setSnapshot({ queue, currentIndex: player.getCurrentTrackIndex() });
|
|
56
|
+
})
|
|
57
|
+
.catch(() => {
|
|
58
|
+
// Player not configured yet; a later event re-reads.
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
void (async () => {
|
|
63
|
+
try {
|
|
64
|
+
const ticket = ++readTicket;
|
|
65
|
+
const queue = await player.getQueue();
|
|
66
|
+
const currentIndex = player.getCurrentTrackIndex();
|
|
67
|
+
if (disposed || seenEvent || ticket !== readTicket) return;
|
|
68
|
+
haveBase = true;
|
|
69
|
+
setSnapshot({ queue, currentIndex });
|
|
70
|
+
} catch {
|
|
71
|
+
// Player not configured yet; the first event re-reads instead.
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
|
|
75
|
+
// A delta only makes sense against the copy it was computed from, so a
|
|
76
|
+
// missed event — or one that beats the seed — is recovered by re-reading
|
|
77
|
+
// rather than patched over.
|
|
78
|
+
const offQueue = player.onQueueChange((delta, currentIndex, reason) => {
|
|
79
|
+
seenEvent = true;
|
|
80
|
+
const missed = revision !== 0 && delta.revision !== revision + 1;
|
|
81
|
+
revision = delta.revision;
|
|
82
|
+
if (!haveBase || missed) {
|
|
83
|
+
reread();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setSnapshot((prev) => ({
|
|
87
|
+
queue: applyQueueDelta(prev.queue, delta, reason),
|
|
88
|
+
currentIndex,
|
|
89
|
+
}));
|
|
42
90
|
});
|
|
43
91
|
// Keep `currentIndex` live when the active track changes without a
|
|
44
92
|
// queue-content change (auto-advance, skip). Preserves the queue from
|
|
@@ -47,6 +95,7 @@ export function useQueue(): QueueSnapshot {
|
|
|
47
95
|
setSnapshot((prev) => ({ queue: prev.queue, currentIndex: index }));
|
|
48
96
|
});
|
|
49
97
|
return () => {
|
|
98
|
+
disposed = true;
|
|
50
99
|
offQueue();
|
|
51
100
|
offTrack();
|
|
52
101
|
};
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,9 @@ export type {
|
|
|
9
9
|
// Player
|
|
10
10
|
TrackItem,
|
|
11
11
|
PlayerConfig,
|
|
12
|
+
AudioContentType,
|
|
13
|
+
AudioCategoryOptions,
|
|
14
|
+
VoiceVocabulary,
|
|
12
15
|
PlayerState,
|
|
13
16
|
BufferState,
|
|
14
17
|
PlaybackMode,
|
|
@@ -20,6 +23,7 @@ export type {
|
|
|
20
23
|
StateChangeReason,
|
|
21
24
|
TrackChangeReason,
|
|
22
25
|
QueueChangeReason,
|
|
26
|
+
QueueChangeDelta,
|
|
23
27
|
TrackSource,
|
|
24
28
|
PlayerProgress,
|
|
25
29
|
PlaybackError,
|
|
@@ -28,6 +32,8 @@ export type {
|
|
|
28
32
|
SkipCapability,
|
|
29
33
|
SleepTimerState,
|
|
30
34
|
NowPlayingFormat,
|
|
35
|
+
NowPlayingFormatIosExtras,
|
|
36
|
+
NowPlayingFormatAndroidExtras,
|
|
31
37
|
AudioCodec,
|
|
32
38
|
AudioContainer,
|
|
33
39
|
// Lookahead cache
|
|
@@ -71,6 +77,7 @@ export type {
|
|
|
71
77
|
CastSessionDiedEvent,
|
|
72
78
|
CastLocalNetworkPermissionEvent,
|
|
73
79
|
CastConnectOptions,
|
|
80
|
+
CastConfigureOptions,
|
|
74
81
|
// Audio route (cast + on-device)
|
|
75
82
|
AudioRouteKind,
|
|
76
83
|
AudioRoute,
|
|
@@ -118,10 +125,13 @@ export {
|
|
|
118
125
|
nearestBandIndex,
|
|
119
126
|
} from './equalizer';
|
|
120
127
|
|
|
128
|
+
// Pure helper for applying an onQueueChange delta to a queue copy.
|
|
129
|
+
export { applyQueueDelta } from './queueDelta';
|
|
130
|
+
|
|
121
131
|
// React hooks — thin observers over the HybridObject event surface.
|
|
122
132
|
export * from './hooks';
|
|
123
133
|
|
|
124
|
-
// Cast —
|
|
125
|
-
// `
|
|
126
|
-
// `getCastManager()`
|
|
134
|
+
// Cast — `Cast.*`: thin functions over the `CastManager` HybridObject
|
|
135
|
+
// (optional-argument shapes, `CastSubscription` handles, `CastErrorCodes`,
|
|
136
|
+
// `localRoute()`); `getCastManager()` returns the HybridObject itself.
|
|
127
137
|
export * as Cast from './cast';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { QueueChangeDelta, QueueChangeReason, TrackItem } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rebuild the queue from the previous copy plus one change.
|
|
5
|
+
*
|
|
6
|
+
* Exported from the package root as the reference for what a consumer must do
|
|
7
|
+
* with a delta; `useQueue` applies it too. The shapes it reads are documented
|
|
8
|
+
* on [QueueChangeDelta].
|
|
9
|
+
*/
|
|
10
|
+
export function applyQueueDelta(
|
|
11
|
+
previous: TrackItem[],
|
|
12
|
+
delta: QueueChangeDelta,
|
|
13
|
+
reason: QueueChangeReason
|
|
14
|
+
): TrackItem[] {
|
|
15
|
+
switch (reason) {
|
|
16
|
+
case 'set-queue':
|
|
17
|
+
return delta.inserted.slice();
|
|
18
|
+
case 'clear':
|
|
19
|
+
return [];
|
|
20
|
+
case 'add': {
|
|
21
|
+
const next = previous.slice();
|
|
22
|
+
next.splice(delta.insertedAt, 0, ...delta.inserted);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
case 'remove': {
|
|
26
|
+
const drop = new Set(delta.removed);
|
|
27
|
+
return previous.filter((_, i) => !drop.has(i));
|
|
28
|
+
}
|
|
29
|
+
case 'move': {
|
|
30
|
+
const next = previous.slice();
|
|
31
|
+
const [moved] = next.splice(delta.movedFrom, 1);
|
|
32
|
+
if (moved) next.splice(delta.movedTo, 0, moved);
|
|
33
|
+
return next;
|
|
34
|
+
}
|
|
35
|
+
case 'shuffle':
|
|
36
|
+
// `order[i]` is where the track now at `i` used to be.
|
|
37
|
+
return delta.order.map((from) => previous[from]!);
|
|
38
|
+
default:
|
|
39
|
+
return previous;
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -44,6 +44,32 @@ export interface TrackItem {
|
|
|
44
44
|
artworkUrl?: string;
|
|
45
45
|
/** Opaque passthrough the consuming app can attach; the module does not read it. */
|
|
46
46
|
extras?: Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* Consumer-supplied `REPLAYGAIN_TRACK_GAIN` in dB.
|
|
49
|
+
*
|
|
50
|
+
* The four ReplayGain fields are all-or-nothing per track: supplying
|
|
51
|
+
* ANY of them makes this track's ReplayGain come solely from these
|
|
52
|
+
* values, and whatever the file itself carries is discarded in full —
|
|
53
|
+
* including for the mode nothing was supplied for. Supplying a value
|
|
54
|
+
* is the statement of intent; a track that should use the file's own
|
|
55
|
+
* tags carries none of these fields.
|
|
56
|
+
*
|
|
57
|
+
* Presence is what claims the track, not validity: a lone
|
|
58
|
+
* out-of-range peak still discards the file's tags and leaves the
|
|
59
|
+
* track playing at unity.
|
|
60
|
+
*/
|
|
61
|
+
replayGainTrackGain?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Consumer-supplied `REPLAYGAIN_TRACK_PEAK`, linear. Accepted in the
|
|
64
|
+
* `0.001 ... 2.0` band — `1.0` is digital full scale and caps a boost
|
|
65
|
+
* at unity. A gain with no usable peak has its boost clamped to unity
|
|
66
|
+
* and reports a `null` peak.
|
|
67
|
+
*/
|
|
68
|
+
replayGainTrackPeak?: number;
|
|
69
|
+
/** Consumer-supplied `REPLAYGAIN_ALBUM_GAIN` in dB. */
|
|
70
|
+
replayGainAlbumGain?: number;
|
|
71
|
+
/** Consumer-supplied `REPLAYGAIN_ALBUM_PEAK`, linear. */
|
|
72
|
+
replayGainAlbumPeak?: number;
|
|
47
73
|
}
|
|
48
74
|
|
|
49
75
|
/**
|
|
@@ -89,7 +115,7 @@ export interface PlayerConfig {
|
|
|
89
115
|
/**
|
|
90
116
|
* Auto-retry attempts on transient errors (TIMEOUT / CONNECTION_LOST /
|
|
91
117
|
* NETWORK_UNREACHABLE / SOURCE_UNREACHABLE / CANNOT_OPEN). Clamped
|
|
92
|
-
* to [0, 5]; default
|
|
118
|
+
* to [0, 5]; default 1. Fatal errors (codec / format / auth) are
|
|
93
119
|
* never retried regardless of this setting.
|
|
94
120
|
*/
|
|
95
121
|
autoRetries?: number;
|
|
@@ -133,6 +159,30 @@ export interface PlayerConfig {
|
|
|
133
159
|
* configure rule as `audioContentType`.
|
|
134
160
|
*/
|
|
135
161
|
audioCategoryOptions?: AudioCategoryOptions;
|
|
162
|
+
/**
|
|
163
|
+
* Opt in to donating the consumer's catalog to Siri's vocabulary, so voice
|
|
164
|
+
* queries like "Play Pearl Jam" resolve against the app's own artist and
|
|
165
|
+
* playlist names. Enables `donateVoiceVocabulary`, which is inert without
|
|
166
|
+
* it. Default `false`.
|
|
167
|
+
*
|
|
168
|
+
* **iOS only.** Android ignores this field: Google Assistant has no in-app
|
|
169
|
+
* vocabulary donation API, and `donateVoiceVocabulary` is already a no-op
|
|
170
|
+
* there.
|
|
171
|
+
*
|
|
172
|
+
* **Enabling it requires the `com.apple.developer.siri` entitlement** —
|
|
173
|
+
* Xcode's Siri capability — not just `NSSiriUsageDescription` in
|
|
174
|
+
* Info.plist. `INVocabulary` checks for the entitlement at runtime and
|
|
175
|
+
* raises an Objective-C exception without it, which cannot be caught from
|
|
176
|
+
* Swift and terminates the app. iOS gives a process no way to read its own
|
|
177
|
+
* entitlements, so the library cannot verify this on your behalf: leave the
|
|
178
|
+
* flag `false` unless the Siri capability is on the target.
|
|
179
|
+
*
|
|
180
|
+
* **Pinned at the first `configure()` for the life of the process.** The
|
|
181
|
+
* entitlement it depends on is fixed when the app is signed, so there is
|
|
182
|
+
* nothing for a later change to mean; a re-`configure()` with a different
|
|
183
|
+
* value is ignored rather than silently re-arming a crash path.
|
|
184
|
+
*/
|
|
185
|
+
voiceVocabularyDonationEnabled?: boolean;
|
|
136
186
|
/**
|
|
137
187
|
* Disable the visualization pipeline at engine construction. When
|
|
138
188
|
* `false`, the lib skips the viz-side audio chain entirely:
|
|
@@ -195,6 +245,16 @@ export interface PlayerConfig {
|
|
|
195
245
|
* `100`. Control actual runtime usage via `setLookaheadCache`'s `enabled` +
|
|
196
246
|
* `lookaheadCount`; read the active ceiling back from
|
|
197
247
|
* `getLookaheadCacheStatus().maxSizeMb`.
|
|
248
|
+
*
|
|
249
|
+
* Where the playing track is protected from eviction, this is a budget rather
|
|
250
|
+
* than a hard cap — that is both policies on iOS, and `'fifo'` on Android
|
|
251
|
+
* (see `EvictionPolicy`). Eviction never takes the playing track or its
|
|
252
|
+
* immediate neighbours, so when those alone exceed the budget nothing is
|
|
253
|
+
* evictable and usage can exceed it —
|
|
254
|
+
* `getLookaheadCacheStatus().currentSizeMb` may read above `maxSizeMb` until
|
|
255
|
+
* the window moves. Reachable with lossless sources and a large
|
|
256
|
+
* `lookaheadCount`; size the budget for `2 + lookaheadCount` tracks to avoid
|
|
257
|
+
* it. The alternative would be evicting the track being played.
|
|
198
258
|
*/
|
|
199
259
|
lookaheadCacheMaxSizeMb?: number;
|
|
200
260
|
/**
|
|
@@ -471,6 +531,53 @@ export type QueueChangeReason =
|
|
|
471
531
|
| 'clear' // clearQueue emptied the queue
|
|
472
532
|
| 'shuffle'; // shuffleQueue reordered the queue
|
|
473
533
|
|
|
534
|
+
/**
|
|
535
|
+
* What a queue mutation did, delivered by `onQueueChange` alongside the reason.
|
|
536
|
+
*
|
|
537
|
+
* Describes the change rather than re-sending the queue, so a reorder costs two
|
|
538
|
+
* numbers instead of one `TrackItem` per entry. Which fields carry meaning
|
|
539
|
+
* depends on the reason; the rest are empty or `-1`.
|
|
540
|
+
*
|
|
541
|
+
* Apply it to your own copy of the queue, or call `getQueue()` if you would
|
|
542
|
+
* rather re-read than track it. `useQueue()` does the former for you.
|
|
543
|
+
*
|
|
544
|
+
* **Not every emptying of the queue is reported.** `destroy()` and `configure()`
|
|
545
|
+
* clear it without an event, and `configure()` runs that teardown on every call,
|
|
546
|
+
* so a mirror held across one is stale with nothing to signal it.
|
|
547
|
+
*/
|
|
548
|
+
export interface QueueChangeDelta {
|
|
549
|
+
/**
|
|
550
|
+
* Increments once per emit. A jump of more than one means an event was missed
|
|
551
|
+
* and the mirror should be rebuilt from `getQueue()`. Length alone cannot
|
|
552
|
+
* detect that — `move`, `shuffle` and a same-size `set-queue` all preserve it.
|
|
553
|
+
*/
|
|
554
|
+
revision: number;
|
|
555
|
+
/** Queue length after the mutation. */
|
|
556
|
+
length: number;
|
|
557
|
+
/**
|
|
558
|
+
* Tracks now occupying `[insertedAt, insertedAt + inserted.length)`.
|
|
559
|
+
* Populated for `add` and `set-queue`; empty otherwise.
|
|
560
|
+
*
|
|
561
|
+
* `set-queue` carries the whole queue because the subscriber is not
|
|
562
|
+
* necessarily whoever called `setQueue` — and on Android an assistant or
|
|
563
|
+
* Android Auto request replaces the queue with tracks the app never passed in.
|
|
564
|
+
*/
|
|
565
|
+
inserted: TrackItem[];
|
|
566
|
+
/** Where `inserted` begins, or `-1` when nothing was inserted. */
|
|
567
|
+
insertedAt: number;
|
|
568
|
+
/** Positions removed, ascending, as they were before the removal. */
|
|
569
|
+
removed: number[];
|
|
570
|
+
/** Position a `move` came from, else `-1`. */
|
|
571
|
+
movedFrom: number;
|
|
572
|
+
/** Position a `move` went to, else `-1`. */
|
|
573
|
+
movedTo: number;
|
|
574
|
+
/**
|
|
575
|
+
* For `shuffle`: `order[i]` is the position the track now at `i` held before
|
|
576
|
+
* the shuffle. Empty for every other reason.
|
|
577
|
+
*/
|
|
578
|
+
order: number[];
|
|
579
|
+
}
|
|
580
|
+
|
|
474
581
|
/**
|
|
475
582
|
* How the active track is being played:
|
|
476
583
|
* - `'local'` — `track.url` is a `file://` URL.
|
|
@@ -556,10 +663,21 @@ export interface NowPlayingFormat {
|
|
|
556
663
|
/** Channel count (1 = mono, 2 = stereo, 6 = 5.1, etc.), or `null`. */
|
|
557
664
|
channelCount: number | null;
|
|
558
665
|
/**
|
|
559
|
-
* Average
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
666
|
+
* Average encoded bitrate in bits-per-second, or `null` when the source
|
|
667
|
+
* does not declare one and it cannot be measured.
|
|
668
|
+
*
|
|
669
|
+
* Available for `file://` sources of any codec, and for progressive HTTP
|
|
670
|
+
* and HLS sources whose stream or manifest declares a rate — which MP3,
|
|
671
|
+
* M4A/AAC and HLS variants do.
|
|
672
|
+
*
|
|
673
|
+
* **Not available for remote FLAC.** The FLAC header carries no bitrate
|
|
674
|
+
* — the format does not declare one — so it can only be derived from the
|
|
675
|
+
* file's size over its duration. That works for a local file and needs a
|
|
676
|
+
* request the library does not make for a remote one.
|
|
677
|
+
*
|
|
678
|
+
* A derived figure is the whole-file average, so it includes container
|
|
679
|
+
* headers and embedded artwork: typically a few kbps above the audio-only
|
|
680
|
+
* rate.
|
|
563
681
|
*/
|
|
564
682
|
bitrate: number | null;
|
|
565
683
|
/** Track duration in seconds, or `null` when not yet known. */
|
|
@@ -685,8 +803,8 @@ export interface PlaybackError {
|
|
|
685
803
|
/**
|
|
686
804
|
* Raw native message — typically `localizedDescription` on iOS or
|
|
687
805
|
* `PlaybackException.message` on Android. URL query strings are
|
|
688
|
-
* stripped before emission to avoid leaking auth tokens
|
|
689
|
-
*
|
|
806
|
+
* stripped before emission to avoid leaking auth tokens carried in
|
|
807
|
+
* query strings (e.g. `?token=…`).
|
|
690
808
|
*/
|
|
691
809
|
nativeMessage: string;
|
|
692
810
|
/**
|
|
@@ -718,9 +836,8 @@ export interface PlaybackError {
|
|
|
718
836
|
* (no skip-next on the last track, no skip-previous on the first).
|
|
719
837
|
*
|
|
720
838
|
* Wrapped in a struct (rather than two separate boolean callback
|
|
721
|
-
* args) so
|
|
722
|
-
* API change.
|
|
723
|
-
* `(bool, bool) → void` callback signatures specifically.
|
|
839
|
+
* args) so further capability flags can be added without a breaking
|
|
840
|
+
* API change.
|
|
724
841
|
*/
|
|
725
842
|
export interface SkipCapability {
|
|
726
843
|
/** Reflects queue length + current index + repeat mode (symmetric with `canSkipNext`). */
|
|
@@ -739,8 +856,7 @@ export interface SkipCapability {
|
|
|
739
856
|
* converts to a concrete deadline near the track's end.
|
|
740
857
|
*
|
|
741
858
|
* Wrapped in a struct (rather than positional callback args) so fields can be
|
|
742
|
-
* added without a breaking change
|
|
743
|
-
* for multi-primitive callback signatures.
|
|
859
|
+
* added without a breaking change.
|
|
744
860
|
*/
|
|
745
861
|
export interface SleepTimerState {
|
|
746
862
|
/** `true` while a sleep timer is armed (duration or end-of-track). */
|
|
@@ -772,13 +888,54 @@ export interface SleepTimerState {
|
|
|
772
888
|
*
|
|
773
889
|
* Construction-fixed like the cache size: changing it at `configure()` clears
|
|
774
890
|
* the cache and rebuilds with the new policy.
|
|
891
|
+
*
|
|
892
|
+
* **Protecting the playing track differs by platform.** Eviction runs on every
|
|
893
|
+
* download commit, so without protection the entry backing the track being
|
|
894
|
+
* played can be deleted while it plays.
|
|
895
|
+
*
|
|
896
|
+
* - **iOS** protects under both policies.
|
|
897
|
+
* - **Android** protects under `'fifo'` only. Media3's LRU evictor has no
|
|
898
|
+
* protected-key concept, and its recency bump is per byte-range span rather
|
|
899
|
+
* than per track, so a long track's already-played spans stay evictable while
|
|
900
|
+
* it is still playing. Choose `'fifo'` if that matters to you.
|
|
775
901
|
*/
|
|
776
902
|
export type EvictionPolicy = 'lru' | 'fifo';
|
|
777
903
|
|
|
778
904
|
export interface LookaheadCacheConfig {
|
|
905
|
+
/**
|
|
906
|
+
* Whether the cache is in use. Enabled, playback of a track the cache holds
|
|
907
|
+
* in full is served from disk with no request, a track it does not hold is
|
|
908
|
+
* fetched from the origin and written as it plays, and the tracks ahead are
|
|
909
|
+
* prefetched. Disabled, every read goes to the origin and nothing is written,
|
|
910
|
+
* whatever the disk already holds; the item already loading keeps the source
|
|
911
|
+
* it opened. The on-disk entries survive a disable and serve again on enable.
|
|
912
|
+
*/
|
|
779
913
|
enabled: boolean;
|
|
780
914
|
/** Tracks ahead of the current index to proactively cache. 0 disables lookahead; eviction still runs. */
|
|
781
915
|
lookaheadCount: number;
|
|
916
|
+
/**
|
|
917
|
+
* Whether prefetching may use cellular data. Defaults to `true`.
|
|
918
|
+
*
|
|
919
|
+
* The lookahead cache downloads whole upcoming tracks before playback
|
|
920
|
+
* reaches them, so on a metered connection it can spend a lot of data on
|
|
921
|
+
* tracks the listener skips past. Set `false` to back a "download over
|
|
922
|
+
* Wi-Fi only" setting.
|
|
923
|
+
*
|
|
924
|
+
* Applies at runtime — no `configure()` cycle — and gates prefetch only.
|
|
925
|
+
* Playback is never blocked by it, so the track being played carries on
|
|
926
|
+
* either way.
|
|
927
|
+
*
|
|
928
|
+
* The enforcement point differs, because the platforms offer different
|
|
929
|
+
* primitives:
|
|
930
|
+
* - **iOS** sets it on each prefetch request, and the OS refuses to run that
|
|
931
|
+
* request over cellular. A download already on the wire keeps the value it
|
|
932
|
+
* started with and runs to completion.
|
|
933
|
+
* - **Android** has no per-request equivalent, so prefetch stops being
|
|
934
|
+
* scheduled while the default network is cellular. That also cancels the
|
|
935
|
+
* download in flight, whose partial data stays in the cache and resumes
|
|
936
|
+
* when prefetch is permitted again.
|
|
937
|
+
*/
|
|
938
|
+
allowsCellularAccess?: boolean;
|
|
782
939
|
}
|
|
783
940
|
|
|
784
941
|
export interface CacheStatus {
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.queueplayer
|
|
2
|
-
|
|
3
|
-
import java.util.concurrent.atomic.AtomicReference
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Single-slot buffer for a voice intent that arrived before the JS
|
|
7
|
-
* resolver had registered. Holds at most one request per instance; a
|
|
8
|
-
* subsequent [push] replaces any prior entry (latest-wins).
|
|
9
|
-
*
|
|
10
|
-
* Entries carry a `bufferedAtMillis` timestamp and expire after
|
|
11
|
-
* [ttlMillis]. Expired entries are dropped on the next [peek] /
|
|
12
|
-
* [drain] without producing a result, so a stale cold-launch intent
|
|
13
|
-
* never reaches a JS resolver that registers minutes later.
|
|
14
|
-
*
|
|
15
|
-
* Cross-platform parity primitive: iOS's `CarPlayBridge` holds an
|
|
16
|
-
* analogous `PendingIntentBuffer` for `INPlayMediaIntent` cold-start
|
|
17
|
-
* dispatches because Swift Concurrency lacks Kotlin's
|
|
18
|
-
* `MutableStateFlow.filterNotNull().first()` suspend-until-non-null
|
|
19
|
-
* pattern. On Android the lib's existing `BrowseCallbackRegistry`
|
|
20
|
-
* slots + `PlaybackService.dispatchAssistantPlay` already provide the
|
|
21
|
-
* same cold-start-race coverage via coroutine suspension, so this
|
|
22
|
-
* class is a reusable primitive without a load-bearing Android call
|
|
23
|
-
* site today. Kept for symmetry + future single-flight use cases.
|
|
24
|
-
*
|
|
25
|
-
* Thread-safety: every method is safe to call from any thread —
|
|
26
|
-
* [AtomicReference] guards the slot via CAS.
|
|
27
|
-
*
|
|
28
|
-
* Clock contract: callers must supply a monotonically non-decreasing
|
|
29
|
-
* clock. Negative-skew elapsed values are clamped to zero so a
|
|
30
|
-
* backwards-moving clock can't extend an entry's effective TTL.
|
|
31
|
-
*/
|
|
32
|
-
internal class PendingIntentBuffer<Request : Any>(
|
|
33
|
-
private val ttlMillis: Long = 20_000L,
|
|
34
|
-
private val clock: () -> Long = System::currentTimeMillis,
|
|
35
|
-
) {
|
|
36
|
-
|
|
37
|
-
private data class Entry<Request : Any>(val request: Request, val bufferedAtMillis: Long)
|
|
38
|
-
|
|
39
|
-
private val cell = AtomicReference<Entry<Request>?>(null)
|
|
40
|
-
|
|
41
|
-
/** Store [request], replacing any prior entry. */
|
|
42
|
-
fun push(request: Request) {
|
|
43
|
-
cell.set(Entry(request, clock()))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Remove and return the buffered request, or `null` if empty or
|
|
48
|
-
* expired. Drains the slot in both cases — an expired entry is not
|
|
49
|
-
* retained for a later non-expired read.
|
|
50
|
-
*/
|
|
51
|
-
fun drain(): Request? {
|
|
52
|
-
val current = cell.getAndSet(null) ?: return null
|
|
53
|
-
val elapsed = (clock() - current.bufferedAtMillis).coerceAtLeast(0L)
|
|
54
|
-
if (elapsed > ttlMillis) return null
|
|
55
|
-
return current.request
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Inspect the buffered request without removing it. Auto-clears an
|
|
60
|
-
* expired entry as a side effect so a stale request can't be
|
|
61
|
-
* inspected indefinitely.
|
|
62
|
-
*/
|
|
63
|
-
fun peek(): Request? {
|
|
64
|
-
while (true) {
|
|
65
|
-
val current = cell.get() ?: return null
|
|
66
|
-
val elapsed = (clock() - current.bufferedAtMillis).coerceAtLeast(0L)
|
|
67
|
-
if (elapsed > ttlMillis) {
|
|
68
|
-
if (cell.compareAndSet(current, null)) return null
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
return current.request
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Drop any buffered request without returning it. */
|
|
76
|
-
fun clear() {
|
|
77
|
-
cell.set(null)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** True when the slot is empty (or holds only an expired entry). */
|
|
81
|
-
fun isEmpty(): Boolean = peek() == null
|
|
82
|
-
}
|
package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.queueplayer.cast.http
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Single-callback contract by which the host service learns whether
|
|
5
|
-
* the local HTTP server has any live sessions. While `active` is true
|
|
6
|
-
* the host must keep itself foregrounded so Android doesn't kill the
|
|
7
|
-
* process out from under the server.
|
|
8
|
-
*
|
|
9
|
-
* Cast-core stays unaware of foreground-service mechanics; the
|
|
10
|
-
* implementer translates this signal into `startForeground` /
|
|
11
|
-
* `stopForeground` etc.
|
|
12
|
-
*/
|
|
13
|
-
fun interface LocalMediaServerLifecycle {
|
|
14
|
-
fun onActiveStateChanged(active: Boolean)
|
|
15
|
-
}
|