Spaces:
Running
Running
Upload 36 files
Browse files
src/components/MultiSourceCaptioningView.tsx
CHANGED
@@ -28,6 +28,8 @@ export default function MultiSourceCaptioningView() {
|
|
28 |
const [uploadedUrl, setUploadedUrl] = useState<string>("");
|
29 |
const [videoProcessing, setVideoProcessing] = useState(false);
|
30 |
const [imageProcessed, setImageProcessed] = useState(false);
|
|
|
|
|
31 |
|
32 |
const videoRef = useRef<HTMLVideoElement | null>(null);
|
33 |
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
@@ -109,9 +111,9 @@ export default function MultiSourceCaptioningView() {
|
|
109 |
};
|
110 |
}, [mode, isLoaded, prompt, runInference, webcamActive]);
|
111 |
|
112 |
-
//
|
113 |
useEffect(() => {
|
114 |
-
if (mode !== "URL" || !isLoaded) return;
|
115 |
let interval: ReturnType<typeof setInterval> | null = null;
|
116 |
const processFrame = async () => {
|
117 |
if (!videoRef.current || !canvasRef.current) return;
|
@@ -147,7 +149,7 @@ export default function MultiSourceCaptioningView() {
|
|
147 |
return () => {
|
148 |
if (interval) clearInterval(interval);
|
149 |
};
|
150 |
-
}, [mode, isLoaded, prompt, runInference]);
|
151 |
|
152 |
// File mode: process uploaded image (only on button click)
|
153 |
const handleProcessImage = async () => {
|
@@ -219,6 +221,46 @@ export default function MultiSourceCaptioningView() {
|
|
219 |
};
|
220 |
}, [mode, isLoaded, prompt, runInference, uploadedFile, videoProcessing]);
|
221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
// Handle file upload
|
223 |
const handleFileChange = (e: any) => {
|
224 |
const file = e.target.files?.[0] || null;
|
@@ -227,6 +269,7 @@ export default function MultiSourceCaptioningView() {
|
|
227 |
setError(null);
|
228 |
setImageProcessed(false);
|
229 |
setVideoProcessing(false);
|
|
|
230 |
};
|
231 |
|
232 |
// Handle start/stop for video processing
|
@@ -234,6 +277,16 @@ export default function MultiSourceCaptioningView() {
|
|
234 |
setVideoProcessing((prev) => !prev);
|
235 |
};
|
236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
return (
|
238 |
<div className="absolute inset-0 text-white">
|
239 |
<div className="flex flex-col items-center justify-center h-full w-full">
|
@@ -328,6 +381,12 @@ export default function MultiSourceCaptioningView() {
|
|
328 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
329 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
330 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
</div>
|
332 |
{processing && <div className="text-blue-400 mt-2">Processing frame...</div>}
|
333 |
{error && <div className="text-red-400 mt-2">Error: {error}</div>}
|
@@ -418,6 +477,12 @@ export default function MultiSourceCaptioningView() {
|
|
418 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
419 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
420 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
421 |
</div>
|
422 |
)}
|
423 |
{processing && <div className="text-blue-400 mt-2">Processing frame...</div>}
|
|
|
28 |
const [uploadedUrl, setUploadedUrl] = useState<string>("");
|
29 |
const [videoProcessing, setVideoProcessing] = useState(false);
|
30 |
const [imageProcessed, setImageProcessed] = useState(false);
|
31 |
+
const [exampleProcessing, setExampleProcessing] = useState(false);
|
32 |
+
const [urlProcessing, setUrlProcessing] = useState(false);
|
33 |
|
34 |
const videoRef = useRef<HTMLVideoElement | null>(null);
|
35 |
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
|
111 |
};
|
112 |
}, [mode, isLoaded, prompt, runInference, webcamActive]);
|
113 |
|
114 |
+
// URL mode: process video frames only when urlProcessing is true
|
115 |
useEffect(() => {
|
116 |
+
if (mode !== "URL" || !isLoaded || !urlProcessing) return;
|
117 |
let interval: ReturnType<typeof setInterval> | null = null;
|
118 |
const processFrame = async () => {
|
119 |
if (!videoRef.current || !canvasRef.current) return;
|
|
|
149 |
return () => {
|
150 |
if (interval) clearInterval(interval);
|
151 |
};
|
152 |
+
}, [mode, isLoaded, prompt, runInference, urlProcessing]);
|
153 |
|
154 |
// File mode: process uploaded image (only on button click)
|
155 |
const handleProcessImage = async () => {
|
|
|
221 |
};
|
222 |
}, [mode, isLoaded, prompt, runInference, uploadedFile, videoProcessing]);
|
223 |
|
224 |
+
// File mode: process example video frames (start/stop)
|
225 |
+
useEffect(() => {
|
226 |
+
if (mode !== "File" || uploadedFile || !isLoaded || !exampleProcessing) return;
|
227 |
+
let interval: ReturnType<typeof setInterval> | null = null;
|
228 |
+
const processFrame = async () => {
|
229 |
+
if (!videoRef.current || !canvasRef.current) return;
|
230 |
+
const video = videoRef.current;
|
231 |
+
const canvas = canvasRef.current;
|
232 |
+
if (video.paused || video.ended || video.videoWidth === 0) return;
|
233 |
+
canvas.width = video.videoWidth;
|
234 |
+
canvas.height = video.videoHeight;
|
235 |
+
const ctx = canvas.getContext("2d");
|
236 |
+
if (!ctx) return;
|
237 |
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
238 |
+
try {
|
239 |
+
setProcessing(true);
|
240 |
+
setError(null);
|
241 |
+
const fakeVideo = {
|
242 |
+
videoWidth: canvas.width,
|
243 |
+
videoHeight: canvas.height,
|
244 |
+
getContext: () => ctx,
|
245 |
+
} as unknown as HTMLVideoElement;
|
246 |
+
const result = await runInference(fakeVideo, prompt);
|
247 |
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
248 |
+
const boxes = extractJsonFromMarkdown(result) || [];
|
249 |
+
drawBoundingBoxesOnCanvas(ctx, boxes);
|
250 |
+
} catch (e) {
|
251 |
+
setError(e instanceof Error ? e.message : String(e));
|
252 |
+
} finally {
|
253 |
+
setProcessing(false);
|
254 |
+
}
|
255 |
+
};
|
256 |
+
interval = setInterval(() => {
|
257 |
+
processFrame();
|
258 |
+
}, 1000);
|
259 |
+
return () => {
|
260 |
+
if (interval) clearInterval(interval);
|
261 |
+
};
|
262 |
+
}, [mode, isLoaded, prompt, runInference, uploadedFile, exampleProcessing]);
|
263 |
+
|
264 |
// Handle file upload
|
265 |
const handleFileChange = (e: any) => {
|
266 |
const file = e.target.files?.[0] || null;
|
|
|
269 |
setError(null);
|
270 |
setImageProcessed(false);
|
271 |
setVideoProcessing(false);
|
272 |
+
setExampleProcessing(false);
|
273 |
};
|
274 |
|
275 |
// Handle start/stop for video processing
|
|
|
277 |
setVideoProcessing((prev) => !prev);
|
278 |
};
|
279 |
|
280 |
+
// Handle start/stop for example video processing
|
281 |
+
const handleToggleExampleProcessing = () => {
|
282 |
+
setExampleProcessing((prev) => !prev);
|
283 |
+
};
|
284 |
+
|
285 |
+
// Handle start/stop for URL video processing
|
286 |
+
const handleToggleUrlProcessing = () => {
|
287 |
+
setUrlProcessing((prev) => !prev);
|
288 |
+
};
|
289 |
+
|
290 |
return (
|
291 |
<div className="absolute inset-0 text-white">
|
292 |
<div className="flex flex-col items-center justify-center h-full w-full">
|
|
|
381 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
382 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
383 |
/>
|
384 |
+
<button
|
385 |
+
className="mt-4 px-6 py-2 rounded-lg bg-blue-600 text-white font-semibold"
|
386 |
+
onClick={handleToggleUrlProcessing}
|
387 |
+
>
|
388 |
+
{urlProcessing ? "Stop Processing" : "Start Processing"}
|
389 |
+
</button>
|
390 |
</div>
|
391 |
{processing && <div className="text-blue-400 mt-2">Processing frame...</div>}
|
392 |
{error && <div className="text-red-400 mt-2">Error: {error}</div>}
|
|
|
477 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
478 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
479 |
/>
|
480 |
+
<button
|
481 |
+
className="mt-4 px-6 py-2 rounded-lg bg-blue-600 text-white font-semibold"
|
482 |
+
onClick={handleToggleExampleProcessing}
|
483 |
+
>
|
484 |
+
{exampleProcessing ? "Stop Processing" : "Start Processing"}
|
485 |
+
</button>
|
486 |
</div>
|
487 |
)}
|
488 |
{processing && <div className="text-blue-400 mt-2">Processing frame...</div>}
|