/* ── Upload Area ── */ function UploadArea({ file, onFileChange }) { const inputRef = React.useRef(); return (
inputRef.current.click()} >
Drop a video here or click to upload — MP4, AVI, MOV, max 200 MB
{file &&
{file.name}
} { onFileChange(e.target.files[0] || null); e.target.value = ''; }} />
); } /* ── Status Spinner ── */ function StatusIndicator({ status }) { if (!status || status === 'done') return null; return ( <>

{STATUS_MESSAGES[status] || 'Processing\u2026'}

); } /* ── Video Comparison ── */ const VideoComparison = React.memo(function VideoComparison({ file, processedUrl, isProcessing }) { const videoRef = React.useRef(null); const [localUrl, setLocalUrl] = React.useState(null); React.useEffect(() => { if (file) { const url = URL.createObjectURL(file); setLocalUrl(url); return () => URL.revokeObjectURL(url); } setLocalUrl(null); }, [file]); if (!localUrl) return null; const showProcessed = processedUrl || isProcessing; return (

{showProcessed ? 'Face Detection' : 'Uploaded Video'}

{showProcessed && (
{processedUrl ?
); }); /* ── Face Row ── */ function FaceRow({ face, index }) { const pct = (face.score * 100).toFixed(2); const w = (face.score * 100).toFixed(1); return (
{`Face
{pct}% authentic
); } /* ── Results Panel ── */ function ResultsPanel({ data }) { if (!data || !data.result) return null; const cls = data.result.toLowerCase(); return (

Results

Authenticity score: likelihood the face is real.{' '} Red <20%,{' '} Orange 20-80%,{' '} Green >80%.

{data.result}
Confidence: {data.confidence}%
Model Score: {data.score}
Faces Analyzed: {data.num_faces}
{data.faces_detail && data.faces_detail.map((face, i) => ( ))}
); } /* ── Product Page ── */ function ProductPage({ file, setFile, status, setStatus, error, setError, result, setResult, submitting, setSubmitting }) { const timerRef = React.useRef(null); const reset = () => { setResult(null); setError(null); setStatus(null); }; const handleFileChange = (f) => { setFile(f); reset(); }; const pollJob = React.useCallback((jobId) => { timerRef.current = setTimeout(async () => { try { const res = await fetch(`/status/${jobId}`); const data = await res.json(); setStatus(data.status); if (data.result) { setResult(prev => ({ ...prev, ...data })); } if (data.status === 'done') { setSubmitting(false); if (data.error && !data.result) setError(data.error); } else { pollJob(jobId); } } catch { setSubmitting(false); setStatus(null); setError('Connection lost. Please try again.'); } }, 1000); }, []); React.useEffect(() => () => { if (timerRef.current) clearTimeout(timerRef.current); }, []); const handleSubmit = async (e) => { e.preventDefault(); if (!file) return; reset(); setSubmitting(true); setStatus('uploading'); const fd = new FormData(); fd.append('video', file); try { const res = await fetch('/predict', { method: 'POST', body: fd }); const data = await res.json(); if (data.error) { setError(data.error); setSubmitting(false); setStatus(null); } else { pollJob(data.job_id); } } catch { setError('Upload failed. Please try again.'); setSubmitting(false); setStatus(null); } }; return ( <>

AI-Deepfake Video Detection

Free deepfake detection tool for videos. Upload a video and get per-face authenticity scores in seconds. AI-powered synthetic face detection.

{error &&
{error}
}
AI Deepfake Detection
); }