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
- handler.post {
438
- if (currentPlaylistId == playlistId) {
439
- val playlist = playlistManager.getPlaylist(playlistId)
440
- if (playlist != null) {
441
- updatePlayerQueue(playlist.tracks)
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
- DispatchQueue.main.async { [weak self] in
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.5.9-alpha.1",
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",