Fix iTunes album tracks failing on region-restricted releases
iTunes API can return collection metadata without song tracks for
region-restricted albums. The _lookup fallback only checked if results
was empty, so a collection-only response was accepted and cached as
{'items': []}. All future lookups returned the cached empty result.
Three fixes:
- get_album_tracks now checks for actual song items and tries fallback
storefronts when only collection metadata is returned
- Skip cached results with empty items array (prevents stale cache hits)
- Backend returns descriptive 404 error, frontend surfaces it in toast
This commit is contained in:
parent
8c96e0e197
commit
71bff55c6a
3 changed files with 38 additions and 4 deletions
|
|
@ -735,7 +735,7 @@ class iTunesClient:
|
|||
# Check cache for album tracks listing
|
||||
cache = get_metadata_cache()
|
||||
cached = cache.get_entity('itunes', 'album', f"{album_id}_tracks")
|
||||
if cached:
|
||||
if cached and cached.get('items'):
|
||||
return cached
|
||||
|
||||
results = self._lookup(id=album_id, entity='song')
|
||||
|
|
@ -743,6 +743,39 @@ class iTunesClient:
|
|||
if not results:
|
||||
return None
|
||||
|
||||
# Check if results contain actual song tracks (not just collection metadata).
|
||||
# iTunes sometimes returns only the collection/album info without songs
|
||||
# (region-restricted tracks). The _lookup fallback only checks if results
|
||||
# is empty, so a collection-only response bypasses fallback storefronts.
|
||||
has_songs = any(
|
||||
item.get('wrapperType') == 'track' and item.get('kind') == 'song'
|
||||
for item in results
|
||||
)
|
||||
if not has_songs:
|
||||
# Try fallback storefronts for actual song tracks
|
||||
logger.info(f"Album {album_id} returned collection info but no songs, trying fallback storefronts")
|
||||
for fallback in self.FALLBACK_COUNTRIES:
|
||||
if fallback == self.country:
|
||||
continue
|
||||
try:
|
||||
fb_results = self.session.get(
|
||||
self.LOOKUP_URL,
|
||||
params={'id': album_id, 'entity': 'song', 'country': fallback},
|
||||
timeout=15
|
||||
)
|
||||
if fb_results.status_code == 200:
|
||||
fb_data = fb_results.json().get('results', [])
|
||||
if any(i.get('wrapperType') == 'track' and i.get('kind') == 'song' for i in fb_data):
|
||||
logger.info(f"Found song tracks via fallback storefront: {fallback}")
|
||||
results = fb_data
|
||||
has_songs = True
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
if not has_songs:
|
||||
logger.warning(f"Album {album_id} has no song tracks in any storefront")
|
||||
return None
|
||||
|
||||
# First result is usually the album/collection info
|
||||
# Extract album information to include in each track (like Spotify does)
|
||||
album_info = None
|
||||
|
|
|
|||
|
|
@ -11653,8 +11653,8 @@ def get_artist_album_tracks(artist_id, album_id):
|
|||
|
||||
# Get album tracks
|
||||
tracks_data = client.get_album_tracks(resolved_album_id)
|
||||
if not tracks_data or 'items' not in tracks_data:
|
||||
return jsonify({"error": "No tracks found for album"}), 404
|
||||
if not tracks_data or 'items' not in tracks_data or len(tracks_data['items']) == 0:
|
||||
return jsonify({"error": "No tracks found for album — it may be region-restricted or unavailable on this metadata source"}), 404
|
||||
|
||||
# Handle both dict and object responses from spotify_client.get_album()
|
||||
if isinstance(album_data, dict):
|
||||
|
|
|
|||
|
|
@ -37520,7 +37520,8 @@ async function createArtistAlbumVirtualPlaylist(album, albumType) {
|
|||
if (response.status === 401) {
|
||||
throw new Error('Spotify not authenticated. Please check your API settings.');
|
||||
}
|
||||
throw new Error(`Failed to load album tracks: ${response.status}`);
|
||||
const errData = await response.json().catch(() => ({}));
|
||||
throw new Error(errData.error || `Failed to load album tracks: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
|
|
|||
Loading…
Reference in a new issue