Add manual mapping indicators and search button for missing tracks

- 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
This commit is contained in:
2026-02-05 10:20:31 -05:00
parent cb57b406c1
commit 2155a287a5
2 changed files with 22 additions and 1 deletions

View File

@@ -519,11 +519,17 @@ public class AdminController : ControllerBase
// FIRST: Check for manual mapping (same as SpotifyTrackMatchingService) // FIRST: Check for manual mapping (same as SpotifyTrackMatchingService)
var manualMappingKey = $"spotify:manual-map:{decodedName}:{track.SpotifyId}"; var manualMappingKey = $"spotify:manual-map:{decodedName}:{track.SpotifyId}";
var manualJellyfinId = await _cache.GetAsync<string>(manualMappingKey); var manualJellyfinId = await _cache.GetAsync<string>(manualMappingKey);
bool isManualMapping = false;
string? manualMappingType = null;
string? manualMappingId = null;
if (!string.IsNullOrEmpty(manualJellyfinId)) if (!string.IsNullOrEmpty(manualJellyfinId))
{ {
// Manual Jellyfin mapping exists - this track is definitely local // Manual Jellyfin mapping exists - this track is definitely local
isLocal = true; isLocal = true;
isManualMapping = true;
manualMappingType = "jellyfin";
manualMappingId = manualJellyfinId;
_logger.LogDebug("✓ Manual Jellyfin mapping found for {Title}: Jellyfin ID {Id}", _logger.LogDebug("✓ Manual Jellyfin mapping found for {Title}: Jellyfin ID {Id}",
track.Title, manualJellyfinId); track.Title, manualJellyfinId);
} }
@@ -558,6 +564,9 @@ public class AdminController : ControllerBase
// External manual mapping exists // External manual mapping exists
isLocal = false; isLocal = false;
externalProvider = provider; externalProvider = provider;
isManualMapping = true;
manualMappingType = "external";
manualMappingId = externalId;
_logger.LogDebug("✓ Manual external mapping found for {Title}: {Provider} {ExternalId}", _logger.LogDebug("✓ Manual external mapping found for {Title}: {Provider} {ExternalId}",
track.Title, provider, externalId); track.Title, provider, externalId);
} }
@@ -624,7 +633,10 @@ public class AdminController : ControllerBase
albumArtUrl = track.AlbumArtUrl, albumArtUrl = track.AlbumArtUrl,
isLocal = isLocal, isLocal = isLocal,
externalProvider = externalProvider, externalProvider = externalProvider,
searchQuery = isLocal == false ? $"{track.Title} {track.PrimaryArtist}" : null searchQuery = isLocal == false ? $"{track.Title} {track.PrimaryArtist}" : null,
isManualMapping = isManualMapping,
manualMappingType = manualMappingType,
manualMappingId = manualMappingId
}); });
} }

View File

@@ -1788,9 +1788,17 @@
if (t.isLocal === true) { if (t.isLocal === true) {
statusBadge = '<span class="status-badge success" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>Local</span>'; statusBadge = '<span class="status-badge success" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>Local</span>';
// Add manual mapping indicator for local tracks
if (t.isManualMapping && t.manualMappingType === 'jellyfin') {
statusBadge += '<span class="status-badge" style="font-size:0.75rem;padding:2px 8px;margin-left:4px;background:var(--info);color:white;"><span class="status-dot" style="background:white;"></span>Manual</span>';
}
} else if (t.isLocal === false) { } else if (t.isLocal === false) {
const provider = t.externalProvider || 'External'; const provider = t.externalProvider || 'External';
statusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(provider)}</span>`; statusBadge = `<span class="status-badge warning" style="font-size:0.75rem;padding:2px 8px;margin-left:8px;"><span class="status-dot"></span>${escapeHtml(provider)}</span>`;
// Add manual mapping indicator for external tracks
if (t.isManualMapping && t.manualMappingType === 'external') {
statusBadge += '<span class="status-badge" style="font-size:0.75rem;padding:2px 8px;margin-left:4px;background:var(--info);color:white;"><span class="status-dot" style="background:white;"></span>Manual</span>';
}
// Add manual map button for external tracks using data attributes // Add manual map button for external tracks using data attributes
const firstArtist = (t.artists && t.artists.length > 0) ? t.artists[0] : ''; const firstArtist = (t.artists && t.artists.length > 0) ? t.artists[0] : '';
mapButton = `<button class="small map-track-btn" mapButton = `<button class="small map-track-btn"
@@ -1832,6 +1840,7 @@
${t.album ? escapeHtml(t.album) : ''} ${t.album ? escapeHtml(t.album) : ''}
${t.isrc ? '<br><small>ISRC: ' + t.isrc + '</small>' : ''} ${t.isrc ? '<br><small>ISRC: ' + t.isrc + '</small>' : ''}
${t.isLocal === false && t.searchQuery && t.externalProvider ? '<br><small style="color:var(--accent)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.searchQuery) + '\', \'' + escapeJs(t.externalProvider) + '\'); return false;" style="color:var(--accent);text-decoration:underline;">🔍 Search: ' + escapeHtml(t.searchQuery) + '</a></small>' : ''} ${t.isLocal === false && t.searchQuery && t.externalProvider ? '<br><small style="color:var(--accent)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.searchQuery) + '\', \'' + escapeJs(t.externalProvider) + '\'); return false;" style="color:var(--accent);text-decoration:underline;">🔍 Search: ' + escapeHtml(t.searchQuery) + '</a></small>' : ''}
${t.isLocal === null && t.searchQuery ? '<br><small style="color:var(--text-secondary)"><a href="#" onclick="searchProvider(\'' + escapeJs(t.title + ' ' + (t.artists && t.artists.length > 0 ? t.artists[0] : '')) + '\', \'SquidWTF\'); return false;" style="color:var(--text-secondary);text-decoration:underline;">🔍 Search: ' + escapeHtml(t.title + ' ' + (t.artists && t.artists.length > 0 ? t.artists[0] : '')) + '</a></small>' : ''}
</div> </div>
</div> </div>
`; `;