diff --git a/backend/routers/playlists.py b/backend/routers/playlists.py index 19ca392..dde2b2b 100644 --- a/backend/routers/playlists.py +++ b/backend/routers/playlists.py @@ -189,6 +189,10 @@ def _index_playlist_task(playlist_id: int, youtube_playlist_id: str, channel_id: playlist.video_count = len(video_yt_ids) playlist.indexed_at = datetime.utcnow() playlist.video_ids = json.dumps(video_yt_ids) + # Backfill thumbnail from first video if playlist has none + if not playlist.thumbnail_url and video_yt_ids: + from ..services.ytdlp import _stable_thumbnail + playlist.thumbnail_url = _stable_thumbnail(video_yt_ids[0]) db.commit() except Exception: db.rollback() diff --git a/backend/services/ytdlp.py b/backend/services/ytdlp.py index b148ef2..197fde0 100644 --- a/backend/services/ytdlp.py +++ b/backend/services/ytdlp.py @@ -378,11 +378,20 @@ def fetch_channel_playlists(channel_id: str, max_results: int = 100) -> list[dic title = info.get("title") or info.get("playlist_title") or "" if not pl_id or not title or pl_id == channel_id: continue + # Thumbnail: yt-dlp gives a thumbnails array for playlist entries; + # fall back to singular thumbnail field. Never use _stable_thumbnail + # here because the id is a playlist ID, not a video ID. + thumbs = info.get("thumbnails") or [] + thumb_url = info.get("thumbnail") + if thumbs: + best = max(thumbs, key=lambda t: (t.get("width") or 0) * (t.get("height") or 0), default=None) + if best: + thumb_url = best.get("url") or thumb_url playlists.append({ "youtube_playlist_id": pl_id, "title": title, "description": info.get("description"), - "thumbnail_url": _stable_thumbnail(info.get("id")) if info.get("_type") == "url" else None, + "thumbnail_url": thumb_url, "video_count": info.get("playlist_count") or info.get("n_entries") or 0, }) except json.JSONDecodeError: