From ed6ab6cd5b6fec5f8e7d7cbcaa5224393729164e Mon Sep 17 00:00:00 2001 From: Hyteq Date: Mon, 23 Jun 2025 09:18:15 +0300 Subject: feat: cleaned up video player, added functionality, zooming in/out, timeline position viewer, keyboard controls --- apps/web/src/components/ui/video-player.tsx | 117 ++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 apps/web/src/components/ui/video-player.tsx (limited to 'apps/web/src/components/ui/video-player.tsx') diff --git a/apps/web/src/components/ui/video-player.tsx b/apps/web/src/components/ui/video-player.tsx new file mode 100644 index 00000000..127cc5db --- /dev/null +++ b/apps/web/src/components/ui/video-player.tsx @@ -0,0 +1,117 @@ +"use client"; + +import { useRef, useEffect } from "react"; +import { Button } from "./button"; +import { Play, Pause, Volume2 } from "lucide-react"; +import { usePlaybackStore } from "@/stores/playback-store"; + +interface VideoPlayerProps { + src: string; + poster?: string; + className?: string; +} + +export function VideoPlayer({ src, poster, className = "" }: VideoPlayerProps) { + const videoRef = useRef(null); + const { isPlaying, currentTime, volume, play, pause, setVolume, setDuration, setCurrentTime } = usePlaybackStore(); + + useEffect(() => { + const video = videoRef.current; + if (!video) return; + + const handleTimeUpdate = () => { + setCurrentTime(video.currentTime); + }; + + const handleLoadedMetadata = () => { + setDuration(video.duration); + }; + + const handleSeekEvent = (e: CustomEvent) => { + video.currentTime = e.detail.time; + }; + + video.addEventListener("timeupdate", handleTimeUpdate); + video.addEventListener("loadedmetadata", handleLoadedMetadata); + window.addEventListener("playback-seek", handleSeekEvent as EventListener); + + return () => { + video.removeEventListener("timeupdate", handleTimeUpdate); + video.removeEventListener("loadedmetadata", handleLoadedMetadata); + window.removeEventListener("playback-seek", handleSeekEvent as EventListener); + }; + }, [setCurrentTime, setDuration]); + + useEffect(() => { + const video = videoRef.current; + if (!video) return; + + if (isPlaying) { + video.play().catch(console.error); + } else { + video.pause(); + } + }, [isPlaying]); + + useEffect(() => { + const video = videoRef.current; + if (!video) return; + video.volume = volume; + }, [volume]); + + const handleSeek = (e: React.MouseEvent) => { + const video = videoRef.current; + if (!video) return; + + const rect = video.getBoundingClientRect(); + const x = e.clientX - rect.left; + const percentage = x / rect.width; + const newTime = percentage * video.duration; + + video.currentTime = newTime; + setCurrentTime(newTime); + }; + + return ( +
+