From 8fad6d8c4eba5a50c5daab46c058b1b6dc3e69fe Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Wed, 4 Feb 2026 19:35:34 -0500 Subject: [PATCH] Fix manual mapping detection in Active Playlists tab --- allstarr/Controllers/AdminController.cs | 14 +++++- allstarr/wwwroot/index.html | 57 +++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/allstarr/Controllers/AdminController.cs b/allstarr/Controllers/AdminController.cs index 0e06227..bf09ec4 100644 --- a/allstarr/Controllers/AdminController.cs +++ b/allstarr/Controllers/AdminController.cs @@ -494,8 +494,20 @@ public class AdminController : ControllerBase { var isLocal = false; - if (localTracks.Count > 0) + // FIRST: Check for manual mapping (same as SpotifyTrackMatchingService) + var manualMappingKey = $"spotify:manual-map:{decodedName}:{track.SpotifyId}"; + var manualJellyfinId = await _cache.GetAsync(manualMappingKey); + + if (!string.IsNullOrEmpty(manualJellyfinId)) { + // Manual mapping exists - this track is definitely local + isLocal = true; + _logger.LogDebug("✓ Manual mapping found for {Title}: Jellyfin ID {Id}", + track.Title, manualJellyfinId); + } + else if (localTracks.Count > 0) + { + // SECOND: No manual mapping, try fuzzy matching var bestMatch = localTracks .Select(local => new { diff --git a/allstarr/wwwroot/index.html b/allstarr/wwwroot/index.html index 51f9414..f7163f1 100644 --- a/allstarr/wwwroot/index.html +++ b/allstarr/wwwroot/index.html @@ -286,6 +286,8 @@ .toast.success { border-color: var(--success); } .toast.error { border-color: var(--error); } + .toast.warning { border-color: var(--warning); } + .toast.info { border-color: var(--accent); } @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } @@ -1018,12 +1020,12 @@ }); // Toast notification - function showToast(message, type = 'success') { + function showToast(message, type = 'success', duration = 3000) { const toast = document.createElement('div'); toast.className = 'toast ' + type; toast.textContent = message; document.body.appendChild(toast); - setTimeout(() => toast.remove(), 3000); + setTimeout(() => toast.remove(), duration); } // Modal helpers @@ -2063,9 +2065,17 @@ const data = await res.json(); if (res.ok) { - showToast('✓ Track mapped successfully', 'success'); + showToast('✓ Track mapped successfully - rebuilding playlist...', 'success'); closeModal('manual-map-modal'); + // Show rebuilding indicator + showPlaylistRebuildingIndicator(playlistName); + + // Show detailed info toast after a moment + setTimeout(() => { + showToast('🔄 Searching external providers to rebuild playlist with your manual mapping...', 'info', 8000); + }, 1000); + // Update the track in the UI without refreshing if (data.track) { const trackItem = document.querySelector(`.track-item[data-position="${position}"]`); @@ -2116,6 +2126,47 @@ } } + function showPlaylistRebuildingIndicator(playlistName) { + // Find the playlist in the UI and show rebuilding state + const playlistCards = document.querySelectorAll('.playlist-card'); + for (const card of playlistCards) { + const nameEl = card.querySelector('h3'); + if (nameEl && nameEl.textContent.trim() === playlistName) { + // Add rebuilding indicator + const existingIndicator = card.querySelector('.rebuilding-indicator'); + if (!existingIndicator) { + const indicator = document.createElement('div'); + indicator.className = 'rebuilding-indicator'; + indicator.style.cssText = ` + position: absolute; + top: 8px; + right: 8px; + background: var(--warning); + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 0.7rem; + font-weight: 500; + display: flex; + align-items: center; + gap: 4px; + z-index: 10; + `; + indicator.innerHTML = 'Rebuilding...'; + card.style.position = 'relative'; + card.appendChild(indicator); + + // Auto-remove after 30 seconds and refresh + setTimeout(() => { + indicator.remove(); + fetchPlaylists(); // Refresh to get updated counts + }, 30000); + } + break; + } + } + } + function escapeJs(text) { if (!text) return ''; return text.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"');