diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index 61eff33e..500606e3 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -686,6 +686,13 @@ function exportMirroredPlaylist(playlistId, name) { overlay.querySelectorAll('.pl-export-choice').forEach(btn => { btn.addEventListener('click', () => { const mode = btn.dataset.mode; + // Gated service button (not connected) → nudge to Settings instead of a doomed export. + if (btn.dataset.disconnected) { + const dest = mode[0].toUpperCase() + mode.slice(1); + overlay.remove(); + _setExportStatus(playlistId, `Connect ${dest} in Settings → Connections to export here`, 9000); + return; + } const bfEl = overlay.querySelector('#pl-export-backfill'); const backfill = !!(bfEl && bfEl.checked); overlay.remove(); @@ -693,6 +700,21 @@ function exportMirroredPlaylist(playlistId, name) { }); }); document.body.appendChild(overlay); + + // Gate Spotify/Deezer on connection (cheap token/ARL check — no live verify). Disconnected + // services grey out + nudge to Settings rather than letting the export fail with "not connected". + fetch('/api/discover/your-albums/sources').then(r => r.json()).then(data => { + const connected = (data && data.connected) || []; + overlay.querySelectorAll('.pl-export-choice[data-mode]').forEach(btn => { + const m = btn.dataset.mode; + if ((m === 'spotify' || m === 'deezer') && !connected.includes(m)) { + btn.dataset.disconnected = '1'; + btn.style.opacity = '0.5'; + const hint = btn.querySelector('div:last-child'); + if (hint) hint.innerHTML = `Not connected — set up ${m[0].toUpperCase() + m.slice(1)} in Settings → Connections first.`; + } + }); + }).catch(() => {}); } async function _startPlaylistExport(playlistId, mode, name, backfill) {