diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index e21fb3ce..81e81ca2 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -663,11 +663,19 @@ function exportMirroredPlaylist(playlistId, name) {
Sync to ListenBrainz
Create the playlist directly on your ListenBrainz account (needs your LB token).
- -
Each track is matched to its MusicBrainz recording ID (cached → library → file tag → MusicBrainz). Tracks without an ID can't be included — you'll see how many matched.
+ + +
Tracks are matched by ID (MusicBrainz for ListenBrainz/JSPF; the stored Spotify/Deezer ID for those). Tracks without a match can't be included — you'll see how many made it. Renaming/re-syncing can reset play counts on the destination.
`; overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); }); @@ -684,9 +692,14 @@ function exportMirroredPlaylist(playlistId, name) { async function _startPlaylistExport(playlistId, mode, name) { _setExportStatus(playlistId, `Starting export…`); try { - const resp = await fetch(`/api/playlists/${playlistId}/export/listenbrainz`, { + // Spotify/Deezer go to the service endpoint; ListenBrainz/JSPF keep the LB one. + const isService = (mode === 'spotify' || mode === 'deezer'); + const url = isService + ? `/api/playlists/${playlistId}/export/service/${mode}` + : `/api/playlists/${playlistId}/export/listenbrainz`; + const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ mode }), + body: isService ? '{}' : JSON.stringify({ mode }), }); const data = await resp.json(); if (!data.success || !data.job_id) { @@ -709,8 +722,19 @@ async function _pollPlaylistExport(jobId, playlistId, mode, name) { const pct = job.total ? Math.round(100 * (job.done || 0) / job.total) : 0; _setExportStatus(playlistId, `Matching ${job.done || 0}/${job.total || 0} (${pct}%)${st.resolved != null ? ` · ${st.resolved} matched` : ''}`); } else if (job.phase === 'pushing') { - _setExportStatus(playlistId, `Pushing to ListenBrainz…`); + const dest = (mode === 'spotify' || mode === 'deezer') ? (mode[0].toUpperCase() + mode.slice(1)) : 'ListenBrainz'; + _setExportStatus(playlistId, `Pushing to ${dest}…`); } else if (job.phase === 'done') { + // Spotify/Deezer export: report added + unmatched and link the new playlist. + if (mode === 'spotify' || mode === 'deezer') { + const dest = mode[0].toUpperCase() + mode.slice(1); + const push = job.push || {}; + const cov = `${st.resolved || 0} added${st.unmatched ? ` · ${st.unmatched} not on ${dest}` : ''}`; + const link = push.url ? ` open` : ''; + _setExportStatus(playlistId, `Exported to ${dest} · ${cov}${link}`, 12000); + if (typeof showToast === 'function') showToast(`Playlist exported to ${dest} (${cov})`, 'success'); + return; + } const sum = job.summary || {}; const cov = `${sum.included || 0}/${sum.total || 0} matched${sum.skipped ? ` · ${sum.skipped} unmatched` : ''}`; if (mode === 'download') {