Feat: Added 'add to wishlist' button on all download missing track modals.
This commit is contained in:
parent
b7b76b378d
commit
573bd6080c
2 changed files with 204 additions and 1 deletions
41
artist_bubble_snapshots.json
Normal file
41
artist_bubble_snapshots.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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) {
|
|||
<button class="download-control-btn primary" id="begin-analysis-btn-${playlistId}" onclick="startMissingTracksProcess('${playlistId}')">
|
||||
Begin Analysis
|
||||
</button>
|
||||
<button class="download-control-btn" id="add-to-wishlist-btn-${playlistId}" onclick="addModalTracksToWishlist('${playlistId}')" style="background-color: #9333ea; color: white;">
|
||||
Add to Wishlist
|
||||
</button>
|
||||
<button class="download-control-btn danger" id="cancel-all-btn-${playlistId}" onclick="cancelAllOperations('${playlistId}')" style="display: none;">
|
||||
Cancel All
|
||||
</button>
|
||||
|
|
@ -4436,6 +4445,9 @@ async function openDownloadMissingModalForYouTube(virtualPlaylistId, playlistNam
|
|||
<button class="download-control-btn primary" id="begin-analysis-btn-${virtualPlaylistId}" onclick="startMissingTracksProcess('${virtualPlaylistId}')">
|
||||
Begin Analysis
|
||||
</button>
|
||||
<button class="download-control-btn" id="add-to-wishlist-btn-${virtualPlaylistId}" onclick="addModalTracksToWishlist('${virtualPlaylistId}')" style="background-color: #9333ea; color: white;">
|
||||
Add to Wishlist
|
||||
</button>
|
||||
<button class="download-control-btn danger" id="cancel-all-btn-${virtualPlaylistId}" onclick="cancelAllOperations('${virtualPlaylistId}')" style="display: none;">
|
||||
Cancel All
|
||||
</button>
|
||||
|
|
@ -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,
|
|||
<button class="download-control-btn primary" id="begin-analysis-btn-${virtualPlaylistId}" onclick="startMissingTracksProcess('${virtualPlaylistId}')">
|
||||
Begin Analysis
|
||||
</button>
|
||||
<button class="download-control-btn" id="add-to-wishlist-btn-${virtualPlaylistId}" onclick="addModalTracksToWishlist('${virtualPlaylistId}')" style="background-color: #9333ea; color: white;">
|
||||
Add to Wishlist
|
||||
</button>
|
||||
<button class="download-control-btn danger" id="cancel-all-btn-${virtualPlaylistId}" onclick="cancelAllOperations('${virtualPlaylistId}')" style="display: none;">
|
||||
Cancel All
|
||||
</button>
|
||||
|
|
@ -17822,6 +17981,9 @@ async function openDownloadMissingModalForArtistAlbum(virtualPlaylistId, playlis
|
|||
<button class="download-control-btn primary" id="begin-analysis-btn-${virtualPlaylistId}" onclick="startMissingTracksProcess('${virtualPlaylistId}')">
|
||||
Begin Analysis
|
||||
</button>
|
||||
<button class="download-control-btn" id="add-to-wishlist-btn-${virtualPlaylistId}" onclick="addModalTracksToWishlist('${virtualPlaylistId}')" style="background-color: #9333ea; color: white;">
|
||||
Add to Wishlist
|
||||
</button>
|
||||
<button class="download-control-btn danger" id="cancel-all-btn-${virtualPlaylistId}" onclick="cancelAllOperations('${virtualPlaylistId}')" style="display: none;">
|
||||
Cancel All
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in a new issue