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
+25
-10
+25
-10
@@ -243,9 +243,11 @@ window.NSPlayer = {
|
||||
setPlayIcon(false);
|
||||
if (S.radioMode) {
|
||||
const prevLen = S.queue.length;
|
||||
// fillRadioQueue sends addToQueue to native internally — only trigger play here.
|
||||
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) {
|
||||
_queueGen++;
|
||||
_radioFilling = false; // cancel any in-flight radio fill for the old queue
|
||||
S.queue = songs.map(s => ({...s}));
|
||||
S.qIdx = startIdx;
|
||||
_clearPreload();
|
||||
@@ -675,8 +678,11 @@ export async function toggleRadioMode() {
|
||||
if(S.radioMode) await fillRadioQueue();
|
||||
}
|
||||
|
||||
let _radioFilling = false;
|
||||
|
||||
export async function fillRadioQueue() {
|
||||
if(!S.radioMode) return;
|
||||
if (!S.radioMode || _radioFilling) return;
|
||||
_radioFilling = true;
|
||||
const gen = _queueGen;
|
||||
try {
|
||||
const seed = S.queue[S.qIdx];
|
||||
@@ -697,21 +703,30 @@ export async function fillRadioQueue() {
|
||||
if (gen !== _queueGen) 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 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;
|
||||
S.queue.push(...newSongs);
|
||||
renderQueue();
|
||||
// Append-only: never call setQueue here (that resets position to 0).
|
||||
S.queue.slice(prevLen).forEach(s => _nsBridge('addToQueue', JSON.stringify(_trackToNative(s)), -1));
|
||||
} catch {}
|
||||
} catch {
|
||||
} finally {
|
||||
_radioFilling = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── PROGRESS & SCRUBBING ─────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user