Add Redownload button to enhanced library view & fix album download mode
- Redownload button on each album in enhanced view (admin only) - Uses same flow as artist page: fetches API tracklist, opens Download Missing modal with force-download option - Register dashboard bubbles for library redownload and issue downloads - Add library_redownload_ prefix to album download whitelist so it uses 1 worker with source reuse and sends full album context (release_date for year in folder name)
This commit is contained in:
parent
0742cc45e6
commit
da2b42b59a
2 changed files with 133 additions and 2 deletions
|
|
@ -11874,7 +11874,7 @@ async function startMissingTracksProcess(playlistId) {
|
|||
|
||||
// If this is an artist album download, use album name and include full context
|
||||
// Match 'artist_album_', 'enhanced_search_album_', 'enhanced_search_track_', 'discover_album_', and 'seasonal_album_' prefixes
|
||||
if (playlistId.startsWith('artist_album_') || playlistId.startsWith('enhanced_search_album_') || playlistId.startsWith('enhanced_search_track_') || playlistId.startsWith('discover_album_') || playlistId.startsWith('seasonal_album_') || playlistId.startsWith('spotify_library_') || playlistId.startsWith('issue_download_')) {
|
||||
if (playlistId.startsWith('artist_album_') || playlistId.startsWith('enhanced_search_album_') || playlistId.startsWith('enhanced_search_track_') || playlistId.startsWith('discover_album_') || playlistId.startsWith('seasonal_album_') || playlistId.startsWith('spotify_library_') || playlistId.startsWith('issue_download_') || playlistId.startsWith('library_redownload_')) {
|
||||
requestBody.playlist_name = process.album?.name || process.playlist.name;
|
||||
requestBody.is_album_download = true;
|
||||
requestBody.album_context = process.album; // Full Spotify album object
|
||||
|
|
@ -37085,6 +37085,17 @@ function renderExpandedAlbumHeader(album) {
|
|||
reorganizeBtn.onclick = (e) => { e.stopPropagation(); showReorganizeModal(album.id); };
|
||||
enrichRow.appendChild(reorganizeBtn);
|
||||
|
||||
const redownloadBtn = document.createElement('button');
|
||||
redownloadBtn.className = 'enhanced-redownload-album-btn';
|
||||
redownloadBtn.innerHTML = '↻ Redownload';
|
||||
redownloadBtn.title = 'Redownload this album (opens Download Missing modal with force-download)';
|
||||
redownloadBtn.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
const aName = artistDetailPageState.enhancedData ? artistDetailPageState.enhancedData.artist.name : '';
|
||||
redownloadLibraryAlbum(album, aName, redownloadBtn);
|
||||
};
|
||||
enrichRow.appendChild(redownloadBtn);
|
||||
|
||||
const deleteAlbumBtn = document.createElement('button');
|
||||
deleteAlbumBtn.className = 'enhanced-delete-album-btn';
|
||||
deleteAlbumBtn.textContent = 'Delete Album';
|
||||
|
|
@ -55261,7 +55272,7 @@ async function issueDownloadAlbum(spotifyAlbumId, artistName, albumName) {
|
|||
}));
|
||||
|
||||
const playlistName = `[${artistName}] ${albumData.name}`;
|
||||
const artistObject = { id: null, name: artistName };
|
||||
const artistObject = { id: `issue_${artistName}`, name: artistName, image_url: '' };
|
||||
const fullAlbumObject = {
|
||||
name: albumData.name,
|
||||
id: albumData.id,
|
||||
|
|
@ -55276,6 +55287,10 @@ async function issueDownloadAlbum(spotifyAlbumId, artistName, albumName) {
|
|||
virtualPlaylistId, playlistName, enrichedTracks, fullAlbumObject, artistObject, true
|
||||
);
|
||||
|
||||
// Register download bubble so it appears on the dashboard
|
||||
const albumType = fullAlbumObject.album_type || 'album';
|
||||
registerArtistDownload(artistObject, fullAlbumObject, virtualPlaylistId, albumType);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Issue download error:', error);
|
||||
showToast(`Error: ${error.message}`, 'error');
|
||||
|
|
@ -55284,6 +55299,101 @@ async function issueDownloadAlbum(spotifyAlbumId, artistName, albumName) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Redownload Library Album (Enhanced View) ---
|
||||
async function redownloadLibraryAlbum(album, artistName, btn) {
|
||||
const albumName = album.title || '';
|
||||
const spotifyAlbumId = album.spotify_album_id || '';
|
||||
|
||||
if (!spotifyAlbumId && !albumName) {
|
||||
showToast('No album ID or name available for redownload', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const origText = btn ? btn.innerHTML : '';
|
||||
try {
|
||||
if (btn) { btn.disabled = true; btn.textContent = 'Loading...'; }
|
||||
|
||||
let response;
|
||||
if (spotifyAlbumId) {
|
||||
const params = new URLSearchParams({ name: albumName, artist: artistName || '' });
|
||||
response = await fetch(`/api/spotify/album/${encodeURIComponent(spotifyAlbumId)}?${params}`);
|
||||
}
|
||||
|
||||
// Fallback: search by name if no ID or direct fetch failed
|
||||
if (!response || !response.ok) {
|
||||
const query = `${artistName || ''} ${albumName}`.trim();
|
||||
const searchResp = await fetch('/api/enhanced-search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ query })
|
||||
});
|
||||
if (!searchResp.ok) throw new Error('Album search failed');
|
||||
const searchData = await searchResp.json();
|
||||
const found = searchData.spotify_albums?.[0] || searchData.itunes_albums?.[0];
|
||||
if (!found || !found.id) {
|
||||
showToast(`Could not find "${albumName}" by ${artistName || 'unknown'}`, 'warning');
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams({ name: found.name || albumName, artist: found.artist || artistName || '' });
|
||||
response = await fetch(`/api/spotify/album/${encodeURIComponent(found.id)}?${params}`);
|
||||
}
|
||||
|
||||
if (!response.ok) throw new Error(`Failed to load album: ${response.status}`);
|
||||
|
||||
const albumData = await response.json();
|
||||
if (!albumData || !albumData.tracks || albumData.tracks.length === 0) {
|
||||
showToast(`No tracks found for "${albumName}"`, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedId = albumData.id || spotifyAlbumId || album.id;
|
||||
const virtualPlaylistId = `library_redownload_${resolvedId}`;
|
||||
const playlistName = `[${artistName || 'Unknown'}] ${albumData.name}`;
|
||||
|
||||
const enrichedTracks = albumData.tracks.map(track => ({
|
||||
...track,
|
||||
album: {
|
||||
name: albumData.name,
|
||||
id: albumData.id,
|
||||
album_type: albumData.album_type || 'album',
|
||||
images: albumData.images || [],
|
||||
release_date: albumData.release_date,
|
||||
total_tracks: albumData.total_tracks
|
||||
}
|
||||
}));
|
||||
|
||||
const enhancedArtist = artistDetailPageState.enhancedData?.artist;
|
||||
const artistObject = {
|
||||
id: artistDetailPageState.currentArtistId || `library_${artistName || album.id}`,
|
||||
name: artistName || '',
|
||||
image_url: enhancedArtist?.thumb_url || ''
|
||||
};
|
||||
const fullAlbumObject = {
|
||||
name: albumData.name,
|
||||
id: albumData.id,
|
||||
album_type: albumData.album_type || 'album',
|
||||
images: albumData.images || [],
|
||||
release_date: albumData.release_date,
|
||||
total_tracks: albumData.total_tracks,
|
||||
artists: albumData.artists || [{ name: artistName || '' }]
|
||||
};
|
||||
|
||||
await openDownloadMissingModalForArtistAlbum(
|
||||
virtualPlaylistId, playlistName, enrichedTracks, fullAlbumObject, artistObject, true
|
||||
);
|
||||
|
||||
// Register download bubble so it appears on the dashboard
|
||||
const albumType = fullAlbumObject.album_type || 'album';
|
||||
registerArtistDownload(artistObject, fullAlbumObject, virtualPlaylistId, albumType);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Redownload album error:', error);
|
||||
showToast(`Error: ${error.message}`, 'error');
|
||||
} finally {
|
||||
if (btn) { btn.disabled = false; btn.innerHTML = origText; }
|
||||
}
|
||||
}
|
||||
|
||||
// --- Issue Action: Add to Wishlist ---
|
||||
async function issueAddToWishlist(spotifyAlbumId, artistName, albumName) {
|
||||
const btn = document.getElementById('issue-action-wishlist');
|
||||
|
|
|
|||
|
|
@ -35928,6 +35928,27 @@ textarea.enhanced-meta-field-input {
|
|||
border-color: rgba(100, 149, 237, 0.35);
|
||||
}
|
||||
|
||||
.enhanced-redownload-album-btn {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: rgba(34, 197, 94, 0.06);
|
||||
border: 1px solid rgba(34, 197, 94, 0.15);
|
||||
color: rgba(34, 197, 94, 0.6);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.enhanced-redownload-album-btn:hover {
|
||||
background: rgba(34, 197, 94, 0.12);
|
||||
color: rgba(34, 197, 94, 0.9);
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
}
|
||||
.enhanced-redownload-album-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ── Reorganize Modal ── */
|
||||
.reorganize-modal {
|
||||
max-width: 900px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue