diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index e16397b7..b5f2612b 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -829,52 +829,74 @@ async function openMirroredPlaylistModal(playlistId) { const tracks = data.tracks || []; const source = data.source || 'unknown'; const sourceRef = getMirroredSourceRef(data); - const sourceIcons = { spotify: '🎵', tidal: '🌊', youtube: '▶', beatport: '🎛' }; + const sourceIcons = { spotify: '🎵', spotify_public: '🎵', tidal: '🌊', youtube: '▶', beatport: '🎛', deezer: '🎧', qobuz: '♫' }; + const sourceLabels = { spotify: 'Spotify', spotify_public: 'Spotify', tidal: 'Tidal', youtube: 'YouTube', beatport: 'Beatport', deezer: 'Deezer', qobuz: 'Qobuz' }; const sourceIcon = sourceIcons[source] || '📋'; + const srcLabel = sourceLabels[source] || source; + + // Hero artwork: playlist cover → first track with art → gradient fallback. + const heroArt = data.image_url || (tracks.find(t => t.image_url) || {}).image_url || ''; + + // Total runtime for the meta line. + const totalMs = tracks.reduce((sum, t) => sum + (t.duration_ms || 0), 0); + const totalMin = Math.round(totalMs / 60000); + const totalLabel = totalMin >= 60 ? `${Math.floor(totalMin / 60)} hr ${totalMin % 60} min` : `${totalMin} min`; const trackRows = tracks.map(t => { const dur = t.duration_ms ? `${Math.floor(t.duration_ms / 60000)}:${String(Math.floor((t.duration_ms % 60000) / 1000)).padStart(2, '0')}` : ''; - return `
- ${t.position} - ${_esc(t.track_name)} - ${_esc(t.artist_name)} - ${_esc(t.album_name)} - ${dur} + const art = t.image_url + ? `` + : ``; + return `
+ ${t.position} + ${art} +
+ ${_esc(t.track_name)} + ${_esc(t.artist_name)} +
+ ${_esc(t.album_name || '')} + ${dur}
`; }).join(''); + const heroCover = heroArt + ? `
` + : `
${sourceIcon}
`; + overlay.innerHTML = `
-
-
-
${sourceIcon}
-
-

${_esc(data.name)}

-
- ${_esc(source)} - ${tracks.length} tracks - · - Mirrored ${timeAgo(data.updated_at || data.mirrored_at)} +
+ ${heroArt ? `
` : ''} +
+ ${heroCover} +
+ Mirrored Playlist +

${_esc(data.name)}

+
+ ${_esc(srcLabel)} + ${data.owner ? `${_esc(data.owner)}·` : ''} + ${tracks.length} tracks + ${totalMs ? `·${totalLabel}` : ''} + · + Mirrored ${timeAgo(data.updated_at || data.mirrored_at)}
- × +
-
-
- #TrackArtistAlbumTime +
+
+ #TitleAlbumTime
- ${trackRows} + ${trackRows || '
No tracks in this mirror yet.
'}
-