Improve artist handling and preserve album data in sync
Enhanced handling of artist data to support both string and object formats across the database, sync service, and web server. The sync process now preserves full album and artist objects for tracks, enabling wishlist additions with album cover art. The frontend and API were updated to use the full artist objects, and the UI now formats artist names correctly.
This commit is contained in:
parent
90732cc029
commit
bab31218bb
4 changed files with 72 additions and 20 deletions
|
|
@ -2303,7 +2303,16 @@ class MusicDatabase:
|
|||
|
||||
track_name = spotify_track_data.get('name', 'Unknown Track')
|
||||
artists = spotify_track_data.get('artists', [])
|
||||
artist_name = artists[0].get('name', 'Unknown Artist') if artists else 'Unknown Artist'
|
||||
if artists:
|
||||
first_artist = artists[0]
|
||||
if isinstance(first_artist, str):
|
||||
artist_name = first_artist
|
||||
elif isinstance(first_artist, dict):
|
||||
artist_name = first_artist.get('name', 'Unknown Artist')
|
||||
else:
|
||||
artist_name = 'Unknown Artist'
|
||||
else:
|
||||
artist_name = 'Unknown Artist'
|
||||
|
||||
# Check for duplicates by track name + artist (not just Spotify ID)
|
||||
# This prevents adding the same track multiple times with different IDs or edge cases
|
||||
|
|
@ -2319,7 +2328,16 @@ class MusicDatabase:
|
|||
existing_data = json.loads(existing['spotify_data'])
|
||||
existing_name = existing_data.get('name', '')
|
||||
existing_artists = existing_data.get('artists', [])
|
||||
existing_artist = existing_artists[0].get('name', '') if existing_artists else ''
|
||||
if existing_artists:
|
||||
existing_first = existing_artists[0]
|
||||
if isinstance(existing_first, str):
|
||||
existing_artist = existing_first
|
||||
elif isinstance(existing_first, dict):
|
||||
existing_artist = existing_first.get('name', '')
|
||||
else:
|
||||
existing_artist = ''
|
||||
else:
|
||||
existing_artist = ''
|
||||
|
||||
# Case-insensitive comparison of track name and primary artist
|
||||
if (existing_name.lower() == track_name.lower() and
|
||||
|
|
|
|||
|
|
@ -167,7 +167,13 @@ class PlaylistSyncService:
|
|||
|
||||
# Update progress for each track
|
||||
progress_percent = 20 + (40 * (i + 1) / total_tracks) # 20-60% for matching
|
||||
current_track_name = f"{track.artists[0]} - {track.name}" if track.artists else track.name
|
||||
# Extract artist name from both string and dict formats
|
||||
if track.artists:
|
||||
first_artist = track.artists[0]
|
||||
artist_name = first_artist if isinstance(first_artist, str) else (first_artist.get('name', 'Unknown') if isinstance(first_artist, dict) else str(first_artist))
|
||||
current_track_name = f"{artist_name} - {track.name}"
|
||||
else:
|
||||
current_track_name = track.name
|
||||
self._update_progress(playlist.name, "Matching tracks", current_track_name, progress_percent, 5, 2,
|
||||
total_tracks=total_tracks,
|
||||
matched_tracks=len([r for r in match_results if r.is_match]),
|
||||
|
|
@ -266,17 +272,25 @@ class PlaylistSyncService:
|
|||
for match_result in unmatched_tracks:
|
||||
spotify_track = match_result.spotify_track
|
||||
|
||||
# Convert SpotifyTrack to dict format expected by wishlist service
|
||||
spotify_track_data = {
|
||||
'id': spotify_track.id,
|
||||
'name': spotify_track.name,
|
||||
'artists': [{'name': a} if isinstance(a, str) else a for a in spotify_track.artists],
|
||||
'album': {'name': spotify_track.album},
|
||||
'duration_ms': spotify_track.duration_ms,
|
||||
'popularity': getattr(spotify_track, 'popularity', 0),
|
||||
'preview_url': getattr(spotify_track, 'preview_url', None),
|
||||
'external_urls': getattr(spotify_track, 'external_urls', {})
|
||||
}
|
||||
# Check if we have original track data with full album objects
|
||||
original_track_data = None
|
||||
if hasattr(self, '_original_tracks_map') and self._original_tracks_map:
|
||||
original_track_data = self._original_tracks_map.get(spotify_track.id)
|
||||
|
||||
# Use original data if available (preserves album images), otherwise convert
|
||||
if original_track_data:
|
||||
spotify_track_data = original_track_data
|
||||
else:
|
||||
spotify_track_data = {
|
||||
'id': spotify_track.id,
|
||||
'name': spotify_track.name,
|
||||
'artists': [{'name': a} if isinstance(a, str) else a for a in spotify_track.artists],
|
||||
'album': {'name': spotify_track.album},
|
||||
'duration_ms': spotify_track.duration_ms,
|
||||
'popularity': getattr(spotify_track, 'popularity', 0),
|
||||
'preview_url': getattr(spotify_track, 'preview_url', None),
|
||||
'external_urls': getattr(spotify_track, 'external_urls', {})
|
||||
}
|
||||
|
||||
# Add to wishlist with source context
|
||||
success = wishlist_service.add_spotify_track_to_wishlist(
|
||||
|
|
@ -344,8 +358,14 @@ class PlaylistSyncService:
|
|||
for artist in spotify_track.artists:
|
||||
if self._cancelled:
|
||||
return None, 0.0
|
||||
|
||||
artist_name = artist if isinstance(artist, str) else artist
|
||||
|
||||
# Extract artist name from both string and dict formats
|
||||
if isinstance(artist, str):
|
||||
artist_name = artist
|
||||
elif isinstance(artist, dict) and 'name' in artist:
|
||||
artist_name = artist['name']
|
||||
else:
|
||||
artist_name = str(artist)
|
||||
|
||||
# Use the improved database check_track_exists method with server awareness
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -12547,7 +12547,7 @@ def get_playlist_tracks(playlist_id):
|
|||
tracks.append({
|
||||
'id': track_data['id'],
|
||||
'name': track_data['name'],
|
||||
'artists': [artist['name'] for artist in track_data['artists']],
|
||||
'artists': track_data['artists'], # Full artist objects (matches Download Missing Tracks behavior)
|
||||
'album': track_data['album'], # Full album object
|
||||
'duration_ms': track_data['duration_ms'],
|
||||
'popularity': track_data.get('popularity', 0),
|
||||
|
|
@ -12589,7 +12589,7 @@ def get_playlist_tracks(playlist_id):
|
|||
tracks.append({
|
||||
'id': track_data['id'],
|
||||
'name': track_data['name'],
|
||||
'artists': [artist['name'] for artist in track_data['artists']],
|
||||
'artists': track_data['artists'], # Full artist objects (matches Download Missing Tracks behavior)
|
||||
'album': track_data['album'], # Full album object with album_type, total_tracks, etc.
|
||||
'duration_ms': track_data['duration_ms'],
|
||||
'popularity': track_data.get('popularity', 0),
|
||||
|
|
@ -14502,6 +14502,14 @@ def _run_sync_task(playlist_id, playlist_name, tracks_json):
|
|||
# Recreate a Playlist object from the JSON data sent by the frontend
|
||||
# This avoids needing to re-fetch it from Spotify
|
||||
print(f"🔄 Converting JSON tracks to SpotifyTrack objects...")
|
||||
|
||||
# Store original track data with full album objects (for wishlist with cover art)
|
||||
original_tracks_map = {}
|
||||
for t in tracks_json:
|
||||
track_id = t.get('id', '')
|
||||
if track_id:
|
||||
original_tracks_map[track_id] = t
|
||||
|
||||
tracks = []
|
||||
for i, t in enumerate(tracks_json):
|
||||
# Handle album field - extract name if it's a dictionary
|
||||
|
|
@ -14666,7 +14674,10 @@ def _run_sync_task(playlist_id, playlist_name, tracks_json):
|
|||
setup_duration = (sync_start_time - task_start_time) * 1000
|
||||
print(f"⏱️ [TIMING] Setup completed at {time.strftime('%H:%M:%S')} (took {setup_duration:.1f}ms)")
|
||||
print(f"🚀 Starting actual sync process with asyncio.run()...")
|
||||
|
||||
|
||||
# Attach original tracks map to sync_service for wishlist with album images
|
||||
sync_service._original_tracks_map = original_tracks_map
|
||||
|
||||
# Run the sync (this is a blocking call within this thread)
|
||||
result = asyncio.run(sync_service.sync_playlist(playlist, download_missing=False))
|
||||
|
||||
|
|
@ -14704,6 +14715,9 @@ def _run_sync_task(playlist_id, playlist_name, tracks_json):
|
|||
# Clean up the callback
|
||||
if sync_service:
|
||||
sync_service.clear_progress_callback(playlist.name)
|
||||
# Clean up original tracks map
|
||||
if hasattr(sync_service, '_original_tracks_map'):
|
||||
del sync_service._original_tracks_map
|
||||
print(f"✅ Cleanup completed for {playlist_id}")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3975,7 +3975,7 @@ function showPlaylistDetailsModal(playlist) {
|
|||
<span class="playlist-track-number">${index + 1}</span>
|
||||
<div class="playlist-track-info">
|
||||
<div class="playlist-track-name">${escapeHtml(track.name)}</div>
|
||||
<div class="playlist-track-artists">${track.artists.join(', ')}</div>
|
||||
<div class="playlist-track-artists">${formatArtists(track.artists)}</div>
|
||||
</div>
|
||||
<div class="playlist-track-duration">${formatDuration(track.duration_ms)}</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue