/** Résumé d'une vidéo listée depuis une chaîne ou une playlist. */ export interface VideoSummary { /** Identifiant YouTube de la vidéo. */ readonly id: string; /** Titre de la vidéo. */ readonly title: string; /** URL complète de la vidéo. */ readonly url: string; /** URL de la miniature, si connue. */ readonly thumbnailUrl: string | null; /** Durée en secondes, si connue (absente pour un live en cours). */ readonly durationSeconds: number | null; /** Date de mise en ligne au format `YYYYMMDD`, si connue. */ readonly uploadDate: string | null; } /** * Extrait l'URL de miniature d'une entrée yt-dlp. En mode `--flat-playlist`, * l'extracteur YouTube ne renseigne quasiment jamais le champ singulier * `thumbnail` : il fournit uniquement `thumbnails`, un tableau d'objets * `{url, width?, height?}` généralement trié par résolution croissante. * * @param entry - Entrée JSON déjà parsée. * @returns L'URL de la miniature de meilleure résolution connue, ou `null`. */ function extractThumbnailUrl(entry: Record): string | null { if (typeof entry.thumbnail === 'string') { return entry.thumbnail; } if (Array.isArray(entry.thumbnails) && entry.thumbnails.length > 0) { const best = entry.thumbnails.at(-1) as Record | undefined; if (best && typeof best.url === 'string') { return best.url; } } return null; } /** * Convertit une entrée de la sortie `yt-dlp --flat-playlist` (ligne de `-j` * ou élément de `entries` de `-J`) en résumé de vidéo. Retourne `null` pour * toute entrée inexploitable (pas un objet, ou sans identifiant, titre ou * URL) plutôt que d'interrompre le traitement des autres entrées. * * @param raw - Valeur JSON déjà parsée, de forme non garantie. * @returns Le résumé extrait, ou `null` si l'entrée n'a pas pu être exploitée. */ export function summarizeEntry(raw: unknown): VideoSummary | null { if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) { return null; } const entry = raw as Record; const id = typeof entry.id === 'string' ? entry.id : null; const title = typeof entry.title === 'string' ? entry.title : null; const url = typeof entry.url === 'string' ? entry.url : null; if (!id || !title || !url) { return null; } return { id, title, url, thumbnailUrl: extractThumbnailUrl(entry), durationSeconds: typeof entry.duration === 'number' ? entry.duration : null, uploadDate: typeof entry.upload_date === 'string' ? entry.upload_date : null, }; }