From 2155a287a52c27f880481627a023ff908c6d57f5 Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Thu, 5 Feb 2026 10:20:31 -0500 Subject: [PATCH] Add manual mapping indicators and search button for missing tracks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Manual mappings now show a blue 'Manual' badge next to the track status - Added search button (🔍) for missing tracks to help find them - Backend now returns isManualMapping, manualMappingType, and manualMappingId - Frontend displays manual mapping indicators for both local and external tracks - Missing tracks now show a search link to help locate them on SquidWTF --- allstarr/Controllers/AdminController.cs | 14 +++++++++++++- allstarr/wwwroot/index.html | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/allstarr/Controllers/AdminController.cs b/allstarr/Controllers/AdminController.cs index 164e2d8..bc3878b 100644 --- a/allstarr/Controllers/AdminController.cs +++ b/allstarr/Controllers/AdminController.cs @@ -519,11 +519,17 @@ public class AdminController : ControllerBase // FIRST: Check for manual mapping (same as SpotifyTrackMatchingService) var manualMappingKey = $"spotify:manual-map:{decodedName}:{track.SpotifyId}"; var manualJellyfinId = await _cache.GetAsync(manualMappingKey); + bool isManualMapping = false; + string? manualMappingType = null; + string? manualMappingId = null; if (!string.IsNullOrEmpty(manualJellyfinId)) { // Manual Jellyfin mapping exists - this track is definitely local isLocal = true; + isManualMapping = true; + manualMappingType = "jellyfin"; + manualMappingId = manualJellyfinId; _logger.LogDebug("✓ Manual Jellyfin mapping found for {Title}: Jellyfin ID {Id}", track.Title, manualJellyfinId); } @@ -558,6 +564,9 @@ public class AdminController : ControllerBase // External manual mapping exists isLocal = false; externalProvider = provider; + isManualMapping = true; + manualMappingType = "external"; + manualMappingId = externalId; _logger.LogDebug("✓ Manual external mapping found for {Title}: {Provider} {ExternalId}", track.Title, provider, externalId); } @@ -624,7 +633,10 @@ public class AdminController : ControllerBase albumArtUrl = track.AlbumArtUrl, isLocal = isLocal, externalProvider = externalProvider, - searchQuery = isLocal == false ? $"{track.Title} {track.PrimaryArtist}" : null + searchQuery = isLocal == false ? $"{track.Title} {track.PrimaryArtist}" : null, + isManualMapping = isManualMapping, + manualMappingType = manualMappingType, + manualMappingId = manualMappingId }); } diff --git a/allstarr/wwwroot/index.html b/allstarr/wwwroot/index.html index f9c70b1..bc882b2 100644 --- a/allstarr/wwwroot/index.html +++ b/allstarr/wwwroot/index.html @@ -1788,9 +1788,17 @@ if (t.isLocal === true) { statusBadge = 'Local'; + // Add manual mapping indicator for local tracks + if (t.isManualMapping && t.manualMappingType === 'jellyfin') { + statusBadge += 'Manual'; + } } else if (t.isLocal === false) { const provider = t.externalProvider || 'External'; statusBadge = `${escapeHtml(provider)}`; + // Add manual mapping indicator for external tracks + if (t.isManualMapping && t.manualMappingType === 'external') { + statusBadge += 'Manual'; + } // Add manual map button for external tracks using data attributes const firstArtist = (t.artists && t.artists.length > 0) ? t.artists[0] : ''; mapButton = `