Fix Discogs artist page load time — lazy track count loading
- Remove upfront master detail fetching (was 15+ API calls, 40+ seconds) - Discography loads from releases list only (~1 second, 2 API calls) - Track counts populate on-demand via get_album_tracks when clicking album - New get_album_tracks method: tries /masters first, falls back to /releases, returns Spotify-compatible format with proper disc/track numbering - Album type defaults to 'album' for masters without format metadata — Discogs limitation, singles only detectable from individual release format
This commit is contained in:
parent
39d11602ce
commit
d98d3b33c4
1 changed files with 87 additions and 35 deletions
|
|
@ -527,44 +527,17 @@ class DiscogsClient:
|
||||||
|
|
||||||
for item in ordered:
|
for item in ordered:
|
||||||
try:
|
try:
|
||||||
# For master releases, fetch detail to get tracklist and proper metadata
|
|
||||||
if item.get('type') == 'master':
|
|
||||||
master_detail = self._api_get(f'/masters/{item["id"]}')
|
|
||||||
if master_detail:
|
|
||||||
# Merge master detail into the item for better parsing
|
|
||||||
tracklist = [t for t in master_detail.get('tracklist', [])
|
|
||||||
if t.get('type_', '') == 'track' or not t.get('type_')]
|
|
||||||
item['_track_count'] = len(tracklist)
|
|
||||||
# Carry over genres/styles from master
|
|
||||||
if master_detail.get('genres'):
|
|
||||||
item['genre'] = master_detail['genres']
|
|
||||||
if master_detail.get('styles'):
|
|
||||||
item['style'] = master_detail['styles']
|
|
||||||
if master_detail.get('images'):
|
|
||||||
primary = next((img for img in master_detail['images'] if img.get('type') == 'primary'), None)
|
|
||||||
item['cover_image'] = (primary or master_detail['images'][0]).get('uri')
|
|
||||||
|
|
||||||
album = Album.from_discogs_release(item)
|
album = Album.from_discogs_release(item)
|
||||||
|
|
||||||
# Override track count from master detail if available
|
# Use thumb from release list as image
|
||||||
if item.get('_track_count'):
|
thumb = item.get('thumb') or item.get('cover_image') or ''
|
||||||
album = Album(
|
if thumb and 'spacer.gif' not in thumb and not album.image_url:
|
||||||
id=album.id, name=album.name, artists=album.artists,
|
album = Album(id=album.id, name=album.name, artists=album.artists,
|
||||||
release_date=album.release_date, total_tracks=item['_track_count'],
|
release_date=album.release_date, total_tracks=album.total_tracks,
|
||||||
album_type=album.album_type, image_url=album.image_url or item.get('cover_image'),
|
album_type=album.album_type, image_url=thumb,
|
||||||
external_urls=album.external_urls,
|
external_urls=album.external_urls)
|
||||||
)
|
|
||||||
# Re-evaluate album type with actual track count
|
|
||||||
if album.total_tracks <= 3:
|
|
||||||
album = Album(id=album.id, name=album.name, artists=album.artists,
|
|
||||||
release_date=album.release_date, total_tracks=album.total_tracks,
|
|
||||||
album_type='single', image_url=album.image_url, external_urls=album.external_urls)
|
|
||||||
elif album.total_tracks <= 6:
|
|
||||||
album = Album(id=album.id, name=album.name, artists=album.artists,
|
|
||||||
release_date=album.release_date, total_tracks=album.total_tracks,
|
|
||||||
album_type='ep', image_url=album.image_url, external_urls=album.external_urls)
|
|
||||||
|
|
||||||
# Deduplicate by normalized title
|
# Deduplicate by normalized title (but keep deluxe/special editions as separate)
|
||||||
dedup_key = album.name.lower().strip()
|
dedup_key = album.name.lower().strip()
|
||||||
if dedup_key in seen_titles:
|
if dedup_key in seen_titles:
|
||||||
continue
|
continue
|
||||||
|
|
@ -581,6 +554,85 @@ class DiscogsClient:
|
||||||
|
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
|
def get_album_tracks(self, release_id: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Get album tracks by Discogs release or master ID. Returns Spotify-compatible format."""
|
||||||
|
# Try as master first (master IDs are used in artist discography)
|
||||||
|
data = self._api_get(f'/masters/{release_id}')
|
||||||
|
if not data or not data.get('tracklist'):
|
||||||
|
# Fall back to release
|
||||||
|
data = self._api_get(f'/releases/{release_id}')
|
||||||
|
if not data or not data.get('tracklist'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get album image
|
||||||
|
image_url = None
|
||||||
|
images = data.get('images', [])
|
||||||
|
if images:
|
||||||
|
primary = next((img for img in images if img.get('type') == 'primary'), None)
|
||||||
|
image_url = (primary or images[0]).get('uri')
|
||||||
|
|
||||||
|
album_info = {
|
||||||
|
'id': str(data.get('id', release_id)),
|
||||||
|
'name': data.get('title', ''),
|
||||||
|
'images': [{'url': image_url, 'height': 600, 'width': 600}] if image_url else [],
|
||||||
|
'release_date': str(data.get('year', '')) if data.get('year') else '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get artists
|
||||||
|
artists_list = []
|
||||||
|
if data.get('artists'):
|
||||||
|
artists_list = [{'name': a.get('name', '')} for a in data['artists'] if a.get('name')]
|
||||||
|
if not artists_list:
|
||||||
|
artists_list = [{'name': 'Unknown Artist'}]
|
||||||
|
|
||||||
|
tracks = []
|
||||||
|
track_num = 0
|
||||||
|
disc_num = 1
|
||||||
|
for t in data['tracklist']:
|
||||||
|
if t.get('type_') == 'heading':
|
||||||
|
disc_num += 1
|
||||||
|
continue
|
||||||
|
if t.get('type_', '') not in ('track', ''):
|
||||||
|
continue
|
||||||
|
|
||||||
|
track_num += 1
|
||||||
|
|
||||||
|
# Parse duration
|
||||||
|
duration_ms = 0
|
||||||
|
dur_str = t.get('duration', '')
|
||||||
|
if dur_str and ':' in dur_str:
|
||||||
|
parts = dur_str.split(':')
|
||||||
|
try:
|
||||||
|
duration_ms = (int(parts[0]) * 60 + int(parts[1])) * 1000
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Per-track artists or fall back to release artists
|
||||||
|
track_artists = artists_list
|
||||||
|
if t.get('artists'):
|
||||||
|
track_artists = [{'name': a.get('name', '')} for a in t['artists'] if a.get('name')]
|
||||||
|
|
||||||
|
tracks.append({
|
||||||
|
'id': f"{release_id}_t{track_num}",
|
||||||
|
'name': t.get('title', ''),
|
||||||
|
'artists': track_artists,
|
||||||
|
'album': album_info,
|
||||||
|
'duration_ms': duration_ms,
|
||||||
|
'track_number': track_num,
|
||||||
|
'disc_number': disc_num if disc_num > 1 else 1,
|
||||||
|
'explicit': False,
|
||||||
|
'uri': f"discogs:track:{release_id}_{track_num}",
|
||||||
|
'external_urls': {},
|
||||||
|
'_source': 'discogs',
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'items': tracks,
|
||||||
|
'total': len(tracks),
|
||||||
|
'limit': len(tracks),
|
||||||
|
'next': None,
|
||||||
|
}
|
||||||
|
|
||||||
def _get_artist_image_from_albums(self, artist_id: str) -> Optional[str]:
|
def _get_artist_image_from_albums(self, artist_id: str) -> Optional[str]:
|
||||||
"""Get artist image by fetching their first album's cover art.
|
"""Get artist image by fetching their first album's cover art.
|
||||||
Used as fallback when artist has no direct image."""
|
Used as fallback when artist has no direct image."""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue