diff --git a/webui/static/artists.js b/webui/static/artists.js index 910fae45..c4e4744e 100644 --- a/webui/static/artists.js +++ b/webui/static/artists.js @@ -500,7 +500,7 @@ async function selectArtistForDetail(artist, options = {}) { // Update state artistsPageState.selectedArtist = artist; artistsPageState.currentView = 'detail'; - artistsPageState.sourceOverride = options.source || null; + artistsPageState.sourceOverride = options.source || artist.source || null; artistsPageState.pluginOverride = options.plugin || null; // Show detail state @@ -510,7 +510,7 @@ async function selectArtistForDetail(artist, options = {}) { updateArtistDetailHeader(artist); // Load discography (pass artist name for cross-source fallback) - await loadArtistDiscography(artist.id, artist.name, options.source, options.plugin); + await loadArtistDiscography(artist.id, artist.name, artistsPageState.sourceOverride, options.plugin); } /** @@ -528,6 +528,13 @@ async function loadArtistDiscography(artistId, artistName = null, sourceOverride if (artistsPageState.cache.discography[cacheKey]) { console.log('📦 Using cached discography'); const cachedDiscography = artistsPageState.cache.discography[cacheKey]; + if (artistsPageState.selectedArtist) { + artistsPageState.selectedArtist = { + ...artistsPageState.selectedArtist, + source: cachedDiscography.source || sourceOverride || artistsPageState.selectedArtist.source || null, + }; + } + artistsPageState.sourceOverride = cachedDiscography.source || sourceOverride || artistsPageState.sourceOverride || null; displayArtistDiscography(cachedDiscography); // Load similar artists in parallel (don't wait) — always uses primary source @@ -574,6 +581,16 @@ async function loadArtistDiscography(artistId, artistName = null, sourceOverride source: data.source || sourceOverride || null, }; + // Keep the resolved metadata source on the selected artist so album clicks + // can pass it through to /api/album//tracks. + if (artistsPageState.selectedArtist) { + artistsPageState.selectedArtist = { + ...artistsPageState.selectedArtist, + source: discography.source, + }; + } + artistsPageState.sourceOverride = discography.source || artistsPageState.sourceOverride || null; + // Update selected artist with full details from backend (includes MusicBrainz ID) if (data.artist) { console.log('✨ Updating artist details with fresh data from backend'); @@ -2127,8 +2144,9 @@ async function createArtistAlbumVirtualPlaylist(album, albumType) { // Fetch album tracks from backend (pass name/artist for Hydrabase support) const _aat1 = new URLSearchParams({ name: album.name || '', artist: artist.name || '' }); - if (artistsPageState.sourceOverride) { - _aat1.set('source', artistsPageState.sourceOverride); + const albumSource = artistsPageState.sourceOverride || album.source || artist.source || artistsPageState.artistDiscography?.source || null; + if (albumSource) { + _aat1.set('source', albumSource); } if (artistsPageState.pluginOverride) { _aat1.set('plugin', artistsPageState.pluginOverride); @@ -2227,7 +2245,8 @@ async function openDownloadMissingModalForArtistAlbum(virtualPlaylistId, playlis // Additional metadata for artist albums artist: artist, album: album, - albumType: album.album_type + albumType: album.album_type, + source: artist?.source || album?.source || artistsPageState.artistDiscography?.source || null }; // Generate hero section — 'artist_album' for releases, 'playlist' for charts/compilations @@ -4608,4 +4627,3 @@ function renderEnrichmentCards(enrichment) { } // =============================== - diff --git a/webui/static/library.js b/webui/static/library.js index 90f9b3ad..675187b8 100644 --- a/webui/static/library.js +++ b/webui/static/library.js @@ -639,6 +639,7 @@ let artistDetailPageState = { isInitialized: false, currentArtistId: null, currentArtistName: null, + currentArtistSource: null, enhancedView: false, enhancedData: null, expandedAlbums: new Set(), @@ -671,6 +672,7 @@ function navigateToArtistDetail(artistId, artistName) { // Store current artist info and reset enhanced view state artistDetailPageState.currentArtistId = artistId; artistDetailPageState.currentArtistName = artistName; + artistDetailPageState.currentArtistSource = null; artistDetailPageState.enhancedData = null; artistDetailPageState.expandedAlbums = new Set(); artistDetailPageState.selectedTracks = new Set(); @@ -788,6 +790,9 @@ async function loadArtistDetailData(artistId, artistName) { // Populate the page with data (which updates the hero section and sets textContent) populateArtistDetailPage(data); + // Keep the resolved metadata source for album-track lookups. + artistDetailPageState.currentArtistSource = data.discography?.source || data.artist?.source || null; + // Update header with artist name and MusicBrainz link LAST to avoid overwrite updateArtistDetailPageHeaderWithData(data.artist); @@ -1504,7 +1509,8 @@ function createReleaseCard(release) { const currentArtist = artistDetailPageState.currentArtistName ? { id: artistDetailPageState.currentArtistId, name: artistDetailPageState.currentArtistName, - image_url: getArtistImageFromPage() || '' // Get artist image from page + image_url: getArtistImageFromPage() || '', // Get artist image from page + source: artistDetailPageState.currentArtistSource || null } : null; if (!currentArtist) { @@ -1515,6 +1521,9 @@ function createReleaseCard(release) { // Load tracks for the album (pass name/artist for Hydrabase support) const _aat2 = new URLSearchParams({ name: albumData.name || '', artist: currentArtist.name || '' }); + if (currentArtist.source) { + _aat2.set('source', currentArtist.source); + } const response = await fetch(`/api/album/${albumData.id}/tracks?${_aat2}`); if (!response.ok) { throw new Error(`Failed to load album tracks: ${response.status}`); @@ -1997,8 +2006,10 @@ async function openDiscographyModal() { discography = { albums: data.albums || [], singles: data.singles || [] }; if (discography.albums.length > 0 || discography.singles.length > 0) { artistsPageState.artistDiscography = discography; + artistsPageState.sourceOverride = data.source || artistsPageState.sourceOverride || null; // Use metadata source ID for the modal (needed for download API calls) if (metadataArtistId) artist.id = metadataArtistId; + artist.source = data.source || null; artistsPageState.selectedArtist = artist; } else { discography = null; @@ -6650,4 +6661,3 @@ async function updateLibraryWatchlistButtonStatus(artistId) { } // ================================= - diff --git a/webui/static/sync-spotify.js b/webui/static/sync-spotify.js index 327006ec..95e592c2 100644 --- a/webui/static/sync-spotify.js +++ b/webui/static/sync-spotify.js @@ -129,7 +129,18 @@ async function rehydrateArtistAlbumModal(virtualPlaylistId, playlistName, batchI // Fetch the album tracks to get proper artist and album data try { - const response = await fetch(`/api/album/${albumId}/tracks`); + const rehydrateProcess = activeDownloadProcesses[virtualPlaylistId]; + const albumSource = rehydrateProcess?.source || rehydrateProcess?.artist?.source || rehydrateProcess?.album?.source || null; + const artistName = rehydrateProcess?.artist?.name || ''; + const params = new URLSearchParams({ + name: playlistName || '', + artist: artistName + }); + if (albumSource) { + params.set('source', albumSource); + } + + const response = await fetch(`/api/album/${albumId}/tracks?${params}`); const data = await response.json(); if (!data.success || !data.album || !data.tracks) { @@ -139,12 +150,17 @@ async function rehydrateArtistAlbumModal(virtualPlaylistId, playlistName, batchI const album = data.album; const tracks = data.tracks; + const source = data.source || tracks[0]?._source || albumSource || null; // Extract artist info from the first track (all tracks should have same artist) const artist = { id: artistId, - name: tracks[0].artists[0] // Use first artist name from first track + name: tracks[0].artists[0], // Use first artist name from first track + source }; + if (source) { + album.source = source; + } console.log(`✅ Retrieved album data: "${album.name}" by ${artist.name} (${tracks.length} tracks)`); @@ -2536,4 +2552,3 @@ function _extractM3UTracks(tracks) { } // ================================================================================== - diff --git a/webui/static/wishlist-tools.js b/webui/static/wishlist-tools.js index c3a40616..ea476867 100644 --- a/webui/static/wishlist-tools.js +++ b/webui/static/wishlist-tools.js @@ -2389,6 +2389,10 @@ async function confirmMatch() { const artistId = currentMatchingData.selectedArtist.id; const albumId = currentMatchingData.selectedAlbum.id; const _aat3 = new URLSearchParams({ name: currentMatchingData.selectedAlbum.name || '', artist: currentMatchingData.selectedArtist.name || '' }); + const albumSource = currentMatchingData.selectedAlbum?.source || currentMatchingData.selectedArtist?.source || null; + if (albumSource) { + _aat3.set('source', albumSource); + } const tracksResponse = await fetch(`/api/album/${albumId}/tracks?${_aat3}`); if (!tracksResponse.ok) { @@ -7167,4 +7171,3 @@ function updateDbProgressUI(state) { } // =================================================================== -