| import { useState, useCallback } from 'react'; |
| import { analyzeMedia } from '../utils/api'; |
|
|
| const ANALYSIS_STAGES = [ |
| { id: 'upload', label: 'Uploading media...', progress: 10 }, |
| { id: 'preprocess', label: 'Pre-processing media...', progress: 24 }, |
| { id: 'inference', label: 'Running model inference...', progress: 48 }, |
| { id: 'gradcam', label: 'Generating explainability layer...', progress: 68 }, |
| { id: 'fft', label: 'Analysing frequency spectrum (FFT)...', progress: 84 }, |
| { id: 'temporal', label: 'Temporal sequence analysis...', progress: 90 }, |
| { id: 'verdict', label: 'Compiling forensic verdict...', progress: 96 }, |
| ]; |
|
|
| export function useAnalysis() { |
| const [phase, setPhase] = useState('idle'); |
| const [currentStage, setCurrentStage] = useState(null); |
| const [progress, setProgress] = useState(0); |
| const [result, setResult] = useState(null); |
| const [file, setFile] = useState(null); |
| const [previewUrl, setPreviewUrl] = useState(null); |
| const [error, setError] = useState(null); |
|
|
| const reset = useCallback(() => { |
| setPhase('idle'); |
| setCurrentStage(null); |
| setProgress(0); |
| setResult(null); |
| setFile(null); |
| if (previewUrl) URL.revokeObjectURL(previewUrl); |
| setPreviewUrl(null); |
| setError(null); |
| }, [previewUrl]); |
|
|
| const analyze = useCallback(async (selectedFile) => { |
| if (!selectedFile) return; |
|
|
| setFile(selectedFile); |
| setPreviewUrl(URL.createObjectURL(selectedFile)); |
| setPhase('uploading'); |
| setError(null); |
| setProgress(0); |
|
|
| let ticker = null; |
| try { |
| const isVideo = selectedFile.type.startsWith('video/'); |
| const relevantStages = isVideo ? ANALYSIS_STAGES : ANALYSIS_STAGES.filter((stage) => stage.id !== 'temporal'); |
|
|
| for (let index = 0; index < relevantStages.length; index += 1) { |
| const stage = relevantStages[index]; |
| setCurrentStage(stage); |
| setPhase(index === 0 ? 'uploading' : 'analyzing'); |
| setProgress(stage.progress); |
| await new Promise((resolve) => setTimeout(resolve, index === 0 ? 200 : 320)); |
| } |
|
|
| ticker = window.setInterval(() => { |
| setProgress((value) => (value < 97 ? value + 1 : value)); |
| }, 260); |
|
|
| const apiResult = await analyzeMedia(selectedFile); |
| window.clearInterval(ticker); |
| setProgress(100); |
| setResult({ |
| ...apiResult, |
| filename: apiResult.filename || selectedFile.name, |
| filesize: apiResult.filesize || formatFileSize(selectedFile.size), |
| }); |
| setPhase('complete'); |
| } catch (analysisError) { |
| if (ticker) window.clearInterval(ticker); |
| setPhase('idle'); |
| setCurrentStage(null); |
| setProgress(0); |
| setResult(null); |
| setError(analysisError.message || 'Analysis failed'); |
| } |
| }, []); |
|
|
| return { phase, currentStage, progress, result, file, previewUrl, error, analyze, reset }; |
| } |
|
|
| function formatFileSize(bytes) { |
| if (bytes < 1024) return `${bytes} B`; |
| if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; |
| return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; |
| } |
|
|