fix: radio fill lock prevents concurrent fills; random fallback when similar songs exhausted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
b9a5c6468f
commit
89aeeb7421
1 file changed
+26
-11
+26
-11
@@ -243,9 +243,11 @@ window.NSPlayer = {
|
|||||||
setPlayIcon(false);
|
setPlayIcon(false);
|
||||||
if (S.radioMode) {
|
if (S.radioMode) {
|
||||||
const prevLen = S.queue.length;
|
const prevLen = S.queue.length;
|
||||||
// fillRadioQueue sends addToQueue to native internally — only trigger play here.
|
|
||||||
fillRadioQueue().then(() => {
|
fillRadioQueue().then(() => {
|
||||||
if (S.queue.length > prevLen) _nsBridge('play');
|
if (S.queue.length > prevLen) { _nsBridge('play'); return; }
|
||||||
|
// fillRadioQueue was locked by a concurrent fill — wait for it to finish
|
||||||
|
// then trigger play so native doesn't stay stopped after queue end.
|
||||||
|
if (_radioFilling) setTimeout(() => { if (S.queue.length > prevLen) _nsBridge('play'); }, 3000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -320,6 +322,7 @@ let _queueGen = 0;
|
|||||||
|
|
||||||
export function playQueue(songs, startIdx) {
|
export function playQueue(songs, startIdx) {
|
||||||
_queueGen++;
|
_queueGen++;
|
||||||
|
_radioFilling = false; // cancel any in-flight radio fill for the old queue
|
||||||
S.queue = songs.map(s => ({...s}));
|
S.queue = songs.map(s => ({...s}));
|
||||||
S.qIdx = startIdx;
|
S.qIdx = startIdx;
|
||||||
_clearPreload();
|
_clearPreload();
|
||||||
@@ -675,8 +678,11 @@ export async function toggleRadioMode() {
|
|||||||
if(S.radioMode) await fillRadioQueue();
|
if(S.radioMode) await fillRadioQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _radioFilling = false;
|
||||||
|
|
||||||
export async function fillRadioQueue() {
|
export async function fillRadioQueue() {
|
||||||
if(!S.radioMode) return;
|
if (!S.radioMode || _radioFilling) return;
|
||||||
|
_radioFilling = true;
|
||||||
const gen = _queueGen;
|
const gen = _queueGen;
|
||||||
try {
|
try {
|
||||||
const seed = S.queue[S.qIdx];
|
const seed = S.queue[S.qIdx];
|
||||||
@@ -697,21 +703,30 @@ export async function fillRadioQueue() {
|
|||||||
if (gen !== _queueGen) return;
|
if (gen !== _queueGen) return;
|
||||||
if (!newSongs.length) return;
|
if (!newSongs.length) return;
|
||||||
|
|
||||||
// Deduplicate only against the last 50 queued songs so the radio never
|
|
||||||
// stalls on a small library. Songs cycle back after ~50 tracks rather than
|
|
||||||
// blocking forever once they've been seen once.
|
|
||||||
const recentIds = new Set(S.queue.slice(-50).map(s => s.id));
|
const recentIds = new Set(S.queue.slice(-50).map(s => s.id));
|
||||||
const deduped = newSongs.filter(s => !recentIds.has(s.id));
|
const deduped = newSongs.filter(s => !recentIds.has(s.id));
|
||||||
// If every candidate was a recent duplicate (very small library), allow
|
|
||||||
// repeats rather than letting the queue run dry and music stop.
|
|
||||||
newSongs = deduped.length > 0 ? deduped : newSongs.slice(0, 5);
|
|
||||||
|
|
||||||
|
if (deduped.length === 0) {
|
||||||
|
// All similar songs recently played (small library) — try random as final fallback
|
||||||
|
if (gen !== _queueGen) return;
|
||||||
|
const rr = await api('getRandomSongs', '&size=20');
|
||||||
|
const randomFresh = (rr.randomSongs?.song || []).filter(s => !recentIds.has(s.id));
|
||||||
|
// If random is also exhausted, allow repeats to keep music playing
|
||||||
|
newSongs = randomFresh.length > 0 ? randomFresh : newSongs.slice(0, 5);
|
||||||
|
} else {
|
||||||
|
newSongs = deduped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gen !== _queueGen) return;
|
||||||
const prevLen = S.queue.length;
|
const prevLen = S.queue.length;
|
||||||
S.queue.push(...newSongs);
|
S.queue.push(...newSongs);
|
||||||
renderQueue();
|
renderQueue();
|
||||||
// Append-only: never call setQueue here (that resets position to 0).
|
// Append-only: never call setQueue here (that resets position to 0).
|
||||||
S.queue.slice(prevLen).forEach(s => _nsBridge('addToQueue', JSON.stringify(_trackToNative(s)), -1));
|
S.queue.slice(prevLen).forEach(s => _nsBridge('addToQueue', JSON.stringify(_trackToNative(s)), -1));
|
||||||
} catch {}
|
} catch {
|
||||||
|
} finally {
|
||||||
|
_radioFilling = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── PROGRESS & SCRUBBING ─────────────────────────────────────────────────
|
// ─── PROGRESS & SCRUBBING ─────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in new issue
Block a user