/** * Smart URL classifier — maps an arbitrary string into a structured * `VideoSource`. * * Order: * 1. YouTube (youtube.com, youtu.be, shorts, embed) * 2. Vimeo (vimeo.com, player.vimeo.com) * 3. HLS (`.m3u8`) * 4. Native (`.mp4`, `.webm`, `.mov`, `.mkv`, `.ogv`, `.m4v`) * 5. Fallback — treat as iframe embed. * * Pure; no React, no DOM. */ import type { VideoSource } from '../types'; import { extractYouTubeId, parseYouTubeStartTime } from './youtube-id'; import { extractVimeoId } from './vimeo-id'; const NATIVE_VIDEO_EXT = /\.(mp4|webm|mov|mkv|ogv|m4v)(\?.*)?$/i; const HLS_EXT = /\.m3u8(\?.*)?$/i; export function parseEmbedUrl(input: string): VideoSource { const trimmed = input.trim(); // 1. YouTube. const ytId = extractYouTubeId(trimmed); if (ytId) { let startTime: number | undefined; let playlistId: string | undefined; try { const url = new URL(trimmed); startTime = parseYouTubeStartTime(url.searchParams.get('t')); const list = url.searchParams.get('list'); if (list && /^[\w-]+$/.test(list)) playlistId = list; } catch { /* ignore */ } return { type: 'youtube', videoId: ytId, ...(startTime !== undefined && { startTime }), ...(playlistId && { playlistId }), }; } // 2. Vimeo. const vimeoId = extractVimeoId(trimmed); if (vimeoId) { return { type: 'vimeo', videoId: vimeoId }; } // 3. HLS. if (HLS_EXT.test(trimmed)) { return { type: 'hls', url: trimmed }; } // 4. Native video file. if (NATIVE_VIDEO_EXT.test(trimmed)) { return { type: 'url', url: trimmed }; } // 5. Fallback: blob: / data: / unknown http(s) URL — assume native