'use client' import { useState, useEffect, useRef } from 'react' import Link from 'next/link' interface Video { name: string size: number } function formatSize(bytes: number): string { if (!bytes) return '' const units = ['B', 'KB', 'MB', 'GB'] let i = 0 let b = bytes while (b >= 1024 && i < units.length - 1) { b /= 1024 i++ } return `${b.toFixed(1)} ${units[i]}` } function getIcon(filename: string): string { const ext = filename.split('.').pop()?.toLowerCase() || '' if (['mp4', 'webm', 'mov'].includes(ext)) return '\uD83C\uDFAC' if (['mkv', 'avi', 'wmv', 'flv'].includes(ext)) return '\u26A0\uFE0F' return '\uD83D\uDCC4' } function isPlayable(filename: string): boolean { const ext = filename.split('.').pop()?.toLowerCase() || '' return ['mp4', 'webm', 'mov', 'ogg', 'm4v'].includes(ext) } export default function DemoPage() { const [videos, setVideos] = useState([]) const [filteredVideos, setFilteredVideos] = useState([]) const [currentVideo, setCurrentVideo] = useState(null) const [search, setSearch] = useState('') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const videoRef = useRef(null) useEffect(() => { fetch('/api/videos') .then((res) => { if (!res.ok) throw new Error('Failed to load videos') return res.json() }) .then((data) => { setVideos(data) setFilteredVideos(data) setLoading(false) }) .catch((err) => { setError(err.message) setLoading(false) }) }, []) useEffect(() => { const q = search.toLowerCase() setFilteredVideos(videos.filter((v) => v.name.toLowerCase().includes(q))) }, [search, videos]) function playVideo(key: string) { setCurrentVideo(key) } function copyLink() { if (!currentVideo) return navigator.clipboard.writeText(`${window.location.origin}/api/v/${encodeURIComponent(currentVideo)}`) } const ext = currentVideo?.split('.').pop()?.toLowerCase() || '' const playable = currentVideo ? isPlayable(currentVideo) : false return (
{/* Nav */}

Video Library

{/* Sidebar */}
setSearch(e.target.value)} className="w-full px-4 py-3 bg-black/30 border border-slate-700 rounded-lg text-white placeholder-slate-500 mb-4 text-sm focus:outline-none focus:border-red-500" />

Library

{loading &&

Loading videos...

} {error &&

Error: {error}

}
    {filteredVideos.map((v) => (
  • playVideo(v.name)} className={`flex items-center gap-3 px-3 py-2 rounded-lg cursor-pointer transition-all text-sm ${currentVideo === v.name ? 'bg-red-500/20 border-l-2 border-red-500' : 'hover:bg-slate-700/50'} ${!isPlayable(v.name) ? 'opacity-60' : ''}`} > {getIcon(v.name)} {v.name} {formatSize(v.size)}
  • ))} {!loading && filteredVideos.length === 0 && (
  • No videos found
  • )}
{/* Player */}
{!currentVideo && (

Select a video to play

)} {currentVideo && !playable && (

{'\u26A0\uFE0F'}

{ext.toUpperCase()} files cannot play in browsers

Download to play locally, or re-record in MP4 format

)} {currentVideo && playable && ( )}
{currentVideo && (

{currentVideo}

Download
)}
) }