import React, { useRef, useEffect, useState } from 'react' import videojs from 'video.js' import 'video.js/dist/video-js.css' const BASE = (import.meta as any).env?.VITE_MEDIA_API_URL || (import.meta as any).env?.VITE_VISION_API_URL || '/api' type Props = { fileId: string | null onClose: () => void } export const VideoModal: React.FC = ({ fileId, onClose }) => { const videoRef = useRef(null) const playerRef = useRef(null) const [useTranscoded, setUseTranscoded] = useState(false) // Try regular stream first (works for downloads), fallback to transcoded if needed const regularSrc = fileId ? `${BASE}/videos/stream?file_id=${encodeURIComponent(fileId)}` : null const transcodedSrc = fileId ? `${BASE}/videos/stream-transcoded?file_id=${encodeURIComponent(fileId)}` : null const src = useTranscoded ? transcodedSrc : regularSrc // Reset transcoded flag when fileId changes useEffect(() => { setUseTranscoded(false) }, [fileId]) useEffect(() => { if (!fileId || !src || !videoRef.current) return // Dispose existing player if any if (playerRef.current) { playerRef.current.dispose() playerRef.current = null } // Initialize Video.js player const player = videojs(videoRef.current, { controls: true, autoplay: false, // Don't autoplay - let user control preload: 'metadata', // Load metadata first, then data on play fluid: false, // Disable fluid mode to respect container boundaries responsive: false, // Disable responsive mode to prevent overflow playbackRates: [0.5, 1, 1.25, 1.5, 2], html5: { vhs: { overrideNative: true }, nativeVideoTracks: false, nativeAudioTracks: false, nativeTextTracks: false }, sources: [ { src: src, type: 'video/mp4' } ] }) playerRef.current = player let errorHandled = false const handleError = () => { const error = player.error() if (error && !errorHandled) { errorHandled = true console.error('Video.js error:', error) console.error('Error code:', error.code) console.error('Error message:', error.message) // If regular stream fails and we haven't tried transcoded yet, switch to transcoded if (!useTranscoded && transcodedSrc) { console.log('Switching to transcoded endpoint...') // Clear the player ref to prevent double disposal playerRef.current = null // Dispose player before switching try { player.dispose() } catch (e) { // Ignore disposal errors } // Use setTimeout to allow cleanup to complete before switching setTimeout(() => { setUseTranscoded(true) }, 100) } else { // Show user-friendly error console.error('Video playback failed. Please try downloading the video instead.') } } } player.on('error', handleError) player.on('loadedmetadata', () => { console.log('Video metadata loaded, duration:', player.duration()) }) player.on('canplay', () => { console.log('Video can play') }) // Cleanup return () => { if (playerRef.current) { playerRef.current.dispose() playerRef.current = null } } }, [fileId, src, useTranscoded, transcodedSrc]) if (!fileId || !src) return null return ( <>
e.stopPropagation()} > {/* Close button - positioned absolutely in top right corner */}

Video Player

Watch your recording

e.stopPropagation()} > Download Video
) } export default VideoModal