#911: album Redownload uses the stored match id, not a fresh search

The Enhanced-view album Redownload only honoured album.spotify_album_id. For an iTunes-matched
album (no spotify id) it fell through to a fresh /api/enhanced-search and grabbed the FIRST hit
— which can be a different edition than the one you have (issue: matched the 66-track 'Original
Soundtrack Collection', got the 19-track 'Volume 1').

Now it prefers the album row's stored source id (spotify, then iTunes — the iTunes endpoint
already returns a Spotify-shaped payload) and only searches when neither exists. Also fixed the
search fallback to fetch from the MATCHING source endpoint instead of always hitting Spotify
(latent bug for iTunes search hits).

Frontend-only orchestration fix; no JS test runner in the repo, the album endpoints are unchanged.
This commit is contained in:
BoulderBadgeDad 2026-06-23 11:12:29 -07:00
parent f2f4f8ccee
commit 69cb51cc13

View file

@ -5416,20 +5416,34 @@ function _pollRedownloadProgress(taskId, overlay) {
async function redownloadLibraryAlbum(album, artistName, btn) { async function redownloadLibraryAlbum(album, artistName, btn) {
const albumName = album.title || ''; const albumName = album.title || '';
const spotifyAlbumId = album.spotify_album_id || ''; const spotifyAlbumId = album.spotify_album_id || '';
const itunesAlbumId = album.itunes_album_id || '';
if (!spotifyAlbumId && !albumName) { if (!spotifyAlbumId && !itunesAlbumId && !albumName) {
showToast('No album ID or name available for redownload', 'warning'); showToast('No album ID or name available for redownload', 'warning');
return; return;
} }
// Fetch a specific album edition by its source id. The iTunes endpoint returns a
// Spotify-shaped payload, so all the downstream handling is identical.
const fetchAlbumBySource = (source, id, name, artist) => {
const params = new URLSearchParams({ name: name || albumName, artist: artist || artistName || '' });
const base = source === 'itunes' ? '/api/itunes/album/' : '/api/spotify/album/';
return fetch(`${base}${encodeURIComponent(id)}?${params}`);
};
const origText = btn ? btn.innerHTML : ''; const origText = btn ? btn.innerHTML : '';
try { try {
if (btn) { btn.disabled = true; btn.textContent = 'Loading...'; } if (btn) { btn.disabled = true; btn.textContent = 'Loading...'; }
// #911 — redownload the edition the user ACTUALLY matched, using the album row's
// stored source id. A fresh search can resolve to a different edition (e.g. a
// single-volume release instead of the matched full collection), so use the
// spotify/iTunes id when present and only fall back to a search if neither exists.
let response; let response;
if (spotifyAlbumId) { if (spotifyAlbumId) {
const params = new URLSearchParams({ name: albumName, artist: artistName || '' }); response = await fetchAlbumBySource('spotify', spotifyAlbumId);
response = await fetch(`/api/spotify/album/${encodeURIComponent(spotifyAlbumId)}?${params}`); } else if (itunesAlbumId) {
response = await fetchAlbumBySource('itunes', itunesAlbumId);
} }
if (!response || !response.ok) { if (!response || !response.ok) {
@ -5441,13 +5455,15 @@ async function redownloadLibraryAlbum(album, artistName, btn) {
}); });
if (!searchResp.ok) throw new Error('Album search failed'); if (!searchResp.ok) throw new Error('Album search failed');
const searchData = await searchResp.json(); const searchData = await searchResp.json();
const found = searchData.spotify_albums?.[0] || searchData.itunes_albums?.[0]; const spotHit = searchData.spotify_albums?.[0];
const found = spotHit || searchData.itunes_albums?.[0];
if (!found || !found.id) { if (!found || !found.id) {
showToast(`Could not find "${albumName}" by ${artistName || 'unknown'}`, 'warning'); showToast(`Could not find "${albumName}" by ${artistName || 'unknown'}`, 'warning');
return; return;
} }
const params = new URLSearchParams({ name: found.name || albumName, artist: found.artist || artistName || '' }); // Fetch from the MATCHING source endpoint — the old fallback always hit the
response = await fetch(`/api/spotify/album/${encodeURIComponent(found.id)}?${params}`); // Spotify endpoint, which is wrong for an iTunes search hit.
response = await fetchAlbumBySource(spotHit ? 'spotify' : 'itunes', found.id, found.name, found.artist);
} }
if (!response.ok) throw new Error(`Failed to load album: ${response.status}`); if (!response.ok) throw new Error(`Failed to load album: ${response.status}`);