diff --git a/src/player.js b/src/player.js index be5d2c4..1041096 100644 --- a/src/player.js +++ b/src/player.js @@ -359,12 +359,9 @@ export function playQueue(songs, startIdx) { _radioFilling = false; // cancel any in-flight radio fill for the old queue S._origQueue = null; _histClear(); - // On Android with shuffle on, start at a random position so we don't always - // begin at index 0 (the first album/playlist track). - if (HAS_BRIDGE && S.shuffle && songs.length > 1) { - startIdx = Math.floor(Math.random() * songs.length); - } - if (!HAS_BRIDGE && S.shuffle && songs.length > 1) { + // Spotify-style shuffle: build a fixed shuffled order once, pin the tapped song first. + // Applies to both web and Android — queue is traversed sequentially in this fixed order. + if (S.shuffle && songs.length > 1) { const start = songs[startIdx]; const rest = songs.filter((_, i) => i !== startIdx).sort(() => Math.random() - 0.5); S._origQueue = songs.map(s => ({...s})); @@ -387,7 +384,7 @@ export function playQueue(songs, startIdx) { // @JavascriptInterface call (which blocks the JS thread while Java // parses the JSON and creates MediaItems). requestAnimationFrame(() => { - try { window.NSBridge.setQueue(trackListJson, startIdx, true); } + try { window.NSBridge.setQueue(trackListJson, S.qIdx, true); } catch (e) { console.warn('NSBridge.setQueue failed', e); } // fillRadioQueue AFTER setQueue so addToQueue lands in the new native queue, // not the old one that setQueue is about to wipe. @@ -655,11 +652,7 @@ export function setPlayIcon(playing) { function _computeNextIdx() { if (S.repeat === 'one') return S.qIdx; - if (S.shuffle && S.queue.length > 1) { - let n = S.qIdx; - while (n === S.qIdx) n = Math.floor(Math.random() * S.queue.length); - return n; - } + // No special shuffle case — queue is already in shuffled order when S.shuffle is on. if (S.qIdx + 1 < S.queue.length) return S.qIdx + 1; if (S.repeat === 'all') return 0; return -1; @@ -759,7 +752,7 @@ export function _applyShuffleRepeat() { export function toggleShuffle() { S.shuffle = !S.shuffle; localStorage.setItem('ns_shuffle', S.shuffle ? '1' : '0'); - if (!HAS_BRIDGE && S.queue.length > 1) { + if (S.queue.length > 1) { if (S.shuffle) { const cur = S.queue[S.qIdx]; S._origQueue = [...S.queue]; @@ -774,9 +767,14 @@ export function toggleShuffle() { S.qIdx = idx >= 0 ? idx : 0; } renderQueue(); + // Re-send the reordered queue to native so ExoPlayer's order matches JS. + if (HAS_BRIDGE) { + const tl = JSON.stringify(buildTrackList()); + try { window.NSBridge.setQueue(tl, S.qIdx, _nativePlaying); } + catch(e) { console.warn('NSBridge.setQueue failed', e); } + } } _applyShuffleRepeat(); - if (HAS_BRIDGE) _nsBridge('setShuffleMode', S.shuffle); } export function toggleRepeat() {