import type { VideoProvider, VideoMetadata, EmbedOptions, ThumbnailSize } from './types'; import { getCachedMetadata, setCachedMetadata } from './metadata-cache'; // Bunny Stream URL patterns // e.g. https://iframe.mediadelivery.net/play/libraryId/videoId // e.g. https://video.bunnycdn.com/play/libraryId/videoId const BUNNY_PATTERNS = [ /(?:iframe\.mediadelivery\.net|video\.bunnycdn\.com)\/(?:play|embed)\/[0-9]+\/([a-zA-Z0-9_-]+)/, ]; export const bunnyProvider: VideoProvider = { id: 'bunny', name: 'Bunny Stream', icon: 'Video', canHandle(url: string): boolean { return BUNNY_PATTERNS.some(pattern => pattern.test(url)); }, extractVideoId(url: string): string | null { for (const pattern of BUNNY_PATTERNS) { const match = url.match(pattern); if (match && match[1]) { return match[1]; } } return null; }, getEmbedUrl(videoId: string, options: EmbedOptions = {}): string { // Requires library ID, but our current DB only stores `videoId` for standard providers // For Bunny, we typically store the full embed URL as `originalUrl` // So if this function is called, we try to extract it from the environment or default const libraryId = process.env.NEXT_PUBLIC_BUNNY_STREAM_LIBRARY_ID || process.env.BUNNY_STREAM_LIBRARY_ID || '0'; const params = new URLSearchParams(); if (options.autoplay) params.set('autoplay', 'true'); if (options.loop) params.set('loop', 'true'); if (options.muted) params.set('muted', 'true'); // We can use video.bunnycdn.com or iframe.mediadelivery.net return `https://iframe.mediadelivery.net/embed/${libraryId}/${videoId}?${params.toString()}`; }, getThumbnailUrl(videoId: string, size: ThumbnailSize = 'medium'): string { const libraryId = process.env.NEXT_PUBLIC_BUNNY_STREAM_LIBRARY_ID || process.env.BUNNY_STREAM_LIBRARY_ID || '0'; // Bunny stream thumbnails: https://vz-uuid.b-cdn.net/{videoId}/thumbnail.jpg // Since we don't have the b-cdn pull zone readily available in pure abstract, // we should rely on fetching metadata for actual thumbnails, OR construct via API // Actually, Bunny's public thumbnail format is: return `https://vz-965f4f4a-fc1.b-cdn.net/${videoId}/thumbnail.jpg`; // Fallback approximate }, async getMetadata(videoId: string): Promise { const cacheKey = `bunny:${videoId}`; const cached = getCachedMetadata(cacheKey); if (cached) return cached; // We can't fetch title/duration via public API without an API key, // so we return basic metadata. When videos are uploaded via our server, // the title will be passed during creation. const fallback: VideoMetadata = { title: 'Bunny Video', thumbnailUrl: this.getThumbnailUrl(videoId, 'large'), }; setCachedMetadata(cacheKey, fallback); return fallback; }, };