feat: prev-in-shuffle retraces actual play order via history stack
Added a play history stack (_playHist) that records S.qIdx before each forward advance (manual next, auto-advance via onTrackChanged). prevSong now pops from this stack instead of blindly doing S.qIdx--, so pressing back during shuffle always returns to the song that was actually playing before, regardless of where shuffle jumped. History clears on new queue. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
2d303083a0
commit
050aff6dbe
1 file changed
+20
-4
+20
-4
@@ -199,6 +199,7 @@ window.NSPlayer = {
|
|||||||
const trackId = p.trackId || '';
|
const trackId = p.trackId || '';
|
||||||
if (trackId && S.queue[idx]?.id && String(trackId) !== String(S.queue[idx].id) && p.reason !== 'queue') return;
|
if (trackId && S.queue[idx]?.id && String(trackId) !== String(S.queue[idx].id) && p.reason !== 'queue') return;
|
||||||
const indexChanged = (idx !== S.qIdx);
|
const indexChanged = (idx !== S.qIdx);
|
||||||
|
if (indexChanged && p.reason !== 'queue') _histPush(S.qIdx);
|
||||||
S.qIdx = idx;
|
S.qIdx = idx;
|
||||||
const s = S.queue[S.qIdx];
|
const s = S.queue[S.qIdx];
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
@@ -314,6 +315,17 @@ function _clearPreload() {
|
|||||||
if(_preLink) { _preLink.remove(); _preLink = null; }
|
if(_preLink) { _preLink.remove(); _preLink = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── PLAY HISTORY (for prev-in-shuffle) ──────────────────────────────────────
|
||||||
|
// Records the qIdx before each forward advance so prevSong() can retrace the
|
||||||
|
// actual play order instead of blindly decrementing (which is wrong in shuffle).
|
||||||
|
const _playHist = [];
|
||||||
|
function _histPush(idx) {
|
||||||
|
if (_playHist[_playHist.length - 1] === idx) return;
|
||||||
|
_playHist.push(idx);
|
||||||
|
if (_playHist.length > 50) _playHist.shift();
|
||||||
|
}
|
||||||
|
function _histClear() { _playHist.length = 0; }
|
||||||
|
|
||||||
// ─── PLAYBACK ─────────────────────────────────────────────────────────────
|
// ─── PLAYBACK ─────────────────────────────────────────────────────────────
|
||||||
let _playGen = 0;
|
let _playGen = 0;
|
||||||
// Incremented on every playQueue() call so in-flight fillRadioQueue callbacks
|
// Incremented on every playQueue() call so in-flight fillRadioQueue callbacks
|
||||||
@@ -324,6 +336,7 @@ export function playQueue(songs, startIdx) {
|
|||||||
_queueGen++;
|
_queueGen++;
|
||||||
_radioFilling = false; // cancel any in-flight radio fill for the old queue
|
_radioFilling = false; // cancel any in-flight radio fill for the old queue
|
||||||
S._origQueue = null;
|
S._origQueue = null;
|
||||||
|
_histClear();
|
||||||
if (!HAS_BRIDGE && S.shuffle && songs.length > 1) {
|
if (!HAS_BRIDGE && S.shuffle && songs.length > 1) {
|
||||||
const start = songs[startIdx];
|
const start = songs[startIdx];
|
||||||
const rest = songs.filter((_, i) => i !== startIdx).sort(() => Math.random() - 0.5);
|
const rest = songs.filter((_, i) => i !== startIdx).sort(() => Math.random() - 0.5);
|
||||||
@@ -647,11 +660,13 @@ export function prevSong() {
|
|||||||
_nsBridge('seekTo', 0);
|
_nsBridge('seekTo', 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (S.qIdx > 0) {
|
// Pop history to retrace actual play order (correct in shuffle where next jumps randomly).
|
||||||
S.qIdx--;
|
// Fall back to S.qIdx-1 when history is empty (e.g. at the very start of a queue).
|
||||||
|
const prevIdx = _playHist.length > 0 ? _playHist.pop() : (S.qIdx > 0 ? S.qIdx - 1 : -1);
|
||||||
|
if (prevIdx >= 0 && prevIdx < S.queue.length) {
|
||||||
|
S.qIdx = prevIdx;
|
||||||
const s = S.queue[S.qIdx];
|
const s = S.queue[S.qIdx];
|
||||||
if (s) { _resetProgressUI(); updatePlayerUI(s, true); onNpSongChange(s.id); renderQueue(); }
|
if (s) { _resetProgressUI(); updatePlayerUI(s, true); syncNowPlaying(); onNpSongChange(s.id); renderQueue(); }
|
||||||
// Explicit index so native doesn't have to agree on what "previous" means.
|
|
||||||
_nsBridge('seekToIndex', S.qIdx, 0);
|
_nsBridge('seekToIndex', S.qIdx, 0);
|
||||||
_nsBridge('play');
|
_nsBridge('play');
|
||||||
}
|
}
|
||||||
@@ -667,6 +682,7 @@ export function nextSong() {
|
|||||||
_setSkipping();
|
_setSkipping();
|
||||||
const ni = _computeNextIdx();
|
const ni = _computeNextIdx();
|
||||||
if (ni >= 0 && ni < S.queue.length) {
|
if (ni >= 0 && ni < S.queue.length) {
|
||||||
|
_histPush(S.qIdx);
|
||||||
S.qIdx = ni;
|
S.qIdx = ni;
|
||||||
const s = S.queue[S.qIdx];
|
const s = S.queue[S.qIdx];
|
||||||
if (s) { _resetProgressUI(); updatePlayerUI(s, true); onNpSongChange(s.id); renderQueue(); }
|
if (s) { _resetProgressUI(); updatePlayerUI(s, true); onNpSongChange(s.id); renderQueue(); }
|
||||||
|
|||||||
Reference in new issue
Block a user