import type { RichContent } from "../../model/interfaces.ts"; import type { RichContentProvider } from "../rich-content-service.ts"; import { fetchWithTimeout } from "../rich-content-service.ts"; function extractVideoId(url: string): string | null { try { const u = new URL(url); if (u.hostname === "youtu.be") { return u.pathname.slice(1).split("/")[0] || null; } if (u.hostname === "youtube.com" || u.hostname === "www.youtube.com") { if (u.pathname === "/watch" || u.pathname.startsWith("/watch?")) { return u.searchParams.get("v"); } if ( u.pathname.startsWith("/embed/") || u.pathname.startsWith("/shorts/") ) { return u.pathname.split("/")[2] || null; } } } catch { // invalid URL } return null; } export const youtubeProvider: RichContentProvider = { name: "youtube", matches(url: string): boolean { return extractVideoId(url) !== null; }, async fetch(url: string): Promise { const videoId = extractVideoId(url)!; const thumbnailUrl = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`; let title: string | undefined; try { const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`; const res = await fetchWithTimeout(oembedUrl); if (res.ok) { const data = await res.json() as { title?: string }; title = data.title; } } catch { // oembed failed — thumbnail still works } return { type: "youtube", siteName: "YouTube", url, videoId, title, thumbnailUrl, embedUrl: `https://www.youtube.com/embed/${videoId}?rel=0`, }; }, };