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
@@ -10,7 +10,10 @@ Pod::Spec.new do |s|
10
10
  s.license = package["license"]
11
11
  s.authors = package["author"]
12
12
 
13
- s.platforms = { :ios => min_ios_version_supported }
13
+ # Declared explicitly rather than inherited from
14
+ # `min_ios_version_supported`, which resolves lower: AVPlayer and
15
+ # AVPlayerItem only drop their main-thread requirement at iOS 16.
16
+ s.platforms = { :ios => '16.0' }
14
17
  s.source = { :git => "https://github.com/ghenry22/react-native-queue-player.git", :tag => "#{s.version}" }
15
18
 
16
19
  # NOTE on cross-pod consumer-Swift access:
@@ -64,11 +67,11 @@ Pod::Spec.new do |s|
64
67
 
65
68
  # Opt-in Chromecast subspec. Consumer Podfile adds:
66
69
  # pod 'QueuePlayer/Chromecast', :path => '...'
67
- # alongside the default `pod 'QueuePlayer'`. iOS 15.0 floor matches
68
- # Google Cast SDK 4.8.x's supported deployment target.
70
+ # alongside the default `pod 'QueuePlayer'`. The 16.0 floor tracks the
71
+ # library's own and sits above Google Cast SDK 4.8.x's iOS 15.0 minimum.
69
72
  s.subspec 'Chromecast' do |ss|
70
73
  ss.source_files = 'ios/Cast/Chromecast/**/*.swift'
71
- ss.ios.deployment_target = '15.0'
74
+ ss.ios.deployment_target = '16.0'
72
75
  ss.dependency 'google-cast-sdk', '~> 4.8.4'
73
76
  end
74
77
 
@@ -87,11 +90,13 @@ Pod::Spec.new do |s|
87
90
  s.dependency 'React-jsi'
88
91
  s.dependency 'React-callinvoker'
89
92
 
90
- # Cast serves consumer-supplied `file://` URLs to pull-only receivers
91
- # (Chromecast / future DLNA) via a native `Network.framework` listener
92
- # (`LocalMediaServer` / `MediaHTTPConnection`) no third-party HTTP
93
- # server dependency, no `:modular_headers` requirement on the consumer.
94
- # Orthogonal to the streaming-only lookahead cache.
93
+ # The shared local-HTTP media server (`ios/MediaServer/MediaHTTPConnection`)
94
+ # serves media over a native `Network.framework` listener — no third-party
95
+ # HTTP server dependency, no `:modular_headers` requirement on the consumer.
96
+ # Two servers use it: Cast's `LocalMediaServer` serves consumer-supplied
97
+ # `file://` URLs to pull-only receivers (Chromecast / future DLNA), and the
98
+ # lookahead cache's `ReadThroughServer` serves the local player from the
99
+ # cache or, on a miss, the origin.
95
100
 
96
101
  load 'nitrogen/generated/ios/QueuePlayer+autolinking.rb'
97
102
  add_nitrogen_files(s)
package/README.md CHANGED
@@ -24,13 +24,13 @@ A complete audio playback library for React Native — gapless and crossfade eng
24
24
  - **Remote-control skip mode** — choose track skip or interval skip (jump ±N seconds) for the lock screen / notification, adjustable at runtime.
25
25
  - **Sleep timer** — pause after a duration or at the end of the current track, with a volume fade; runs on a native wall-clock deadline.
26
26
  - **Now Playing format** — codec, container, sample rate, bitrate, and ReplayGain metadata for the active track.
27
- - **React hooks** — `useProgress`, `usePlaybackState`, `useActiveTrack`, `useCanSkip`, `useSleepTimer`, `useEqualizer`, `useVisualizerState`, `useLookaheadCache`, `useNowPlayingFormat`, `useCast`, `useAudioRoute`.
27
+ - **React hooks** — `useProgress`, `usePlaybackState`, `useActiveTrack`, `useQueue`, `useCanSkip`, `useSleepTimer`, `useBufferState`, `useFullyBuffered`, `useEqualizer`, `useVisualizerState`, `useLookaheadCache`, `useNowPlayingFormat`, `useCast`, `useAudioRoute`.
28
28
 
29
29
  ## Requirements
30
30
 
31
31
  - React Native with the New Architecture enabled.
