From b90c270d54dd67b073bc26139147b0217f203536 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sat, 7 Mar 2026 00:11:15 -0800 Subject: [PATCH] Add Download Now button to wishlist modal and library page download bubbles --- webui/index.html | 4 ++ webui/static/script.js | 103 +++++++++++++++++++++++++++++++++++++++++ webui/static/style.css | 18 ++++++- 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/webui/index.html b/webui/index.html index 806a954c..c564b839 100644 --- a/webui/index.html +++ b/webui/index.html @@ -4144,6 +4144,10 @@ onclick="closeAddToWishlistModal()"> Close + diff --git a/webui/static/script.js b/webui/static/script.js index 463ad745..c7e82a13 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -13440,6 +13440,46 @@ function closeAddToWishlistModal() { } } +/** + * Handle "Download Now" button click from the Add to Wishlist modal. + * Captures modal data, closes the wishlist modal, then opens the download missing tracks modal. + */ +async function handleWishlistDownloadNow() { + if (!currentWishlistModalData) { + showToast('No album data available', 'error'); + return; + } + + // Capture data before closeAddToWishlistModal clears it + const { album, artist, tracks, albumType } = currentWishlistModalData; + + // Close the wishlist modal + closeAddToWishlistModal(); + + // Build virtual playlist ID and name (same pattern as createArtistAlbumVirtualPlaylist) + const virtualPlaylistId = `artist_album_${artist.id}_${album.id}`; + const playlistName = `[${artist.name}] ${album.name}`; + + // If a download process already exists for this album, just show the existing modal + if (activeDownloadProcesses[virtualPlaylistId]) { + const process = activeDownloadProcesses[virtualPlaylistId]; + if (process.modalElement) { + process.modalElement.style.display = 'flex'; + } + return; + } + + // Open download missing modal (reuses existing function) + showLoadingOverlay('Loading album...'); + await openDownloadMissingModalForArtistAlbum( + virtualPlaylistId, playlistName, tracks, album, artist, false + ); + hideLoadingOverlay(); + + // Register download bubble (reuses existing artist bubble system) + registerArtistDownload(artist, album, virtualPlaylistId, albumType); +} + /** * Add all tracks from any download modal to the wishlist * Universal handler for all modal types (artist albums, playlists, YouTube, Tidal, etc.) @@ -13644,6 +13684,7 @@ window.downloadSelectedCategory = downloadSelectedCategory; window.openAddToWishlistModal = openAddToWishlistModal; window.closeAddToWishlistModal = closeAddToWishlistModal; window.handleAddToWishlist = handleAddToWishlist; +window.handleWishlistDownloadNow = handleWishlistDownloadNow; window.addModalTracksToWishlist = addModalTracksToWishlist; // Helper functions @@ -26165,6 +26206,7 @@ function updateArtistDownloadsSection() { } downloadsUpdateTimeout = setTimeout(() => { showArtistDownloadsSection(); + showLibraryDownloadsSection(); updateDashboardDownloads(); }, 300); // 300ms debounce } @@ -27096,6 +27138,64 @@ function showArtistDownloadsSection() { }); } +/** + * Show download bubbles on the Library page (mirrors showArtistDownloadsSection) + */ +function showLibraryDownloadsSection() { + const libraryContent = document.querySelector('.library-content'); + if (!libraryContent) return; + + let downloadsSection = document.getElementById('library-downloads-section'); + + // Create section if it doesn't exist + if (!downloadsSection) { + downloadsSection = document.createElement('div'); + downloadsSection.id = 'library-downloads-section'; + downloadsSection.className = 'artist-downloads-section'; + + // Insert before the artist grid + const artistGrid = document.getElementById('library-artists-grid'); + if (artistGrid) { + libraryContent.insertBefore(downloadsSection, artistGrid); + } + } + + // Count active artists (reuses artistDownloadBubbles state) + const activeArtists = Object.keys(artistDownloadBubbles).filter(artistId => + artistDownloadBubbles[artistId].downloads.length > 0 + ); + + if (activeArtists.length === 0) { + downloadsSection.style.display = 'none'; + return; + } + + downloadsSection.style.display = 'block'; + downloadsSection.innerHTML = ` +
+

Current Downloads

+

Active download processes

+
+
+ ${activeArtists.map(artistId => createArtistBubbleCard(artistDownloadBubbles[artistId])).join('')} +
+ `; + + // Add click handlers + glow effects + activeArtists.forEach(artistId => { + const bubbleCard = downloadsSection.querySelector(`[data-artist-id="${artistId}"]`); + if (bubbleCard) { + bubbleCard.addEventListener('click', () => openArtistDownloadModal(artistId)); + const artist = artistDownloadBubbles[artistId].artist; + if (artist.image_url) { + extractImageColors(artist.image_url, (colors) => { + applyDynamicGlow(bubbleCard, colors); + }); + } + } + }); +} + /** * Create HTML for an artist bubble card */ @@ -29949,6 +30049,9 @@ function initializeLibraryPage() { // Load initial data loadLibraryArtists(); + // Show download bubbles if any exist + showLibraryDownloadsSection(); + libraryPageState.isInitialized = true; console.log("✅ Library page initialized successfully"); diff --git a/webui/static/style.css b/webui/static/style.css index 32347c60..fa88076e 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -14183,7 +14183,6 @@ body { /* Artist Downloads Section */ .artist-downloads-section { - max-width: 800px; padding: 0 20px; animation: fadeInUp 0.5s ease-out; width: 100%; @@ -16735,6 +16734,23 @@ body { border-color: rgba(255, 255, 255, 0.3); } +.wishlist-modal-btn-download { + background: linear-gradient(135deg, #1db954, #1ed760); + color: #ffffff; + border: 1px solid rgba(29, 185, 84, 0.3); +} + +.wishlist-modal-btn-download:hover { + background: linear-gradient(135deg, #1ed760, #1db954); + transform: translateY(-1px); + box-shadow: 0 8px 24px rgba(29, 185, 84, 0.4); +} + +.wishlist-modal-btn-download:active { + transform: translateY(0); + box-shadow: 0 4px 12px rgba(29, 185, 84, 0.3); +} + .wishlist-modal-btn-primary { background: linear-gradient(135deg, rgb(var(--accent-rgb)) 0%, rgb(var(--accent-light-rgb)) 100%); color: #ffffff;