From 573bd6080c84fa4303f9d0919fd39375b6e6d728 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Sun, 23 Nov 2025 13:25:35 -0800 Subject: [PATCH] Feat: Added 'add to wishlist' button on all download missing track modals. --- artist_bubble_snapshots.json | 41 +++++++++ webui/static/script.js | 164 ++++++++++++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 artist_bubble_snapshots.json diff --git a/artist_bubble_snapshots.json b/artist_bubble_snapshots.json new file mode 100644 index 00000000..9fb7248e --- /dev/null +++ b/artist_bubble_snapshots.json @@ -0,0 +1,41 @@ +{ + "bubbles": { + "165ZgPlLkK7bf5bDoFc6Sb": { + "artist": { + "id": "165ZgPlLkK7bf5bDoFc6Sb", + "name": "Limp Bizkit", + "image_url": "https://i.scdn.co/image/4654955890cb62a6abe9daadfd2b3ecdee8036e4", + "genres": [ + "nu metal", + "rap metal", + "alternative metal", + "rap rock" + ], + "popularity": 79, + "confidence": 0.21052631578947367 + }, + "downloads": [ + { + "virtualPlaylistId": "artist_album_165ZgPlLkK7bf5bDoFc6Sb_5mi7FKaWE5CtcOjdyxScA7", + "album": { + "album_type": "album", + "external_urls": { + "spotify": "https://open.spotify.com/album/5mi7FKaWE5CtcOjdyxScA7" + }, + "id": "5mi7FKaWE5CtcOjdyxScA7", + "image_url": "https://i.scdn.co/image/ab67616d0000b2734a31b146c7cf07705d912efe", + "name": "Chocolate Starfish And The Hot Dog Flavored Water", + "release_date": "2000-10-17", + "total_tracks": 15 + }, + "albumType": "albums", + "status": "in_progress", + "startTime": "2025-11-23T21:24:21.696Z" + } + ], + "hasCompletedDownloads": false + } + }, + "timestamp": "2025-11-23T13:24:22.715644", + "snapshot_id": "20251123_132422" +} \ No newline at end of file diff --git a/webui/static/script.js b/webui/static/script.js index 125bb7d2..edf817a2 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -2349,9 +2349,11 @@ async function rehydrateArtistAlbumModal(virtualPlaylistId, playlistName, batchI // Update button states to reflect running status const beginBtn = document.getElementById(`begin-analysis-btn-${virtualPlaylistId}`); const cancelBtn = document.getElementById(`cancel-all-btn-${virtualPlaylistId}`); + const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${virtualPlaylistId}`); if (beginBtn) beginBtn.style.display = 'none'; if (cancelBtn) cancelBtn.style.display = 'inline-block'; - + if (wishlistBtn) wishlistBtn.style.display = 'none'; + // Hide the modal - this is background rehydration, not user-requested if (process.modalElement) { process.modalElement.style.display = 'none'; @@ -2669,6 +2671,10 @@ async function rehydrateModal(processInfo, userRequested = false) { document.getElementById(`begin-analysis-btn-${playlist_id}`).style.display = 'none'; document.getElementById(`cancel-all-btn-${playlist_id}`).style.display = 'inline-block'; + // Hide wishlist button if it exists + const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${playlist_id}`); + if (wishlistBtn) wishlistBtn.style.display = 'none'; + startModalDownloadPolling(playlist_id); process.modalElement.style.display = 'none'; @@ -4078,6 +4084,9 @@ async function openDownloadMissingModal(playlistId) { + @@ -4436,6 +4445,9 @@ async function openDownloadMissingModalForYouTube(virtualPlaylistId, playlistNam + @@ -4947,6 +4959,12 @@ async function startMissingTracksProcess(playlistId) { document.getElementById(`begin-analysis-btn-${playlistId}`).style.display = 'none'; document.getElementById(`cancel-all-btn-${playlistId}`).style.display = 'inline-block'; + // Hide wishlist button if it exists (only for non-wishlist modals) + const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${playlistId}`); + if (wishlistBtn) { + wishlistBtn.style.display = 'none'; + } + // Add to discover download sidebar if this is a discover page download if (process.discoverMetadata) { const playlistName = process.playlist.name; @@ -5036,6 +5054,21 @@ async function startMissingTracksProcess(playlistId) { } catch (error) { showToast(`Failed to start process: ${error.message}`, 'error'); process.status = 'cancelled'; + + // Reset button states on error + const beginBtn = document.getElementById(`begin-analysis-btn-${playlistId}`); + const cancelBtn = document.getElementById(`cancel-all-btn-${playlistId}`); + const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${playlistId}`); + if (beginBtn) beginBtn.style.display = 'inline-block'; + if (cancelBtn) cancelBtn.style.display = 'none'; + if (wishlistBtn) wishlistBtn.style.display = 'inline-block'; + + // Show the force download toggle again + const forceToggleContainer = document.querySelector(`#force-download-all-${playlistId}`)?.closest('.force-download-toggle-container'); + if (forceToggleContainer) { + forceToggleContainer.style.display = 'flex'; + } + cleanupDownloadProcess(playlistId); } } @@ -8257,6 +8290,128 @@ function closeAddToWishlistModal() { } } +/** + * Add all tracks from any download modal to the wishlist + * Universal handler for all modal types (artist albums, playlists, YouTube, Tidal, etc.) + */ +async function addModalTracksToWishlist(playlistId) { + const process = activeDownloadProcesses[playlistId]; + if (!process) { + console.error('❌ No active process found for:', playlistId); + showToast('Error: Could not find playlist data', 'error'); + return; + } + + // Verify we have tracks + if (!process.tracks || process.tracks.length === 0) { + console.error('❌ No tracks found in process:', process); + showToast('Error: No tracks to add', 'error'); + return; + } + + const tracks = process.tracks; + // Get artist/album context if available (for artist album downloads) + const artist = process.artist || { name: 'Unknown Artist', id: null }; + const album = process.album || process.playlist || { name: 'Playlist', id: playlistId }; + + console.log(`🔄 Adding ${tracks.length} tracks from "${album.name}" to wishlist`); + + // Disable the button to prevent double-clicks + const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${playlistId}`); + if (wishlistBtn) { + wishlistBtn.disabled = true; + wishlistBtn.classList.add('loading'); + wishlistBtn.textContent = 'Adding...'; + } + + try { + let successCount = 0; + let errorCount = 0; + + // Add each track to wishlist individually + for (const track of tracks) { + try { + // Format artists field to match backend expectations + let formattedArtists = track.artists; + if (typeof track.artists === 'string') { + formattedArtists = [{ name: track.artists }]; + } else if (Array.isArray(track.artists)) { + formattedArtists = track.artists.map(artistItem => { + if (typeof artistItem === 'string') { + return { name: artistItem }; + } else if (typeof artistItem === 'object' && artistItem !== null) { + return artistItem; + } else { + return { name: 'Unknown Artist' }; + } + }); + } else { + formattedArtists = [{ name: artist.name }]; + } + + const formattedTrack = { + ...track, + artists: formattedArtists + }; + + const response = await fetch('/api/add-album-to-wishlist', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + track: formattedTrack, + artist: artist, + album: album, + source_type: 'album', + source_context: { + album_name: album.name, + artist_name: artist.name, + album_type: album.album_type || 'album' + } + }) + }); + + const result = await response.json(); + + if (result.success) { + successCount++; + } else { + errorCount++; + console.error(`❌ Failed to add "${track.name}" to wishlist: ${result.error}`); + } + } catch (error) { + errorCount++; + console.error(`❌ Error adding "${track.name}" to wishlist:`, error); + } + } + + // Show result toast + if (successCount > 0) { + const message = errorCount > 0 + ? `Added ${successCount}/${tracks.length} tracks to wishlist (${errorCount} failed)` + : `Added ${successCount} tracks to wishlist`; + showToast(message, 'success'); + + // Close the modal on success + await closeDownloadMissingModal(playlistId); + } else { + showToast('Failed to add any tracks to wishlist', 'error'); + } + + } catch (error) { + console.error('❌ Error in addModalTracksToWishlist:', error); + showToast(`Error adding to wishlist: ${error.message}`, 'error'); + } finally { + // Re-enable button if still on screen (in case of error) + if (wishlistBtn) { + wishlistBtn.disabled = false; + wishlistBtn.classList.remove('loading'); + wishlistBtn.textContent = 'Add to Wishlist'; + } + } +} + /** * Format duration from milliseconds to MM:SS format */ @@ -8290,6 +8445,7 @@ window.handleWishlistButtonClick = handleWishlistButtonClick; window.openAddToWishlistModal = openAddToWishlistModal; window.closeAddToWishlistModal = closeAddToWishlistModal; window.handleAddToWishlist = handleAddToWishlist; +window.addModalTracksToWishlist = addModalTracksToWishlist; // Helper functions window.escapeHtml = escapeHtml; @@ -11388,6 +11544,9 @@ async function openDownloadMissingModalForTidal(virtualPlaylistId, playlistName, + @@ -17822,6 +17981,9 @@ async function openDownloadMissingModalForArtistAlbum(virtualPlaylistId, playlis +