Restore lost redownloadLibraryAlbum function (#699)
The Redownload button on the enhanced artist-view album row was
calling redownloadLibraryAlbum(album, artistName, btn), but the
function body was dropped from the source tree when commit a66c4d06
split the 78K-line script.js into 17 domain modules. The onclick
threw ReferenceError silently — no toast, no log, no popup, no
visible failure for the user.
Function restored verbatim from a66c4d06~1:webui/static/script.js
into library.js next to deleteLibraryAlbum, since it depends on
artistDetailPageState and the existing
openDownloadMissingModalForArtistAlbum / registerArtistDownload
helpers in shared-helpers.js.
This commit is contained in:
parent
718eb0cb10
commit
87516e5b7b
2 changed files with 94 additions and 0 deletions
|
|
@ -3415,6 +3415,7 @@ function closeHelperSearch() {
|
|||
const WHATS_NEW = {
|
||||
'2.6.2': [
|
||||
{ date: 'May 24, 2026 — 2.6.2 release' },
|
||||
{ title: 'Fix: Redownload Album button on the enhanced artist view was dead', desc: 'the Redownload button on the enhanced artist-page album row was throwing a silent ReferenceError on click — no popup, no toast, no log line, button just did nothing. the underlying function was lost when script.js got split into 17 domain modules and nothing caught it for a while. restored. closes #699.' },
|
||||
{ title: 'iTunes / Apple Music link import', desc: 'new iTunes Link tab on the Sync page, between Deezer Link and YouTube. paste an Apple Music album, track, or playlist URL and SoulSync pulls the tracklist, runs it through the same discovery → sync → download flow as the other link tabs. handles the new Apple Music SPA token shape — token gets scraped from the JS bundle on first use and cached for 6 hours, with a 401 retry that refetches if Apple rotates mid-session.' },
|
||||
{ title: 'Auto-Sync: bulk schedule by source + custom interval columns', desc: 'each source group in the sidebar gets a Bulk button — opens a popover that lets you schedule every playlist in that group at a chosen interval in one click (1h / 2h / 4h / 8h / 12h / 16h / 24h / 48h / 72h / weekly) or "Custom interval…" for an arbitrary number of hours. also includes "Unschedule all" to clear a source\'s schedules. on the board itself, a schedule with a non-standard interval (e.g. 6h or 36h, created via Automations page or the custom prompt) now renders as its own dashed-border column instead of disappearing because it didn\'t match a hardcoded bucket.' },
|
||||
{ title: 'Auto-Sync manager: filter, failure indicators, history filters + load more', desc: 'a handful of UX additions on the Playlist Auto-Sync modal. sidebar gets a "Filter playlists…" search input so you can narrow down a long mirrored-playlist list. scheduled cards on the board now show a red ! badge when the last three pipeline runs all failed (yellow ⚠ if at least one of the last few failed) so chronically broken schedules surface visually instead of getting buried in the run-history tab. run history tab title shows a red error count when there are failed runs in the loaded window. the history tab itself gains All / Errors / Completed filter pills, a "Load more" footer that pulls another 50 entries, and a "Run pipeline again" button inside the expanded detail panel so you can re-trigger a specific playlist without leaving the modal. also dropped the "Discovered: completed" pill — `tracks_discovered` was a status string, not a count, and the same data is already in the before/after stats grid above.' },
|
||||
|
|
|
|||
|
|
@ -5299,6 +5299,99 @@ function _pollRedownloadProgress(taskId, overlay) {
|
|||
}, 300000);
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
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 || [],
|
||||
image_url: albumData.images?.[0]?.url || null,
|
||||
release_date: albumData.release_date,
|
||||
total_tracks: albumData.total_tracks,
|
||||
artists: albumData.artists || [{ name: artistName || '' }]
|
||||
};
|
||||
|
||||
await openDownloadMissingModalForArtistAlbum(
|
||||
virtualPlaylistId, playlistName, enrichedTracks, fullAlbumObject, artistObject, true
|
||||
);
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteLibraryAlbum(albumId) {
|
||||
const choice = await _showAlbumDeleteDialog();
|
||||
if (!choice) return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue