diff --git a/src/player.js b/src/player.js index ae6e7d7..7ac01a8 100644 --- a/src/player.js +++ b/src/player.js @@ -281,7 +281,6 @@ let _preLink = null; function _nextIdx() { if(S.repeat === 'one') return S.qIdx; - if(S.shuffle) return -1; if(S.qIdx + 1 < S.queue.length) return S.qIdx + 1; if(S.repeat === 'all') return 0; return -1; @@ -323,8 +322,17 @@ let _queueGen = 0; export function playQueue(songs, startIdx) { _queueGen++; _radioFilling = false; // cancel any in-flight radio fill for the old queue - S.queue = songs.map(s => ({...s})); - S.qIdx = startIdx; + S._origQueue = null; + if (!HAS_BRIDGE && 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})); + S.queue = [start, ...rest].map(s => ({...s})); + S.qIdx = 0; + } else { + S.queue = songs.map(s => ({...s})); + S.qIdx = startIdx; + } _clearPreload(); if (HAS_BRIDGE) { @@ -621,8 +629,7 @@ export function nextSong() { if (S.radioMode && S.queue.length - S.qIdx < 5) fillRadioQueue(); return; } - if(S.shuffle) S.qIdx = Math.floor(Math.random() * S.queue.length); - else if(S.qIdx < S.queue.length - 1) S.qIdx++; + if(S.qIdx < S.queue.length - 1) S.qIdx++; else if(S.repeat) S.qIdx = 0; else if(S.radioMode) { fillRadioQueue().then(() => { if(S.qIdx < S.queue.length-1) { S.qIdx++; playCurrent(); } }); return; } else return; @@ -651,6 +658,22 @@ 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.shuffle) { + const cur = S.queue[S.qIdx]; + S._origQueue = [...S.queue]; + const rest = S.queue.filter((_, i) => i !== S.qIdx).sort(() => Math.random() - 0.5); + S.queue = [cur, ...rest]; + S.qIdx = 0; + } else if (S._origQueue) { + const cur = S.queue[S.qIdx]; + S.queue = S._origQueue; + S._origQueue = null; + const idx = S.queue.findIndex(s => s.id === cur?.id); + S.qIdx = idx >= 0 ? idx : 0; + } + renderQueue(); + } _applyShuffleRepeat(); if (HAS_BRIDGE) _nsBridge('setShuffleMode', S.shuffle); } diff --git a/src/state.js b/src/state.js index d0a75ff..d8600e0 100644 --- a/src/state.js +++ b/src/state.js @@ -5,6 +5,7 @@ export const S = { // Playback queue: [], qIdx: -1, + _origQueue: null, shuffle: false, repeat: false, radioMode: false, diff --git a/styles.css b/styles.css index d1f18ce..52a572e 100644 --- a/styles.css +++ b/styles.css @@ -298,8 +298,7 @@ button:focus{outline:none} .ctrl-buttons{display:flex;align-items:center;gap:10px} .ctrl-btn{background:none;border:none;color:var(--text2);cursor:pointer;padding:5px;border-radius:7px;display:flex;align-items:center;transition:color .15s} #btn-repeat{position:relative} -#btn-repeat.repeat-one::after,#np-repeat-btn.repeat-one::after{content:'1';position:absolute;top:1px;right:1px;font-size:7px;font-weight:700;line-height:11px;text-align:center;width:11px;height:11px;border-radius:50%;background:var(--accent);color:#000;pointer-events:none} -#np-repeat-btn{position:relative} +#btn-repeat.repeat-one::after,#np-repeat-btn.repeat-one::after{content:'1';position:absolute;top:1px;right:1px;font-size:7px;font-weight:700;line-height:11px;text-align:center;width:11px;height:11px;border-radius:50%;background:var(--accent);color:#000;pointer-events:none}#np-repeat-btn{position:relative} .ctrl-btn:hover{color:var(--text)} .ctrl-btn.active{color:var(--accent)} .play-pause-btn{width:38px;height:38px;border-radius:50%;background:var(--accent);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .15s,background .15s;box-shadow:0 0 18px var(--accent-glow)}