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.
Files changed (355) hide show
  1. package/QueuePlayer.podspec +14 -9
  2. package/README.md +3 -3
  3. package/android/consumer-rules.pro +1 -1
  4. package/android/src/main/cpp/airplay2_control.c +4 -4
  5. package/android/src/main/cpp/airplay2_control.h +4 -4
  6. package/android/src/main/cpp/airplay2_jni.cpp +3 -19
  7. package/android/src/main/cpp/airplay2_pair.c +7 -7
  8. package/android/src/main/cpp/airplay2_pair.h +2 -2
  9. package/android/src/main/cpp/airplay2_rtsp.c +8 -8
  10. package/android/src/main/cpp/airplay2_session.c +2 -2
  11. package/android/src/main/cpp/airplay_jni.cpp +8 -69
  12. package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +7 -16
  13. package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +1 -1
  14. package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +2 -14
  15. package/android/src/main/java/com/margelo/nitro/queueplayer/CellularTransportMonitor.kt +77 -0
  16. package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +187 -52
  17. package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +27 -7
  18. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +33 -12
  19. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +31 -8
  20. package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +45 -30
  21. package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +111 -15
  22. package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +60 -33
  23. package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +1 -1
  24. package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +3 -1
  25. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +103 -29
  26. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +77 -29
  27. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +6 -6
  28. package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +48 -1
  29. package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +3 -2
  30. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +71 -76
  31. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +3 -3
  32. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +2 -2
  33. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +68 -46
  34. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +6 -5
  35. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +5 -6
  36. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +11 -8
  37. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +26 -6
  38. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +22 -3
  39. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +38 -12
  40. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +17 -13
  41. package/android/src/main/java/com/margelo/nitro/queueplayer/SuppliedReplayGain.kt +66 -0
  42. package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +839 -271
  43. package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +4 -8
  44. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +4 -3
  45. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +5 -4
  46. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +0 -2
  47. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +4 -2
  48. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +16 -9
  49. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +1 -1
  50. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +6 -14
  51. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +1 -13
  52. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +1 -1
  53. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +3 -3
  54. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +11 -35
  55. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +0 -5
  56. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +0 -6
  57. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +32 -33
  58. package/android/src/test/java/androidx/media3/session/{MediaSessionControllerRequestTestSeam.kt → MediaSessionControllerRequestTestSupport.kt} +1 -1
  59. package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +12 -0
  60. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineFocusLossTest.kt +115 -0
  61. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +45 -0
  62. package/android/src/test/java/com/margelo/nitro/queueplayer/EngineEndSignalOrderTest.kt +70 -0
  63. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +17 -0
  64. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
  65. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +17 -0
  66. package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +15 -0
  67. package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +135 -4
  68. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +1 -0
  69. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +36 -2
  70. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +24 -18
  71. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterCancelTest.kt +85 -0
  72. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +112 -1
  73. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +5 -3
  74. package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +45 -0
  75. package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +1 -1
  76. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +47 -0
  77. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +53 -29
  78. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +8 -8
  79. package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +33 -1
  80. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +128 -5
  81. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +209 -14
  82. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +150 -10
  83. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainMergeTest.kt +258 -0
  84. package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +2 -2
  85. package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +2 -2
  86. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowDynamicsProcessingRejectingEnable.kt +19 -0
  87. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizer.kt +1 -1
  88. package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizerRejectingBandWrites.kt +27 -0
  89. package/android/src/test/java/com/margelo/nitro/queueplayer/SuppliedReplayGainTest.kt +61 -0
  90. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +4 -4
  91. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
  92. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastCommandRoutingTest.kt +2 -1
  93. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +2 -1
  94. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +88 -38
  95. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +205 -27
  96. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +277 -38
  97. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +5 -3
  98. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +2 -1
  99. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +85 -3
  100. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +11 -7
  101. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +2 -1
  102. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +90 -4
  103. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +8 -26
  104. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +3 -3
  105. package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +0 -4
  106. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +2 -2
  107. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitorTest.kt +173 -0
  108. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/ShadowConnectivityManagerRecordingRequests.kt +27 -0
  109. package/app.plugin.js +17 -1
  110. package/ios/AVPlayerItemQueueItemId.swift +26 -12
  111. package/ios/AVQueueBuilder.swift +120 -85
  112. package/ios/ArtworkLoader.swift +0 -7
  113. package/ios/ArtworkResolver.swift +22 -4
  114. package/ios/AssetReplayGainReader.swift +118 -0
  115. package/ios/AudioSession.swift +67 -16
  116. package/ios/AudioTapProvider.swift +284 -149
  117. package/ios/CarPlayBridge.swift +0 -12
  118. package/ios/CarPlayCoordinator.swift +18 -2
  119. package/ios/CarPlaySceneDelegate.swift +2 -2
  120. package/ios/Cast/AirPlayRouteState.swift +9 -8
  121. package/ios/Cast/Core/CastEventBridge.swift +66 -26
  122. package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
  123. package/ios/Cast/Core/CastSession.swift +2 -2
  124. package/ios/Cast/Core/CastTransportRouter.swift +21 -9
  125. package/ios/Cast/Core/LocalMediaServer.swift +41 -52
  126. package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +32 -11
  127. package/ios/Cast/Core/MediaServerHandle.swift +0 -4
  128. package/ios/Cast/Core/MediaTokenRegistry.swift +0 -6
  129. package/ios/CastManager.swift +1 -1
  130. package/ios/CrossfadeEngine.swift +660 -226
  131. package/ios/EQTap.swift +20 -17
  132. package/ios/Equalizer.swift +1 -1
  133. package/ios/FLACStreamInfo.swift +65 -0
  134. package/ios/GaplessEngine.swift +57 -30
  135. package/ios/InputGuards.swift +35 -5
  136. package/ios/LookaheadCache.swift +202 -58
  137. package/ios/LookaheadCachePrefetcher.swift +48 -36
  138. package/ios/MediaServer/MediaHTTPConnection.swift +691 -0
  139. package/ios/NetworkRecoveryPolicy.swift +32 -0
  140. package/ios/NowPlayingFormatExtractor.swift +25 -18
  141. package/ios/NowPlayingInfo.swift +84 -48
  142. package/ios/OutputRouteMonitor.swift +16 -3
  143. package/ios/PendingBrowseRequests.swift +1 -1
  144. package/ios/PlaceholderArtwork.swift +9 -5
  145. package/ios/PlaybackEngine.swift +165 -76
  146. package/ios/PlaybackErrorMapping.swift +3 -3
  147. package/ios/PlaybackModeStateMachine.swift +2 -2
  148. package/ios/PlaybackNetworkMonitor.swift +73 -0
  149. package/ios/PlayerStateDerivation.swift +6 -6
  150. package/ios/QueueMutationArithmetic.swift +25 -0
  151. package/ios/QueueSkipArithmetic.swift +5 -3
  152. package/ios/QueueWindowArithmetic.swift +152 -0
  153. package/ios/ReadThroughServer.swift +283 -0
  154. package/ios/RemoteCommands.swift +44 -4
  155. package/ios/ReplayGainData.swift +22 -3
  156. package/ios/ReplayGainExtractor.swift +70 -27
  157. package/ios/Siri/VoiceDonation.swift +14 -9
  158. package/ios/StreamingBitrateProbe.swift +22 -68
  159. package/ios/SuppliedReplayGain.swift +83 -0
  160. package/ios/Tests/AVQueueBuilderTests.swift +158 -221
  161. package/ios/Tests/ActiveItemEngineStub.swift +44 -0
  162. package/ios/Tests/AssetReplayGainReaderTests.swift +228 -0
  163. package/ios/Tests/AudioTapProviderDispatchTargetTests.swift +158 -0
  164. package/ios/Tests/AudioTapProviderReplayGainTests.swift +122 -10
  165. package/ios/Tests/BitrateReresolveBudgetTests.swift +71 -0
  166. package/ios/Tests/CastMediaItemTranslationTests.swift +72 -0
  167. package/ios/Tests/CastNowPlayingControllerTests.swift +1 -1
  168. package/ios/Tests/CrossfadeAdvanceWithoutFadeTests.swift +120 -0
  169. package/ios/Tests/CrossfadeEngineIncomingMixTests.swift +122 -0
  170. package/ios/Tests/CrossfadeEngineTests.swift +164 -0
  171. package/ios/Tests/CrossfadeMixReapplyTests.swift +66 -0
  172. package/ios/Tests/CrossfadePlaybackIntentTests.swift +81 -0
  173. package/ios/Tests/EngineSwapTests.swift +91 -0
  174. package/ios/Tests/EqualizerAudioMixProviderTests.swift +46 -0
  175. package/ios/Tests/EqualizerHybridTests.swift +5 -2
  176. package/ios/Tests/FLACStreamInfoTests.swift +88 -0
  177. package/ios/Tests/GaplessEngineLifecycleTests.swift +4 -4
  178. package/ios/Tests/InputGuardsTests.swift +41 -0
  179. package/ios/Tests/InterruptionIntentTests.swift +77 -0
  180. package/ios/Tests/LookaheadCacheCellularAccessTests.swift +185 -0
  181. package/ios/Tests/LookaheadCachePrefetcherTests.swift +53 -30
  182. package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +160 -1
  183. package/ios/Tests/LookaheadCacheTests.swift +44 -18
  184. package/ios/Tests/MediaHTTPConnectionTests.swift +79 -0
  185. package/ios/Tests/MutationDeferralTests.swift +220 -0
  186. package/ios/Tests/NowPlayingInfoTests.swift +86 -99
  187. package/ios/Tests/NowPlayingSnapshotTests.swift +127 -0
  188. package/ios/Tests/OriginRestartStitcherTests.swift +60 -0
  189. package/ios/Tests/PlaybackNetworkMonitorTests.swift +95 -0
  190. package/ios/Tests/PlayerFixtures.swift +48 -0
  191. package/ios/Tests/PlayerStateDerivationTests.swift +10 -10
  192. package/ios/Tests/QueueMutationGenerationTests.swift +141 -0
  193. package/ios/Tests/QueueRebuildPrefixTests.swift +317 -0
  194. package/ios/Tests/QueueStateTests.swift +3 -1
  195. package/ios/Tests/QueueWindowArithmeticTests.swift +56 -0
  196. package/ios/Tests/QueueWindowSliceTests.swift +162 -0
  197. package/ios/Tests/ReadThroughRoutingLifecycleTests.swift +63 -0
  198. package/ios/Tests/ReadThroughServerTests.swift +345 -0
  199. package/ios/Tests/RemoteCommandsTests.swift +28 -0
  200. package/ios/Tests/ReplayGainExtractorTests.swift +216 -6
  201. package/ios/Tests/ReplayGainMergeTests.swift +230 -0
  202. package/ios/Tests/RetryRecoveryTests.swift +303 -0
  203. package/ios/Tests/SkipCapabilityTests.swift +233 -8
  204. package/ios/Tests/SkipIndexTests.swift +32 -2
  205. package/ios/Tests/SleepTimerPauseIntentTests.swift +43 -0
  206. package/ios/Tests/StallRecoveryTests.swift +97 -0
  207. package/ios/Tests/StreamingBitrateProbeTests.swift +17 -20
  208. package/ios/Tests/TopUpWindowGateTests.swift +394 -0
  209. package/ios/Tests/TrackPlayer+TestHops.swift +14 -0
  210. package/ios/Tests/TrackPlayerCallOrderTests.swift +93 -0
  211. package/ios/Tests/TrackPlayerConfigureTeardownTests.swift +86 -0
  212. package/ios/Tests/TrackPlayerEndVerdictTests.swift +165 -0
  213. package/ios/Tests/TrackPlayerSeekTests.swift +137 -0
  214. package/ios/Tests/TrackPlayerThreadingTests.swift +127 -0
  215. package/ios/Tests/TrackSourceClassifierTests.swift +33 -7
  216. package/ios/Tests/VoiceVocabularyOptInTests.swift +38 -0
  217. package/ios/TrackPlayer+Automotive.swift +101 -0
  218. package/ios/TrackPlayer+Cache.swift +232 -0
  219. package/ios/TrackPlayer+Config.swift +291 -0
  220. package/ios/TrackPlayer+EngineDelegate.swift +209 -0
  221. package/ios/TrackPlayer+EventsAPI.swift +111 -0
  222. package/ios/TrackPlayer+EventsDispatch.swift +968 -0
  223. package/ios/TrackPlayer+EventsWiring.swift +173 -0
  224. package/ios/TrackPlayer+Lifecycle.swift +930 -0
  225. package/ios/TrackPlayer+NowPlayingFormat.swift +258 -0
  226. package/ios/TrackPlayer+Queue.swift +940 -0
  227. package/ios/TrackPlayer+Recovery.swift +196 -0
  228. package/ios/TrackPlayer+Skip.swift +455 -0
  229. package/ios/TrackPlayer+SleepTimer.swift +175 -0
  230. package/ios/TrackPlayer+State.swift +108 -0
  231. package/ios/TrackPlayer+Threading.swift +135 -0
  232. package/ios/TrackPlayer+Transport.swift +267 -0
  233. package/ios/TrackPlayer+Window.swift +195 -0
  234. package/ios/TrackPlayer.swift +305 -4619
  235. package/ios/Visualizer.swift +6 -5
  236. package/ios/tests-harness/Podfile +1 -1
  237. package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +19 -11
  238. package/ios/tests-harness/scripts/seed-xcodeproj.rb +2 -2
  239. package/lib/module/hooks/useActiveTrack.js +29 -22
  240. package/lib/module/hooks/useActiveTrack.js.map +1 -1
  241. package/lib/module/hooks/useCast.js +8 -26
  242. package/lib/module/hooks/useCast.js.map +1 -1
  243. package/lib/module/hooks/useEqualizer.js +19 -12
  244. package/lib/module/hooks/useEqualizer.js.map +1 -1
  245. package/lib/module/hooks/useLookaheadCache.js +3 -7
  246. package/lib/module/hooks/useLookaheadCache.js.map +1 -1
  247. package/lib/module/hooks/useQueue.js +66 -19
  248. package/lib/module/hooks/useQueue.js.map +1 -1
  249. package/lib/module/index.js +6 -3
  250. package/lib/module/index.js.map +1 -1
  251. package/lib/module/queueDelta.js +41 -0
  252. package/lib/module/queueDelta.js.map +1 -0
  253. package/lib/module/types.js +28 -5
  254. package/lib/module/types.js.map +1 -1
  255. package/lib/typescript/TrackPlayer.nitro.d.ts +53 -28
  256. package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -1
  257. package/lib/typescript/hooks/useActiveTrack.d.ts +5 -7
  258. package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -1
  259. package/lib/typescript/hooks/useCast.d.ts +6 -1
  260. package/lib/typescript/hooks/useCast.d.ts.map +1 -1
  261. package/lib/typescript/hooks/useEqualizer.d.ts +2 -1
  262. package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -1
  263. package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -1
  264. package/lib/typescript/hooks/useQueue.d.ts +4 -4
  265. package/lib/typescript/hooks/useQueue.d.ts.map +1 -1
  266. package/lib/typescript/index.d.ts +2 -1
  267. package/lib/typescript/index.d.ts.map +1 -1
  268. package/lib/typescript/queueDelta.d.ts +10 -0
  269. package/lib/typescript/queueDelta.d.ts.map +1 -0
  270. package/lib/typescript/types.d.ts +168 -12
  271. package/lib/typescript/types.d.ts.map +1 -1
  272. package/nitrogen/generated/android/c++/JFunc_void_QueueChangeDelta_double_QueueChangeReason.hpp +85 -0
  273. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +45 -19
  274. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +3 -3
  275. package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +8 -4
  276. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
  277. package/nitrogen/generated/android/c++/JQueueChangeDelta.hpp +128 -0
  278. package/nitrogen/generated/android/c++/JTrackItem.hpp +19 -3
  279. 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
  280. 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
  281. 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
  282. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +0 -2
  283. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +0 -2
  284. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +0 -2
  285. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +0 -2
  286. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +0 -2
  287. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +0 -2
  288. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +0 -2
  289. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +0 -2
  290. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +0 -2
  291. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +0 -2
  292. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +0 -2
  293. 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
  294. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +0 -2
  295. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +0 -2
  296. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +0 -2
  297. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +0 -2
  298. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +0 -2
  299. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +0 -2
  300. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +0 -2
  301. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +0 -2
  302. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +0 -2
  303. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +0 -2
  304. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +0 -2
  305. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +2 -0
  306. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +2 -0
  307. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +6 -4
  308. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +2 -0
  309. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +9 -4
  310. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +7 -2
  311. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/QueueChangeDelta.kt +86 -0
  312. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +24 -4
  313. package/nitrogen/generated/android/queueplayerOnLoad.cpp +2 -2
  314. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +16 -16
  315. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +80 -65
  316. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
  317. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -3
  318. package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +5 -5
  319. package/nitrogen/generated/ios/swift/Func_void_QueueChangeDelta_double_QueueChangeReason.swift +46 -0
  320. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +3 -3
  321. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +32 -24
  322. package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +20 -2
  323. package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
  324. package/nitrogen/generated/ios/swift/QueueChangeDelta.swift +82 -0
  325. package/nitrogen/generated/ios/swift/TrackItem.swift +73 -1
  326. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +6 -3
  327. package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +7 -3
  328. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
  329. package/nitrogen/generated/shared/c++/QueueChangeDelta.hpp +113 -0
  330. package/nitrogen/generated/shared/c++/TrackItem.hpp +18 -2
  331. package/package.json +8 -8
  332. package/src/TrackPlayer.nitro.ts +53 -27
  333. package/src/hooks/useActiveTrack.ts +29 -22
  334. package/src/hooks/useCast.ts +14 -15
  335. package/src/hooks/useEqualizer.ts +19 -12
  336. package/src/hooks/useLookaheadCache.ts +3 -7
  337. package/src/hooks/useQueue.ts +66 -17
  338. package/src/index.ts +13 -3
  339. package/src/queueDelta.ts +41 -0
  340. package/src/types.ts +169 -12
  341. package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +0 -82
  342. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +0 -15
  343. package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +0 -192
  344. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +0 -135
  345. package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +0 -27
  346. package/ios/Cast/Core/LocalAddressMonitor.swift +0 -110
  347. package/ios/Cast/Core/MediaHTTPConnection.swift +0 -349
  348. package/ios/MetadataReader.swift +0 -555
  349. package/ios/Tests/CrossfadeEngineStubTests.swift +0 -43
  350. package/ios/Tests/MetadataReaderTests.swift +0 -603
  351. package/ios/Tests/PlaybackEngineProtocolTests.swift +0 -92
  352. package/ios/Tests/VoiceDonationTests.swift +0 -26
  353. package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__double_QueueChangeReason.hpp +0 -101
  354. package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__double_QueueChangeReason.swift +0 -46
  355. /package/ios/{Cast/Core → MediaServer}/MimeTypes.swift +0 -0
