Enhance video playback controls for better user experience

Implement play/pause, mute, and fullscreen toggles in the video modal, along with temporary control visibility. Includes improved error handling for fragment loading stats.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 11420304-80a9-4ef2-adff-cbdaa418ffa8
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/11420304-80a9-4ef2-adff-cbdaa418ffa8/Qpg7dKb
This commit is contained in:
sebastjanartic 2025-08-07 09:44:45 +00:00
parent 1bd4584dcc
commit 4ee20431aa

View File

@ -59,6 +59,10 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
const [showShareMenu, setShowShareMenu] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [videoThumbnail, setVideoThumbnail] = useState<string | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [isMuted, setIsMuted] = useState(false);
const [showControls, setShowControls] = useState(true);
const [controlsTimeout, setControlsTimeout] = useState<NodeJS.Timeout | null>(null);
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
@ -163,12 +167,18 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
console.log('Razlog preklopa: adaptivni algoritem na podlagi omrežne hitrosti');
});
// Fragment loading stats
// Fragment loading stats - fixed error handling
hls.on(Hls.Events.FRAG_LOADED, (event, data) => {
const stats = data.stats;
const loadTime = stats.loading.end - stats.loading.start;
const speed = (stats.total * 8) / loadTime; // bits per ms = kbps
console.log(`Fragment naložen v ${loadTime}ms, hitrost: ${Math.round(speed)} kbps`);
try {
if (data.frag && data.frag.stats) {
const stats = data.frag.stats;
const loadTime = stats.loading.end - stats.loading.start;
const speed = (stats.total * 8) / loadTime; // bits per ms = kbps
console.log(`Fragment naložen v ${loadTime}ms, hitrost: ${Math.round(speed)} kbps`);
}
} catch (error) {
// Ignore stats errors, they don't affect playback
}
});
// Buffer monitoring for dynamic adjustment
@ -222,7 +232,7 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
console.log('Using native video support for MP4');
}
// Capture thumbnail when video loads
// Video event listeners
videoElement.addEventListener('loadeddata', () => {
console.log('Video data loaded, capturing thumbnail');
setTimeout(() => captureVideoThumbnail(), 1000);
@ -232,6 +242,10 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
console.log('Video can play, capturing thumbnail');
captureVideoThumbnail();
});
videoElement.addEventListener('play', () => setIsPlaying(true));
videoElement.addEventListener('pause', () => setIsPlaying(false));
videoElement.addEventListener('volumechange', () => setIsMuted(videoElement.muted));
}
// Cleanup when modal closes
@ -280,6 +294,52 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
}
};
const togglePlay = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
handleVideoPlay();
}
}
};
const toggleMute = () => {
if (videoRef.current) {
videoRef.current.muted = !videoRef.current.muted;
}
};
const toggleFullscreen = () => {
if (videoRef.current) {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
videoRef.current.requestFullscreen();
}
}
};
const showControlsTemporarily = () => {
setShowControls(true);
if (controlsTimeout) {
clearTimeout(controlsTimeout);
}
const timeout = setTimeout(() => {
setShowControls(false);
}, 3000);
setControlsTimeout(timeout);
};
useEffect(() => {
return () => {
if (controlsTimeout) {
clearTimeout(controlsTimeout);
}
};
}, [controlsTimeout]);
const getShareUrl = () => {
if (!video?.id) return window.location.origin;
return `${window.location.origin}?video=${video.id}`;
@ -339,15 +399,21 @@ export default function VideoModal({ video, isOpen, onClose }: VideoModalProps)
</Button>
<div className="relative bg-black rounded-lg overflow-hidden">
<video
ref={videoRef}
className="w-full h-auto max-h-[80vh]"
controls
preload="metadata"
onPlay={handleVideoPlay}
data-testid="video-player"
crossOrigin="anonymous"
<div
className="relative flex items-center justify-center bg-black"
onMouseMove={showControlsTemporarily}
onMouseEnter={() => setShowControls(true)}
onMouseLeave={() => setShowControls(false)}
>
<video
ref={videoRef}
className="w-full h-auto max-h-[80vh] cursor-pointer"
preload="metadata"
onPlay={handleVideoPlay}
data-testid="video-player"
crossOrigin="anonymous"
onClick={togglePlay}
>
Your browser does not support the video tag.
</video>