react-native-nitro-player 0.5.9-alpha.1 → 0.6.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.
|
@@ -41,6 +41,28 @@ class TrackPlayerCore private constructor(
|
|
|
41
41
|
private val handler = android.os.Handler(android.os.Looper.getMainLooper())
|
|
42
42
|
private lateinit var player: ExoPlayer
|
|
43
43
|
private val playlistManager = PlaylistManager.getInstance(context)
|
|
44
|
+
|
|
45
|
+
// Named Runnable so handler.removeCallbacks() can coalesce rapid playlist
|
|
46
|
+
// mutations (e.g. N individual removes followed by a batch add during shuffle)
|
|
47
|
+
// into a single player update, preventing audio gaps on Android.
|
|
48
|
+
private val updateCurrentPlaylistRunnable = Runnable {
|
|
49
|
+
val playlistId = currentPlaylistId ?: return@Runnable
|
|
50
|
+
val playlist = playlistManager.getPlaylist(playlistId) ?: return@Runnable
|
|
51
|
+
|
|
52
|
+
// Always update the canonical track list first.
|
|
53
|
+
currentTracks = playlist.tracks
|
|
54
|
+
|
|
55
|
+
if (::player.isInitialized && player.currentMediaItem != null && player.currentMediaItemIndex >= 0) {
|
|
56
|
+
// Something is actively playing — rebuild only the items AFTER the
|
|
57
|
+
// current position using surgical removeMediaItems/addMediaItems.
|
|
58
|
+
// This avoids setMediaItems() which replaces the entire ExoPlayer
|
|
59
|
+
// queue (including the current item) and causes an audible gap.
|
|
60
|
+
rebuildQueueFromCurrentPosition()
|
|
61
|
+
} else {
|
|
62
|
+
// Nothing playing yet — safe to do a full replace.
|
|
63
|
+
updatePlayerQueue(playlist.tracks)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
44
66
|
private val downloadManager = DownloadManagerCore.getInstance(context)
|
|
45
67
|
private val mediaLibraryManager = MediaLibraryManager.getInstance(context)
|
|
46
68
|
private var mediaSessionManager: MediaSessionManager? = null
|
|
@@ -434,14 +456,13 @@ class TrackPlayerCore private constructor(
|
|
|
434
456
|
* Update the player queue when playlist changes
|
|
435
457
|
*/
|
|
436
458
|
fun updatePlaylist(playlistId: String) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
459
|
+
// Debounce: rapid back-to-back calls (e.g. removing N tracks then adding
|
|
460
|
+
// the shuffled replacement) are coalesced into a single setMediaItems call.
|
|
461
|
+
// removeCallbacks cancels any pending-but-not-yet-executed callback so only
|
|
462
|
+
// the final playlist state triggers a player rebuild.
|
|
463
|
+
if (currentPlaylistId != playlistId) return
|
|
464
|
+
handler.removeCallbacks(updateCurrentPlaylistRunnable)
|
|
465
|
+
handler.post(updateCurrentPlaylistRunnable)
|
|
445
466
|
}
|
|
446
467
|
|
|
447
468
|
/**
|
|
@@ -48,6 +48,10 @@ class TrackPlayerCore: NSObject {
|
|
|
48
48
|
private var currentPlaylistId: String?
|
|
49
49
|
private var currentTrackIndex: Int = -1
|
|
50
50
|
private var currentTracks: [TrackItem] = []
|
|
51
|
+
// Debounce work item — rapid playlist mutations (e.g. N individual removes
|
|
52
|
+
// during shuffle) are coalesced into a single rebuildAVQueueFromCurrentPosition
|
|
53
|
+
// call, preventing audio gaps/interruptions on iOS.
|
|
54
|
+
private var pendingPlaylistUpdateWorkItem: DispatchWorkItem?
|
|
51
55
|
private var isManuallySeeked = false
|
|
52
56
|
private var currentRepeatMode: RepeatMode = .off
|
|
53
57
|
private var lookaheadCount: Int = 5 // Number of tracks to preload ahead
|
|
@@ -649,7 +653,13 @@ class TrackPlayerCore: NSObject {
|
|
|
649
653
|
}
|
|
650
654
|
|
|
651
655
|
func updatePlaylist(playlistId: String) {
|
|
652
|
-
|
|
656
|
+
guard currentPlaylistId == playlistId else { return }
|
|
657
|
+
|
|
658
|
+
// Cancel any pending rebuild so back-to-back calls (e.g. N individual removes
|
|
659
|
+
// during shuffle) collapse into a single rebuild at the end.
|
|
660
|
+
pendingPlaylistUpdateWorkItem?.cancel()
|
|
661
|
+
|
|
662
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
653
663
|
guard let self = self else { return }
|
|
654
664
|
guard self.currentPlaylistId == playlistId,
|
|
655
665
|
let playlist = self.playlistManager.getPlaylist(playlistId: playlistId)
|
|
@@ -667,6 +677,9 @@ class TrackPlayerCore: NSObject {
|
|
|
667
677
|
// Rebuild only the items after the currently playing item
|
|
668
678
|
self.rebuildAVQueueFromCurrentPosition()
|
|
669
679
|
}
|
|
680
|
+
|
|
681
|
+
pendingPlaylistUpdateWorkItem = workItem
|
|
682
|
+
DispatchQueue.main.async(execute: workItem)
|
|
670
683
|
}
|
|
671
684
|
|
|
672
685
|
// MARK: - Public Methods
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-player",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A powerful audio player library for React Native with playlist management, playback controls, and support for Android Auto and CarPlay",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|