From ac1fbd4b3442113ea4efaa523c9c6aeea7fd32c2 Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Fri, 6 Feb 2026 22:12:15 -0500 Subject: [PATCH] fix: progress bar and add missing tracks section - Fix external track detection in progress bar (check for external provider names in ProviderIds) - Add missing tracks section at bottom of Active Playlists tab - Shows all unmatched tracks across all playlists - Includes Map to Local and Map to External buttons for each missing track - Auto-refreshes with other playlist data --- allstarr/Controllers/AdminController.cs | 13 +++- allstarr/wwwroot/index.html | 99 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/allstarr/Controllers/AdminController.cs b/allstarr/Controllers/AdminController.cs index 887b4ce..a537d1e 100644 --- a/allstarr/Controllers/AdminController.cs +++ b/allstarr/Controllers/AdminController.cs @@ -374,12 +374,17 @@ public class AdminController : ControllerBase foreach (var item in cachedPlaylistItems) { - // Check if it's external by looking for ProviderIds (external songs have this) + // Check if it's external by looking for external provider in ProviderIds + // External providers: SquidWTF, Deezer, Qobuz, Tidal var isExternal = false; - if (item.TryGetValue("ProviderIds", out var providerIdsObj) && providerIdsObj != null) + if (item.TryGetValue("ProviderIds", out var providerIdsObj) && providerIdsObj is Dictionary providerIds) { - // Has ProviderIds = external track - isExternal = true; + // Check for external provider keys (not MusicBrainz, ISRC, etc) + isExternal = providerIds.Keys.Any(k => + k.Equals("SquidWTF", StringComparison.OrdinalIgnoreCase) || + k.Equals("Deezer", StringComparison.OrdinalIgnoreCase) || + k.Equals("Qobuz", StringComparison.OrdinalIgnoreCase) || + k.Equals("Tidal", StringComparison.OrdinalIgnoreCase)); } if (isExternal) diff --git a/allstarr/wwwroot/index.html b/allstarr/wwwroot/index.html index 9510c96..7453d25 100644 --- a/allstarr/wwwroot/index.html +++ b/allstarr/wwwroot/index.html @@ -718,6 +718,43 @@ + + +
+

+ Missing Tracks (All Playlists) +
+ +
+

+

+ Tracks that couldn't be matched locally or externally. Map them manually to add them to your playlists. +

+
+
+ Total Missing: + 0 +
+
+ + + + + + + + + + + + + + + +
PlaylistTrackArtistAlbumActions
+ Loading missing tracks... +
+
@@ -1429,6 +1466,66 @@ } } + async function fetchMissingTracks() { + try { + const res = await fetch('/api/admin/playlists'); + const data = await res.json(); + + const tbody = document.getElementById('missing-tracks-table-body'); + const missingTracks = []; + + // Collect all missing tracks from all playlists + for (const playlist of data.playlists) { + if (playlist.externalMissing > 0) { + // Fetch tracks for this playlist + try { + const tracksRes = await fetch(`/api/admin/playlists/${encodeURIComponent(playlist.name)}/tracks`); + const tracksData = await tracksRes.json(); + + // Filter to only missing tracks (isLocal === null) + const missing = tracksData.tracks.filter(t => t.isLocal === null); + missing.forEach(t => { + missingTracks.push({ + playlist: playlist.name, + ...t + }); + }); + } catch (err) { + console.error(`Failed to fetch tracks for ${playlist.name}:`, err); + } + } + } + + // Update summary + document.getElementById('missing-total').textContent = missingTracks.length; + + if (missingTracks.length === 0) { + tbody.innerHTML = '🎉 No missing tracks! All tracks are matched.'; + return; + } + + tbody.innerHTML = missingTracks.map(t => { + return ` + + ${escapeHtml(t.playlist)} + ${escapeHtml(t.title)} + ${escapeHtml(t.artist)} + ${t.album ? escapeHtml(t.album) : '-'} + + + + + + `; + }).join(''); + } catch (error) { + console.error('Failed to fetch missing tracks:', error); + showToast('Failed to fetch missing tracks', 'error'); + } + } + async function fetchConfig() { try { const res = await fetch('/api/admin/config'); @@ -2536,6 +2633,7 @@ fetchStatus(); fetchPlaylists(); fetchTrackMappings(); + fetchMissingTracks(); fetchJellyfinUsers(); fetchJellyfinPlaylists(); fetchConfig(); @@ -2545,6 +2643,7 @@ fetchStatus(); fetchPlaylists(); fetchTrackMappings(); + fetchMissingTracks(); }, 30000);