react-native-queue-player 1.1.2 → 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/build.gradle +7 -0
- package/android/consumer-rules.pro +72 -3
- 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 +26 -22
- 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 +20 -72
- 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 +870 -278
- 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/CastEventBridge.kt +1 -10
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +17 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastTransportRouter.kt +19 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/RemotePlaybackStateMapping.kt +18 -0
- 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 +23 -12
- 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 +58 -21
- 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/AirPlaySession.kt +26 -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/MetadataSyncTarget.kt +20 -0
- 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/chromecast/ChromecastSession.kt +73 -13
- 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 +23 -12
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +43 -15
- 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 +8 -4
- 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 +68 -0
- 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 +163 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +123 -0
- 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 +27 -11
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSyncTest.kt +303 -0
- 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/Chromecast/ChromecastSession.swift +90 -11
- package/ios/Cast/Core/CastEventBridge.swift +66 -26
- package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
- package/ios/Cast/Core/CastSession.swift +11 -0
- 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 +5 -0
- 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/PlaybackStateRouterTests.swift +1 -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/TrackPlayerCastStateTests.swift +78 -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 -4610
- 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/airplay/AirPlay2MetadataSync.kt +0 -189
- 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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// Reads the four ReplayGain tags off an `AVAsset`'s own metadata,
|
|
5
|
+
/// replacing the byte-prefix probe. AVFoundation surfaces the same
|
|
6
|
+
/// tags under three different shapes depending on container:
|
|
7
|
+
///
|
|
8
|
+
/// - MP3 / ID3v2 `TXXX`: the name lives in the item's `extraAttributes`
|
|
9
|
+
/// info key; the item's own key is the frame id (`TXXX`).
|
|
10
|
+
/// - FLAC / Vorbis comment: the name *is* the item's key.
|
|
11
|
+
/// - M4A / iTunes freeform: the item's key is the reverse-DNS
|
|
12
|
+
/// identifier (`com.apple.iTunes.replaygain_track_gain`).
|
|
13
|
+
///
|
|
14
|
+
/// `tagName(info:key:)` is the pure reduction of those three shapes to
|
|
15
|
+
/// one comparable name, extracted so it is testable without building
|
|
16
|
+
/// an `AVMetadataItem`.
|
|
17
|
+
enum AssetReplayGainReader {
|
|
18
|
+
|
|
19
|
+
/// The four names a derived tag name has to match to be worth
|
|
20
|
+
/// loading a value for. Everything else on the asset is skipped
|
|
21
|
+
/// before its `stringValue` is touched.
|
|
22
|
+
private static let replayGainNames: Set<String> = [
|
|
23
|
+
ReplayGainExtractor.trackGainKey,
|
|
24
|
+
ReplayGainExtractor.trackPeakKey,
|
|
25
|
+
ReplayGainExtractor.albumGainKey,
|
|
26
|
+
ReplayGainExtractor.albumPeakKey,
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
/// Read ReplayGain from an item AVPlayer has already prepared.
|
|
30
|
+
///
|
|
31
|
+
/// Returns `nil` unless `item.status == .readyToPlay`. That single
|
|
32
|
+
/// condition is the whole gate: on a prepared item `load(.metadata)`
|
|
33
|
+
/// costs zero requests and zero bytes, while interrogating an
|
|
34
|
+
/// unprepared asset downloads essentially the entire file (measured:
|
|
35
|
+
/// 2.889 MB of a 2.89 MB MP3, 3.776 MB of a 3.78 MB FLAC).
|
|
36
|
+
///
|
|
37
|
+
/// Deliberately NOT gated on `asset.statusOfValue(forKey: "metadata")`:
|
|
38
|
+
/// without `"metadata"` in `automaticallyLoadedAssetKeys` that status
|
|
39
|
+
/// reads `.unknown` even at `.readyToPlay` (measured 8/8), so such a
|
|
40
|
+
/// guard would be permanently false. The key is not set because it
|
|
41
|
+
/// buys nothing — the prepared read is already free.
|
|
42
|
+
///
|
|
43
|
+
/// Values go through `ReplayGainExtractor.rgDataFromTags`, the same
|
|
44
|
+
/// acceptance function every other ReplayGain entry point uses, so
|
|
45
|
+
/// the dB / peak rules cannot drift per source.
|
|
46
|
+
static func read(item: AVPlayerItem) async -> ReplayGainData? {
|
|
47
|
+
guard item.status == .readyToPlay else { return nil }
|
|
48
|
+
// `AVPlayerItem.asset` is a MainActor-isolated property under strict
|
|
49
|
+
// concurrency (`AVAsset` is not Sendable); `status` is explicitly
|
|
50
|
+
// nonisolated. Hop to main for the asset read, then work against the
|
|
51
|
+
// captured reference — same shape as `NowPlayingFormatExtractor.extract`.
|
|
52
|
+
let asset: AVAsset = await MainActor.run { item.asset }
|
|
53
|
+
// Opus applies `OpusHead.output_gain` in the decoder, so applying a
|
|
54
|
+
// `REPLAYGAIN_*` tag on top of it double-corrects (NOTES.md §25).
|
|
55
|
+
//
|
|
56
|
+
// The container is read from `sourceURL`, the media's own URL. A routed
|
|
57
|
+
// track's asset URL is the read-through server's, which carries no
|
|
58
|
+
// extension — asking it would fail this guard open on every Opus track.
|
|
59
|
+
let sourceURL = await MainActor.run { item.sourceURL }
|
|
60
|
+
if let source = sourceURL, let url = URL(string: source), isOpusContainer(url: url) {
|
|
61
|
+
return nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let metadata: [AVMetadataItem]
|
|
65
|
+
do {
|
|
66
|
+
metadata = try await asset.load(.metadata)
|
|
67
|
+
} catch {
|
|
68
|
+
return nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var tags: [String: String] = [:]
|
|
72
|
+
for entry in metadata {
|
|
73
|
+
// `try?` flattens here — both loaders already return an optional,
|
|
74
|
+
// so a load failure and an absent value arrive as the same `nil`.
|
|
75
|
+
let attributes = try? await entry.load(.extraAttributes)
|
|
76
|
+
let info = attributes?[.info] as? String
|
|
77
|
+
guard let name = tagName(info: info, key: entry.key as? String),
|
|
78
|
+
replayGainNames.contains(name) else { continue }
|
|
79
|
+
guard let value = try? await entry.load(.stringValue) else { continue }
|
|
80
|
+
tags[name] = value
|
|
81
|
+
}
|
|
82
|
+
return ReplayGainExtractor.rgDataFromTags(tags)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Derive the comparable tag name from a metadata item's info key
|
|
86
|
+
/// [info] and its own key [key]: [info] when it is non-blank, else
|
|
87
|
+
/// [key]; then the segment after the last `.`; then uppercased.
|
|
88
|
+
/// Returns `nil` when neither input yields a usable name.
|
|
89
|
+
static func tagName(info: String?, key: String?) -> String? {
|
|
90
|
+
let source: String
|
|
91
|
+
if let info = info, !info.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
92
|
+
source = info.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
93
|
+
} else if let key = key, !key.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
94
|
+
source = key.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
95
|
+
} else {
|
|
96
|
+
return nil
|
|
97
|
+
}
|
|
98
|
+
// Reverse-DNS freeform keys carry the tag name in their last
|
|
99
|
+
// segment; ID3 info fields and Vorbis keys carry no `.` at all and
|
|
100
|
+
// pass through whole.
|
|
101
|
+
let name: String
|
|
102
|
+
if let separator = source.lastIndex(of: ".") {
|
|
103
|
+
name = String(source[source.index(after: separator)...])
|
|
104
|
+
} else {
|
|
105
|
+
name = source
|
|
106
|
+
}
|
|
107
|
+
guard !name.isEmpty else { return nil }
|
|
108
|
+
return name.uppercased()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// True when [url] names an Opus container. Opus carries its gain in
|
|
112
|
+
/// `OpusHead.output_gain`, which the decoder applies, so a
|
|
113
|
+
/// `REPLAYGAIN_*` tag found alongside it would be applied twice.
|
|
114
|
+
static func isOpusContainer(url: URL) -> Bool {
|
|
115
|
+
let ext = url.pathExtension.lowercased()
|
|
116
|
+
return ext == "opus" || ext == "ogg"
|
|
117
|
+
}
|
|
118
|
+
}
|
package/ios/AudioSession.swift
CHANGED
|
@@ -9,7 +9,7 @@ import AVFoundation
|
|
|
9
9
|
/// bus, and calls `activate` + `installObservers` during its own
|
|
10
10
|
/// lifecycle.
|
|
11
11
|
///
|
|
12
|
-
/// Interruption semantics (
|
|
12
|
+
/// Interruption semantics (per Apple's AVAudioSession interruption docs):
|
|
13
13
|
///
|
|
14
14
|
/// - `.began`: system is telling us to pause (incoming call, Siri,
|
|
15
15
|
/// FaceTime, alarm). Callers typically call `pause()` on the
|
|
@@ -54,21 +54,37 @@ final class AudioSession {
|
|
|
54
54
|
case newDeviceAvailable
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
///
|
|
58
|
-
///
|
|
59
|
-
///
|
|
60
|
-
|
|
57
|
+
/// The three callback slots are written by the owner on its player queue
|
|
58
|
+
/// (`configure()` / `destroy()`) and read on main by the notification
|
|
59
|
+
/// observers — and `onError` on the player queue as well — so each read and
|
|
60
|
+
/// write goes through one lock.
|
|
61
|
+
private let callbackLock = NSLock()
|
|
62
|
+
private var interruptionCallback: ((InterruptionKind) -> Void)?
|
|
63
|
+
private var routeChangeCallback: ((RouteChangeKind) -> Void)?
|
|
64
|
+
private var errorCallback: ((AudioSessionError) -> Void)?
|
|
65
|
+
|
|
66
|
+
/// Fires on AVAudioSession interruption notifications, on main.
|
|
67
|
+
var onInterruption: ((InterruptionKind) -> Void)? {
|
|
68
|
+
get { callbackLock.withLock { interruptionCallback } }
|
|
69
|
+
set { callbackLock.withLock { interruptionCallback = newValue } }
|
|
70
|
+
}
|
|
61
71
|
|
|
62
72
|
/// Fires on AVAudioSession route-change notifications for the
|
|
63
|
-
/// subset of reasons the caller should react to.
|
|
64
|
-
var onRouteChange: ((RouteChangeKind) -> Void)?
|
|
73
|
+
/// subset of reasons the caller should react to, on main.
|
|
74
|
+
var onRouteChange: ((RouteChangeKind) -> Void)? {
|
|
75
|
+
get { callbackLock.withLock { routeChangeCallback } }
|
|
76
|
+
set { callbackLock.withLock { routeChangeCallback = newValue } }
|
|
77
|
+
}
|
|
65
78
|
|
|
66
79
|
/// Fires when an AVAudioSession lifecycle call (`setCategory`,
|
|
67
80
|
/// `setActive(true)`, `setActive(false)`) throws. Failure is
|
|
68
81
|
/// non-fatal at the audio-session level — playback itself hasn't
|
|
69
82
|
/// started yet, so the player state machine should not transition
|
|
70
|
-
/// to `.error` on these (do not call `emitState` from this
|
|
71
|
-
var onError: ((AudioSessionError) -> Void)?
|
|
83
|
+
/// to `.error` on these (do not call `emitState` from this callback).
|
|
84
|
+
var onError: ((AudioSessionError) -> Void)? {
|
|
85
|
+
get { callbackLock.withLock { errorCallback } }
|
|
86
|
+
set { callbackLock.withLock { errorCallback = newValue } }
|
|
87
|
+
}
|
|
72
88
|
|
|
73
89
|
private var interruptionObserver: NSObjectProtocol?
|
|
74
90
|
private var routeChangeObserver: NSObjectProtocol?
|
|
@@ -99,20 +115,50 @@ final class AudioSession {
|
|
|
99
115
|
mode: AVAudioSession.Mode = .default,
|
|
100
116
|
options: AVAudioSession.CategoryOptions = []
|
|
101
117
|
) {
|
|
102
|
-
|
|
118
|
+
guard configureCategory(mode: mode, options: options) else { return }
|
|
119
|
+
activateIfNeeded()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Apply the playback category without claiming the session. Returns whether
|
|
123
|
+
/// it succeeded, since activating after a failed category is pointless.
|
|
124
|
+
///
|
|
125
|
+
/// Category and activation are separable on purpose: the category is what
|
|
126
|
+
/// route sharing and the interruption observers need, while activation is
|
|
127
|
+
/// what actually takes audio away from whatever else is playing. Doing both
|
|
128
|
+
/// at configure time silenced another app's audio the moment a consumer
|
|
129
|
+
/// launched, before the user had pressed anything.
|
|
130
|
+
@discardableResult
|
|
131
|
+
func configureCategory(
|
|
132
|
+
mode: AVAudioSession.Mode = .default,
|
|
133
|
+
options: AVAudioSession.CategoryOptions = []
|
|
134
|
+
) -> Bool {
|
|
103
135
|
do {
|
|
104
|
-
try
|
|
136
|
+
try AVAudioSession.sharedInstance().setCategory(.playback, mode: mode, options: options)
|
|
137
|
+
return true
|
|
105
138
|
} catch {
|
|
106
139
|
let desc = String(describing: error)
|
|
107
140
|
NSLog("[AudioSession] setCategory(.playback) failed: %@", desc)
|
|
108
141
|
onError?(.setCategoryFailed(message: desc, nativeCode: (error as NSError).code))
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
return
|
|
142
|
+
// If setCategory failed, setActive is highly likely to fail too — no
|
|
143
|
+
// value in chaining a second error for the same root cause.
|
|
144
|
+
return false
|
|
113
145
|
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// The system took the session for an interruption. A later
|
|
149
|
+
/// `activateIfNeeded` claims it again: the session is no longer ours
|
|
150
|
+
/// whatever this object recorded before the interruption.
|
|
151
|
+
func noteInterrupted() {
|
|
152
|
+
isActive = false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Claim the audio session. Idempotent: repeated calls after the first are
|
|
156
|
+
/// no-ops, so every playback entry point can call it without bookkeeping.
|
|
157
|
+
func activateIfNeeded() {
|
|
158
|
+
guard !isActive else { return }
|
|
114
159
|
do {
|
|
115
|
-
try
|
|
160
|
+
try AVAudioSession.sharedInstance().setActive(true)
|
|
161
|
+
isActive = true
|
|
116
162
|
} catch {
|
|
117
163
|
let desc = String(describing: error)
|
|
118
164
|
NSLog("[AudioSession] setActive(true) failed: %@", desc)
|
|
@@ -120,11 +166,16 @@ final class AudioSession {
|
|
|
120
166
|
}
|
|
121
167
|
}
|
|
122
168
|
|
|
169
|
+
/// Whether this object has claimed the session. Cleared on deactivate so a
|
|
170
|
+
/// later play re-claims it.
|
|
171
|
+
private var isActive = false
|
|
172
|
+
|
|
123
173
|
/// Deactivate the session + notify other apps they can resume.
|
|
124
174
|
/// Consumers call this during `destroy` so e.g. a paused music
|
|
125
175
|
/// app or podcast player can auto-resume. Same error-logging
|
|
126
176
|
/// rationale as `activate`.
|
|
127
177
|
func deactivate() {
|
|
178
|
+
isActive = false
|
|
128
179
|
do {
|
|
129
180
|
try AVAudioSession.sharedInstance().setActive(
|
|
130
181
|
false, options: [.notifyOthersOnDeactivation])
|