feat(playlist): show thumbnails in import preview

플레이리스트 가져오기 미리보기 목록에 각 항목 썸네일을 표시한다.
YouTube video id 로 mqdefault 썸네일 URL 을 만들고, id 가 없으면
yt-dlp 가 준 thumbnail 필드로 폴백. 깨진 이미지는 빈 박스 처리.

- youtube.ts: PlaylistEntry 에 thumbnailUrl 추가 + flat-playlist 파싱에서 채움
- playlist.js: probe 매핑에 thumbnailUrl 반영, 각 행에 img 렌더(lazy + onerror)
- styles.css: 행 그리드에 썸네일 칼럼(96px 16:9) 추가

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
Claude (owner)
2026-06-01 22:52:05 +09:00
parent 0e39836c1f
commit 0faf7af42a
4 changed files with 34 additions and 3 deletions

View File

@@ -97,6 +97,8 @@ export interface PlaylistEntry {
title: string
/** 메타에 없으면 null. UI 표시용. */
durationSec: number | null
/** 미리보기용 썸네일 URL. 못 구하면 null. */
thumbnailUrl: string | null
/** 1 부터 시작하는 원래 플레이리스트 위치 — 시퀀셜 ID zero-pad 자릿수 계산용. */
originalIndex: number
}
@@ -157,7 +159,14 @@ export async function probeYoutubePlaylist(url: string): Promise<PlaylistProbeRe
const dur = obj.duration
const durationSec =
typeof dur === 'number' && Number.isFinite(dur) ? Math.round(dur) : null
entries.push({ url: entryUrl, title, durationSec, originalIndex: idx })
// 썸네일: YouTube video id 가 있으면 표준 mqdefault URL 로 만든다(목록용 320x180).
// id 가 없으면 yt-dlp 가 준 thumbnail 필드를 폴백으로 사용.
const thumbnailUrl = id
? `https://i.ytimg.com/vi/${id}/mqdefault.jpg`
: typeof obj.thumbnail === 'string' && obj.thumbnail
? obj.thumbnail
: null
entries.push({ url: entryUrl, title, durationSec, thumbnailUrl, originalIndex: idx })
}
if (entries.length === 0) {
reject(new Error('플레이리스트에서 항목을 찾지 못했습니다.'))