@@ -0,0 +1,258 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Test
5
+
6
+ /**
7
+ * Coverage for [ReplayGainData.merge] — the all-or-nothing, per-item
8
+ * choice between consumer-supplied ReplayGain and values detected from
9
+ * the file. Mirrors the iOS `ReplayGainMergeTests` matrix row for row,
10
+ * with the same fixtures, so both platforms hold the same rule.
11
+ *
12
+ * The rule is one predicate on the item, not two pair decisions and not
13
+ * four field decisions: a claimed track's ReplayGain comes solely from
14
+ * the consumer and detection is discarded in full — including for the
15
+ * mode the consumer said nothing about. `merge` takes the claim and the
16
+ * surviving values as two separate inputs; which fields make a claim and
17
+ * which values survive is [SuppliedReplayGain]'s job, covered in
18
+ * `SuppliedReplayGainTest`.
19
+ *
20
+ * Supplied and detected fixtures carry different values in every field,
21
+ * so a row that returned the wrong source cannot pass.
22
+ */
23
+ class ReplayGainMergeTest {
24
+
25
+ // region Fixtures
26
+
27
+ private val detectedAll = ReplayGainData(
28
+ trackGainDb = -20.0f, trackPeak = 0.5f,
29
+ albumGainDb = -6.0f, albumPeak = 0.6f,
30
+ )
31
+ private val detectedTrackOnly = ReplayGainData(
32
+ trackGainDb = -20.0f, trackPeak = 0.5f,
33
+ albumGainDb = null, albumPeak = null,
34
+ )
35
+ private val detectedAlbumOnly = ReplayGainData(
36
+ trackGainDb = null, trackPeak = null,
37
+ albumGainDb = -6.0f, albumPeak = 0.6f,
38
+ )
39
+
40
+ private val suppliedAll = ReplayGainData(
41
+ trackGainDb = -3.0f, trackPeak = 0.25f,
42
+ albumGainDb = -9.0f, albumPeak = 0.3f,
43
+ )
44
+ private val suppliedTrackGain = ReplayGainData(
45
+ trackGainDb = -3.0f, trackPeak = null,
46
+ albumGainDb = null, albumPeak = null,
47
+ )
48
+ private val suppliedAlbumGain = ReplayGainData(
49
+ trackGainDb = null, trackPeak = null,
50
+ albumGainDb = -9.0f, albumPeak = null,
51
+ )
52
+ private val suppliedTrackPeak = ReplayGainData(
53
+ trackGainDb = null, trackPeak = 0.25f,
54
+ albumGainDb = null, albumPeak = null,
55
+ )
56
+ private val suppliedAlbumPeak = ReplayGainData(
57
+ trackGainDb = null, trackPeak = null,
58
+ albumGainDb = null, albumPeak = 0.3f,
59
+ )
60
+ private val suppliedTrackPair = ReplayGainData(
61
+ trackGainDb = -3.0f, trackPeak = 0.25f,
62
+ albumGainDb = null, albumPeak = null,
63
+ )
64
+ private val suppliedAlbumPair = ReplayGainData(
65
+ trackGainDb = null, trackPeak = null,
66
+ albumGainDb = -9.0f, albumPeak = 0.3f,
67
+ )
68
+
69
+ // endregion
70
+
71
+ // region No claim — detection is used verbatim
72
+
73
+ @Test
74
+ fun `an unclaimed track uses every detected field`() {
75
+ assertMerge(
76
+ claimed = false, supplied = null, detected = detectedAll,
77
+ expected = detectedAll,
78
+ message = "no supplied field means detection is used, all four",
79
+ )
80
+ }
81
+
82
+ @Test
83
+ fun `an unclaimed track with no detection has no ReplayGain`() {
84
+ assertMerge(
85
+ claimed = false, supplied = null, detected = null,
86
+ expected = null,
87
+ message = "nothing supplied and nothing detected is no ReplayGain",
88
+ )
89
+ }
90
+
91
+ @Test
92
+ fun `an unclaimed track keeps a track-only detection`() {
93
+ assertMerge(
94
+ claimed = false, supplied = null, detected = detectedTrackOnly,
95
+ expected = detectedTrackOnly,
96
+ message = "a partial detection is used as-is, not padded",
97
+ )
98
+ }
99
+
100
+ @Test
101
+ fun `an unclaimed track keeps an album-only detection`() {
102
+ assertMerge(
103
+ claimed = false, supplied = null, detected = detectedAlbumOnly,
104
+ expected = detectedAlbumOnly,
105
+ message = "the mirrored partial-detection case",
106
+ )
107
+ }
108
+
109
+ // endregion
110
+
111
+ // region Claimed — one supplied field discards all detection
112
+
113
+ @Test
114
+ fun `a supplied track gain discards every detected field`() {
115
+ assertMerge(
116
+ claimed = true, supplied = suppliedTrackGain, detected = detectedAll,
117
+ expected = suppliedTrackGain,
118
+ message = "supplied trackGain applies; detected peak and album pair are dropped",
119
+ )
120
+ }
121
+
122
+ @Test
123
+ fun `a supplied album gain discards every detected field`() {
124
+ assertMerge(
125
+ claimed = true, supplied = suppliedAlbumGain, detected = detectedAll,
126
+ expected = suppliedAlbumGain,
127
+ message = "supplied albumGain applies; detected track pair and album peak are dropped",
128
+ )
129
+ }
130
+
131
+ @Test
132
+ fun `a supplied track peak alone leaves no gain anywhere`() {
133
+ assertMerge(
134
+ claimed = true, supplied = suppliedTrackPeak, detected = detectedAll,
135
+ expected = suppliedTrackPeak,
136
+ message = "a peak with no gain is ownership with nothing to apply",
137
+ )
138
+ }
139
+
140
+ @Test
141
+ fun `a supplied album peak alone leaves no gain anywhere`() {
142
+ assertMerge(
143
+ claimed = true, supplied = suppliedAlbumPeak, detected = detectedAll,
144
+ expected = suppliedAlbumPeak,
145
+ message = "the mirrored peak-only case",
146
+ )
147
+ }
148
+
149
+ @Test
150
+ fun `a supplied track pair discards the detected album pair`() {
151
+ assertMerge(
152
+ claimed = true, supplied = suppliedTrackPair, detected = detectedAll,
153
+ expected = suppliedTrackPair,
154
+ message = "album mode plays at unity even though the file carries album tags",
155
+ )
156
+ }
157
+
158
+ @Test
159
+ fun `a supplied album pair discards the detected track pair`() {
160
+ assertMerge(
161
+ claimed = true, supplied = suppliedAlbumPair, detected = detectedAll,
162
+ expected = suppliedAlbumPair,
163
+ message = "track mode plays at unity even though the file carries track tags",
164
+ )
165
+ }
166
+
167
+ @Test
168
+ fun `a fully supplied set replaces a fully detected set`() {
169
+ assertMerge(
170
+ claimed = true, supplied = suppliedAll, detected = detectedAll,
171
+ expected = suppliedAll,
172
+ message = "all four supplied values win over all four detected values",
173
+ )
174
+ }
175
+
176
+ @Test
177
+ fun `a supplied track gain discards an album-only detection`() {
178
+ assertMerge(
179
+ claimed = true, supplied = suppliedTrackGain, detected = detectedAlbumOnly,
180
+ expected = suppliedTrackGain,
181
+ message = "detection is discarded even where supplied and detected do not overlap",
182
+ )
183
+ }
184
+
185
+ // endregion
186
+
187
+ // region Claimed with no detection to discard
188
+
189
+ @Test
190
+ fun `a fully supplied set applies to an untagged file`() {
191
+ assertMerge(
192
+ claimed = true, supplied = suppliedAll, detected = null,
193
+ expected = suppliedAll,
194
+ message = "a supplied set is applied where the file carries no tags",
195
+ )
196
+ }
197
+
198
+ @Test
199
+ fun `a supplied track gain applies to an untagged file`() {
200
+ assertMerge(
201
+ claimed = true, supplied = suppliedTrackGain, detected = null,
202
+ expected = suppliedTrackGain,
203
+ message = "a single supplied field on an untagged file applies alone",
204
+ )
205
+ }
206
+
207
+ // endregion
208
+
209
+ // region Claimed but nothing usable — ownership without a value
210
+
211
+ @Test
212
+ fun `a claim with no usable value discards a full detection`() {
213
+ assertMerge(
214
+ claimed = true, supplied = null, detected = detectedAll,
215
+ expected = null,
216
+ message = "a claim discards detection even when it carries no value",
217
+ )
218
+ }
219
+
220
+ @Test
221
+ fun `a claim with no usable value discards a partial detection`() {
222
+ assertMerge(
223
+ claimed = true, supplied = null, detected = detectedTrackOnly,
224
+ expected = null,
225
+ message = "a partial detection is discarded by a claim just as a full one is",
226
+ )
227
+ }
228
+
229
+ @Test
230
+ fun `a claim with no usable value and no detection has no ReplayGain`() {
231
+ assertMerge(
232
+ claimed = true, supplied = null, detected = null,
233
+ expected = null,
234
+ message = "ownership claimed, nothing supplied, nothing detected",
235
+ )
236
+ }
237
+
238
+ // endregion
239
+
240
+ // region Helper
241
+
242
+ private fun assertMerge(
243
+ claimed: Boolean,
244
+ supplied: ReplayGainData?,
245
+ detected: ReplayGainData?,
246
+ expected: ReplayGainData?,
247
+ message: String,
248
+ ) {
249
+ val actual = ReplayGainData.merge(
250
+ suppliedClaimed = claimed,
251
+ supplied = supplied,
252
+ detected = detected,
253
+ )
254
+ assertEquals(message, expected, actual)
255
+ }
256
+
257
+ // endregion
258
+ }
@@ -41,8 +41,8 @@ import org.robolectric.android.controller.ServiceController
41
41
  * enforces a unique session ID per JVM and a leaked session
42
42
  * blocks the next [PlaybackService.onCreate] in the same run.
43
43
  *
44
- * Standard Robolectric service-lifecycle plumbing — no test
45
- * injection seam on the lib side.
44
+ * Standard Robolectric service-lifecycle driving — no test
45
+ * injection point on the lib side.
46
46
  */
47
47
  @UnstableApi
48
48
  internal class PlaybackServiceTestHandle(
@@ -27,7 +27,7 @@ import org.robolectric.annotation.Config
27
27
  *
28
28
  * The wrapper's `handle*` overrides are `protected` on a `final`
29
29
  * Kotlin class, so the suite drives them through [HandleDriver] — a
30
- * thin reflection seam onto the exact overridden methods — to assert
30
+ * thin reflection helper into the exact overridden methods — to assert
31
31
  * the routing directly without standing up a live `MediaController`.
32
32
  *
33
33
  * Controller-vs-internal gating turns on whether
@@ -422,7 +422,7 @@ class SessionCommandForwardingPlayerTest {
422
422
  }
423
423
 
424
424
  /**
425
- * Reflection seam onto a [SessionCommandForwardingPlayer]'s
425
+ * Reflection helper into a [SessionCommandForwardingPlayer]'s
426
426
  * `protected` `handle*` overrides. The wrapper is a `final` Kotlin
427
427
  * class so it cannot be subclassed; reflecting straight onto the
428
428
  * declared overrides invokes the exact production methods without
@@ -0,0 +1,19 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import android.media.audiofx.DynamicsProcessing
4
+ import org.robolectric.annotation.Implements
5
+ import org.robolectric.shadows.ShadowDynamicsProcessing
6
+
7
+ /**
8
+ * A [DynamicsProcessing] whose construction and band writes succeed and
9
+ * whose `setEnabled` throws. The framework constructor never calls
10
+ * `setEnabled`; the engine does, after applying the bands and before
11
+ * recording the attach — so this is the one call that fails an attach
12
+ * inside its configure step rather than at construction.
13
+ */
14
+ @Implements(DynamicsProcessing::class)
15
+ class ShadowDynamicsProcessingRejectingEnable : ShadowDynamicsProcessing() {
16
+
17
+ override fun native_setEnabled(enabled: Boolean): Int =
18
+ throw IllegalStateException("setEnabled() called on uninitialized AudioEffect")
19
+ }
@@ -20,7 +20,7 @@ import java.util.Optional
20
20
  * [Equalizer] API.
21
21
  */
22
22
  @Implements(Equalizer::class)
23
- class ShadowEqualizer : ShadowAudioEffect() {
23
+ open class ShadowEqualizer : ShadowAudioEffect() {
24
24
 
25
25
  override fun getDefaultParameter(parameter: ByteBuffer): Optional<ByteBuffer> {
26
26
  val key = parameter.duplicate().order(ByteOrder.nativeOrder())
@@ -0,0 +1,27 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import android.media.audiofx.AudioEffect
4
+ import android.media.audiofx.Equalizer
5
+ import org.robolectric.annotation.Implements
6
+ import java.nio.ByteBuffer
7
+ import java.nio.ByteOrder
8
+
9
+ /**
10
+ * A five-band equalizer whose construction and parameter reads succeed and
11
+ * whose first band-level write fails, so an attach reaches its configure
12
+ * step and throws there. `Equalizer.setBandLevel` routes the returned error
13
+ * through `checkStatus`, which raises `RuntimeException`.
14
+ */
15
+ @Implements(Equalizer::class)
16
+ class ShadowEqualizerRejectingBandWrites : ShadowEqualizer() {
17
+
18
+ override fun native_setParameter(psize: Int, param: ByteArray, vsize: Int, value: ByteArray): Int {
19
+ val id = ByteBuffer.wrap(param).order(ByteOrder.nativeOrder()).int
20
+ if (id == PARAM_BAND_LEVEL) return AudioEffect.ERROR_INVALID_OPERATION
21
+ return super.native_setParameter(psize, param, vsize, value)
22
+ }
23
+
24
+ private companion object {
25
+ const val PARAM_BAND_LEVEL = 2
26
+ }
27
+ }
@@ -0,0 +1,61 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import androidx.media3.common.util.UnstableApi
4
+ import org.junit.Assert.assertEquals
5
+ import org.junit.Assert.assertNotNull
6
+ import org.junit.Assert.assertNull
7
+ import org.junit.Test
8
+
9
+ /**
10
+ * [SuppliedReplayGain.from] separates the consumer's claim over a track
11
+ * from the values that survive validation: any present field claims the
12
+ * track, and only accepted values are carried.
13
+ */
14
+ @UnstableApi
15
+ class SuppliedReplayGainTest {
16
+
17
+ @Test
18
+ fun `a track with no ReplayGain field makes no claim`() {
19
+ assertNull(SuppliedReplayGain.from(track()))
20
+ }
21
+
22
+ @Test
23
+ fun `one valid field claims the track and is carried`() {
24
+ val claim = SuppliedReplayGain.from(track(trackGain = -3.0))
25
+
26
+ assertNotNull(claim)
27
+ assertEquals(
28
+ ReplayGainData(trackGainDb = -3.0f, trackPeak = null, albumGainDb = null, albumPeak = null),
29
+ claim?.data)
30
+ }
31
+
32
+ @Test
33
+ fun `only invalid fields still claim the track but carry nothing`() {
34
+ val claim = SuppliedReplayGain.from(
35
+ track(trackGain = Double.NaN, trackPeak = 5.0, albumGain = Double.POSITIVE_INFINITY, albumPeak = 0.0))
36
+
37
+ assertNotNull("presence claims the track, whatever the values", claim)
38
+ assertNull("no value passed acceptance, so there is nothing to apply", claim?.data)
39
+ }
40
+
41
+ @Test
42
+ fun `a mix carries the accepted fields and drops the rest`() {
43
+ val claim = SuppliedReplayGain.from(
44
+ track(trackGain = -3.0, trackPeak = 5.0, albumGain = -9.0, albumPeak = 0.3))
45
+
46
+ assertEquals(
47
+ ReplayGainData(trackGainDb = -3.0f, trackPeak = null, albumGainDb = -9.0f, albumPeak = 0.3f),
48
+ claim?.data)
49
+ }
50
+
51
+ private fun track(
52
+ trackGain: Double? = null,
53
+ trackPeak: Double? = null,
54
+ albumGain: Double? = null,
55
+ albumPeak: Double? = null,
56
+ ) = TrackItem(
57
+ id = null, url = "https://example.com/a.mp3", title = null, artist = null, album = null,
58
+ duration = null, artworkUrl = null, extras = null,
59
+ replayGainTrackGain = trackGain, replayGainTrackPeak = trackPeak,
60
+ replayGainAlbumGain = albumGain, replayGainAlbumPeak = albumPeak)
61
+ }
@@ -38,7 +38,7 @@ import java.nio.ByteBuffer
38
38
  * stopped when the AirPlay engine is released or the session closes
39
39
  * (`MetadataAwareSession.onDeactivated`).
40
40
  *
41
- * The route flip is driven through the production seam — `CastSinkRouter`'s
41
+ * The route flip is driven through the production path — `CastSinkRouter`'s
42
42
  * activate/deactivate transitions, which `TrackPlayer`'s route listener turns
43
43
  * into a posted `applyEngineSwapForMode`. The sync itself is observed through a
44
44
  * fake [CastSession.PcmStream] that also implements [MetadataAwareSession] and
@@ -64,7 +64,7 @@ class TrackPlayerAirPlayMetadataWiringTest {
64
64
  PlayerConfig(
65
65
  httpHeaders = null, userAgent = null, autoRetries = null,
66
66
  retryBackoffMs = null, networkTimeoutMs = null, audioContentType = null,
67
- audioCategoryOptions = null, visualizationEnabled = null,
67
+ audioCategoryOptions = null, voiceVocabularyDonationEnabled = null, visualizationEnabled = null,
68
68
  clampSeekToBuffered = null,
69
69
  lookaheadCacheMaxSizeMb = null, lookaheadCacheEvictionPolicy = null, progressUpdateIntervalMs = null, backgroundProgressUpdateIntervalMs = null, placeholderArtworkUri = null, skipToPreviousBehavior = null,
70
70
  ),
@@ -74,8 +74,8 @@ class TrackPlayerAirPlayMetadataWiringTest {
74
74
  // matching the production path where `onActivated` is fired after the queue
75
75
  // install reads a populated player.
76
76
  player.tracks = listOf(
77
- TrackItem("t1", "https://example.com/1.m4a", null, null, null, null, null, null),
78
- TrackItem("t2", "https://example.com/2.m4a", null, null, null, null, null, null),
77
+ TrackItem("t1", "https://example.com/1.m4a", null, null, null, null, null, null, null, null, null, null),
78
+ TrackItem("t2", "https://example.com/2.m4a", null, null, null, null, null, null, null, null, null, null),
79
79
  )
80
80
  player.currentTrackIndex = 0
81
81
  }
@@ -44,7 +44,7 @@ class TrackPlayerAudioFocusTest {
44
44
  player = TrackPlayer()
45
45
  serviceHandle = bindPlaybackServiceForTest(player, context())
46
46
  player.configureInternal(
47
- PlayerConfig(httpHeaders = null, userAgent = null, autoRetries = null, retryBackoffMs = null, networkTimeoutMs = null, audioContentType = null, audioCategoryOptions = null, visualizationEnabled = null, clampSeekToBuffered = null, lookaheadCacheMaxSizeMb = null, lookaheadCacheEvictionPolicy = null, progressUpdateIntervalMs = null, backgroundProgressUpdateIntervalMs = null, placeholderArtworkUri = null, skipToPreviousBehavior = null), context()
47
+ PlayerConfig(httpHeaders = null, userAgent = null, autoRetries = null, retryBackoffMs = null, networkTimeoutMs = null, audioContentType = null, audioCategoryOptions = null, voiceVocabularyDonationEnabled = null, visualizationEnabled = null, clampSeekToBuffered = null, lookaheadCacheMaxSizeMb = null, lookaheadCacheEvictionPolicy = null, progressUpdateIntervalMs = null, backgroundProgressUpdateIntervalMs = null, placeholderArtworkUri = null, skipToPreviousBehavior = null), context()
48
48
  )
49
49
  }
50
50
 
@@ -41,7 +41,8 @@ class TrackPlayerCastCommandRoutingTest {
41
41
  PlayerConfig(
42
42
  httpHeaders = null, userAgent = null, autoRetries = null, retryBackoffMs = null,
43
43
  networkTimeoutMs = null, audioContentType = null, audioCategoryOptions = null,
44
- visualizationEnabled = null, clampSeekToBuffered = null,
44
+ voiceVocabularyDonationEnabled = null,
45
+ visualizationEnabled = null, clampSeekToBuffered = null,
45
46
  lookaheadCacheMaxSizeMb = null, lookaheadCacheEvictionPolicy = null,
46
47
  progressUpdateIntervalMs = null, backgroundProgressUpdateIntervalMs = null,
47
48
  placeholderArtworkUri = null, skipToPreviousBehavior = null
@@ -40,7 +40,8 @@ class TrackPlayerCastStateTest {
40
40
  PlayerConfig(
41
41
  httpHeaders = null, userAgent = null, autoRetries = null, retryBackoffMs = null,
42
42
  networkTimeoutMs = null, audioContentType = null, audioCategoryOptions = null,
43
- visualizationEnabled = null, clampSeekToBuffered = null,
43
+ voiceVocabularyDonationEnabled = null,
44
+ visualizationEnabled = null, clampSeekToBuffered = null,
44
45
  lookaheadCacheMaxSizeMb = null, lookaheadCacheEvictionPolicy = null,
45
46
  progressUpdateIntervalMs = null, backgroundProgressUpdateIntervalMs = null,
46
47
  placeholderArtworkUri = null, skipToPreviousBehavior = null