Fix Lyria silent failures and surface generation status in the player.
Robust candidate-part parsing, quota-aware errors, live composing feedback, and two new desert dub tracks in the library. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+54
-4
@@ -267,7 +267,10 @@
|
||||
font-size: 0.75rem;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.status.generating { color: var(--accent); }
|
||||
.status.error { color: #ff5c7a; }
|
||||
.stack {
|
||||
font-size: 0.7rem;
|
||||
color: #555568;
|
||||
@@ -518,6 +521,7 @@
|
||||
let currentTrackId = null;
|
||||
let radioSettings = { playback: { shuffle: true, mix_existing_and_new: true, new_song_chance: 0.35 } };
|
||||
let savingSettings = false;
|
||||
let lastGenError = null;
|
||||
|
||||
function fmtUsd(n) {
|
||||
return '$' + Number(n || 0).toFixed(2);
|
||||
@@ -539,6 +543,34 @@
|
||||
loadLibrary();
|
||||
}
|
||||
|
||||
function applyGenerationState(gen) {
|
||||
if (!gen) return;
|
||||
if (gen.busy) {
|
||||
genBtn.disabled = true;
|
||||
const title = gen.track_title ? ` "${gen.track_title}"` : '';
|
||||
const phase = gen.phase === 'planning'
|
||||
? `DeepSeek planning${title}…`
|
||||
: `Lyria composing${title}… (~30s)`;
|
||||
statusEl.textContent = phase;
|
||||
statusEl.classList.add('generating');
|
||||
statusEl.classList.remove('error');
|
||||
return;
|
||||
}
|
||||
genBtn.disabled = false;
|
||||
statusEl.classList.remove('generating');
|
||||
if (gen.error) {
|
||||
statusEl.textContent = gen.error;
|
||||
statusEl.classList.add('error');
|
||||
if (gen.error !== lastGenError) {
|
||||
lastGenError = gen.error;
|
||||
addBubble('dj', `Couldn't finish that track: ${gen.error}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
lastGenError = null;
|
||||
statusEl.classList.remove('error');
|
||||
}
|
||||
|
||||
function updateDashboard(stats) {
|
||||
if (!stats) return;
|
||||
const today = stats.today || {};
|
||||
@@ -552,6 +584,7 @@
|
||||
radioSettings.playback = stats.playback;
|
||||
syncSettingsUI(stats.playback);
|
||||
}
|
||||
applyGenerationState(stats.generation);
|
||||
updateModeBadge();
|
||||
}
|
||||
|
||||
@@ -568,11 +601,12 @@
|
||||
skipBtn.textContent = shuffle ? 'Shuffle next' : 'Skip';
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
async function loadStats(returnData = false) {
|
||||
try {
|
||||
const res = await fetch(`${API}/api/stats`);
|
||||
const data = await res.json();
|
||||
updateDashboard(data);
|
||||
return returnData ? data : undefined;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -704,7 +738,8 @@
|
||||
const data = await res.json();
|
||||
if (data.reply) addBubble('dj', data.reply);
|
||||
if (data.generating) {
|
||||
statusEl.textContent = 'DJ is composing your request…';
|
||||
statusEl.textContent = 'DeepSeek planning your request…';
|
||||
statusEl.classList.add('generating');
|
||||
pollForNewTrack();
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -722,10 +757,17 @@
|
||||
clearInterval(pollTimer);
|
||||
pollTimer = setInterval(async () => {
|
||||
n += 1;
|
||||
const stats = await loadStats(true);
|
||||
if (stats?.generation?.busy) return;
|
||||
if (stats?.generation?.error) {
|
||||
clearInterval(pollTimer);
|
||||
return;
|
||||
}
|
||||
await refreshNow();
|
||||
await loadStats();
|
||||
const now = await fetch(`${API}/api/now`).then(r => r.json()).catch(() => null);
|
||||
if (now?.track?.track_id !== currentTrackId) clearInterval(pollTimer);
|
||||
if (n > 60) clearInterval(pollTimer);
|
||||
}, 5000);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
async function refreshNow() {
|
||||
@@ -756,6 +798,10 @@
|
||||
} else if (data.status === 'limit') {
|
||||
statusEl.textContent = data.message || 'Daily limit reached';
|
||||
updateDashboard({ today: data.budget, costs: radioSettings.costs });
|
||||
} else if (data.status === 'error') {
|
||||
statusEl.textContent = data.message || 'Generation failed';
|
||||
statusEl.classList.add('error');
|
||||
applyGenerationState(data.generation || { error: data.message });
|
||||
} else if (data.status === 'ok') {
|
||||
if (data.track) applyTrack(data.track, data.source || 'generated');
|
||||
await loadStats();
|
||||
@@ -780,8 +826,12 @@
|
||||
statusEl.textContent = data.message || 'Daily limit — no more new songs today';
|
||||
} else if (data.status === 'busy') {
|
||||
statusEl.textContent = data.message;
|
||||
statusEl.classList.add('generating');
|
||||
pollForNewTrack();
|
||||
return;
|
||||
} else if (data.status === 'error') {
|
||||
statusEl.textContent = data.message || 'Generation failed';
|
||||
statusEl.classList.add('error');
|
||||
} else if (data.status === 'ok' && data.track) {
|
||||
applyTrack(data.track, data.source);
|
||||
} else if (data.status === 'idle') {
|
||||
|
||||
|
Before
After
|
Reference in New Issue
Block a user