Fix playlist cover art not syncing to Plex/Jellyfin from Deezer and other sources
- Pass playlist image_url to _run_sync_task from all source-specific sync start handlers (Deezer, Tidal, Spotify public, YouTube, automation mirror) — previously only the /api/sync/start endpoint passed it - Fix plex_client.set_playlist_image: use uploadPoster(url=) instead of uploadPoster(data=) which is not a valid PlexAPI argument - deezer_client: use picture_xl > picture_big > picture_medium fallback for better cover art resolution - tidal_client: extract image_url in get_playlist() from JSON:API relationships (was only extracted in metadata-only listing) - parse_youtube_playlist: capture playlist thumbnail from yt-dlp result - Add visible logging for image upload attempts and outcomes
This commit is contained in:
parent
fe399636b2
commit
71a56bf9b7
4 changed files with 43 additions and 25 deletions
|
|
@ -1114,7 +1114,7 @@ class DeezerClient:
|
|||
'name': data.get('title', ''),
|
||||
'description': data.get('description', ''),
|
||||
'track_count': total_tracks,
|
||||
'image_url': data.get('picture_medium', ''),
|
||||
'image_url': data.get('picture_xl') or data.get('picture_big') or data.get('picture_medium', ''),
|
||||
'owner': data.get('creator', {}).get('name', ''),
|
||||
'tracks': tracks,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -480,19 +480,16 @@ class PlexClient:
|
|||
return False
|
||||
|
||||
def set_playlist_image(self, playlist_name: str, image_url: str) -> bool:
|
||||
"""Set the poster image for a playlist by downloading from a URL."""
|
||||
"""Set the poster image for a playlist from a URL."""
|
||||
if not self.ensure_connection() or not image_url:
|
||||
return False
|
||||
try:
|
||||
playlist = self.server.playlist(playlist_name)
|
||||
import requests as _req
|
||||
img_resp = _req.get(image_url, timeout=15)
|
||||
if img_resp.ok and img_resp.content:
|
||||
playlist.uploadPoster(data=img_resp.content)
|
||||
logger.info(f"Set playlist poster for '{playlist_name}'")
|
||||
return True
|
||||
playlist.uploadPoster(url=image_url)
|
||||
logger.info(f"Set playlist poster for '{playlist_name}'")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not set playlist poster for '{playlist_name}': {e}")
|
||||
logger.warning(f"Could not set playlist poster for '{playlist_name}': {e}")
|
||||
return False
|
||||
|
||||
def _find_track(self, title: str, artist: str, album: str) -> Optional[PlexTrack]:
|
||||
|
|
|
|||
|
|
@ -1180,6 +1180,17 @@ class TidalClient:
|
|||
public=playlist_attrs.get('accessType', '') == "PUBLIC"
|
||||
)
|
||||
|
||||
# Extract cover image URL from relationships (same logic as get_user_playlists_metadata_only)
|
||||
try:
|
||||
relationships = playlist_data.get('relationships', {})
|
||||
image_rel = relationships.get('image', {}).get('data', {})
|
||||
if image_rel:
|
||||
image_id = image_rel.get('id', '')
|
||||
if image_id:
|
||||
playlist.image_url = f"https://resources.tidal.com/images/{image_id.replace('-', '/')}/640x640.jpg"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
logger.info(f"Retrieved Tidal playlist '{playlist.name}' with {len(tracks)} tracks")
|
||||
return playlist
|
||||
|
||||
|
|
|
|||
|
|
@ -957,7 +957,7 @@ def _register_automation_handlers():
|
|||
log_line=f'Starting sync: {len(tracks_json)} tracks', log_type='success')
|
||||
threading.Thread(
|
||||
target=_run_sync_task,
|
||||
args=(sync_id, pl['name'], tracks_json, auto_id),
|
||||
args=(sync_id, pl['name'], tracks_json, auto_id, 1, pl.get('image_url', '')),
|
||||
daemon=True,
|
||||
name=f'auto-sync-{playlist_id}'
|
||||
).start()
|
||||
|
|
@ -17391,7 +17391,8 @@ def parse_youtube_playlist(url):
|
|||
'tracks': tracks,
|
||||
'track_count': len(tracks),
|
||||
'url': url,
|
||||
'source': 'youtube'
|
||||
'source': 'youtube',
|
||||
'image_url': playlist_info.get('thumbnail', '') or '',
|
||||
}
|
||||
|
||||
print(f"Successfully parsed YouTube playlist: {len(tracks)} tracks extracted")
|
||||
|
|
@ -34410,14 +34411,15 @@ def start_tidal_sync(playlist_id):
|
|||
|
||||
with sync_lock:
|
||||
sync_states[sync_playlist_id] = {"status": "starting", "progress": {}}
|
||||
|
||||
|
||||
# Submit sync task
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id())
|
||||
playlist_image_url = getattr(state['playlist'], 'image_url', '')
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id(), playlist_image_url)
|
||||
active_sync_workers[sync_playlist_id] = future
|
||||
|
||||
|
||||
print(f"Started Tidal sync for: {playlist_name} ({len(spotify_tracks)} tracks)")
|
||||
return jsonify({"success": True, "sync_playlist_id": sync_playlist_id})
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting Tidal sync: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
|
@ -35338,7 +35340,8 @@ def start_deezer_sync(playlist_id):
|
|||
sync_states[sync_playlist_id] = {"status": "starting", "progress": {}}
|
||||
|
||||
# Submit sync task
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id())
|
||||
playlist_image_url = state['playlist'].get('image_url', '')
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id(), playlist_image_url)
|
||||
active_sync_workers[sync_playlist_id] = future
|
||||
|
||||
print(f"Started Deezer sync for: {playlist_name} ({len(spotify_tracks)} tracks)")
|
||||
|
|
@ -36175,7 +36178,8 @@ def start_spotify_public_sync(url_hash):
|
|||
sync_states[sync_playlist_id] = {"status": "starting", "progress": {}}
|
||||
|
||||
# Submit sync task
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id())
|
||||
playlist_image_url = state['playlist'].get('image_url', '')
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id(), playlist_image_url)
|
||||
active_sync_workers[sync_playlist_id] = future
|
||||
|
||||
print(f"Started Spotify Public sync for: {playlist_name} ({len(spotify_tracks)} tracks)")
|
||||
|
|
@ -37204,14 +37208,15 @@ def start_youtube_sync(url_hash):
|
|||
|
||||
with sync_lock:
|
||||
sync_states[sync_playlist_id] = {"status": "starting", "progress": {}}
|
||||
|
||||
|
||||
# Submit sync task
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id())
|
||||
playlist_image_url = state['playlist'].get('image_url', '')
|
||||
future = sync_executor.submit(_run_sync_task, sync_playlist_id, sync_data['playlist_name'], spotify_tracks, None, get_current_profile_id(), playlist_image_url)
|
||||
active_sync_workers[sync_playlist_id] = future
|
||||
|
||||
|
||||
print(f"Started YouTube sync for: {playlist_name} ({len(spotify_tracks)} tracks)")
|
||||
return jsonify({"success": True, "sync_playlist_id": sync_playlist_id})
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting YouTube sync: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
|
@ -37834,16 +37839,21 @@ def _run_sync_task(playlist_id, playlist_name, tracks_json, automation_id=None,
|
|||
print(f"Sync finished for {playlist_id} - state updated")
|
||||
|
||||
# Set playlist poster image if available (Plex, Jellyfin, Emby)
|
||||
if playlist_image_url and getattr(result, 'synced_tracks', 0) > 0:
|
||||
_synced = getattr(result, 'synced_tracks', 0)
|
||||
print(f"[PLAYLIST IMAGE] image_url={playlist_image_url!r}, synced_tracks={_synced}")
|
||||
if playlist_image_url and _synced > 0:
|
||||
try:
|
||||
active_server = config_manager.get_active_media_server()
|
||||
print(f"[PLAYLIST IMAGE] active_server={active_server}")
|
||||
if active_server == 'plex' and plex_client:
|
||||
plex_client.set_playlist_image(playlist_name, playlist_image_url)
|
||||
ok = plex_client.set_playlist_image(playlist_name, playlist_image_url)
|
||||
print(f"[PLAYLIST IMAGE] Plex upload result: {ok}")
|
||||
elif active_server in ('jellyfin', 'emby') and jellyfin_client:
|
||||
jellyfin_client.set_playlist_image(playlist_name, playlist_image_url)
|
||||
ok = jellyfin_client.set_playlist_image(playlist_name, playlist_image_url)
|
||||
print(f"[PLAYLIST IMAGE] Jellyfin upload result: {ok}")
|
||||
# Navidrome doesn't support custom playlist images
|
||||
except Exception as img_err:
|
||||
print(f"Could not set playlist image: {img_err}")
|
||||
print(f"[PLAYLIST IMAGE] Exception: {img_err}")
|
||||
|
||||
# Record sync history completion with per-track data
|
||||
try:
|
||||
|
|
|
|||
Loading…
Reference in a new issue