// Utility to extract preview images from URLs export const extractYouTubeVideoId = (url: string): string | null => { // Handle various YouTube URL formats const patterns = [ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/, /youtube\.com\/shorts\/([^&\n?#]+)/, ] for (const pattern of patterns) { const match = url.match(pattern) if (match && match[1]) { return match[1] } } return null } export const getYouTubeThumbnail = (url: string): string | null => { const videoId = extractYouTubeVideoId(url) if (!videoId) return null // Use maxresdefault for best quality, falls back to hqdefault if not available return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg` } const extractOgImage = (html: string): string | null => { // Extract og:image meta tag from HTML const ogImageMatch = html.match(/]*property=["']og:image["'][^>]*content=["']([^"']+)["'][^>]*>/i) if (ogImageMatch && ogImageMatch[1]) { return ogImageMatch[1] } // Try reversed order (content before property) const ogImageMatch2 = html.match(/]*content=["']([^"']+)["'][^>]*property=["']og:image["'][^>]*>/i) if (ogImageMatch2 && ogImageMatch2[1]) { return ogImageMatch2[1] } return null } // Cache for fetched OG images to avoid repeated requests const ogImageCache = new Map() export const fetchOgImage = async (url: string): Promise => { // Check cache first if (ogImageCache.has(url)) { return ogImageCache.get(url) || null } try { // Use allorigins.win as a free CORS proxy (no auth required) const proxyUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(url)}` const response = await fetch(proxyUrl, { signal: AbortSignal.timeout(5000) // 5 second timeout }) if (!response.ok) { ogImageCache.set(url, null) return null } const data = await response.json() const html = data.contents const ogImage = extractOgImage(html) ogImageCache.set(url, ogImage) return ogImage } catch (error) { console.warn('Failed to fetch OG image for:', url, error) ogImageCache.set(url, null) return null } } export const getPreviewImage = (url: string, type: string): string | null => { // YouTube videos - instant thumbnail if (type === 'youtube') { return getYouTubeThumbnail(url) } // For other URLs, return null and let component fetch async return null }