document.addEventListener('DOMContentLoaded', () => { // DOM Elements const dropZone = document.getElementById('drop-zone'); const fileInput = document.getElementById('file-input'); const filePreview = document.getElementById('file-preview'); const fileName = document.getElementById('file-name'); const fileSize = document.getElementById('file-size'); const removeFileBtn = document.getElementById('remove-file-btn'); const transcribeBtn = document.getElementById('transcribe-btn'); const uploadSection = document.getElementById('upload-section'); const processingSection = document.getElementById('processing-section'); const resultSection = document.getElementById('result-section'); const detectedLang = document.getElementById('detected-lang'); const captionsList = document.getElementById('captions-list'); const fullTextContent = document.getElementById('full-text-content'); const copyTextBtn = document.getElementById('copy-text-btn'); const downloadTxtBtn = document.getElementById('download-txt-btn'); const downloadSrtBtn = document.getElementById('download-srt-btn'); const newTranscriptionBtn = document.getElementById('new-transcription-btn'); const tabBtns = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toast-message'); // State let selectedFile = null; let transcriptionResult = null; // --- File Upload Logic --- // Click to upload dropZone.addEventListener('click', (e) => { if (e.target !== removeFileBtn && !removeFileBtn.contains(e.target)) { fileInput.click(); } }); fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleFile(e.target.files[0]); } }); // Drag and drop dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }); dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('dragover'); }); dropZone.addEventListener('drop', (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); if (e.dataTransfer.files.length > 0) { handleFile(e.dataTransfer.files[0]); } }); function handleFile(file) { // Validate file type const validTypes = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm', 'video/x-matroska']; if (!validTypes.includes(file.type) && !file.name.match(/\.(mp4|mov|avi|webm|mkv)$/i)) { showToast('Invalid file format. Please upload a video.', true); return; } // Validate file size (e.g., 50MB max to prevent huge uploads locally) const maxSize = 50 * 1024 * 1024; if (file.size > maxSize) { showToast('File is too large. Max size is 50MB.', true); return; } selectedFile = file; fileName.textContent = file.name; fileSize.textContent = formatBytes(file.size); dropZone.style.display = 'none'; filePreview.classList.remove('hidden'); transcribeBtn.classList.remove('hidden'); } removeFileBtn.addEventListener('click', (e) => { e.stopPropagation(); selectedFile = null; fileInput.value = ''; dropZone.style.display = 'block'; filePreview.classList.add('hidden'); transcribeBtn.classList.add('hidden'); }); // --- API & Processing --- transcribeBtn.addEventListener('click', async () => { if (!selectedFile) return; // Show processing uploadSection.classList.add('hidden'); processingSection.classList.remove('hidden'); const formData = new FormData(); formData.append('file', selectedFile); const apiKeyInput = document.getElementById('api-key-input'); if (apiKeyInput && apiKeyInput.value.trim() !== '') { formData.append('api_key', apiKeyInput.value.trim()); } let apiUrl = '/api/transcribe'; // Fallback for local development if opened directly or via Live Server if (window.location.protocol === 'file:' || window.location.port === '5500' || window.location.hostname === '127.0.0.1' && window.location.port !== '8000') { apiUrl = 'http://127.0.0.1:8000/api/transcribe'; } try { const response = await fetch(apiUrl, { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `Server error: ${response.status}`); } const data = await response.json(); transcriptionResult = data; renderResults(data); processingSection.classList.add('hidden'); resultSection.classList.remove('hidden'); showToast('Transcription successful!'); } catch (error) { console.error('Transcription error:', error); showToast(error.message || 'Transcription failed', true); // Revert UI processingSection.classList.add('hidden'); uploadSection.classList.remove('hidden'); } }); // --- Result Rendering --- function renderResults(data) { // Language detection let langName = 'Mixed / Unknown'; if (data.segments && data.segments.length > 0) { const lang = data.segments[0].lang || 'hi-en'; if (lang === 'en') langName = 'English'; else if (lang === 'hi') langName = 'Hindi'; else if (lang.includes('hi') && lang.includes('en')) langName = 'Hinglish'; else langName = lang.toUpperCase(); } detectedLang.textContent = `Detected: ${langName}`; // Render full text fullTextContent.textContent = data.text; // Render captions captionsList.innerHTML = ''; if (data.segments && data.segments.length > 0) { data.segments.forEach(seg => { const item = document.createElement('div'); item.className = 'caption-item'; item.innerHTML = `
${seg.ts}
${seg.text}
`; captionsList.appendChild(item); }); } else { captionsList.innerHTML = '
No timeline data available.
'; } } // --- Tabs Logic --- tabBtns.forEach(btn => { btn.addEventListener('click', () => { // Remove active classes tabBtns.forEach(b => b.classList.remove('active')); tabContents.forEach(c => c.classList.remove('active')); // Add active to clicked btn.classList.add('active'); document.getElementById(btn.dataset.target).classList.add('active'); }); }); // --- Actions --- copyTextBtn.addEventListener('click', () => { if (!transcriptionResult || !transcriptionResult.text) return; navigator.clipboard.writeText(transcriptionResult.text).then(() => { showToast('Text copied to clipboard'); }).catch(err => { console.error('Copy failed', err); showToast('Failed to copy text', true); }); }); downloadTxtBtn.addEventListener('click', () => { if (!transcriptionResult || !transcriptionResult.text) return; downloadFile(transcriptionResult.text, 'transcript.txt', 'text/plain'); }); downloadSrtBtn.addEventListener('click', () => { if (!transcriptionResult || !transcriptionResult.srt) { showToast('SRT data not available', true); return; } downloadFile(transcriptionResult.srt, 'subtitles.srt', 'text/plain'); }); newTranscriptionBtn.addEventListener('click', () => { resultSection.classList.add('hidden'); uploadSection.classList.remove('hidden'); // Reset state removeFileBtn.click(); transcriptionResult = null; // Reset tabs tabBtns[0].click(); }); function downloadFile(content, fileName, mimeType) { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // --- Utility --- function formatBytes(bytes, decimals = 2) { if (!+bytes) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } let toastTimeout; function showToast(message, isError = false) { toastMessage.textContent = message; if (isError) { toast.classList.add('error'); toast.querySelector('i').className = 'fa-solid fa-circle-exclamation'; } else { toast.classList.remove('error'); toast.querySelector('i').className = 'fa-solid fa-circle-check'; } toast.classList.add('show'); clearTimeout(toastTimeout); toastTimeout = setTimeout(() => { toast.classList.remove('show'); }, 3000); } });