3v324v23's picture
feat: initial release of Multiverse AI Studio
4aed5f4
Raw
History Blame Contribute Delete
3.82 kB
import React, { useRef, useState, useEffect } from 'react';
import { Play, Pause, RefreshCw, Volume2, VolumeX, Maximize } from 'lucide-react';
interface VideoPlayerProps {
src: string;
}
export default function VideoPlayer({ src }: VideoPlayerProps) {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const [isMuted, setIsMuted] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
setIsPlaying(false);
setProgress(0);
}, [src]);
const togglePlay = () => {
if (!videoRef.current) return;
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
};
const handleTimeUpdate = () => {
if (!videoRef.current) return;
const percentage = (videoRef.current.currentTime / videoRef.current.duration) * 100;
setProgress(percentage);
};
const toggleMute = () => {
if (!videoRef.current) return;
videoRef.current.muted = !isMuted;
setIsMuted(!isMuted);
};
const handleFullscreen = () => {
if (!videoRef.current) return;
if (videoRef.current.requestFullscreen) {
videoRef.current.requestFullscreen();
}
};
return (
<div className="relative aspect-video w-full rounded-2xl overflow-hidden border border-white/10 bg-black group shadow-2xl animate-in fade-in slide-in-from-bottom-4 duration-700">
<video
ref={videoRef}
src={src}
onTimeUpdate={handleTimeUpdate}
onClick={togglePlay}
onEnded={() => setIsPlaying(false)}
loop
className="w-full h-full object-cover cursor-pointer"
/>
{/* Dark Overlay gradient at bottom */}
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none" />
{/* Floating Center Play Button when paused */}
{!isPlaying && (
<button
onClick={togglePlay}
className="absolute inset-0 m-auto w-16 h-16 rounded-full bg-white/10 hover:bg-white/20 text-white flex items-center justify-center backdrop-blur-md border border-white/20 transition-transform scale-95 hover:scale-105 active:scale-95 shadow-[0_0_30px_rgba(0,0,0,0.5)]"
>
<Play className="w-8 h-8 fill-white ml-1" />
</button>
)}
{/* Floating Control Bar */}
<div className="absolute bottom-4 left-4 right-4 bg-black/60 backdrop-blur-md border border-white/10 px-4 py-3 rounded-xl flex items-center justify-between gap-4 opacity-0 group-hover:opacity-100 transition-opacity duration-300 shadow-lg">
{/* Play / Pause Toggle */}
<button onClick={togglePlay} className="text-gray-300 hover:text-white transition-colors">
{isPlaying ? <Pause className="w-5 h-5 fill-white" /> : <Play className="w-5 h-5 fill-white" />}
</button>
{/* Progress track */}
<div className="flex-1 h-1 bg-gray-800 rounded-full overflow-hidden relative">
<div
className="absolute top-0 bottom-0 left-0 bg-gradient-to-r from-fuchsia-500 to-pink-500 rounded-full"
style={{ width: `${progress}%` }}
/>
</div>
{/* Control buttons */}
<div className="flex items-center space-x-4">
<button onClick={toggleMute} className="text-gray-300 hover:text-white transition-colors">
{isMuted ? <VolumeX className="w-5 h-5 text-red-400" /> : <Volume2 className="w-5 h-5" />}
</button>
<button onClick={handleFullscreen} className="text-gray-300 hover:text-white transition-colors">
<Maximize className="w-4 h-4" />
</button>
</div>
</div>
</div>
);
}