Enhance video streaming capabilities and UI integration

- Added support for streaming video files with proper MIME type handling in the media API.
- Implemented transcoding functionality for H.264 compatibility on-the-fly using FFmpeg.
- Updated VideoModal component to utilize Video.js for improved video playback experience.
- Enhanced user interface with download options and better error handling for video playback.
- Updated package.json and package-lock.json to include new dependencies for video.js and related types.
This commit is contained in:
salirezav
2025-10-31 18:29:05 -04:00
parent 00d4e5b275
commit 70f614e9ff
4 changed files with 666 additions and 24 deletions

View File

@@ -1,4 +1,7 @@
import React from 'react'
import React, { useRef, useEffect } 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 = {
@@ -7,8 +10,59 @@ type Props = {
}
export const VideoModal: React.FC<Props> = ({ fileId, onClose }) => {
if (!fileId) return null
const src = `${BASE}/videos/stream?file_id=${encodeURIComponent(fileId)}`
const videoRef = useRef<HTMLVideoElement>(null)
const playerRef = useRef<any>(null)
// Use transcoded endpoint for browser compatibility (H.264)
const src = fileId ? `${BASE}/videos/stream-transcoded?file_id=${encodeURIComponent(fileId)}` : null
useEffect(() => {
if (!fileId || !src || !videoRef.current) return
// Initialize Video.js player
const player = videojs(videoRef.current, {
controls: true,
autoplay: true,
preload: 'auto',
fluid: true,
responsive: true,
playbackRates: [0.5, 1, 1.25, 1.5, 2],
sources: [
{
src: src,
type: 'video/mp4'
}
]
})
playerRef.current = player
player.on('error', () => {
const error = player.error()
if (error) {
console.error('Video.js error:', error)
console.error('Error code:', error.code)
console.error('Error message:', error.message)
}
})
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])
if (!fileId || !src) return null
return (
<div
@@ -31,16 +85,28 @@ export const VideoModal: React.FC<Props> = ({ fileId, onClose }) => {
</button>
<div className="p-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Video Player</h3>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Watch your recording</p>
<div className="flex items-center justify-between">
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Video Player</h3>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">Watch your recording</p>
</div>
<a
href={src}
download
className="px-3 py-1.5 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors"
onClick={(e) => e.stopPropagation()}
>
Download Video
</a>
</div>
</div>
<div className="p-4 bg-black">
<div className="relative w-full" style={{ aspectRatio: '16/9', maxHeight: '70vh' }}>
<video
src={src}
controls
className="w-full h-full rounded-lg object-contain"
autoPlay
ref={videoRef}
className="video-js vjs-default-skin w-full h-full"
playsInline
key={fileId}
/>
</div>
</div>
@@ -50,5 +116,3 @@ export const VideoModal: React.FC<Props> = ({ fileId, onClose }) => {
}
export default VideoModal