react-native-nitro-player 0.4.0 ā 0.5.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.
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +89 -115
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadFileManager.kt +10 -7
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadWorker.kt +30 -3
- package/ios/core/TrackPlayerCore.swift +101 -113
- package/ios/download/DownloadFileManager.swift +12 -4
- package/ios/download/DownloadManagerCore.swift +6 -1
- package/ios/media/MediaSessionManager.swift +181 -108
- package/lib/hooks/callbackManager.d.ts +18 -0
- package/lib/hooks/callbackManager.js +66 -0
- package/lib/hooks/useNowPlaying.js +30 -18
- package/lib/hooks/useOnPlaybackProgressChange.js +2 -2
- package/package.json +2 -2
- package/src/hooks/callbackManager.ts +87 -0
- package/src/hooks/useNowPlaying.ts +31 -19
- package/src/hooks/useOnPlaybackProgressChange.ts +2 -2
|
@@ -280,8 +280,7 @@ class TrackPlayerCore: NSObject {
|
|
|
280
280
|
print("\nš TrackPlayerCore: Track finished playing")
|
|
281
281
|
|
|
282
282
|
guard let finishedItem = notification.object as? AVPlayerItem else {
|
|
283
|
-
|
|
284
|
-
skipToNext()
|
|
283
|
+
// Don't call skipToNext ā AVQueuePlayer with actionAtItemEnd = .advance already auto-advances
|
|
285
284
|
return
|
|
286
285
|
}
|
|
287
286
|
|
|
@@ -352,7 +351,9 @@ class TrackPlayerCore: NSObject {
|
|
|
352
351
|
print("š TrackPlayerCore: Repeat mode is OFF")
|
|
353
352
|
}
|
|
354
353
|
|
|
355
|
-
// Track ended naturally
|
|
354
|
+
// Track ended naturally ā notify with .end reason
|
|
355
|
+
// AVQueuePlayer with actionAtItemEnd = .advance auto-advances to next item
|
|
356
|
+
// The KVO observer (currentItemDidChange) will handle the track change notification
|
|
356
357
|
notifyTrackChange(
|
|
357
358
|
getCurrentTrack()
|
|
358
359
|
?? TrackItem(
|
|
@@ -365,9 +366,6 @@ class TrackPlayerCore: NSObject {
|
|
|
365
366
|
artwork: nil,
|
|
366
367
|
extraPayload: nil
|
|
367
368
|
), .end)
|
|
368
|
-
|
|
369
|
-
// Try to play next track
|
|
370
|
-
skipToNext()
|
|
371
369
|
}
|
|
372
370
|
|
|
373
371
|
@objc private func playerItemFailedToPlayToEndTime(notification: Notification) {
|
|
@@ -642,12 +640,21 @@ class TrackPlayerCore: NSObject {
|
|
|
642
640
|
func updatePlaylist(playlistId: String) {
|
|
643
641
|
DispatchQueue.main.async { [weak self] in
|
|
644
642
|
guard let self = self else { return }
|
|
645
|
-
|
|
643
|
+
guard self.currentPlaylistId == playlistId,
|
|
646
644
|
let playlist = self.playlistManager.getPlaylist(playlistId: playlistId)
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
645
|
+
else { return }
|
|
646
|
+
|
|
647
|
+
// If nothing is playing yet, do a full load
|
|
648
|
+
guard let player = self.player, player.currentItem != nil else {
|
|
649
|
+
self.updatePlayerQueue(tracks: playlist.tracks)
|
|
650
|
+
return
|
|
650
651
|
}
|
|
652
|
+
|
|
653
|
+
// Update tracks list without interrupting playback
|
|
654
|
+
self.currentTracks = playlist.tracks
|
|
655
|
+
|
|
656
|
+
// Rebuild only the items after the currently playing item
|
|
657
|
+
self.rebuildAVQueueFromCurrentPosition()
|
|
651
658
|
}
|
|
652
659
|
}
|
|
653
660
|
|
|
@@ -1134,21 +1141,34 @@ class TrackPlayerCore: NSObject {
|
|
|
1134
1141
|
var queue: [TrackItem] = []
|
|
1135
1142
|
|
|
1136
1143
|
// Add tracks before current (original playlist)
|
|
1137
|
-
|
|
1138
|
-
|
|
1144
|
+
// When a temp track is playing, include the original track at currentTrackIndex
|
|
1145
|
+
// (it already played before the temp track started)
|
|
1146
|
+
let beforeEnd = currentTemporaryType != .none
|
|
1147
|
+
? min(currentTrackIndex + 1, currentTracks.count) : currentTrackIndex
|
|
1148
|
+
if beforeEnd > 0 {
|
|
1149
|
+
queue.append(contentsOf: Array(currentTracks[0..<beforeEnd]))
|
|
1139
1150
|
}
|
|
1140
1151
|
|
|
1141
|
-
// Add current track
|
|
1152
|
+
// Add current track (temp or original)
|
|
1142
1153
|
if let current = getCurrentTrack() {
|
|
1143
1154
|
queue.append(current)
|
|
1144
1155
|
}
|
|
1145
1156
|
|
|
1146
1157
|
// Add playNext stack (LIFO - most recently added plays first)
|
|
1147
|
-
//
|
|
1148
|
-
|
|
1158
|
+
// Skip index 0 if current track is from playNext (it's already added as current)
|
|
1159
|
+
if currentTemporaryType == .playNext && playNextStack.count > 1 {
|
|
1160
|
+
queue.append(contentsOf: Array(playNextStack.dropFirst()))
|
|
1161
|
+
} else if currentTemporaryType != .playNext {
|
|
1162
|
+
queue.append(contentsOf: playNextStack)
|
|
1163
|
+
}
|
|
1149
1164
|
|
|
1150
1165
|
// Add upNext queue (in order, FIFO)
|
|
1151
|
-
|
|
1166
|
+
// Skip index 0 if current track is from upNext (it's already added as current)
|
|
1167
|
+
if currentTemporaryType == .upNext && upNextQueue.count > 1 {
|
|
1168
|
+
queue.append(contentsOf: Array(upNextQueue.dropFirst()))
|
|
1169
|
+
} else if currentTemporaryType != .upNext {
|
|
1170
|
+
queue.append(contentsOf: upNextQueue)
|
|
1171
|
+
}
|
|
1152
1172
|
|
|
1153
1173
|
// Add remaining original tracks
|
|
1154
1174
|
if currentTrackIndex + 1 < currentTracks.count {
|
|
@@ -1314,39 +1334,23 @@ class TrackPlayerCore: NSObject {
|
|
|
1314
1334
|
private func skipToNextInternal() {
|
|
1315
1335
|
guard let queuePlayer = self.player else { return }
|
|
1316
1336
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1337
|
+
// Remove current temp track from its list before advancing
|
|
1338
|
+
if let trackId = queuePlayer.currentItem?.trackId {
|
|
1339
|
+
if currentTemporaryType == .playNext {
|
|
1340
|
+
if let idx = playNextStack.firstIndex(where: { $0.id == trackId }) {
|
|
1341
|
+
playNextStack.remove(at: idx)
|
|
1342
|
+
}
|
|
1343
|
+
} else if currentTemporaryType == .upNext {
|
|
1344
|
+
if let idx = upNextQueue.firstIndex(where: { $0.id == trackId }) {
|
|
1345
|
+
upNextQueue.remove(at: idx)
|
|
1346
|
+
}
|
|
1326
1347
|
}
|
|
1327
1348
|
}
|
|
1328
1349
|
|
|
1329
|
-
// Check if there are more items in the queue
|
|
1330
|
-
if
|
|
1331
|
-
print(" š Calling advanceToNextItem()...")
|
|
1350
|
+
// Check if there are more items in the player queue
|
|
1351
|
+
if queuePlayer.items().count > 1 {
|
|
1332
1352
|
queuePlayer.advanceToNextItem()
|
|
1333
|
-
|
|
1334
|
-
// NOTE: Don't manually update currentTrackIndex here!
|
|
1335
|
-
// The KVO observer (currentItemDidChange) will update it automatically
|
|
1336
|
-
|
|
1337
|
-
print(" AFTER advanceToNextItem():")
|
|
1338
|
-
print(" Items in player queue: \(queuePlayer.items().count)")
|
|
1339
|
-
|
|
1340
|
-
if let newCurrentItem = queuePlayer.currentItem, let trackId = newCurrentItem.trackId {
|
|
1341
|
-
if let track = self.currentTracks.first(where: { $0.id == trackId }) {
|
|
1342
|
-
print(" New current item: \(track.title) (ID: \(track.id))")
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
print(" ā³ Waiting for KVO observer to update index...")
|
|
1347
1353
|
} else {
|
|
1348
|
-
print(" ā ļø No more tracks in playlist")
|
|
1349
|
-
// At end of playlist - stop or loop
|
|
1350
1354
|
queuePlayer.pause()
|
|
1351
1355
|
self.notifyPlaybackStateChange(.stopped, .end)
|
|
1352
1356
|
}
|
|
@@ -1365,29 +1369,31 @@ class TrackPlayerCore: NSObject {
|
|
|
1365
1369
|
private func skipToPreviousInternal() {
|
|
1366
1370
|
guard let queuePlayer = self.player else { return }
|
|
1367
1371
|
|
|
1368
|
-
print("\nā®ļø TrackPlayerCore: SKIP TO PREVIOUS")
|
|
1369
|
-
print(" Current index: \(self.currentTrackIndex)")
|
|
1370
|
-
print(" Temporary type: \(self.currentTemporaryType)")
|
|
1371
|
-
print(" Current time: \(queuePlayer.currentTime().seconds)s")
|
|
1372
|
-
|
|
1373
1372
|
let currentTime = queuePlayer.currentTime()
|
|
1374
1373
|
if currentTime.seconds > Constants.skipToPreviousThreshold {
|
|
1375
1374
|
// If more than threshold seconds in, restart current track
|
|
1376
|
-
print(
|
|
1377
|
-
" š More than \(Int(Constants.skipToPreviousThreshold))s in, restarting current track")
|
|
1378
1375
|
queuePlayer.seek(to: .zero)
|
|
1379
1376
|
} else if self.currentTemporaryType != .none {
|
|
1380
|
-
// Playing temporary track
|
|
1381
|
-
|
|
1382
|
-
|
|
1377
|
+
// Playing temporary track ā remove from its list, then restart
|
|
1378
|
+
if let trackId = queuePlayer.currentItem?.trackId {
|
|
1379
|
+
if currentTemporaryType == .playNext {
|
|
1380
|
+
if let idx = playNextStack.firstIndex(where: { $0.id == trackId }) {
|
|
1381
|
+
playNextStack.remove(at: idx)
|
|
1382
|
+
}
|
|
1383
|
+
} else if currentTemporaryType == .upNext {
|
|
1384
|
+
if let idx = upNextQueue.firstIndex(where: { $0.id == trackId }) {
|
|
1385
|
+
upNextQueue.remove(at: idx)
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
// Go to current original track position (skip back from temp)
|
|
1390
|
+
self.playFromIndex(index: self.currentTrackIndex)
|
|
1383
1391
|
} else if self.currentTrackIndex > 0 {
|
|
1384
1392
|
// Go to previous track in original playlist
|
|
1385
1393
|
let previousIndex = self.currentTrackIndex - 1
|
|
1386
|
-
print(" ā®ļø Going to previous track at index \(previousIndex)")
|
|
1387
1394
|
self.playFromIndex(index: previousIndex)
|
|
1388
1395
|
} else {
|
|
1389
1396
|
// Already at first track, restart it
|
|
1390
|
-
print(" š Already at first track, restarting it")
|
|
1391
1397
|
queuePlayer.seek(to: .zero)
|
|
1392
1398
|
}
|
|
1393
1399
|
}
|
|
@@ -1577,44 +1583,38 @@ class TrackPlayerCore: NSObject {
|
|
|
1577
1583
|
}
|
|
1578
1584
|
|
|
1579
1585
|
private func skipToIndexInternal(index: Int) -> Bool {
|
|
1580
|
-
print("\nšÆ TrackPlayerCore: SKIP TO INDEX \(index)")
|
|
1581
|
-
|
|
1582
1586
|
// Get actual queue to validate index and determine position
|
|
1583
1587
|
let actualQueue = getActualQueueInternal()
|
|
1584
1588
|
let totalQueueSize = actualQueue.count
|
|
1585
1589
|
|
|
1586
1590
|
// Validate index
|
|
1587
|
-
guard index >= 0 && index < totalQueueSize else {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
+
guard index >= 0 && index < totalQueueSize else { return false }
|
|
1592
|
+
|
|
1593
|
+
// Calculate queue section boundaries using effective sizes
|
|
1594
|
+
// (reduced by 1 when current track is from that temp list, matching getActualQueueInternal)
|
|
1595
|
+
// When temp is playing, the original track at currentTrackIndex is included in "before",
|
|
1596
|
+
// so the current playing position shifts by 1
|
|
1597
|
+
let currentPos = currentTemporaryType != .none
|
|
1598
|
+
? currentTrackIndex + 1 : currentTrackIndex
|
|
1599
|
+
let effectivePlayNextSize = currentTemporaryType == .playNext
|
|
1600
|
+
? max(0, playNextStack.count - 1) : playNextStack.count
|
|
1601
|
+
let effectiveUpNextSize = currentTemporaryType == .upNext
|
|
1602
|
+
? max(0, upNextQueue.count - 1) : upNextQueue.count
|
|
1591
1603
|
|
|
1592
|
-
// Calculate queue section boundaries
|
|
1593
|
-
// ActualQueue structure: [before_current] + [current] + [playNext] + [upNext] + [remaining_original]
|
|
1594
|
-
let currentPos = currentTrackIndex
|
|
1595
1604
|
let playNextStart = currentPos + 1
|
|
1596
|
-
let playNextEnd = playNextStart +
|
|
1605
|
+
let playNextEnd = playNextStart + effectivePlayNextSize
|
|
1597
1606
|
let upNextStart = playNextEnd
|
|
1598
|
-
let upNextEnd = upNextStart +
|
|
1607
|
+
let upNextEnd = upNextStart + effectiveUpNextSize
|
|
1599
1608
|
let originalRemainingStart = upNextEnd
|
|
1600
1609
|
|
|
1601
|
-
print(" Queue structure:")
|
|
1602
|
-
print(" currentPos: \(currentPos)")
|
|
1603
|
-
print(" playNextStart: \(playNextStart), playNextEnd: \(playNextEnd)")
|
|
1604
|
-
print(" upNextStart: \(upNextStart), upNextEnd: \(upNextEnd)")
|
|
1605
|
-
print(" originalRemainingStart: \(originalRemainingStart)")
|
|
1606
|
-
print(" totalQueueSize: \(totalQueueSize)")
|
|
1607
|
-
|
|
1608
1610
|
// Case 1: Target is before current - use playFromIndex on original
|
|
1609
1611
|
if index < currentPos {
|
|
1610
|
-
print(" š Target is before current, jumping to original playlist index \(index)")
|
|
1611
1612
|
playFromIndexInternal(index: index)
|
|
1612
1613
|
return true
|
|
1613
1614
|
}
|
|
1614
1615
|
|
|
1615
1616
|
// Case 2: Target is current - seek to beginning
|
|
1616
1617
|
if index == currentPos {
|
|
1617
|
-
print(" š Target is current track, seeking to beginning")
|
|
1618
1618
|
player?.seek(to: .zero)
|
|
1619
1619
|
return true
|
|
1620
1620
|
}
|
|
@@ -1622,12 +1622,13 @@ class TrackPlayerCore: NSObject {
|
|
|
1622
1622
|
// Case 3: Target is in playNext section
|
|
1623
1623
|
if index >= playNextStart && index < playNextEnd {
|
|
1624
1624
|
let playNextIndex = index - playNextStart
|
|
1625
|
-
|
|
1625
|
+
// Offset by 1 if current is from playNext (index 0 is already playing)
|
|
1626
|
+
let actualListIndex = currentTemporaryType == .playNext
|
|
1627
|
+
? playNextIndex + 1 : playNextIndex
|
|
1626
1628
|
|
|
1627
1629
|
// Remove tracks before the target from playNext (they're being skipped)
|
|
1628
|
-
if
|
|
1629
|
-
playNextStack.removeFirst(
|
|
1630
|
-
print(" Removed \(playNextIndex) tracks from playNext stack")
|
|
1630
|
+
if actualListIndex > 0 {
|
|
1631
|
+
playNextStack.removeFirst(actualListIndex)
|
|
1631
1632
|
}
|
|
1632
1633
|
|
|
1633
1634
|
// Rebuild queue and advance
|
|
@@ -1639,16 +1640,16 @@ class TrackPlayerCore: NSObject {
|
|
|
1639
1640
|
// Case 4: Target is in upNext section
|
|
1640
1641
|
if index >= upNextStart && index < upNextEnd {
|
|
1641
1642
|
let upNextIndex = index - upNextStart
|
|
1642
|
-
|
|
1643
|
+
// Offset by 1 if current is from upNext (index 0 is already playing)
|
|
1644
|
+
let actualListIndex = currentTemporaryType == .upNext
|
|
1645
|
+
? upNextIndex + 1 : upNextIndex
|
|
1643
1646
|
|
|
1644
1647
|
// Clear all playNext tracks (they're being skipped)
|
|
1645
1648
|
playNextStack.removeAll()
|
|
1646
|
-
print(" Cleared all playNext tracks")
|
|
1647
1649
|
|
|
1648
1650
|
// Remove tracks before target from upNext
|
|
1649
|
-
if
|
|
1650
|
-
upNextQueue.removeFirst(
|
|
1651
|
-
print(" Removed \(upNextIndex) tracks from upNext queue")
|
|
1651
|
+
if actualListIndex > 0 {
|
|
1652
|
+
upNextQueue.removeFirst(actualListIndex)
|
|
1652
1653
|
}
|
|
1653
1654
|
|
|
1654
1655
|
// Rebuild queue and advance
|
|
@@ -1659,35 +1660,21 @@ class TrackPlayerCore: NSObject {
|
|
|
1659
1660
|
|
|
1660
1661
|
// Case 5: Target is in remaining original tracks
|
|
1661
1662
|
if index >= originalRemainingStart {
|
|
1662
|
-
// Get the target track directly from actualQueue
|
|
1663
1663
|
let targetTrack = actualQueue[index]
|
|
1664
1664
|
|
|
1665
|
-
print(" š Case 5: Target is in remaining original tracks")
|
|
1666
|
-
print(" targetTrack.id: \(targetTrack.id)")
|
|
1667
|
-
print(" currentTracks.count: \(currentTracks.count)")
|
|
1668
|
-
print(" currentTracks IDs: \(currentTracks.map { $0.id })")
|
|
1669
|
-
|
|
1670
1665
|
// Find this track's index in the original playlist
|
|
1671
1666
|
guard let originalIndex = currentTracks.firstIndex(where: { $0.id == targetTrack.id }) else {
|
|
1672
|
-
print(" ā Could not find track \(targetTrack.id) in original playlist")
|
|
1673
|
-
print(" Available tracks: \(currentTracks.map { $0.id })")
|
|
1674
1667
|
return false
|
|
1675
1668
|
}
|
|
1676
1669
|
|
|
1677
|
-
print(" originalIndex found: \(originalIndex)")
|
|
1678
|
-
|
|
1679
1670
|
// Clear all temporary tracks (they're being skipped)
|
|
1680
1671
|
playNextStack.removeAll()
|
|
1681
1672
|
upNextQueue.removeAll()
|
|
1682
1673
|
currentTemporaryType = .none
|
|
1683
|
-
print(" Cleared all temporary tracks")
|
|
1684
1674
|
|
|
1685
|
-
|
|
1686
|
-
let success = playFromIndexInternalWithResult(index: originalIndex)
|
|
1687
|
-
return success
|
|
1675
|
+
return playFromIndexInternalWithResult(index: originalIndex)
|
|
1688
1676
|
}
|
|
1689
1677
|
|
|
1690
|
-
print(" ā Unexpected case, index \(index) not handled")
|
|
1691
1678
|
return false
|
|
1692
1679
|
}
|
|
1693
1680
|
|
|
@@ -1798,6 +1785,7 @@ class TrackPlayerCore: NSObject {
|
|
|
1798
1785
|
if self.player?.currentItem != nil {
|
|
1799
1786
|
self.rebuildAVQueueFromCurrentPosition()
|
|
1800
1787
|
}
|
|
1788
|
+
mediaSessionManager?.onQueueChanged()
|
|
1801
1789
|
}
|
|
1802
1790
|
|
|
1803
1791
|
/**
|
|
@@ -1827,6 +1815,7 @@ class TrackPlayerCore: NSObject {
|
|
|
1827
1815
|
if self.player?.currentItem != nil {
|
|
1828
1816
|
self.rebuildAVQueueFromCurrentPosition()
|
|
1829
1817
|
}
|
|
1818
|
+
mediaSessionManager?.onQueueChanged()
|
|
1830
1819
|
}
|
|
1831
1820
|
|
|
1832
1821
|
/**
|
|
@@ -1836,24 +1825,26 @@ class TrackPlayerCore: NSObject {
|
|
|
1836
1825
|
private func rebuildAVQueueFromCurrentPosition() {
|
|
1837
1826
|
guard let player = self.player else { return }
|
|
1838
1827
|
|
|
1839
|
-
print("\nš TrackPlayerCore: REBUILDING QUEUE FROM CURRENT POSITION")
|
|
1840
|
-
print(" playNext stack: \(playNextStack.count) tracks")
|
|
1841
|
-
print(" upNext queue: \(upNextQueue.count) tracks")
|
|
1842
|
-
|
|
1843
|
-
// Don't interrupt currently playing item
|
|
1844
1828
|
let currentItem = player.currentItem
|
|
1845
1829
|
let playingItems = player.items()
|
|
1846
1830
|
|
|
1847
|
-
// Build new queue order:
|
|
1848
|
-
// [playNext stack] + [upNext queue] + [remaining original tracks]
|
|
1849
1831
|
var newQueueTracks: [TrackItem] = []
|
|
1850
1832
|
|
|
1851
1833
|
// Add playNext stack (LIFO - most recently added plays first)
|
|
1852
|
-
//
|
|
1853
|
-
|
|
1834
|
+
// Skip index 0 if current track is from playNext (it's already playing)
|
|
1835
|
+
if currentTemporaryType == .playNext && playNextStack.count > 1 {
|
|
1836
|
+
newQueueTracks.append(contentsOf: Array(playNextStack.dropFirst()))
|
|
1837
|
+
} else if currentTemporaryType != .playNext {
|
|
1838
|
+
newQueueTracks.append(contentsOf: playNextStack)
|
|
1839
|
+
}
|
|
1854
1840
|
|
|
1855
1841
|
// Add upNext queue (in order, FIFO)
|
|
1856
|
-
|
|
1842
|
+
// Skip index 0 if current track is from upNext (it's already playing)
|
|
1843
|
+
if currentTemporaryType == .upNext && upNextQueue.count > 1 {
|
|
1844
|
+
newQueueTracks.append(contentsOf: Array(upNextQueue.dropFirst()))
|
|
1845
|
+
} else if currentTemporaryType != .upNext {
|
|
1846
|
+
newQueueTracks.append(contentsOf: upNextQueue)
|
|
1847
|
+
}
|
|
1857
1848
|
|
|
1858
1849
|
// Add remaining original tracks
|
|
1859
1850
|
if currentTrackIndex + 1 < currentTracks.count {
|
|
@@ -1861,8 +1852,6 @@ class TrackPlayerCore: NSObject {
|
|
|
1861
1852
|
newQueueTracks.append(contentsOf: remainingOriginal)
|
|
1862
1853
|
}
|
|
1863
1854
|
|
|
1864
|
-
print(" New queue: \(newQueueTracks.count) tracks total")
|
|
1865
|
-
|
|
1866
1855
|
// Remove all items from player EXCEPT the currently playing one
|
|
1867
1856
|
for item in playingItems where item != currentItem {
|
|
1868
1857
|
player.remove(item)
|
|
@@ -1877,7 +1866,6 @@ class TrackPlayerCore: NSObject {
|
|
|
1877
1866
|
}
|
|
1878
1867
|
}
|
|
1879
1868
|
|
|
1880
|
-
print(" ā
Queue rebuilt successfully")
|
|
1881
1869
|
}
|
|
1882
1870
|
|
|
1883
1871
|
/**
|
|
@@ -56,20 +56,28 @@ final class DownloadFileManager {
|
|
|
56
56
|
|
|
57
57
|
func saveDownloadedFile(
|
|
58
58
|
from temporaryLocation: URL, trackId: String, storageLocation: StorageLocation,
|
|
59
|
-
originalURL: String? = nil
|
|
59
|
+
originalURL: String? = nil,
|
|
60
|
+
suggestedFilename: String? = nil
|
|
60
61
|
) -> String? {
|
|
61
62
|
print("šÆ DownloadFileManager: saveDownloadedFile called for trackId=\(trackId)")
|
|
62
63
|
print(" From: \(temporaryLocation.path)")
|
|
63
64
|
print(" Original URL: \(originalURL ?? "nil")")
|
|
65
|
+
print(" Suggested Filename: \(suggestedFilename ?? "nil")")
|
|
64
66
|
|
|
65
67
|
let destinationDirectory =
|
|
66
68
|
storageLocation == .private ? privateDownloadsDirectory : publicDownloadsDirectory
|
|
67
69
|
print(" Destination directory: \(destinationDirectory.path)")
|
|
68
70
|
|
|
69
|
-
// Determine file extension
|
|
70
|
-
// The temp file has .tmp extension which AVPlayer cannot play
|
|
71
|
+
// Determine file extension
|
|
71
72
|
var fileExtension = "mp3" // Default fallback
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
if let suggestedFilename = suggestedFilename, !suggestedFilename.isEmpty {
|
|
75
|
+
let url = URL(fileURLWithPath: suggestedFilename)
|
|
76
|
+
let pathExtension = url.pathExtension.lowercased()
|
|
77
|
+
if !pathExtension.isEmpty {
|
|
78
|
+
fileExtension = pathExtension
|
|
79
|
+
}
|
|
80
|
+
} else if let originalURL = originalURL, let url = URL(string: originalURL) {
|
|
73
81
|
let pathExtension = url.pathExtension.lowercased()
|
|
74
82
|
if !pathExtension.isEmpty {
|
|
75
83
|
fileExtension = pathExtension
|
|
@@ -681,11 +681,16 @@ extension DownloadManagerCore: URLSessionDownloadDelegate {
|
|
|
681
681
|
let (storageLocation, originalURL) = queue.sync {
|
|
682
682
|
(self.config.storageLocation ?? .private, self.trackMetadata[trackId]?.url)
|
|
683
683
|
}
|
|
684
|
+
|
|
685
|
+
// Get suggested filename from response
|
|
686
|
+
let suggestedFilename = downloadTask.response?.suggestedFilename
|
|
687
|
+
|
|
684
688
|
let destinationPath = DownloadFileManager.shared.saveDownloadedFile(
|
|
685
689
|
from: location,
|
|
686
690
|
trackId: trackId,
|
|
687
691
|
storageLocation: storageLocation,
|
|
688
|
-
originalURL: originalURL
|
|
692
|
+
originalURL: originalURL,
|
|
693
|
+
suggestedFilename: suggestedFilename
|
|
689
694
|
)
|
|
690
695
|
|
|
691
696
|
// Now handle the rest asynchronously
|