Spaces:
Running
Running
Upload 36 files
Browse files
src/components/MultiSourceCaptioningView.tsx
CHANGED
@@ -26,6 +26,8 @@ export default function MultiSourceCaptioningView() {
|
|
26 |
const [webcamActive, setWebcamActive] = useState(false);
|
27 |
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
28 |
const [uploadedUrl, setUploadedUrl] = useState<string>("");
|
|
|
|
|
29 |
|
30 |
const videoRef = useRef<HTMLVideoElement | null>(null);
|
31 |
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
@@ -147,41 +149,39 @@ export default function MultiSourceCaptioningView() {
|
|
147 |
};
|
148 |
}, [mode, isLoaded, prompt, runInference]);
|
149 |
|
150 |
-
// File mode: process uploaded image
|
151 |
-
|
152 |
-
if (
|
153 |
const img = imageRef.current;
|
154 |
const canvas = canvasRef.current;
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
172 |
-
const boxes = extractJsonFromMarkdown(result) || [];
|
173 |
-
drawBoundingBoxesOnCanvas(ctx, boxes);
|
174 |
-
} catch (e) {
|
175 |
-
setError(e instanceof Error ? e.message : String(e));
|
176 |
-
} finally {
|
177 |
-
setProcessing(false);
|
178 |
-
}
|
179 |
-
};
|
180 |
-
}, [mode, isLoaded, prompt, runInference, uploadedFile]);
|
181 |
|
182 |
-
// File mode: process uploaded video frames
|
183 |
useEffect(() => {
|
184 |
-
if (mode !== "File" || !isLoaded || !uploadedFile || !isVideoFile(uploadedFile)) return;
|
185 |
let interval: ReturnType<typeof setInterval> | null = null;
|
186 |
const processFrame = async () => {
|
187 |
if (!videoRef.current || !canvasRef.current) return;
|
@@ -217,14 +217,21 @@ export default function MultiSourceCaptioningView() {
|
|
217 |
return () => {
|
218 |
if (interval) clearInterval(interval);
|
219 |
};
|
220 |
-
}, [mode, isLoaded, prompt, runInference, uploadedFile]);
|
221 |
|
222 |
// Handle file upload
|
223 |
-
const handleFileChange = (e:
|
224 |
const file = e.target.files?.[0] || null;
|
225 |
setUploadedFile(file);
|
226 |
setUploadedUrl(file ? URL.createObjectURL(file) : "");
|
227 |
setError(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
};
|
229 |
|
230 |
return (
|
@@ -360,6 +367,13 @@ export default function MultiSourceCaptioningView() {
|
|
360 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
361 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
362 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
</div>
|
364 |
)}
|
365 |
{/* Show uploaded video */}
|
@@ -379,6 +393,12 @@ export default function MultiSourceCaptioningView() {
|
|
379 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
380 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
381 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
</div>
|
383 |
)}
|
384 |
{/* Show example video if no file uploaded */}
|
|
|
26 |
const [webcamActive, setWebcamActive] = useState(false);
|
27 |
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
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);
|
|
|
149 |
};
|
150 |
}, [mode, isLoaded, prompt, runInference]);
|
151 |
|
152 |
+
// File mode: process uploaded image (only on button click)
|
153 |
+
const handleProcessImage = async () => {
|
154 |
+
if (!isLoaded || !uploadedFile || !isImageFile(uploadedFile) || !imageRef.current || !canvasRef.current) return;
|
155 |
const img = imageRef.current;
|
156 |
const canvas = canvasRef.current;
|
157 |
+
canvas.width = img.naturalWidth;
|
158 |
+
canvas.height = img.naturalHeight;
|
159 |
+
const ctx = canvas.getContext("2d");
|
160 |
+
if (!ctx) return;
|
161 |
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
162 |
+
try {
|
163 |
+
setProcessing(true);
|
164 |
+
setError(null);
|
165 |
+
const fakeVideo = {
|
166 |
+
videoWidth: canvas.width,
|
167 |
+
videoHeight: canvas.height,
|
168 |
+
getContext: () => ctx,
|
169 |
+
} as unknown as HTMLVideoElement;
|
170 |
+
const result = await runInference(fakeVideo, prompt);
|
171 |
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
172 |
+
const boxes = extractJsonFromMarkdown(result) || [];
|
173 |
+
drawBoundingBoxesOnCanvas(ctx, boxes);
|
174 |
+
setImageProcessed(true);
|
175 |
+
} catch (e) {
|
176 |
+
setError(e instanceof Error ? e.message : String(e));
|
177 |
+
} finally {
|
178 |
+
setProcessing(false);
|
179 |
+
}
|
180 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
|
182 |
+
// File mode: process uploaded video frames (start/stop)
|
183 |
useEffect(() => {
|
184 |
+
if (mode !== "File" || !isLoaded || !uploadedFile || !isVideoFile(uploadedFile) || !videoProcessing) return;
|
185 |
let interval: ReturnType<typeof setInterval> | null = null;
|
186 |
const processFrame = async () => {
|
187 |
if (!videoRef.current || !canvasRef.current) return;
|
|
|
217 |
return () => {
|
218 |
if (interval) clearInterval(interval);
|
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;
|
225 |
setUploadedFile(file);
|
226 |
setUploadedUrl(file ? URL.createObjectURL(file) : "");
|
227 |
setError(null);
|
228 |
+
setImageProcessed(false);
|
229 |
+
setVideoProcessing(false);
|
230 |
+
};
|
231 |
+
|
232 |
+
// Handle start/stop for video processing
|
233 |
+
const handleToggleVideoProcessing = () => {
|
234 |
+
setVideoProcessing((prev) => !prev);
|
235 |
};
|
236 |
|
237 |
return (
|
|
|
367 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
368 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
369 |
/>
|
370 |
+
<button
|
371 |
+
className="mt-4 px-6 py-2 rounded-lg bg-blue-600 text-white font-semibold"
|
372 |
+
onClick={handleProcessImage}
|
373 |
+
disabled={processing}
|
374 |
+
>
|
375 |
+
{processing ? "Processing..." : imageProcessed ? "Reprocess Image" : "Process Image"}
|
376 |
+
</button>
|
377 |
</div>
|
378 |
)}
|
379 |
{/* Show uploaded video */}
|
|
|
393 |
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
394 |
style={{ zIndex: 10, pointerEvents: "none" }}
|
395 |
/>
|
396 |
+
<button
|
397 |
+
className="mt-4 px-6 py-2 rounded-lg bg-blue-600 text-white font-semibold"
|
398 |
+
onClick={handleToggleVideoProcessing}
|
399 |
+
>
|
400 |
+
{videoProcessing ? "Stop Processing" : "Start Processing"}
|
401 |
+
</button>
|
402 |
</div>
|
403 |
)}
|
404 |
{/* Show example video if no file uploaded */}
|