react-native-nitro-player 0.5.3 ā 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +5 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +4 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +14 -13
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/NitroPlayerLogger.kt +31 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +142 -95
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +7 -6
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +2 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadWorker.kt +1 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +25 -24
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +20 -19
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +16 -9
- package/ios/HybridAudioRoutePicker.swift +1 -1
- package/ios/HybridDownloadManager.swift +3 -3
- package/ios/HybridEqualizer.swift +3 -3
- package/ios/HybridTrackPlayer.swift +8 -4
- package/ios/core/NitroPlayerLogger.swift +22 -0
- package/ios/core/TrackPlayerCore.swift +195 -256
- package/ios/download/DownloadDatabase.swift +35 -39
- package/ios/download/DownloadFileManager.swift +17 -17
- package/ios/download/DownloadManagerCore.swift +29 -33
- package/ios/equalizer/EqualizerCore.swift +25 -20
- package/ios/playlist/PlaylistManager.swift +19 -9
- package/ios/queue/QueueManager.swift +1 -1
- package/lib/specs/TrackPlayer.nitro.d.ts +1 -0
- package/lib/types/PlayerQueue.d.ts +1 -1
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JReason.hpp +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Reason.kt +2 -1
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +12 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +8 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +12 -0
- package/nitrogen/generated/ios/swift/Reason.swift +4 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/Reason.hpp +4 -0
- package/package.json +1 -1
- package/src/specs/TrackPlayer.nitro.ts +1 -0
- package/src/types/PlayerQueue.ts +1 -1
|
@@ -82,6 +82,8 @@ class TrackPlayerCore private constructor(
|
|
|
82
82
|
private val onPlaybackProgressChangeListeners =
|
|
83
83
|
Collections.synchronizedList(mutableListOf<WeakCallbackBox<(Double, Double, Boolean?) -> Unit>>())
|
|
84
84
|
|
|
85
|
+
private var currentRepeatMode: RepeatMode = RepeatMode.OFF
|
|
86
|
+
|
|
85
87
|
// Temporary tracks for addToUpNext and playNext
|
|
86
88
|
private var playNextStack: MutableList<TrackItem> = mutableListOf() // LIFO - last added plays first
|
|
87
89
|
private var upNextQueue: MutableList<TrackItem> = mutableListOf() // FIFO - first added plays first
|
|
@@ -168,7 +170,7 @@ class TrackPlayerCore private constructor(
|
|
|
168
170
|
// Notify JavaScript
|
|
169
171
|
onAndroidAutoConnectionChange?.invoke(connected)
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š Android Auto connection changed: connected=$connected, type=$connectionType")
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
registerCarConnectionReceiver()
|
|
@@ -180,20 +182,25 @@ class TrackPlayerCore private constructor(
|
|
|
180
182
|
mediaItem: MediaItem?,
|
|
181
183
|
reason: Int,
|
|
182
184
|
) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
NitroPlayerLogger.log("TrackPlayerCore") { "\nš onMediaItemTransition called" }
|
|
186
|
+
NitroPlayerLogger.log("TrackPlayerCore") {
|
|
185
187
|
" reason: ${when (reason) {
|
|
186
188
|
Player.MEDIA_ITEM_TRANSITION_REASON_AUTO -> "AUTO (track ended)"
|
|
187
189
|
Player.MEDIA_ITEM_TRANSITION_REASON_SEEK -> "SEEK"
|
|
188
190
|
Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED -> "PLAYLIST_CHANGED"
|
|
189
|
-
Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT -> "REPEAT"
|
|
190
191
|
else -> "UNKNOWN($reason)"
|
|
191
|
-
}}"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
}}"
|
|
193
|
+
}
|
|
194
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " previousMediaItem: ${previousMediaItem?.mediaId}" }
|
|
195
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " new mediaItem: ${mediaItem?.mediaId}" }
|
|
196
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " playNextStack: ${playNextStack.map { it.id }}" }
|
|
197
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " upNextQueue: ${upNextQueue.map { it.id }}" }
|
|
198
|
+
|
|
199
|
+
// TRACK repeat: REPEAT_MODE_ONE fires this callback every loop ā skip entirely
|
|
200
|
+
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT) {
|
|
201
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " š TRACK repeat loop ā skipping notifyTrackChange" }
|
|
202
|
+
return
|
|
203
|
+
}
|
|
197
204
|
|
|
198
205
|
// Remove finished track from temporary lists
|
|
199
206
|
// Handle AUTO (natural end) and SEEK (skip next) transitions
|
|
@@ -205,26 +212,26 @@ class TrackPlayerCore private constructor(
|
|
|
205
212
|
) {
|
|
206
213
|
previousMediaItem?.mediaId?.let { mediaId ->
|
|
207
214
|
val trackId = extractTrackId(mediaId)
|
|
208
|
-
|
|
215
|
+
NitroPlayerLogger.log("TrackPlayerCore") { "š Track finished/skipped, checking for removal: $trackId" }
|
|
209
216
|
|
|
210
217
|
// Find and remove from playNext stack (like iOS does)
|
|
211
218
|
val playNextIndex = playNextStack.indexOfFirst { it.id == trackId }
|
|
212
219
|
if (playNextIndex >= 0) {
|
|
213
220
|
val track = playNextStack.removeAt(playNextIndex)
|
|
214
|
-
|
|
221
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " ā
Removed from playNext stack: ${track.title}" }
|
|
215
222
|
} else {
|
|
216
223
|
// Find and remove from upNext queue
|
|
217
224
|
val upNextIndex = upNextQueue.indexOfFirst { it.id == trackId }
|
|
218
225
|
if (upNextIndex >= 0) {
|
|
219
226
|
val track = upNextQueue.removeAt(upNextIndex)
|
|
220
|
-
|
|
227
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " ā
Removed from upNext queue: ${track.title}" }
|
|
221
228
|
} else {
|
|
222
|
-
|
|
229
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " ā¹ļø Was an original playlist track" }
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
}
|
|
226
233
|
} else {
|
|
227
|
-
|
|
234
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " āļø Skipping removal (reason=$reason, prev=${previousMediaItem != null})" }
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
// Store current item as previous for next transition
|
|
@@ -232,14 +239,14 @@ class TrackPlayerCore private constructor(
|
|
|
232
239
|
|
|
233
240
|
// Update temporary type for current track
|
|
234
241
|
currentTemporaryType = determineCurrentTemporaryType()
|
|
235
|
-
|
|
242
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " Updated currentTemporaryType: $currentTemporaryType" }
|
|
236
243
|
|
|
237
244
|
// Update currentTrackIndex when we land on an original playlist track
|
|
238
245
|
if (currentTemporaryType == TemporaryType.NONE && mediaItem != null) {
|
|
239
246
|
val trackId = extractTrackId(mediaItem.mediaId)
|
|
240
247
|
val newIndex = currentTracks.indexOfFirst { it.id == trackId }
|
|
241
248
|
if (newIndex >= 0 && newIndex != currentTrackIndex) {
|
|
242
|
-
|
|
249
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " š Updating currentTrackIndex from $currentTrackIndex to $newIndex" }
|
|
243
250
|
currentTrackIndex = newIndex
|
|
244
251
|
}
|
|
245
252
|
}
|
|
@@ -255,7 +262,8 @@ class TrackPlayerCore private constructor(
|
|
|
255
262
|
if (playlist != null && currentPlaylistId != playlistId) {
|
|
256
263
|
// This shouldn't happen if playlists are loaded correctly,
|
|
257
264
|
// but handle it as a safety measure
|
|
258
|
-
|
|
265
|
+
NitroPlayerLogger.log(
|
|
266
|
+
"TrackPlayerCore",
|
|
259
267
|
"ā ļø TrackPlayerCore: Detected track from different playlist, updating...",
|
|
260
268
|
)
|
|
261
269
|
}
|
|
@@ -301,6 +309,19 @@ class TrackPlayerCore private constructor(
|
|
|
301
309
|
}
|
|
302
310
|
|
|
303
311
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
|
312
|
+
if (playbackState == Player.STATE_ENDED && currentRepeatMode == RepeatMode.PLAYLIST) {
|
|
313
|
+
NitroPlayerLogger.log("TrackPlayerCore") { "š PLAYLIST repeat ā rebuilding original queue and restarting" }
|
|
314
|
+
handler.post {
|
|
315
|
+
playNextStack.clear()
|
|
316
|
+
upNextQueue.clear()
|
|
317
|
+
currentTemporaryType = TemporaryType.NONE
|
|
318
|
+
// Rebuild ExoPlayer queue from beginning of original playlist
|
|
319
|
+
rebuildQueueAndPlayFromIndex(0)
|
|
320
|
+
val firstTrack = currentTracks.getOrNull(0)
|
|
321
|
+
if (firstTrack != null) notifyTrackChange(firstTrack, Reason.REPEAT)
|
|
322
|
+
}
|
|
323
|
+
return
|
|
324
|
+
}
|
|
304
325
|
emitStateChange()
|
|
305
326
|
}
|
|
306
327
|
|
|
@@ -353,7 +374,7 @@ class TrackPlayerCore private constructor(
|
|
|
353
374
|
playNextStack.clear()
|
|
354
375
|
upNextQueue.clear()
|
|
355
376
|
currentTemporaryType = TemporaryType.NONE
|
|
356
|
-
|
|
377
|
+
NitroPlayerLogger.log("TrackPlayerCore", " š§¹ Cleared temporary tracks")
|
|
357
378
|
|
|
358
379
|
val playlist = playlistManager.getPlaylist(playlistId)
|
|
359
380
|
if (playlist != null) {
|
|
@@ -395,7 +416,7 @@ class TrackPlayerCore private constructor(
|
|
|
395
416
|
}
|
|
396
417
|
}
|
|
397
418
|
} catch (e: Exception) {
|
|
398
|
-
|
|
419
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā TrackPlayerCore: Error playing from playlist track - ${e.message}")
|
|
399
420
|
e.printStackTrace()
|
|
400
421
|
}
|
|
401
422
|
}
|
|
@@ -498,8 +519,9 @@ class TrackPlayerCore private constructor(
|
|
|
498
519
|
mediaId
|
|
499
520
|
}
|
|
500
521
|
|
|
501
|
-
|
|
502
|
-
|
|
522
|
+
// currentTracks is already the cached tracks for currentPlaylistId ā no need to
|
|
523
|
+
// re-fetch from PlaylistManager on every call.
|
|
524
|
+
return currentTracks.find { it.id == trackId }
|
|
503
525
|
}
|
|
504
526
|
|
|
505
527
|
fun play() {
|
|
@@ -527,32 +549,32 @@ class TrackPlayerCore private constructor(
|
|
|
527
549
|
playNextStack.clear()
|
|
528
550
|
upNextQueue.clear()
|
|
529
551
|
currentTemporaryType = TemporaryType.NONE
|
|
530
|
-
|
|
552
|
+
NitroPlayerLogger.log("TrackPlayerCore", " š§¹ Cleared temporary tracks")
|
|
531
553
|
|
|
532
554
|
var targetPlaylistId: String? = null
|
|
533
555
|
var songIndex: Int = -1
|
|
534
556
|
|
|
535
557
|
// Case 1: If fromPlaylist is provided, use that playlist
|
|
536
558
|
if (fromPlaylist != null) {
|
|
537
|
-
|
|
559
|
+
NitroPlayerLogger.log("TrackPlayerCore", "šµ TrackPlayerCore: Looking for song in specified playlist: $fromPlaylist")
|
|
538
560
|
val playlist = playlistManager.getPlaylist(fromPlaylist)
|
|
539
561
|
if (playlist != null) {
|
|
540
562
|
songIndex = playlist.tracks.indexOfFirst { it.id == songId }
|
|
541
563
|
if (songIndex >= 0) {
|
|
542
564
|
targetPlaylistId = fromPlaylist
|
|
543
|
-
|
|
565
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā
Found song at index $songIndex in playlist $fromPlaylist")
|
|
544
566
|
} else {
|
|
545
|
-
|
|
567
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Song $songId not found in specified playlist $fromPlaylist")
|
|
546
568
|
return
|
|
547
569
|
}
|
|
548
570
|
} else {
|
|
549
|
-
|
|
571
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Playlist $fromPlaylist not found")
|
|
550
572
|
return
|
|
551
573
|
}
|
|
552
574
|
}
|
|
553
575
|
// Case 2: If fromPlaylist is not provided, search in current/loaded playlist first
|
|
554
576
|
else {
|
|
555
|
-
|
|
577
|
+
NitroPlayerLogger.log("TrackPlayerCore", "šµ TrackPlayerCore: No playlist specified, checking current playlist")
|
|
556
578
|
|
|
557
579
|
// Check if song exists in currently loaded playlist
|
|
558
580
|
if (currentPlaylistId != null) {
|
|
@@ -561,21 +583,21 @@ class TrackPlayerCore private constructor(
|
|
|
561
583
|
songIndex = currentPlaylist.tracks.indexOfFirst { it.id == songId }
|
|
562
584
|
if (songIndex >= 0) {
|
|
563
585
|
targetPlaylistId = currentPlaylistId
|
|
564
|
-
|
|
586
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā
Found song at index $songIndex in current playlist $currentPlaylistId")
|
|
565
587
|
}
|
|
566
588
|
}
|
|
567
589
|
}
|
|
568
590
|
|
|
569
591
|
// If not found in current playlist, search in all playlists
|
|
570
592
|
if (songIndex == -1) {
|
|
571
|
-
|
|
593
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š Song not found in current playlist, searching all playlists...")
|
|
572
594
|
val allPlaylists = playlistManager.getAllPlaylists()
|
|
573
595
|
|
|
574
596
|
for (playlist in allPlaylists) {
|
|
575
597
|
songIndex = playlist.tracks.indexOfFirst { it.id == songId }
|
|
576
598
|
if (songIndex >= 0) {
|
|
577
599
|
targetPlaylistId = playlist.id
|
|
578
|
-
|
|
600
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā
Found song at index $songIndex in playlist ${playlist.id}")
|
|
579
601
|
break
|
|
580
602
|
}
|
|
581
603
|
}
|
|
@@ -584,20 +606,20 @@ class TrackPlayerCore private constructor(
|
|
|
584
606
|
if (songIndex == -1 && allPlaylists.isNotEmpty()) {
|
|
585
607
|
targetPlaylistId = allPlaylists[0].id
|
|
586
608
|
songIndex = 0
|
|
587
|
-
|
|
609
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Song not found in any playlist, using first playlist and starting at index 0")
|
|
588
610
|
}
|
|
589
611
|
}
|
|
590
612
|
}
|
|
591
613
|
|
|
592
614
|
// Now play the song
|
|
593
615
|
if (targetPlaylistId == null || songIndex < 0) {
|
|
594
|
-
|
|
616
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā Could not determine playlist or song index")
|
|
595
617
|
return
|
|
596
618
|
}
|
|
597
619
|
|
|
598
620
|
// Load playlist if it's different from current
|
|
599
621
|
if (currentPlaylistId != targetPlaylistId) {
|
|
600
|
-
|
|
622
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š Loading new playlist: $targetPlaylistId")
|
|
601
623
|
val playlist = playlistManager.getPlaylist(targetPlaylistId)
|
|
602
624
|
if (playlist != null) {
|
|
603
625
|
currentPlaylistId = targetPlaylistId
|
|
@@ -605,12 +627,12 @@ class TrackPlayerCore private constructor(
|
|
|
605
627
|
|
|
606
628
|
// Wait a bit for playlist to load, then play from index
|
|
607
629
|
// Note: Removed postDelayed to avoid race conditions with subsequent queue operations
|
|
608
|
-
|
|
630
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā¶ļø Playing from index: $songIndex")
|
|
609
631
|
playFromIndex(songIndex)
|
|
610
632
|
}
|
|
611
633
|
} else {
|
|
612
634
|
// Playlist already loaded, just play from index
|
|
613
|
-
|
|
635
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā¶ļø Playing from index: $songIndex")
|
|
614
636
|
playFromIndex(songIndex)
|
|
615
637
|
}
|
|
616
638
|
}
|
|
@@ -629,11 +651,11 @@ class TrackPlayerCore private constructor(
|
|
|
629
651
|
|
|
630
652
|
if (currentPosition > 2000) {
|
|
631
653
|
// More than 2 seconds in, restart current track
|
|
632
|
-
|
|
654
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: Past threshold, restarting current track")
|
|
633
655
|
player.seekTo(0)
|
|
634
656
|
} else if (currentTemporaryType != TemporaryType.NONE) {
|
|
635
657
|
// Playing temporary track within threshold ā remove from its list, go back to original
|
|
636
|
-
|
|
658
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: Removing temp track, going back to original")
|
|
637
659
|
val currentMediaItem = player.currentMediaItem
|
|
638
660
|
if (currentMediaItem != null) {
|
|
639
661
|
val trackId = extractTrackId(currentMediaItem.mediaId)
|
|
@@ -642,10 +664,12 @@ class TrackPlayerCore private constructor(
|
|
|
642
664
|
val idx = playNextStack.indexOfFirst { it.id == trackId }
|
|
643
665
|
if (idx >= 0) playNextStack.removeAt(idx)
|
|
644
666
|
}
|
|
667
|
+
|
|
645
668
|
TemporaryType.UP_NEXT -> {
|
|
646
669
|
val idx = upNextQueue.indexOfFirst { it.id == trackId }
|
|
647
670
|
if (idx >= 0) upNextQueue.removeAt(idx)
|
|
648
671
|
}
|
|
672
|
+
|
|
649
673
|
else -> {}
|
|
650
674
|
}
|
|
651
675
|
}
|
|
@@ -653,11 +677,11 @@ class TrackPlayerCore private constructor(
|
|
|
653
677
|
playFromIndexInternal(currentTrackIndex)
|
|
654
678
|
} else if (currentTrackIndex > 0) {
|
|
655
679
|
// Go to previous track in original playlist
|
|
656
|
-
|
|
680
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: Going to previous track, currentTrackIndex: $currentTrackIndex -> ${currentTrackIndex - 1}")
|
|
657
681
|
playFromIndexInternal(currentTrackIndex - 1)
|
|
658
682
|
} else {
|
|
659
683
|
// Already at first track, seek to beginning
|
|
660
|
-
|
|
684
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: Already at first track, seeking to beginning")
|
|
661
685
|
player.seekTo(0)
|
|
662
686
|
}
|
|
663
687
|
}
|
|
@@ -671,20 +695,21 @@ class TrackPlayerCore private constructor(
|
|
|
671
695
|
}
|
|
672
696
|
|
|
673
697
|
fun setRepeatMode(mode: RepeatMode): Boolean {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
when (mode) {
|
|
678
|
-
RepeatMode.OFF -> Player.REPEAT_MODE_OFF
|
|
698
|
+
currentRepeatMode = mode
|
|
699
|
+
if (::player.isInitialized) {
|
|
700
|
+
handler.post {
|
|
701
|
+
player.repeatMode = when (mode) {
|
|
679
702
|
RepeatMode.TRACK -> Player.REPEAT_MODE_ONE
|
|
680
|
-
|
|
703
|
+
else -> Player.REPEAT_MODE_OFF
|
|
681
704
|
}
|
|
682
|
-
|
|
683
|
-
println("š TrackPlayerCore: ExoPlayer repeat mode set to: $exoRepeatMode")
|
|
705
|
+
}
|
|
684
706
|
}
|
|
707
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š setRepeatMode: $mode")
|
|
685
708
|
return true
|
|
686
709
|
}
|
|
687
710
|
|
|
711
|
+
fun getRepeatMode(): RepeatMode = currentRepeatMode
|
|
712
|
+
|
|
688
713
|
fun getState(): PlayerState {
|
|
689
714
|
// Called from Promise.async background thread
|
|
690
715
|
// Check if we're already on the main thread
|
|
@@ -739,9 +764,6 @@ class TrackPlayerCore private constructor(
|
|
|
739
764
|
else -> TrackPlayerState.STOPPED
|
|
740
765
|
}
|
|
741
766
|
|
|
742
|
-
// Get current playlist
|
|
743
|
-
val currentPlaylist = currentPlaylistId?.let { playlistManager.getPlaylist(it) }
|
|
744
|
-
|
|
745
767
|
// Use ExoPlayer's currentMediaItemIndex
|
|
746
768
|
val currentIndex =
|
|
747
769
|
if (player.currentMediaItemIndex >= 0) {
|
|
@@ -894,12 +916,24 @@ class TrackPlayerCore private constructor(
|
|
|
894
916
|
// (reduced by 1 when current track is from that temp list, matching getActualQueueInternal)
|
|
895
917
|
// When temp is playing, the original track at currentTrackIndex is included in "before",
|
|
896
918
|
// so the current playing position shifts by 1
|
|
897
|
-
val currentPos =
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
919
|
+
val currentPos =
|
|
920
|
+
if (currentTemporaryType != TemporaryType.NONE) {
|
|
921
|
+
currentTrackIndex + 1
|
|
922
|
+
} else {
|
|
923
|
+
currentTrackIndex
|
|
924
|
+
}
|
|
925
|
+
val effectivePlayNextSize =
|
|
926
|
+
if (currentTemporaryType == TemporaryType.PLAY_NEXT) {
|
|
927
|
+
maxOf(0, playNextStack.size - 1)
|
|
928
|
+
} else {
|
|
929
|
+
playNextStack.size
|
|
930
|
+
}
|
|
931
|
+
val effectiveUpNextSize =
|
|
932
|
+
if (currentTemporaryType == TemporaryType.UP_NEXT) {
|
|
933
|
+
maxOf(0, upNextQueue.size - 1)
|
|
934
|
+
} else {
|
|
935
|
+
upNextQueue.size
|
|
936
|
+
}
|
|
903
937
|
|
|
904
938
|
val playNextStart = currentPos + 1
|
|
905
939
|
val playNextEnd = playNextStart + effectivePlayNextSize
|
|
@@ -923,8 +957,12 @@ class TrackPlayerCore private constructor(
|
|
|
923
957
|
if (index >= playNextStart && index < playNextEnd) {
|
|
924
958
|
val playNextIndex = index - playNextStart
|
|
925
959
|
// Offset by 1 if current is from playNext (index 0 is already playing)
|
|
926
|
-
val actualListIndex =
|
|
927
|
-
|
|
960
|
+
val actualListIndex =
|
|
961
|
+
if (currentTemporaryType == TemporaryType.PLAY_NEXT) {
|
|
962
|
+
playNextIndex + 1
|
|
963
|
+
} else {
|
|
964
|
+
playNextIndex
|
|
965
|
+
}
|
|
928
966
|
|
|
929
967
|
// Remove tracks before the target from playNext (they're being skipped)
|
|
930
968
|
if (actualListIndex > 0) {
|
|
@@ -941,8 +979,12 @@ class TrackPlayerCore private constructor(
|
|
|
941
979
|
if (index >= upNextStart && index < upNextEnd) {
|
|
942
980
|
val upNextIndex = index - upNextStart
|
|
943
981
|
// Offset by 1 if current is from upNext (index 0 is already playing)
|
|
944
|
-
val actualListIndex =
|
|
945
|
-
|
|
982
|
+
val actualListIndex =
|
|
983
|
+
if (currentTemporaryType == TemporaryType.UP_NEXT) {
|
|
984
|
+
upNextIndex + 1
|
|
985
|
+
} else {
|
|
986
|
+
upNextIndex
|
|
987
|
+
}
|
|
946
988
|
|
|
947
989
|
// Clear all playNext tracks (they're being skipped)
|
|
948
990
|
playNextStack.clear()
|
|
@@ -993,22 +1035,22 @@ class TrackPlayerCore private constructor(
|
|
|
993
1035
|
*/
|
|
994
1036
|
private fun rebuildQueueAndPlayFromIndex(index: Int) {
|
|
995
1037
|
if (!::player.isInitialized) {
|
|
996
|
-
|
|
1038
|
+
NitroPlayerLogger.log("TrackPlayerCore", " ā Player not initialized")
|
|
997
1039
|
return
|
|
998
1040
|
}
|
|
999
1041
|
|
|
1000
1042
|
if (index < 0 || index >= currentTracks.size) {
|
|
1001
|
-
|
|
1043
|
+
NitroPlayerLogger.log("TrackPlayerCore", " ā Invalid index $index for currentTracks size ${currentTracks.size}")
|
|
1002
1044
|
return
|
|
1003
1045
|
}
|
|
1004
1046
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1047
|
+
NitroPlayerLogger.log("TrackPlayerCore") { "\nš TrackPlayerCore: REBUILD QUEUE AND PLAY FROM INDEX $index" }
|
|
1048
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " currentTracks.size: ${currentTracks.size}" }
|
|
1049
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " currentTracks IDs: ${currentTracks.map { it.id }}" }
|
|
1008
1050
|
|
|
1009
1051
|
// Build queue from the target index onwards
|
|
1010
1052
|
val tracksToPlay = currentTracks.subList(index, currentTracks.size)
|
|
1011
|
-
|
|
1053
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " tracksToPlay (${tracksToPlay.size}): ${tracksToPlay.map { it.id }}" }
|
|
1012
1054
|
|
|
1013
1055
|
val playlistId = currentPlaylistId ?: ""
|
|
1014
1056
|
val mediaItems =
|
|
@@ -1019,7 +1061,7 @@ class TrackPlayerCore private constructor(
|
|
|
1019
1061
|
|
|
1020
1062
|
// Update our internal tracking of the position in original playlist
|
|
1021
1063
|
currentTrackIndex = index
|
|
1022
|
-
|
|
1064
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " Setting currentTrackIndex to $index" }
|
|
1023
1065
|
|
|
1024
1066
|
// Clear the entire player queue and set new items
|
|
1025
1067
|
player.clearMediaItems()
|
|
@@ -1028,7 +1070,7 @@ class TrackPlayerCore private constructor(
|
|
|
1028
1070
|
player.playWhenReady = true
|
|
1029
1071
|
player.prepare()
|
|
1030
1072
|
|
|
1031
|
-
|
|
1073
|
+
NitroPlayerLogger.log("TrackPlayerCore") { " ā
Queue rebuilt with ${player.mediaItemCount} items, playing from index 0 (track ${tracksToPlay.firstOrNull()?.id})" }
|
|
1032
1074
|
}
|
|
1033
1075
|
|
|
1034
1076
|
// MARK: - Temporary Track Management
|
|
@@ -1044,18 +1086,18 @@ class TrackPlayerCore private constructor(
|
|
|
1044
1086
|
}
|
|
1045
1087
|
|
|
1046
1088
|
private fun addToUpNextInternal(trackId: String) {
|
|
1047
|
-
|
|
1089
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: addToUpNext($trackId)")
|
|
1048
1090
|
|
|
1049
1091
|
// Find the track from current playlist or all playlists
|
|
1050
1092
|
val track = findTrackById(trackId)
|
|
1051
1093
|
if (track == null) {
|
|
1052
|
-
|
|
1094
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā TrackPlayerCore: Track $trackId not found")
|
|
1053
1095
|
return
|
|
1054
1096
|
}
|
|
1055
1097
|
|
|
1056
1098
|
// Add to end of upNext queue (FIFO)
|
|
1057
1099
|
upNextQueue.add(track)
|
|
1058
|
-
|
|
1100
|
+
NitroPlayerLogger.log("TrackPlayerCore", " ā
Added '${track.title}' to upNext queue (position: ${upNextQueue.size})")
|
|
1059
1101
|
|
|
1060
1102
|
// Rebuild the player queue if actively playing
|
|
1061
1103
|
if (::player.isInitialized && player.currentMediaItem != null) {
|
|
@@ -1074,18 +1116,18 @@ class TrackPlayerCore private constructor(
|
|
|
1074
1116
|
}
|
|
1075
1117
|
|
|
1076
1118
|
private fun playNextInternal(trackId: String) {
|
|
1077
|
-
|
|
1119
|
+
NitroPlayerLogger.log("TrackPlayerCore", "āļø TrackPlayerCore: playNext($trackId)")
|
|
1078
1120
|
|
|
1079
1121
|
// Find the track from current playlist or all playlists
|
|
1080
1122
|
val track = findTrackById(trackId)
|
|
1081
1123
|
if (track == null) {
|
|
1082
|
-
|
|
1124
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā TrackPlayerCore: Track $trackId not found")
|
|
1083
1125
|
return
|
|
1084
1126
|
}
|
|
1085
1127
|
|
|
1086
1128
|
// Insert at beginning of playNext stack (LIFO)
|
|
1087
1129
|
playNextStack.add(0, track)
|
|
1088
|
-
|
|
1130
|
+
NitroPlayerLogger.log("TrackPlayerCore", " ā
Added '${track.title}' to playNext stack (position: 1)")
|
|
1089
1131
|
|
|
1090
1132
|
// Rebuild the player queue if actively playing
|
|
1091
1133
|
if (::player.isInitialized && player.currentMediaItem != null) {
|
|
@@ -1103,7 +1145,7 @@ class TrackPlayerCore private constructor(
|
|
|
1103
1145
|
val currentIndex = player.currentMediaItemIndex
|
|
1104
1146
|
if (currentIndex < 0) return
|
|
1105
1147
|
|
|
1106
|
-
val newQueueTracks =
|
|
1148
|
+
val newQueueTracks = ArrayList<TrackItem>(playNextStack.size + upNextQueue.size + currentTracks.size)
|
|
1107
1149
|
|
|
1108
1150
|
// Add playNext stack (LIFO - most recently added plays first)
|
|
1109
1151
|
// Skip index 0 if current track is from playNext (it's already playing)
|
|
@@ -1135,9 +1177,9 @@ class TrackPlayerCore private constructor(
|
|
|
1135
1177
|
track.toMediaItem(mediaId)
|
|
1136
1178
|
}
|
|
1137
1179
|
|
|
1138
|
-
// Remove all items after current
|
|
1139
|
-
|
|
1140
|
-
player.
|
|
1180
|
+
// Remove all items after current in one batch (single timeline event vs N events)
|
|
1181
|
+
if (player.mediaItemCount > currentIndex + 1) {
|
|
1182
|
+
player.removeMediaItems(currentIndex + 1, player.mediaItemCount)
|
|
1141
1183
|
}
|
|
1142
1184
|
|
|
1143
1185
|
// Add new items
|
|
@@ -1204,9 +1246,9 @@ class TrackPlayerCore private constructor(
|
|
|
1204
1246
|
mediaLibraryManager.setMediaLibrary(library)
|
|
1205
1247
|
// Notify Android Auto to refresh
|
|
1206
1248
|
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
1207
|
-
|
|
1249
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā
TrackPlayerCore: Android Auto media library set successfully")
|
|
1208
1250
|
} catch (e: Exception) {
|
|
1209
|
-
|
|
1251
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā TrackPlayerCore: Error setting media library - ${e.message}")
|
|
1210
1252
|
e.printStackTrace()
|
|
1211
1253
|
}
|
|
1212
1254
|
}
|
|
@@ -1229,11 +1271,11 @@ class TrackPlayerCore private constructor(
|
|
|
1229
1271
|
// Convert to 0.0-1.0 range for ExoPlayer
|
|
1230
1272
|
val normalizedVolume = (clampedVolume / 100.0).toFloat()
|
|
1231
1273
|
player.volume = normalizedVolume
|
|
1232
|
-
|
|
1274
|
+
NitroPlayerLogger.log("TrackPlayerCore", "š TrackPlayerCore: Volume set to $clampedVolume% (normalized: $normalizedVolume)")
|
|
1233
1275
|
}
|
|
1234
1276
|
true
|
|
1235
1277
|
} else {
|
|
1236
|
-
|
|
1278
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø TrackPlayerCore: Cannot set volume - player not initialized")
|
|
1237
1279
|
false
|
|
1238
1280
|
}
|
|
1239
1281
|
|
|
@@ -1266,7 +1308,7 @@ class TrackPlayerCore private constructor(
|
|
|
1266
1308
|
val liveCallbacks =
|
|
1267
1309
|
synchronized(onChangeTrackListeners) {
|
|
1268
1310
|
onChangeTrackListeners.removeAll { !it.isAlive }
|
|
1269
|
-
onChangeTrackListeners.
|
|
1311
|
+
onChangeTrackListeners.map { it.callback }
|
|
1270
1312
|
}
|
|
1271
1313
|
|
|
1272
1314
|
handler.post {
|
|
@@ -1274,7 +1316,7 @@ class TrackPlayerCore private constructor(
|
|
|
1274
1316
|
try {
|
|
1275
1317
|
callback(track, reason)
|
|
1276
1318
|
} catch (e: Exception) {
|
|
1277
|
-
|
|
1319
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Error in track change listener: ${e.message}")
|
|
1278
1320
|
}
|
|
1279
1321
|
}
|
|
1280
1322
|
}
|
|
@@ -1287,7 +1329,7 @@ class TrackPlayerCore private constructor(
|
|
|
1287
1329
|
val liveCallbacks =
|
|
1288
1330
|
synchronized(onPlaybackStateChangeListeners) {
|
|
1289
1331
|
onPlaybackStateChangeListeners.removeAll { !it.isAlive }
|
|
1290
|
-
onPlaybackStateChangeListeners.
|
|
1332
|
+
onPlaybackStateChangeListeners.map { it.callback }
|
|
1291
1333
|
}
|
|
1292
1334
|
|
|
1293
1335
|
handler.post {
|
|
@@ -1295,7 +1337,7 @@ class TrackPlayerCore private constructor(
|
|
|
1295
1337
|
try {
|
|
1296
1338
|
callback(state, reason)
|
|
1297
1339
|
} catch (e: Exception) {
|
|
1298
|
-
|
|
1340
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Error in playback state listener: ${e.message}")
|
|
1299
1341
|
}
|
|
1300
1342
|
}
|
|
1301
1343
|
}
|
|
@@ -1308,7 +1350,7 @@ class TrackPlayerCore private constructor(
|
|
|
1308
1350
|
val liveCallbacks =
|
|
1309
1351
|
synchronized(onSeekListeners) {
|
|
1310
1352
|
onSeekListeners.removeAll { !it.isAlive }
|
|
1311
|
-
onSeekListeners.
|
|
1353
|
+
onSeekListeners.map { it.callback }
|
|
1312
1354
|
}
|
|
1313
1355
|
|
|
1314
1356
|
handler.post {
|
|
@@ -1316,7 +1358,7 @@ class TrackPlayerCore private constructor(
|
|
|
1316
1358
|
try {
|
|
1317
1359
|
callback(position, duration)
|
|
1318
1360
|
} catch (e: Exception) {
|
|
1319
|
-
|
|
1361
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Error in seek listener: ${e.message}")
|
|
1320
1362
|
}
|
|
1321
1363
|
}
|
|
1322
1364
|
}
|
|
@@ -1330,7 +1372,7 @@ class TrackPlayerCore private constructor(
|
|
|
1330
1372
|
val liveCallbacks =
|
|
1331
1373
|
synchronized(onPlaybackProgressChangeListeners) {
|
|
1332
1374
|
onPlaybackProgressChangeListeners.removeAll { !it.isAlive }
|
|
1333
|
-
onPlaybackProgressChangeListeners.
|
|
1375
|
+
onPlaybackProgressChangeListeners.map { it.callback }
|
|
1334
1376
|
}
|
|
1335
1377
|
|
|
1336
1378
|
handler.post {
|
|
@@ -1338,7 +1380,7 @@ class TrackPlayerCore private constructor(
|
|
|
1338
1380
|
try {
|
|
1339
1381
|
callback(position, duration, isPlaying)
|
|
1340
1382
|
} catch (e: Exception) {
|
|
1341
|
-
|
|
1383
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø Error in playback progress listener: ${e.message}")
|
|
1342
1384
|
}
|
|
1343
1385
|
}
|
|
1344
1386
|
}
|
|
@@ -1371,14 +1413,15 @@ class TrackPlayerCore private constructor(
|
|
|
1371
1413
|
// Wait up to 5 seconds for the result
|
|
1372
1414
|
latch.await(5, TimeUnit.SECONDS)
|
|
1373
1415
|
} catch (e: InterruptedException) {
|
|
1374
|
-
|
|
1416
|
+
NitroPlayerLogger.log("TrackPlayerCore", "ā ļø TrackPlayerCore: Interrupted while waiting for actual queue")
|
|
1375
1417
|
}
|
|
1376
1418
|
|
|
1377
1419
|
return result ?: emptyList()
|
|
1378
1420
|
}
|
|
1379
1421
|
|
|
1380
1422
|
private fun getActualQueueInternal(): List<TrackItem> {
|
|
1381
|
-
val
|
|
1423
|
+
val capacity = currentTracks.size + playNextStack.size + upNextQueue.size
|
|
1424
|
+
val queue = ArrayList<TrackItem>(capacity)
|
|
1382
1425
|
|
|
1383
1426
|
if (!::player.isInitialized) return emptyList()
|
|
1384
1427
|
|
|
@@ -1388,8 +1431,12 @@ class TrackPlayerCore private constructor(
|
|
|
1388
1431
|
// Add tracks before current (original playlist)
|
|
1389
1432
|
// When a temp track is playing, include the original track at currentTrackIndex
|
|
1390
1433
|
// (it already played before the temp track started)
|
|
1391
|
-
val beforeEnd =
|
|
1392
|
-
|
|
1434
|
+
val beforeEnd =
|
|
1435
|
+
if (currentTemporaryType != TemporaryType.NONE) {
|
|
1436
|
+
minOf(currentIndex + 1, currentTracks.size)
|
|
1437
|
+
} else {
|
|
1438
|
+
currentIndex
|
|
1439
|
+
}
|
|
1393
1440
|
if (beforeEnd > 0) {
|
|
1394
1441
|
queue.addAll(currentTracks.subList(0, beforeEnd))
|
|
1395
1442
|
}
|