'use client'; import { useState, useEffect, useRef } from 'react'; import { X, Play, Pause, Volume2, VolumeX, Maximize, Star, Bookmark } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { createPortal } from 'react-dom'; interface Video { id: number; title: string; path: string; size: number; thumbnail: string; type: string; bookmark_count: number; avg_rating: number; star_count: number; } interface FileSystemItem { name: string; path: string; isDirectory: boolean; size: number; thumbnail?: string; type?: string; id?: number; } interface VideoViewerProps { video: Video | FileSystemItem; isOpen: boolean; onClose: () => void; showBookmarks?: boolean; showRatings?: boolean; formatFileSize?: (bytes: number) => string; onBookmark?: (videoId: number) => void; onUnbookmark?: (videoId: number) => void; onRate?: (videoId: number, rating: number) => void; } export default function VideoViewer({ video, isOpen, onClose, showBookmarks = false, showRatings = false, formatFileSize, onBookmark, onUnbookmark, onRate }: VideoViewerProps) { const [isPlaying, setIsPlaying] = useState(false); const [isMuted, setIsMuted] = useState(false); const [volume, setVolume] = useState(1); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [showControls, setShowControls] = useState(true); const [isBookmarked, setIsBookmarked] = useState(false); const [bookmarkCount, setBookmarkCount] = useState(0); const videoRef = useRef(null); // Update local bookmark state when video changes useEffect(() => { if (video && 'bookmark_count' in video) { setIsBookmarked(video.bookmark_count > 0); setBookmarkCount(video.bookmark_count); } }, [video]); useEffect(() => { if (isOpen && videoRef.current && video) { videoRef.current.src = `/api/stream/${('id' in video ? video.id : video.id) || ''}`; videoRef.current.load(); // Auto-play when video is loaded videoRef.current.addEventListener('loadeddata', () => { if (videoRef.current) { videoRef.current.play().then(() => { setIsPlaying(true); }).catch((error) => { console.log('Auto-play prevented by browser:', error); }); } }); } }, [isOpen, video]); // Keyboard shortcuts useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (!videoRef.current) return; switch (e.key) { case 'Escape': e.preventDefault(); onClose(); break; case 'ArrowRight': e.preventDefault(); videoRef.current.currentTime = Math.min( videoRef.current.currentTime + 10, videoRef.current.duration || 0 ); break; case 'ArrowLeft': e.preventDefault(); videoRef.current.currentTime = Math.max( videoRef.current.currentTime - 10, 0 ); break; case ' ': e.preventDefault(); handlePlayPause(); break; case 'f': case 'F': e.preventDefault(); handleFullscreen(); break; case 'm': case 'M': e.preventDefault(); handleMute(); break; } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [isOpen, onClose]); const handlePlayPause = () => { if (videoRef.current) { if (isPlaying) { videoRef.current.pause(); } else { videoRef.current.play(); } setIsPlaying(!isPlaying); } }; const handleMute = () => { if (videoRef.current) { videoRef.current.muted = !isMuted; setIsMuted(!isMuted); } }; const handleVolumeChange = (e: React.ChangeEvent) => { const newVolume = parseFloat(e.target.value); setVolume(newVolume); if (videoRef.current) { videoRef.current.volume = newVolume; } }; const handleTimeUpdate = () => { if (videoRef.current) { setCurrentTime(videoRef.current.currentTime); } }; const handleLoadedMetadata = () => { if (videoRef.current) { setDuration(videoRef.current.duration); } }; const handleSeek = (e: React.ChangeEvent) => { const newTime = parseFloat(e.target.value); if (videoRef.current) { videoRef.current.currentTime = newTime; setCurrentTime(newTime); } }; const handleFullscreen = () => { if (videoRef.current) { if (document.fullscreenElement) { document.exitFullscreen(); } else { videoRef.current.requestFullscreen(); } } }; const handleBookmark = () => { if (onBookmark && video && 'id' in video && video.id !== undefined) { onBookmark(video.id); // Update local state immediately setIsBookmarked(true); setBookmarkCount(prev => prev + 1); } }; const handleUnbookmark = () => { if (onUnbookmark && video && 'id' in video && video.id !== undefined) { onUnbookmark(video.id); // Update local state immediately setIsBookmarked(false); setBookmarkCount(prev => Math.max(0, prev - 1)); } }; const handleRate = (rating: number) => { if (onRate && video && 'id' in video && video.id !== undefined) { onRate(video.id, rating); } }; const getVideoTitle = () => { if (video && 'title' in video) return video.title; if (video && 'name' in video) return video.name; return 'Video'; }; const getVideoSize = () => { if (!video) return '0 Bytes'; if (formatFileSize) { return formatFileSize(video.size); } // Default format function const bytes = video.size; if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const getBookmarkCount = () => { if (video && 'bookmark_count' in video) return video.bookmark_count; return 0; }; const getAvgRating = () => { if (video && 'avg_rating' in video) return video.avg_rating; return 0; }; const getVideoId = () => { if (video && 'id' in video && video.id !== undefined) return video.id; return 0; }; const formatTime = (time: number) => { const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; if (!isOpen || typeof window === 'undefined') return null; return createPortal(
{/* Close button */} {/* Video container */}
setShowControls(true)} onMouseLeave={() => setShowControls(false)} > {/* Title overlay */}

{getVideoTitle()}

{/* Controls overlay */}
{/* Progress bar */}
{formatTime(currentTime)} {formatTime(duration)}
{/* Video Info Bar (similar to photo viewer) */}

{getVideoTitle()}

{getVideoSize()}

{(showBookmarks || showRatings) && (
{showBookmarks && ( )} {showRatings && (
{[1, 2, 3, 4, 5].map((rating) => ( ))}
)}
)}
{/* Control buttons */}
, document.body ); }