Fix playlist thumbnails — extract from yt-dlp thumbnails array

_stable_thumbnail expects a video ID but was being passed a playlist ID
(PLxxx), producing a broken URL. Now picks the best thumbnail from
yt-dlp's thumbnails array, falling back to the singular thumbnail field.

Also backfills playlist.thumbnail_url from the first video when indexing
a playlist that still has no thumbnail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 22:42:30 +02:00
parent abd7ed7c5a
commit 6cfaca382c
2 changed files with 14 additions and 1 deletions

View File

@@ -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()

View File

@@ -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: