Download ui/src/app/components/VideoInput.tsx from anthony01/LumiSign: direct link, hf CLI and curl.
- Browser
- Download file 5.28 kB
-
https://huggingface.co/spaces/anthony01/LumiSign/resolve/main/ui/src/app/components/VideoInput.tsx
- Command line
-
hf download hf://spaces/anthony01/LumiSign/ui/src/app/components/VideoInput.tsx
-
curl -L -o VideoInput.tsx https://huggingface.co/spaces/anthony01/LumiSign/resolve/main/ui/src/app/components/VideoInput.tsx
5.28 kB
| import { useEffect, useRef } from 'react'; | |
| interface VideoInputProps { | |
| onVideoUpload: (file: File) => void; | |
| videoFile: File | null; | |
| isProcessing: boolean; | |
| onToggleCamera: () => void; | |
| cameraStream: MediaStream | null; | |
| previewUrl: string | null; | |
| } | |
| export function VideoInput({ | |
| onVideoUpload, | |
| videoFile, | |
| isProcessing, | |
| onToggleCamera, | |
| cameraStream, | |
| previewUrl, | |
| }: VideoInputProps) { | |
| const fileInputRef = useRef<HTMLInputElement>(null); | |
| const previewRef = useRef<HTMLVideoElement>(null); | |
| useEffect(() => { | |
| const video = previewRef.current; | |
| if (!video) return; | |
| if (cameraStream) { | |
| video.srcObject = cameraStream; | |
| video.muted = true; | |
| void video.play(); | |
| return; | |
| } | |
| if (previewUrl) { | |
| video.srcObject = null; | |
| video.src = previewUrl; | |
| void video.play(); | |
| return; | |
| } | |
| video.srcObject = null; | |
| video.removeAttribute('src'); | |
| video.load(); | |
| }, [cameraStream, previewUrl]); | |
| const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const file = e.target.files?.[0]; | |
| if (file) { | |
| onVideoUpload(file); | |
| } | |
| }; | |
| return ( | |
| <div className="bg-white rounded-lg border border-gray-200 p-6 mb-6"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div> | |
| <h2 className="text-xl mb-1">Video Input</h2> | |
| <p className="text-sm text-gray-600">Upload a video or use your camera to capture sign language</p> | |
| </div> | |
| <div className="flex gap-2"> | |
| <button | |
| onClick={onToggleCamera} | |
| disabled={isProcessing} | |
| className="px-4 py-2 border border-gray-300 rounded-lg bg-white hover:bg-gray-50 flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-60" | |
| > | |
| <span>📹</span> | |
| Use Camera | |
| </button> | |
| <button | |
| onClick={() => fileInputRef.current?.click()} | |
| disabled={isProcessing} | |
| className="px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-60" | |
| > | |
| <span>⬆</span> | |
| Upload Video | |
| </button> | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| accept="video/*" | |
| onChange={handleFileChange} | |
| className="hidden" | |
| /> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4"> | |
| {/* Original Video Feed */} | |
| <div className="bg-gray-900 rounded-lg p-8 aspect-video flex flex-col items-center justify-center"> | |
| {cameraStream || previewUrl ? ( | |
| <video | |
| ref={previewRef} | |
| className="h-full w-full rounded-lg object-cover" | |
| playsInline | |
| /> | |
| ) : ( | |
| <> | |
| <div className="text-gray-600 mb-3"> | |
| <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"> | |
| <rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect> | |
| <polyline points="23 7 16 12 23 17"></polyline> | |
| </svg> | |
| </div> | |
| <div className="text-gray-500 text-center"> | |
| <div className="font-medium mb-1">Original Video Feed</div> | |
| <div className="text-sm">Camera inactive</div> | |
| </div> | |
| </> | |
| )} | |
| {videoFile && ( | |
| <div className="mt-4 text-white text-sm"> | |
| ✓ {videoFile.name} | |
| </div> | |
| )} | |
| <div className="mt-6 text-gray-600 text-sm"> | |
| Duration: 00:00 | |
| </div> | |
| </div> | |
| {/* Enhanced Video */} | |
| <div className="bg-gray-900 rounded-lg p-8 aspect-video flex flex-col items-center justify-center"> | |
| <div className="text-gray-600 mb-3"> | |
| <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"> | |
| <circle cx="12" cy="12" r="3"></circle> | |
| <circle cx="12" cy="12" r="10"></circle> | |
| <line x1="12" y1="2" x2="12" y2="4"></line> | |
| <line x1="12" y1="20" x2="12" y2="22"></line> | |
| <line x1="4.93" y1="4.93" x2="6.34" y2="6.34"></line> | |
| <line x1="17.66" y1="17.66" x2="19.07" y2="19.07"></line> | |
| <line x1="2" y1="12" x2="4" y2="12"></line> | |
| <line x1="20" y1="12" x2="22" y2="12"></line> | |
| <line x1="6.34" y1="17.66" x2="4.93" y2="19.07"></line> | |
| <line x1="19.07" y1="4.93" x2="17.66" y2="6.34"></line> | |
| </svg> | |
| </div> | |
| <div className="text-gray-500 text-center"> | |
| <div className="font-medium mb-1">Enhanced Video</div> | |
| <div className="text-sm">Brightness correction applied</div> | |
| </div> | |
| <div className="mt-6 flex gap-8 text-sm"> | |
| <div className="text-gray-600"> | |
| Resolution: -- | |
| </div> | |
| <div className="text-gray-600"> | |
| Enhancement: Pending | |
| </div> | |
| <div className="text-gray-600"> | |
| Quality: -- | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |