From 810247ba8c68b9d6f437eb8ad17d0e38eda32349 Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Fri, 6 Feb 2026 11:18:48 -0500 Subject: [PATCH] Add manual track mappings display to web UI - Shows all manual mappings in Active Playlists tab - Displays summary counts (total, jellyfin, external) - Table shows playlist, Spotify ID, type, target, and creation date - Color-coded badges for jellyfin vs external mappings - Auto-refreshes every 30 seconds - Helps review mappings before phasing out local ones --- allstarr/wwwroot/index.html | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/allstarr/wwwroot/index.html b/allstarr/wwwroot/index.html index f91f22f..26e33d8 100644 --- a/allstarr/wwwroot/index.html +++ b/allstarr/wwwroot/index.html @@ -676,6 +676,51 @@ + + +
+

+ Manual Track Mappings +
+ +
+

+

+ Manual mappings override automatic matching. Local (Jellyfin) mappings will be phased out in favor of the Spotify Import plugin. +

+
+
+ Total: + 0 +
+
+ Jellyfin (Local): + 0 +
+
+ External: + 0 +
+
+ + + + + + + + + + + + + + + +
PlaylistSpotify IDTypeTargetCreated
+ Loading mappings... +
+
@@ -1337,6 +1382,52 @@ } } + async function fetchTrackMappings() { + try { + const res = await fetch('/api/admin/mappings/tracks'); + const data = await res.json(); + + // Update summary + document.getElementById('mappings-total').textContent = data.totalCount || 0; + document.getElementById('mappings-jellyfin').textContent = data.jellyfinCount || 0; + document.getElementById('mappings-external').textContent = data.externalCount || 0; + + const tbody = document.getElementById('mappings-table-body'); + + if (data.mappings.length === 0) { + tbody.innerHTML = 'No manual mappings found.'; + return; + } + + tbody.innerHTML = data.mappings.map(m => { + const typeColor = m.type === 'jellyfin' ? 'var(--accent)' : 'var(--success)'; + const typeBadge = `${m.type}`; + + let targetDisplay = ''; + if (m.type === 'jellyfin') { + targetDisplay = `${m.jellyfinId}`; + } else { + targetDisplay = `${m.externalProvider}/${m.externalId}`; + } + + const createdDate = m.createdAt ? new Date(m.createdAt).toLocaleString() : '-'; + + return ` + + ${escapeHtml(m.playlist)} + ${m.spotifyId} + ${typeBadge} + ${targetDisplay} + ${createdDate} + + `; + }).join(''); + } catch (error) { + console.error('Failed to fetch track mappings:', error); + showToast('Failed to fetch track mappings', 'error'); + } + } + async function fetchConfig() { try { const res = await fetch('/api/admin/config'); @@ -2500,6 +2591,7 @@ // Initial load fetchStatus(); fetchPlaylists(); + fetchTrackMappings(); fetchJellyfinUsers(); fetchJellyfinPlaylists(); fetchConfig(); @@ -2508,6 +2600,7 @@ setInterval(() => { fetchStatus(); fetchPlaylists(); + fetchTrackMappings(); }, 30000);