diff --git a/webui/static/script.js b/webui/static/script.js index d49a306e..2811f349 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -69,6 +69,7 @@ let artistsPageState = { // --- Artist Downloads Management State --- let artistDownloadBubbles = {}; // Track artist download bubbles: artistId -> { artist, downloads: [], element } let artistDownloadModalOpen = false; // Track if artist download modal is open +let downloadsUpdateTimeout = null; // Debounce downloads section updates let artistsSearchTimeout = null; let artistsSearchController = null; @@ -5267,6 +5268,7 @@ window.formatArtists = formatArtists; window.closeArtistDownloadModal = closeArtistDownloadModal; window.openArtistDownloadProcess = openArtistDownloadProcess; window.bulkCompleteArtistDownloads = bulkCompleteArtistDownloads; +window.refreshAllArtistDownloadStatuses = refreshAllArtistDownloadStatuses; // APPEND THIS JAVASCRIPT SNIPPET (B) @@ -10401,12 +10403,24 @@ function registerArtistDownload(artist, album, virtualPlaylistId, albumType) { artistDownloadBubbles[artistId].downloads.push(downloadInfo); // Show/update the artist downloads section - showArtistDownloadsSection(); + updateArtistDownloadsSection(); // Monitor this download for completion monitorArtistDownload(artistId, virtualPlaylistId); } +/** + * Debounced update for artist downloads section to prevent rapid updates + */ +function updateArtistDownloadsSection() { + if (downloadsUpdateTimeout) { + clearTimeout(downloadsUpdateTimeout); + } + downloadsUpdateTimeout = setTimeout(() => { + showArtistDownloadsSection(); + }, 300); // 300ms debounce +} + /** * Show or update the artist downloads section in search state */ @@ -10482,6 +10496,15 @@ function createArtistBubbleCard(artistBubbleData) { const completedCount = downloads.filter(d => d.status === 'view_results').length; const allCompleted = activeCount === 0 && completedCount > 0; + // Debug logging for bubble card creation + console.log(`🔵 Creating bubble for ${artist.name}:`, { + totalDownloads: downloads.length, + activeCount, + completedCount, + allCompleted, + downloadStatuses: downloads.map(d => `${d.album.name}: ${d.status}`) + }); + const imageUrl = artist.image_url || ''; const backgroundStyle = imageUrl ? `background-image: url('${imageUrl}');` : @@ -10530,9 +10553,17 @@ function monitorArtistDownload(artistId, virtualPlaylistId) { if (process.status === 'complete' && download.status === 'in_progress') { download.status = 'view_results'; console.log(`✅ Download completed for ${artistDownloadBubbles[artistId].artist.name} - ${download.album.name}`); + console.log(`📊 Artist ${artistId} downloads status:`, artistDownloadBubbles[artistId].downloads.map(d => `${d.album.name}: ${d.status}`)); // Update the downloads section - showArtistDownloadsSection(); + updateArtistDownloadsSection(); + + // Check if all downloads for this artist are now completed + const artistDownloads = artistDownloadBubbles[artistId].downloads; + const allCompleted = artistDownloads.every(d => d.status === 'view_results'); + if (allCompleted) { + console.log(`🟢 All downloads completed for ${artistDownloadBubbles[artistId].artist.name} - green checkmark should appear`); + } } // Continue monitoring if still active @@ -10637,7 +10668,11 @@ function startArtistDownloadModalMonitoring(artistId) { itemsContainer.innerHTML = activeDownloads.map((download, index) => { const process = activeDownloadProcesses[download.virtualPlaylistId]; if (process) { - download.status = process.status === 'complete' ? 'view_results' : 'in_progress'; + const newStatus = process.status === 'complete' ? 'view_results' : 'in_progress'; + if (download.status !== newStatus) { + console.log(`🔄 Modal: Updating ${download.album.name} status from ${download.status} to ${newStatus}`); + download.status = newStatus; + } } return createArtistDownloadItem(download, index); }).join(''); @@ -10685,17 +10720,31 @@ function bulkCompleteArtistDownloads(artistId) { console.log(`🎯 Bulk completing downloads for artist: ${artistId}`); const artistBubbleData = artistDownloadBubbles[artistId]; - if (!artistBubbleData) return; + if (!artistBubbleData) { + console.warn(`❌ No artist bubble data found for ${artistId}`); + return; + } // Find all downloads in 'view_results' state const completedDownloads = artistBubbleData.downloads.filter(d => d.status === 'view_results'); + console.log(`📋 Found ${completedDownloads.length} completed downloads to close:`, + completedDownloads.map(d => d.album.name)); + + if (completedDownloads.length === 0) { + console.warn(`⚠️ No completed downloads found for bulk close`); + showToast('No completed downloads to close', 'info'); + return; + } // Programmatically close all completed modals completedDownloads.forEach(download => { const process = activeDownloadProcesses[download.virtualPlaylistId]; if (process && process.modalElement) { + console.log(`🗑️ Closing modal for: ${download.album.name}`); // Trigger the close function which handles cleanup closeDownloadMissingModal(download.virtualPlaylistId); + } else { + console.warn(`⚠️ No active process or modal found for: ${download.album.name}`); } }); @@ -10724,12 +10773,43 @@ function cleanupArtistDownload(virtualPlaylistId) { } // Update the downloads section - showArtistDownloadsSection(); + updateArtistDownloadsSection(); break; } } } +/** + * Force refresh all artist download statuses (useful for debugging) + */ +function refreshAllArtistDownloadStatuses() { + console.log('🔄 Force refreshing all artist download statuses...'); + + for (const artistId in artistDownloadBubbles) { + const artistData = artistDownloadBubbles[artistId]; + let hasChanges = false; + + artistData.downloads.forEach(download => { + const process = activeDownloadProcesses[download.virtualPlaylistId]; + if (process) { + const expectedStatus = process.status === 'complete' ? 'view_results' : 'in_progress'; + if (download.status !== expectedStatus) { + console.log(`🔧 Fixing status for ${download.album.name}: ${download.status} → ${expectedStatus}`); + download.status = expectedStatus; + hasChanges = true; + } + } + }); + + if (hasChanges) { + console.log(`✅ Updated statuses for ${artistData.artist.name}`); + } + } + + // Force update the downloads section + showArtistDownloadsSection(); +} + /** * Extract dominant colors from an image for dynamic glow effects */ diff --git a/webui/static/style.css b/webui/static/style.css index a0c5e5b6..34b8fb43 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -6589,9 +6589,10 @@ body { .artist-bubble-container { display: flex; flex-wrap: wrap; - gap: 16px; + gap: 24px; justify-content: center; align-items: center; + padding: 12px; } /* Artist Bubble Card */ @@ -6602,7 +6603,6 @@ body { border-radius: 50%; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - overflow: hidden; background: linear-gradient(135deg, rgba(26, 26, 26, 0.95) 0%, @@ -6646,6 +6646,7 @@ body { background-position: center; background-repeat: no-repeat; border-radius: 50%; + overflow: hidden; } .artist-bubble-overlay {