import { useState, useEffect, useRef } from 'react'; import Navbar from './components/Navbar'; import DashboardTab from './components/DashboardTab'; import TextTranslationTab from './components/TextTranslationTab'; import DocumentTranslationTab from './components/DocumentTranslationTab'; import AudioTranslationTab from './components/AudioTranslationTab'; import VideoTranslationTab from './components/VideoTranslationTab'; import SettingsTab from './components/SettingsTab'; import { LanguageProvider, useLang } from './LanguageContext'; function AppInner() { const { t } = useLang(); const [activeTab, setActiveTab] = useState('dashboard'); const [modelsStatus, setModelsStatus] = useState({ is_cached: false, whisper_cached: false, nllb_cached: false, tts_cached: false, models_dir: '' }); const [isConnected, setIsConnected] = useState(true); const [whisperSize, setWhisperSize] = useState('base'); // Text Translation States const [textInput, setTextInput] = useState(''); const [textOutput, setTextOutput] = useState(''); const [textSrcLang, setTextSrcLang] = useState('auto'); const [textTgtLang, setTextTgtLang] = useState('Hindi'); const [detectedTextLang, setDetectedTextLang] = useState(''); const [isTranslatingText, setIsTranslatingText] = useState(false); const [ttsAudioUrl, setTtsAudioUrl] = useState(''); const [isGeneratingTts, setIsGeneratingTts] = useState(false); // Audio Translation States const [audioFile, setAudioFile] = useState(null); const [audioSrcLang, setAudioSrcLang] = useState('auto'); const [audioTgtLang, setAudioTgtLang] = useState('Hindi'); const [isProcessingAudio, setIsProcessingAudio] = useState(false); const [audioProgress, setAudioProgress] = useState(0); const [audioProgressText, setAudioProgressText] = useState(''); const [audioResult, setAudioResult] = useState(null); const [audioActiveSubTab, setAudioActiveSubTab] = useState('translation'); const [isRecording, setIsRecording] = useState(false); const [recordingTime, setRecordingTime] = useState(0); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); const timerRef = useRef(null); // Video Translation States const [videoFile, setVideoFile] = useState(null); const [videoSrcLang, setVideoSrcLang] = useState('auto'); const [videoTgtLang, setVideoTgtLang] = useState('Hindi'); const [burnSubtitles, setBurnSubtitles] = useState(true); const [overlayVoice, setOverlayVoice] = useState(false); const [isProcessingVideo, setIsProcessingVideo] = useState(false); const [videoProgress, setVideoProgress] = useState(0); const [videoProgressText, setVideoProgressText] = useState(''); const [videoResult, setVideoResult] = useState(null); const [isMenuOpen, setIsMenuOpen] = useState(false); // Document Translation States const [docFile, setDocFile] = useState(null); const [docSrcLang, setDocSrcLang] = useState('auto'); const [docTgtLang, setDocTgtLang] = useState('Hindi'); const [isProcessingDoc, setIsProcessingDoc] = useState(false); const [docResult, setDocResult] = useState(null); const pageTitles = { dashboard: t('page.dashboard'), text: t('page.text'), docs: t('page.docs'), audio: t('page.audio'), video: t('page.video'), settings: t('page.settings'), }; const pageSubtitles = { dashboard: t('page.dashboardSub'), text: t('page.textSub'), docs: t('page.docsSub'), audio: t('page.audioSub'), video: t('page.videoSub'), settings: t('page.settingsSub'), }; const checkServerStatus = async () => { try { const res = await fetch('/api/models-status'); if (res.ok) { const data = await res.json(); setModelsStatus(data); setIsConnected(true); } else { setIsConnected(false); } } catch { setIsConnected(false); } }; useEffect(() => { let cancelled = false; const interval = setInterval(async () => { try { const res = await fetch('/api/models-status'); if (!cancelled) { if (res.ok) { const data = await res.json(); setModelsStatus(data); setIsConnected(true); } else { setIsConnected(false); } } } catch { if (!cancelled) setIsConnected(false); } }, 5000); return () => { cancelled = true; clearInterval(interval); }; }, []); const handleTextTranslate = async () => { if (!textInput.trim()) return; setIsTranslatingText(true); setTtsAudioUrl(''); try { const res = await fetch('/api/translate-text', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: textInput, src_lang: textSrcLang, tgt_lang: textTgtLang }) }); if (res.ok) { const data = await res.json(); setTextOutput(data.translated_text); if (data.detected_src_lang) { setDetectedTextLang(data.detected_src_lang); } } else { alert('Translation failed. Please make sure the backend is running and models are loaded.'); } } catch { alert('Network error connecting to backend.'); } finally { setIsTranslatingText(false); } }; const handleTextToSpeech = async () => { if (!textOutput.trim()) return; setIsGeneratingTts(true); try { const res = await fetch('/api/text-to-speech', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: textOutput, lang: textTgtLang }) }); if (res.ok) { const blob = await res.blob(); const audioUrl = URL.createObjectURL(blob); setTtsAudioUrl(audioUrl); } else { alert('TTS Synthesis failed.'); } } catch { alert('Error generating TTS.'); } finally { setIsGeneratingTts(false); } }; const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorderRef.current = new MediaRecorder(stream); audioChunksRef.current = []; mediaRecorderRef.current.ondataavailable = (e) => { if (e.data.size > 0) audioChunksRef.current.push(e.data); }; mediaRecorderRef.current.onstop = () => { const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/wav' }); const file = new File([audioBlob], "recorded_audio.wav", { type: 'audio/wav' }); setAudioFile(file); setAudioResult(null); stream.getTracks().forEach(track => track.stop()); }; mediaRecorderRef.current.start(); setIsRecording(true); setRecordingTime(0); timerRef.current = setInterval(() => { setRecordingTime(prev => prev + 1); }, 1000); } catch (err) { alert('Could not access microphone: ' + err.message); } }; const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); clearInterval(timerRef.current); } }; const handleAudioUpload = (e) => { const file = e.target.files[0]; if (file) { setAudioFile(file); setAudioResult(null); } }; const processAudio = async () => { if (!audioFile) return; setIsProcessingAudio(true); setAudioProgress(10); setAudioProgressText('Uploading audio file and initializing pipeline...'); const formData = new FormData(); formData.append('file', audioFile); formData.append('model_size', whisperSize); formData.append('src_lang', audioSrcLang); formData.append('tgt_lang', audioTgtLang); try { const progressInterval = setInterval(() => { setAudioProgress((prev) => { if (prev >= 90) { clearInterval(progressInterval); return prev; } if (prev >= 70) { setAudioProgressText('Translating text segments and synthesizing dubbing audio...'); return prev + 1; } if (prev >= 40) { setAudioProgressText('Transcribing speech offline using Whisper ASR...'); return prev + 2; } return prev + 5; }); }, 800); const res = await fetch('/api/translate-audio', { method: 'POST', body: formData }); clearInterval(progressInterval); if (res.ok) { setAudioProgress(100); setAudioProgressText('Synthesis complete!'); const data = await res.json(); setAudioResult(data); } else { alert('Audio processing failed. Check backend logs.'); setIsProcessingAudio(false); } } catch { alert('Error connecting to backend.'); setIsProcessingAudio(false); } finally { setTimeout(() => { setIsProcessingAudio(false); setAudioProgress(0); }, 1000); } }; const handleVideoUpload = (e) => { const file = e.target.files[0]; if (file) { setVideoFile(file); setVideoResult(null); } }; const pollJob = async (job_id, onSuccess, onFail, onProgress) => { const interval = setInterval(async () => { try { const res = await fetch(`/api/jobs/${job_id}`); if (res.ok) { const job = await res.json(); if (onProgress) onProgress(job.progress, job.status); if (job.status === 'completed') { clearInterval(interval); onSuccess(job.result); } else if (job.status === 'failed') { clearInterval(interval); onFail(job.error || 'Job failed'); } } else { clearInterval(interval); onFail('Error checking job status'); } } catch { clearInterval(interval); onFail('Network error checking job status'); } }, 2000); return interval; }; const processVideo = async () => { if (!videoFile) return; setIsProcessingVideo(true); setVideoProgress(5); setVideoProgressText('Uploading video track. Initializing workspace...'); const formData = new FormData(); formData.append('file', videoFile); formData.append('model_size', whisperSize); formData.append('src_lang', videoSrcLang); formData.append('tgt_lang', videoTgtLang); formData.append('burn_subtitles_option', burnSubtitles); formData.append('overlay_voice_option', overlayVoice); try { const res = await fetch('/api/process-video', { method: 'POST', body: formData }); if (res.ok) { const { job_id } = await res.json(); pollJob( job_id, (result) => { setVideoResult(result); setVideoProgress(100); setVideoProgressText('Video processed successfully!'); setTimeout(() => { setIsProcessingVideo(false); setVideoProgress(0); }, 1000); }, (error) => { alert(`Video processing failed: ${error}`); setIsProcessingVideo(false); }, (progress, status) => { setVideoProgress(progress); if (status === 'processing') { if (progress < 40) setVideoProgressText('Running speech-to-text extraction using Whisper...'); else if (progress < 70) setVideoProgressText('Translating timeline and rendering transcript overlays...'); else setVideoProgressText('Injecting subtitle layers and copying streams with FFmpeg...'); } } ); } else { alert('Video processing failed to start.'); setIsProcessingVideo(false); } } catch { alert('Error connecting to video processing endpoint.'); setIsProcessingVideo(false); } }; const handleDocUpload = (e) => { const file = e.target.files[0]; if (file) { setDocFile(file); setDocResult(null); } }; const processDoc = async () => { if (!docFile) return; setIsProcessingDoc(true); const formData = new FormData(); formData.append('file', docFile); formData.append('src_lang', docSrcLang); formData.append('tgt_lang', docTgtLang); try { const res = await fetch('/api/translate-document', { method: 'POST', body: formData }); if (res.ok) { const { job_id } = await res.json(); pollJob( job_id, (result) => { setDocResult(result); setIsProcessingDoc(false); }, (error) => { alert(`Document translation failed: ${error}`); setIsProcessingDoc(false); } ); } else { alert('Document translation failed to start.'); setIsProcessingDoc(false); } } catch { alert('Error connecting to backend.'); setIsProcessingDoc(false); } }; return (
{pageSubtitles[activeTab]}