32
32
  - [`react-native-nitro-modules`](https://nitro.margelo.com/) (peer dependency).
33
- - iOS 15+ (the Chromecast subspec requires iOS 15).
33
+ - iOS 16+ (Chromecast subspec included).
34
34
  - Android `minSdk` 24+.
35
35
  - Expo config-plugin support (the native setup is driven by the bundled Expo plugin).
36
36
 
@@ -107,7 +107,7 @@ The player is a singleton — `getTrackPlayer()` always returns the same instanc
107
107
 
108
108
  Most native setup is automatic (Nitro autolinking + the Android manifest merge + the Expo plugin). A few things you must do by hand — see the [Setup & manual configuration guide](https://ghenry22.github.io/react-native-queue-player/docs/guides/manual-setup) for full detail:
109
109
 
110
- - **iOS background audio** — add `audio` to `UIBackgroundModes` in your app config (`ios.infoPlist`). This is the one core background-playback step the plugin does not set for you:
110
+ - **iOS background audio** — set automatically by the Expo plugin: it adds `audio` to `UIBackgroundModes` (on by default; pass `{ enableIosBGAudio: false }` only if your app declares `UIBackgroundModes` elsewhere). If you opt out, or use bare React Native (no plugin), set it yourself:
111
111
 
112
112
  ```json
113
113
  { "expo": { "ios": { "infoPlist": { "UIBackgroundModes": ["audio"] } } } }
@@ -10,7 +10,7 @@
10
10
 
11
11
  # libraop's JNI_OnLoad binds this class by literal descriptor: src/main/cpp/
12
12
  # airplay_jni.cpp calls FindClass on the string
13
- # "com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI", then resolves all 28
13
+ # "com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI", then resolves all 19
14
14
  # entries of its JNINativeMethod table by name and signature. React Native's own
15
15
  # rules pin the native method names but let the class be renamed, and the rule
16
16
  # that pins the class name ships only in AGP's optional default ProGuard file.
@@ -458,7 +458,7 @@ int ap2_record(ap2_session_t *session) {
458
458
  /* ---- FLUSH ---- */
459
459
 
460
460
  int ap2_flush(ap2_session_t *session) {
461
- /* Build RTP-Info header per OwnTone airplay.c:2409-2415:
461
+ /* Build RTP-Info header per OwnTone airplay.c:
462
462
  * "RTP-Info: seq=<seqnum>;rtptime=<rtptime>" */
463
463
  char rtp_info[128];
464
464
  snprintf(rtp_info, sizeof(rtp_info),
@@ -524,7 +524,7 @@ int ap2_set_metadata(ap2_session_t *session,
524
524
  return -1;
525
525
  }
526
526
 
527
- /* OwnTone airplay.c:2499 — adds RTP-Info header. */
527
+ /* OwnTone airplay.c — adds RTP-Info header. */
528
528
  char rtp_info[128];
529
529
  snprintf(rtp_info, sizeof(rtp_info),
530
530
  "RTP-Info: rtptime=%u\r\n",
@@ -556,7 +556,7 @@ int ap2_set_artwork(ap2_session_t *session,
556
556
  const char *content_type) {
557
557
  if (!data || len == 0) return 0;
558
558
 
559
- /* OwnTone airplay.c:2507-2548 — raw image body with format-specific CT. */
559
+ /* OwnTone airplay.c — raw image body with format-specific CT. */
560
560
  char rtp_info[128];
561
561
  snprintf(rtp_info, sizeof(rtp_info),
562
562
  "RTP-Info: rtptime=%u\r\n",
@@ -581,7 +581,7 @@ int ap2_set_artwork(ap2_session_t *session,
581
581
  int ap2_set_progress(ap2_session_t *session,
582
582
  uint32_t elapsed_ms, uint32_t duration_ms) {
583
583
  /* Convert ms to RTP timestamps.
584
- * Per OwnTone airplay.c:2481-2504 — "progress: start/current/end". */
584
+ * Per OwnTone airplay.c — "progress: start/current/end". */
585
585
  uint32_t rtp_start = session->rtp_timestamp;
586
586
  uint32_t rtp_pos = rtp_start + (uint32_t)((uint64_t)elapsed_ms * session->sample_rate / 1000);
587
587
  uint32_t rtp_end = rtp_start + (uint32_t)((uint64_t)duration_ms * session->sample_rate / 1000);
@@ -41,7 +41,7 @@ int ap2_record(ap2_session_t *session);
41
41
 
42
42
  /**
43
43
  * FLUSH: stop audio playback on the receiver.
44
- * Sends RTP-Info header with current seq/rtptime per OwnTone airplay.c:2402-2418.
44
+ * Sends RTP-Info header with current seq/rtptime per OwnTone airplay.c.
45
45
  * @return 0 on success, -1 on failure
46
46
  */
47
47
  int ap2_flush(ap2_session_t *session);
@@ -55,7 +55,7 @@ int ap2_set_volume(ap2_session_t *session, float vol_db);
55
55
 
56
56
  /**
57
57
  * SET_PARAMETER metadata (DMAP-encoded title/artist/album).
58
- * Per OwnTone airplay.c:2551-2575.
58
+ * Per OwnTone airplay.c.
59
59
  * @return 0 on success, -1 on failure
60
60
  */
61
61
  int ap2_set_metadata(ap2_session_t *session,
@@ -63,7 +63,7 @@ int ap2_set_metadata(ap2_session_t *session,
63
63
 
64
64
  /**
65
65
  * SET_PARAMETER artwork (raw JPEG/PNG image bytes).
66
- * Per OwnTone airplay.c:2507-2548.
66
+ * Per OwnTone airplay.c.
67
67
  * @param content_type "image/jpeg" or "image/png"
68
68
  * @return 0 on success, -1 on failure
69
69
  */
@@ -73,7 +73,7 @@ int ap2_set_artwork(ap2_session_t *session,
73
73
 
74
74
  /**
75
75
  * SET_PARAMETER progress (elapsed/duration in milliseconds).
76
- * Per OwnTone airplay.c:2481-2504. Converts ms to RTP timestamps.
76
+ * Per OwnTone airplay.c. Converts ms to RTP timestamps.
77
77
  * @return 0 on success, -1 on failure
78
78
  */
79
79
  int ap2_set_progress(ap2_session_t *session,
@@ -130,7 +130,7 @@ static jboolean nativeAp2Connect(JNIEnv *, jclass, jlong handle) {
130
130
  * ap2_connect() calls ap2_rtsp_connect() internally. */
131
131
  if (ap2_connect(s) != 0) {
132
132
  /* If pairing required, keep TCP alive for subsequent pair-setup.
133
- * Per OwnTone airplay.c:1485-1539 — socket reused across auth. */
133
+ * Per OwnTone airplay.c — socket reused across auth. */
134
134
  if (s->pairing_required) {
135
135
  __android_log_print(ANDROID_LOG_INFO, TAG,
136
136
  "Connect: pairing required (auth_mode=%d), keeping TCP alive",
@@ -204,11 +204,6 @@ static void nativeAp2Destroy(JNIEnv *, jclass, jlong handle) {
204
204
  // Pairing
205
205
  // ---------------------------------------------------------------------------
206
206
 
207
- static jboolean nativeAp2NeedsPairing(JNIEnv *, jclass, jlong handle) {
208
- auto *s = toSession(handle);
209
- return (s && s->pairing_required && !s->has_auth_key) ? JNI_TRUE : JNI_FALSE;
210
- }
211
-
212
207
  static jboolean nativeAp2PairPinStart(JNIEnv *, jclass, jlong handle) {
213
208
  auto *s = toSession(handle);
214
209
  if (!s) return JNI_FALSE;
@@ -270,11 +265,6 @@ static jstring nativeAp2PairSetup(JNIEnv *env, jclass,
270
265
  return result;
271
266
  }
272
267
 
273
- static jint nativeAp2GetPairingState(JNIEnv *, jclass, jlong handle) {
274
- auto *s = toSession(handle);
275
- return s ? static_cast<jint>(s->state) : 0;
276
- }
277
-
278
268
  static jint nativeAp2GetAuthMode(JNIEnv *, jclass, jlong handle) {
279
269
  auto *s = toSession(handle);
280
270
  return s ? static_cast<jint>(s->auth_mode) : 0;
@@ -341,7 +331,7 @@ static void nativeAp2SetVolume(JNIEnv *, jclass, jlong handle, jfloat vol) {
341
331
  s->volume = vol;
342
332
  /* Send volume over RTSP if streaming.
343
333
  * Convert 0.0-1.0 linear to dB: 0→-144 (mute), 1→0 (full).
344
- * Per OwnTone airplay.c:2456-2478 — SET_PARAMETER text/parameters. */
334
+ * Per OwnTone airplay.c — SET_PARAMETER text/parameters. */
345
335
  if (s->state >= AP2_STATE_RECORD) {
346
336
  float vol_db;
347
337
  if (vol <= 0.0f) {
@@ -403,7 +393,7 @@ static void nativeAp2Pause(JNIEnv *, jclass, jlong handle) {
403
393
  if (!s) return;
404
394
  ap2_audio_pause(s);
405
395
  /* Send FLUSH so receiver knows audio stopped.
406
- * Per OwnTone airplay.c:3660-3662 — FLUSH after stopping audio. */
396
+ * Per OwnTone airplay.c — FLUSH after stopping audio. */
407
397
  if (s->state >= AP2_STATE_RECORD) {
408
398
  ap2_flush(s);
409
399
  }
@@ -466,18 +456,12 @@ static const JNINativeMethod sMethods[] = {
466
456
  "(J)V",
467
457
  reinterpret_cast<void *>(nativeAp2Destroy)},
468
458
  /* Pairing */
469
- {"nativeAp2NeedsPairing",
470
- "(J)Z",
471
- reinterpret_cast<void *>(nativeAp2NeedsPairing)},
472
459
  {"nativeAp2PairPinStart",
473
460
  "(J)Z",
474
461
  reinterpret_cast<void *>(nativeAp2PairPinStart)},
475
462
  {"nativeAp2PairSetup",
476
463
  "(JLjava/lang/String;)Ljava/lang/String;",
477
464
  reinterpret_cast<void *>(nativeAp2PairSetup)},
478
- {"nativeAp2GetPairingState",
479
- "(J)I",
480
- reinterpret_cast<void *>(nativeAp2GetPairingState)},
481
465
  {"nativeAp2GetAuthMode",
482
466
  "(J)I",
483
467
  reinterpret_cast<void *>(nativeAp2GetAuthMode)},
@@ -79,14 +79,14 @@ int ap2_probe_info(ap2_session_t *session) {
79
79
  LOGI("/info: statusFlags=0x%llx", (unsigned long long)status_flags);
80
80
 
81
81
  /* Store individual statusFlags bits for the auth decision tree.
82
- * Per OwnTone airplay.c:181-190 and 3304-3345. */
82
+ * Per OwnTone airplay.c. */
83
83
  session->one_time_pairing = (status_flags & ((uint64_t)1 << 9)) ? 1 : 0;
84
84
  session->pin_required = (status_flags & ((uint64_t)1 << 3)) ? 1 : 0;
85
85
  session->password_required = (status_flags & ((uint64_t)1 << 7)) ? 1 : 0;
86
86
  LOGI("/info: one_time_pairing=%d pin_required=%d password_required=%d",
87
87
  session->one_time_pairing, session->pin_required, session->password_required);
88
88
 
89
- /* Decide auth mode. Per OwnTone airplay.c:3304-3345 and pyatv:
89
+ /* Decide auth mode. Per OwnTone airplay.c and pyatv:
90
90
  * ONE_TIME_PAIRING ALWAYS means PIN mode, even when PASSWORD_REQUIRED
91
91
  * is also set. The PIN is displayed on the receiver; the password is
92
92
  * a separate concern used for HTTP Digest auth on encrypted RTSP.
@@ -100,7 +100,7 @@ int ap2_probe_info(ap2_session_t *session) {
100
100
  session->auth_mode = AP2_AUTH_PIN;
101
101
  session->pairing_required = 1;
102
102
  session->transient_ok = 0;
103
- /* PIN_REQUIRED always clears auth_key — per OwnTone airplay.c:3325-3327. */
103
+ /* PIN_REQUIRED always clears auth_key — per OwnTone airplay.c. */
104
104
  session->has_auth_key = 0;
105
105
  } else if (session->password_required) {
106
106
  session->auth_mode = AP2_AUTH_PASSWORD;
@@ -394,7 +394,7 @@ int ap2_pair_transient(ap2_session_t *session) {
394
394
 
395
395
  int ap2_pair_pin_start(ap2_session_t *session) {
396
396
  /* POST /pair-pin-start — triggers PIN display on receiver.
397
- * Per OwnTone airplay.c:3670 — AIRPLAY_SEQ_PIN_START is a separate
397
+ * Per OwnTone airplay.c — AIRPLAY_SEQ_PIN_START is a separate
398
398
  * sequence from AIRPLAY_SEQ_PAIR_SETUP. */
399
399
  ap2_rtsp_response_t resp;
400
400
  if (ap2_rtsp_request(session, "POST", "/pair-pin-start",
@@ -563,7 +563,7 @@ int ap2_connect(ap2_session_t *session) {
563
563
  /* Step 2: Probe /info — sets auth_mode, pairing_required, etc. */
564
564
  if (ap2_probe_info(session) != 0) return -1;
565
565
 
566
- /* Step 3: Pairing — per OwnTone airplay.c:3304-3345 decision tree. */
566
+ /* Step 3: Pairing — per OwnTone airplay.c decision tree. */
567
567
 
568
568
  if (session->auth_mode == AP2_AUTH_PIN) {
569
569
  /* ONE_TIME_PAIRING or PIN_REQUIRED. */
@@ -588,7 +588,7 @@ int ap2_connect(ap2_session_t *session) {
588
588
  session->has_auth_key = 0;
589
589
  }
590
590
  if (session->password[0]) {
591
- /* Per OwnTone airplay.c:3304-3312 — when ONE_TIME_PAIRING is set,
591
+ /* Per OwnTone airplay.c — when ONE_TIME_PAIRING is set,
592
592
  * always call /pair-pin-start first to put the receiver in pairing
593
593
  * mode before SRP. This is required even for password-protected
594
594
  * receivers: without it the Mac may process the SRP exchange but
@@ -636,7 +636,7 @@ int ap2_connect(ap2_session_t *session) {
636
636
  if (ret == 0) return 0;
637
637
 
638
638
  /* Transient→normal fallback: if receiver rejected transient with 470,
639
- * switch to PIN-based auth. Per OwnTone airplay.c:3434-3443. */
639
+ * switch to PIN-based auth. Per OwnTone airplay.c. */
640
640
  if (session->state == AP2_STATE_PAIR) {
641
641
  LOGI("Transient rejected — receiver requires PIN");
642
642
  session->auth_mode = AP2_AUTH_PIN;
@@ -52,7 +52,7 @@ int ap2_pair_transient(ap2_session_t *session);
52
52
 
53
53
  /**
54
54
  * POST /pair-pin-start — trigger PIN display on receiver.
55
- * Per OwnTone airplay.c:3670 — separate from pair-setup.
55
+ * Per OwnTone airplay.c — separate from pair-setup.
56
56
  * Call this for PIN-based auth BEFORE ap2_pair_setup().
57
57
  * Do NOT call this for password-based auth (password is known, not displayed).
58
58
  *
@@ -65,7 +65,7 @@ int ap2_pair_pin_start(ap2_session_t *session);
65
65
  * Generates and stores Ed25519 identity keys.
66
66
  * The credential can be a 4-digit PIN (displayed on device) or an
67
67
  * arbitrary password string — both use the same SRP mechanism.
68
- * Per OwnTone pair_homekit.c:1184 — strdup(pin) as SRP password.
68
+ * Per OwnTone pair_homekit.c — strdup(pin) as SRP password.
69
69
  *
70
70
  * @param pin PIN or password string
71
71
  * @param auth_key OUT: hex-encoded auth key for persistence (192 chars + NUL)
@@ -202,7 +202,7 @@ static int recv_decrypt_frame(ap2_session_t *s, uint8_t *out, size_t out_cap) {
202
202
  return ret == 0 ? (int)frame_len : -1;
203
203
  }
204
204
 
205
- /* ---- HTTP Digest auth (OwnTone airplay.c:684-779) ---- */
205
+ /* ---- HTTP Digest auth (OwnTone airplay.c) ---- */
206
206
 
207
207
  /**
208
208
  * Compute MD5 hex digest of the concatenated inputs separated by ':'.
@@ -229,7 +229,7 @@ static void md5_hex(const char *a, const char *b, const char *c, char out[33]) {
229
229
 
230
230
  /**
231
231
  * Parse WWW-Authenticate: Digest realm="...", nonce="..." from response.
232
- * OwnTone airplay.c:805-878.
232
+ * OwnTone airplay.c.
233
233
  */
234
234
  static int parse_www_authenticate(ap2_session_t *s, const char *resp_buf) {
235
235
  const char *hdr = strstr(resp_buf, "WWW-Authenticate:");
@@ -279,13 +279,13 @@ static int parse_www_authenticate(ap2_session_t *s, const char *resp_buf) {
279
279
 
280
280
  /**
281
281
  * Build Authorization: Digest ... header string.
282
- * OwnTone airplay.c:684-779 — HA1=MD5("":realm:password), HA2=MD5(method:uri).
282
+ * OwnTone airplay.c — HA1=MD5("":realm:password), HA2=MD5(method:uri).
283
283
  * Returns heap-allocated string like "Authorization: Digest ...\r\n", or NULL.
284
284
  */
285
285
  static char *build_digest_header(ap2_session_t *s, const char *method, const char *uri) {
286
286
  if (!s->has_digest_auth || !s->password[0]) return NULL;
287
287
 
288
- /* HA1 = MD5(username:realm:password) — username is empty per OwnTone:687 */
288
+ /* HA1 = MD5(username:realm:password) — username is empty per OwnTone */
289
289
  char ha1[33];
290
290
  md5_hex("", s->realm, s->password, ha1);
291
291
 
@@ -372,7 +372,7 @@ int ap2_rtsp_request_ex(ap2_session_t *session,
372
372
  const char *dn = session->device_name[0] ? session->device_name : NULL;
373
373
 
374
374
  /* Use session URL for RTSP methods, raw path for HTTP-style endpoints.
375
- * Per OwnTone airplay.c:3814 — session_url for SETUP/RECORD/etc,
375
+ * Per OwnTone airplay.c — session_url for SETUP/RECORD/etc,
376
376
  * raw path for /info, /pair-*, /feedback, /peer-list-changed. */
377
377
  const char *uri = path;
378
378
  if (strcmp(method, "SETUP") == 0 || strcmp(method, "RECORD") == 0 ||
@@ -382,7 +382,7 @@ int ap2_rtsp_request_ex(ap2_session_t *session,
382
382
  }
383
383
 
384
384
  /* Auto-include Digest Authorization on all requests after first 401.
385
- * Per OwnTone airplay.c:908-912 — auth_header_add on every request. */
385
+ * Per OwnTone airplay.c — auth_header_add on every request. */
386
386
  char *digest_hdr = NULL;
387
387
  char *merged_headers = NULL;
388
388
  if (session->has_digest_auth) {
@@ -399,7 +399,7 @@ int ap2_rtsp_request_ex(ap2_session_t *session,
399
399
  }
400
400
  const char *all_extra = merged_headers ? merged_headers : "";
401
401
 
402
- /* DACP-ID / Client-Instance / Active-Remote — per OwnTone airplay.c:919-926.
402
+ /* DACP-ID / Client-Instance / Active-Remote — per OwnTone airplay.c.
403
403
  * These let the receiver persistently identify this client. */
404
404
  char dacp_hdr[192];
405
405
  if (session->dacp_id[0]) {
@@ -562,7 +562,7 @@ int ap2_rtsp_request_ex(ap2_session_t *session,
562
562
  }
563
563
 
564
564
  /* 401 Unauthorized → parse Digest challenge and retry once.
565
- * Per OwnTone airplay.c:3183-3197. Must happen before freeing resp_buf. */
565
+ * Per OwnTone airplay.c. Must happen before freeing resp_buf. */
566
566
  if (resp->status == 401 && session->password[0] && !session->has_digest_auth) {
567
567
  if (parse_www_authenticate(session, (char *)resp_buf) == 0) {
568
568
  LOGI("Digest auth: realm=%s — retrying %s %s",
@@ -80,7 +80,7 @@ ap2_session_t *ap2_session_create(const char *host, int port,
80
80
  pthread_mutex_init(&s->ring_mutex, NULL);
81
81
 
82
82
  /* DACP-ID / Client-Instance — 16-char uppercase hex.
83
- * Active-Remote — 32-bit decimal. Per OwnTone airplay.c:919-926. */
83
+ * Active-Remote — 32-bit decimal. Per OwnTone airplay.c. */
84
84
  {
85
85
  uint8_t id_bytes[8];
86
86
  randombytes_buf(id_bytes, sizeof(id_bytes));
@@ -105,7 +105,7 @@ ap2_session_t *ap2_session_create(const char *host, int port,
105
105
  /* PTP clock ID — random 64-bit identifier for timing sync. */
106
106
  s->ptp_clock_id = ((uint64_t)rand() << 32) | (uint64_t)rand();
107
107
 
108
- /* Random RTSP session ID — per OwnTone airplay.c:1535.
108
+ /* Random RTSP session ID — per OwnTone airplay.c.
109
109
  * Used in the RTSP URL: rtsp://<host>/<session_id>. */
110
110
  s->session_id = (uint32_t)rand();
111
111
  snprintf(s->session_url, sizeof(s->session_url),
@@ -174,14 +174,6 @@ static struct raopcl_s *toRaop(jlong handle) {
174
174
  // JNI methods
175
175
  // ---------------------------------------------------------------------------
176
176
 
177
- static jstring nativeVersion(JNIEnv *env, jclass) {
178
- return env->NewStringUTF("libraop-0.2.0");
179
- }
180
-
181
- static jboolean nativeInitSsl(JNIEnv *, jclass) {
182
- return cross_ssl_load() ? JNI_TRUE : JNI_FALSE;
183
- }
184
-
185
177
  /**
186
178
  * Open a RAOP session. Returns a handle (pointer-as-long) or 0 on failure.
187
179
  *
@@ -356,34 +348,7 @@ static jint nativeRingAvailable(JNIEnv *, jclass, jlong handle) {
356
348
  return static_cast<jint>(avail);
357
349
  }
358
350
 
359
- // -- Legacy methods (still needed for pause/flush/volume/metadata) --
360
-
361
- static jboolean nativeAcceptFrames(JNIEnv *, jclass, jlong handle) {
362
- auto *r = toRaop(handle);
363
- return (r && raopcl_accept_frames(r)) ? JNI_TRUE : JNI_FALSE;
364
- }
365
-
366
- static jboolean nativeSendChunk(JNIEnv *env, jclass,
367
- jlong handle,
368
- jbyteArray data, jint offset, jint length) {
369
- auto *r = toRaop(handle);
370
- if (!r) return JNI_FALSE;
371
-
372
- jbyte *buf = env->GetByteArrayElements(data, nullptr);
373
- if (!buf) return JNI_FALSE;
374
-
375
- uint64_t playtime = 0;
376
- // Fix: length is bytes, send_chunk expects frames (bytes / 4 for stereo S16)
377
- bool ok = raopcl_send_chunk(
378
- r,
379
- reinterpret_cast<uint8_t *>(buf + offset),
380
- static_cast<int>(length / 4),
381
- &playtime
382
- );
383
-
384
- env->ReleaseByteArrayElements(data, buf, JNI_ABORT);
385
- return ok ? JNI_TRUE : JNI_FALSE;
386
- }
351
+ // -- Transport control --
387
352
 
388
353
  static void nativePause(JNIEnv *, jclass, jlong handle) {
389
354
  auto *r = toRaop(handle);
@@ -409,6 +374,8 @@ static jboolean nativeFlush(JNIEnv *, jclass, jlong handle) {
409
374
  return (r && raopcl_flush(r)) ? JNI_TRUE : JNI_FALSE;
410
375
  }
411
376
 
377
+ // -- Volume & progress --
378
+
412
379
  static jboolean nativeSetVolume(JNIEnv *, jclass, jlong handle, jfloat vol) {
413
380
  auto *r = toRaop(handle);
414
381
  return (r && raopcl_set_volume(r, vol)) ? JNI_TRUE : JNI_FALSE;
@@ -422,41 +389,20 @@ static jboolean nativeSetProgressMs(JNIEnv *, jclass, jlong handle,
422
389
  static_cast<uint32_t>(duration))) ? JNI_TRUE : JNI_FALSE;
423
390
  }
424
391
 
425
- static jlong nativeGetProgressMs(JNIEnv *, jclass, jlong handle) {
426
- auto *r = toRaop(handle);
427
- return r ? static_cast<jlong>(raopcl_get_progress_ms(r)) : 0;
428
- }
392
+ // -- State queries --
429
393
 
430
394
  static jboolean nativeIsConnected(JNIEnv *, jclass, jlong handle) {
431
395
  auto *r = toRaop(handle);
432
396
  return (r && raopcl_is_connected(r)) ? JNI_TRUE : JNI_FALSE;
433
397
  }
434
398
 
435
- static jboolean nativeIsPlaying(JNIEnv *, jclass, jlong handle) {
436
- auto *r = toRaop(handle);
437
- return (r && raopcl_is_playing(r)) ? JNI_TRUE : JNI_FALSE;
438
- }
439
-
440
- static jint nativeGetState(JNIEnv *, jclass, jlong handle) {
441
- auto *r = toRaop(handle);
442
- return r ? static_cast<jint>(raopcl_state(r)) : 0;
443
- }
444
-
445
- static jint nativeGetLatency(JNIEnv *, jclass, jlong handle) {
446
- auto *r = toRaop(handle);
447
- return r ? static_cast<jint>(raopcl_latency(r)) : 0;
448
- }
449
-
450
- static jint nativeGetSampleRate(JNIEnv *, jclass, jlong handle) {
451
- auto *r = toRaop(handle);
452
- return r ? static_cast<jint>(raopcl_sample_rate(r)) : 0;
453
- }
454
-
455
399
  static jboolean nativeKeepalive(JNIEnv *, jclass, jlong handle) {
456
400
  auto *r = toRaop(handle);
457
401
  return (r && raopcl_keepalive(r)) ? JNI_TRUE : JNI_FALSE;
458
402
  }
459
403
 
404
+ // -- Configuration --
405
+
460
406
  static jint nativeGetLastRtspStatus(JNIEnv *, jclass, jlong handle) {
461
407
  auto *r = toRaop(handle);
462
408
  return r ? static_cast<jint>(raopcl_last_rtsp_status(r)) : 0;
@@ -484,6 +430,8 @@ static void nativeSetPassword(JNIEnv *env, jclass, jlong handle, jstring jPasswd
484
430
  }
485
431
  }
486
432
 
433
+ // -- Metadata --
434
+
487
435
  static jboolean nativeSetDaap(JNIEnv *env, jclass, jlong handle,
488
436
  jstring jTitle, jstring jArtist, jstring jAlbum) {
489
437
  auto *r = toRaop(handle);
@@ -533,8 +481,6 @@ static jboolean nativeSetArtwork(JNIEnv *env, jclass, jlong handle,
533
481
  // RegisterNatives table
534
482
  // ---------------------------------------------------------------------------
535
483
  static const JNINativeMethod sMethods[] = {
536
- {"nativeVersion", "()Ljava/lang/String;", reinterpret_cast<void *>(nativeVersion)},
537
- {"nativeInitSsl", "()Z", reinterpret_cast<void *>(nativeInitSsl)},
538
484
  {"nativeOpen", "(Ljava/lang/String;IIIIIZIIIF)J", reinterpret_cast<void *>(nativeOpen)},
539
485
  {"nativeConnect", "(JLjava/lang/String;I)Z", reinterpret_cast<void *>(nativeConnect)},
540
486
  {"nativeDisconnect", "(J)Z", reinterpret_cast<void *>(nativeDisconnect)},
@@ -542,19 +488,12 @@ static const JNINativeMethod sMethods[] = {
542
488
  {"nativeFeedPcm", "(J[BII)I", reinterpret_cast<void *>(nativeFeedPcm)},
543
489
  {"nativeBytesSent", "(J)J", reinterpret_cast<void *>(nativeBytesSent)},
544
490
  {"nativeRingAvailable", "(J)I", reinterpret_cast<void *>(nativeRingAvailable)},
545
- {"nativeAcceptFrames", "(J)Z", reinterpret_cast<void *>(nativeAcceptFrames)},
546
- {"nativeSendChunk", "(J[BII)Z", reinterpret_cast<void *>(nativeSendChunk)},
547
491
  {"nativePause", "(J)V", reinterpret_cast<void *>(nativePause)},
548
492
  {"nativeStop", "(J)V", reinterpret_cast<void *>(nativeStop)},
549
493
  {"nativeFlush", "(J)Z", reinterpret_cast<void *>(nativeFlush)},
550
494
  {"nativeSetVolume", "(JF)Z", reinterpret_cast<void *>(nativeSetVolume)},
551
495
  {"nativeSetProgressMs", "(JII)Z", reinterpret_cast<void *>(nativeSetProgressMs)},
552
- {"nativeGetProgressMs", "(J)J", reinterpret_cast<void *>(nativeGetProgressMs)},
553
496
  {"nativeIsConnected", "(J)Z", reinterpret_cast<void *>(nativeIsConnected)},
554
- {"nativeIsPlaying", "(J)Z", reinterpret_cast<void *>(nativeIsPlaying)},
555
- {"nativeGetState", "(J)I", reinterpret_cast<void *>(nativeGetState)},
556
- {"nativeGetLatency", "(J)I", reinterpret_cast<void *>(nativeGetLatency)},
557
- {"nativeGetSampleRate", "(J)I", reinterpret_cast<void *>(nativeGetSampleRate)},
558
497
  {"nativeKeepalive", "(J)Z", reinterpret_cast<void *>(nativeKeepalive)},
559
498
  {"nativeGetLastRtspStatus", "(J)I", reinterpret_cast<void *>(nativeGetLastRtspStatus)},
560
499
  {"nativeSetPassword", "(JLjava/lang/String;)V", reinterpret_cast<void *>(nativeSetPassword)},
@@ -66,10 +66,11 @@ internal class AirPlayEngine(
66
66
 
67
67
  private val playerListener = object : Player.Listener {
68
68
  override fun onPlaybackStateChanged(playbackState: Int) {
69
- delegate?.onStateMaybeChanged(this@AirPlayEngine, playWhenReadyReason = null)
69
+ // End signal before the generic state callback, as in GaplessEngine.
70
70
  if (playbackState == Player.STATE_ENDED) {
71
71
  delegate?.onPlaybackEnded(this@AirPlayEngine)
72
72
  }
73
+ delegate?.onStateMaybeChanged(this@AirPlayEngine, playWhenReadyReason = null)
73
74
  }
74
75
 
75
76
  override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
@@ -129,15 +130,6 @@ internal class AirPlayEngine(
129
130
 
130
131
  // --- PlaybackEngine — lifecycle ---
131
132
 
132
- override suspend fun prepare(
133
- mediaItems: List<MediaItem>,
134
- startIndex: Int,
135
- startPositionMs: Long,
136
- ) {
137
- exoPlayer.setMediaItems(mediaItems, startIndex, startPositionMs)
138
- exoPlayer.prepare()
139
- }
140
-
141
133
  override fun release() {
142
134
  if (released) return
143
135
  released = true
@@ -168,6 +160,11 @@ internal class AirPlayEngine(
168
160
  startIndex: Int,
169
161
  startPositionMs: Long,
170
162
  ) {
163
+ if (items.isEmpty()) {
164
+ // Draining is not pausing: `playWhenReady` survives an empty playlist,
165
+ // so the next queue seated on this player would start on contact.
166
+ exoPlayer.pause()
167
+ }
171
168
  exoPlayer.setMediaItems(items, startIndex, startPositionMs)
172
169
  // ExoPlayer is IDLE after construction; prepare() is required before any
173
170
  // subsequent play() can take effect (mirrors GaplessEngine).
@@ -272,12 +269,6 @@ internal class AirPlayEngine(
272
269
 
273
270
  // ReplayGain absent on the AirPlay engine — interface defaults
274
271
  // (currentReplayGainData() = null, currentReplayGainActive() = false) apply.
275
-
276
- // --- PlaybackEngine — handoff ---
277
-
278
- override suspend fun handoff(to: PlaybackEngine, atTrackBoundary: Boolean) {
279
- throw NotImplementedError("PlaybackEngine.handoff is not supported by AirPlayEngine")
280
- }
281
272
  }
282
273
 
283
274
  /**
@@ -3,7 +3,7 @@ package com.margelo.nitro.queueplayer
3
3
  import java.net.URI
4
4
 
5
5
  /**
6
- * MIME-type plumbing for the lookahead cache. Mirrors iOS's
6
+ * MIME-type handling for the lookahead cache. Mirrors iOS's
7
7
  * `LookaheadCache.extensionFromMimeType` / `extensionFromUrl` —
8
8
  * lets the Android cache record the same Content-Type signal so
9
9
  * Media3 can be told the format up front via
@@ -526,12 +526,8 @@ class CastManager : HybridCastManagerSpec() {
526
526
  }
527
527
  airplayScope.launch {
528
528
  try {
529
- val session = backend.submitPairingPin(pin)
530
- val pendingId = activeAirplaySession.get()?.let { /* keep existing */ null }
531
- val receiverId = pendingId ?: lastConnectedAirplayReceiverId() ?: ""
532
- if (receiverId.isNotEmpty()) {
533
- installAirplaySession(session, receiverId)
534
- }
529
+ val paired = backend.submitPairingPin(pin)
530
+ installAirplaySession(paired.session, paired.receiverId)
535
531
  promise.resolve(Unit)
536
532
  } catch (t: Throwable) {
537
533
  promise.reject(t)
@@ -540,14 +536,6 @@ class CastManager : HybridCastManagerSpec() {
540
536
  return promise
541
537
  }
542
538
 
543
- /// Look up the most recently visible AirPlay receiver that pair-setup
544
- /// would have stashed against. The pending pair's receiverId isn't
545
- /// directly accessible from the public backend surface; lookup via
546
- /// the discovered snapshot is good enough — the user pairs the device
547
- /// they just saw in the picker.
548
- private fun lastConnectedAirplayReceiverId(): String? =
549
- airplayReceivers.firstOrNull()?.id
550
-
551
539
  override fun getLocalNetworkPermissionState(): CastLocalNetworkPermission =
552
540
  localNetworkPermissionState()
553
541