From 46a0999ca2936bab113d1d1861ae64bec8375f03 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sun, 24 May 2026 20:23:37 -0700 Subject: [PATCH] Clarify mirrored playlist auto-sync action Rename the manual pipeline button to Auto-Sync and make non-JSON endpoint failures show an actionable restart message instead of a raw JSON parse error. --- webui/static/stats-automations.js | 35 ++++++++++++++++++++++--------- webui/static/style.css | 1 + 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index 6cf81220..62be137b 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -516,7 +516,7 @@ function renderMirroredCard(p, container) { ${disc > 0 ? `` : ''} - + `; @@ -570,6 +570,25 @@ function getMirroredSourceRef(p) { return (p && p.source_playlist_id) ? String(p.source_playlist_id) : ''; } +async function parseMirroredPipelineResponse(res, fallbackMessage) { + const text = await res.text(); + let data = {}; + if (text) { + try { + data = JSON.parse(text); + } catch (_err) { + const detail = res.status === 404 + ? 'Auto-Sync endpoint not found. Restart the SoulSync server so the new backend routes load.' + : fallbackMessage; + throw new Error(detail); + } + } + if (!res.ok || data.error) { + throw new Error(data.error || fallbackMessage); + } + return data; +} + async function editMirroredSourceRef(playlistId, name, source, currentRef) { const label = (source === 'spotify_public' || source === 'youtube') ? 'original playlist URL' @@ -634,12 +653,9 @@ async function runMirroredPlaylistPipeline(playlistId, name) { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); - const data = await res.json(); - if (!res.ok || data.error) { - throw new Error(data.error || 'Failed to start pipeline'); - } + const data = await parseMirroredPipelineResponse(res, 'Failed to start Auto-Sync'); applyMirroredPipelineState(playlistId, data.state || { status: 'running', progress: 0, phase: 'Starting pipeline...' }); - showToast(`Pipeline started for ${name}`, 'success'); + showToast(`Auto-Sync started for ${name}`, 'success'); pollMirroredPipelineStatus(playlistId, name); } catch (err) { showToast(`Error: ${err.message}`, 'error'); @@ -653,14 +669,13 @@ function pollMirroredPipelineStatus(playlistId, name) { const tick = async () => { try { const res = await fetch(`/api/mirrored-playlists/${playlistId}/pipeline/status`); - const state = await res.json(); - if (!res.ok || state.error) throw new Error(state.error || 'Failed to read pipeline status'); + const state = await parseMirroredPipelineResponse(res, 'Failed to read Auto-Sync status'); applyMirroredPipelineState(playlistId, state); if (state.status === 'finished') { clearInterval(mirroredPipelinePollers[key]); delete mirroredPipelinePollers[key]; - showToast(`Pipeline complete for ${name}`, 'success'); + showToast(`Auto-Sync complete for ${name}`, 'success'); loadMirroredPlaylists(); } else if (state.status === 'error' || state.status === 'skipped') { clearInterval(mirroredPipelinePollers[key]); @@ -990,7 +1005,7 @@ async function openMirroredPlaylistModal(playlistId) { diff --git a/webui/static/style.css b/webui/static/style.css index d633399a..0fa9218b 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -12056,6 +12056,7 @@ body.helper-mode-active #dashboard-activity-feed:hover { font-size: 11px; font-weight: 700; height: 24px; + min-width: 76px; padding: 0 10px; transition: all 0.2s ease; flex-shrink: 0;