// API Base URL const API_URL = ''; // Tab switching document.querySelectorAll('.tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); tab.classList.add('active'); document.getElementById(tab.dataset.tab).classList.add('active'); }); }); // Helper: Show result function showResult(elementId, success, content) { const el = document.getElementById(elementId); el.className = `result show ${success ? 'success' : 'error'}`; el.innerHTML = content; } // Helper: Set loading state function setLoading(button, loading) { button.disabled = loading; button.classList.toggle('loading', loading); } // Format seconds to MM:SS or HH:MM:SS function formatTime(seconds) { if (!seconds || seconds < 0) return '--:--'; const hrs = Math.floor(seconds / 3600); const mins = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); if (hrs > 0) { return `${hrs}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; } return `${mins}:${String(secs).padStart(2, '0')}`; } // Format file size function formatSize(bytes) { if (!bytes) return ''; const units = ['B', 'KB', 'MB', 'GB']; let i = 0; while (bytes >= 1024 && i < units.length - 1) { bytes /= 1024; i++; } return `${bytes.toFixed(1)} ${units[i]}`; } // ==================== DOWNLOAD TAB ==================== const progressContainer = document.getElementById('download-progress'); const progressFill = document.getElementById('progress-fill'); const progressPercent = document.getElementById('progress-percent'); const progressTitle = document.getElementById('progress-title'); const progressEta = document.getElementById('progress-eta'); const progressInfo = document.getElementById('progress-info'); const progressSpeed = document.getElementById('progress-speed'); const progressCurrent = document.getElementById('progress-current'); function updateDownloadProgress(data) { progressFill.style.width = `${data.percent}%`; progressPercent.textContent = `${data.percent}%`; if (data.totalVideos > 1) { progressInfo.textContent = `Video ${data.currentVideo}/${data.totalVideos}`; } else { progressInfo.textContent = ''; } if (data.speed) progressSpeed.textContent = data.speed; if (data.estimatedRemaining) { progressEta.textContent = `ETA: ${formatTime(data.estimatedRemaining)}`; } else if (data.eta) { progressEta.textContent = `ETA: ${data.eta}`; } if (data.title) { progressCurrent.innerHTML = `Downloading: ${data.title}`; } } function resetDownloadProgress() { progressFill.style.width = '0%'; progressPercent.textContent = '0%'; progressTitle.textContent = 'Downloading...'; progressEta.textContent = ''; progressInfo.textContent = ''; progressSpeed.textContent = ''; progressCurrent.textContent = ''; } document.getElementById('download-form').addEventListener('submit', async (e) => { e.preventDefault(); const button = e.target.querySelector('button[type="submit"]'); const url = document.getElementById('download-url').value; const resultDiv = document.getElementById('download-result'); setLoading(button, true); resetDownloadProgress(); progressContainer.style.display = 'block'; resultDiv.classList.remove('show'); const eventSource = new EventSource(`${API_URL}/download-stream?url=${encodeURIComponent(url)}`); eventSource.addEventListener('status', (e) => { progressTitle.textContent = JSON.parse(e.data).message; }); eventSource.addEventListener('info', (e) => { const data = JSON.parse(e.data); progressTitle.textContent = data.totalVideos > 1 ? `Downloading playlist: ${data.playlistTitle} (${data.totalVideos} videos)` : `Downloading: ${data.title}`; }); eventSource.addEventListener('progress', (e) => updateDownloadProgress(JSON.parse(e.data))); eventSource.addEventListener('video-complete', (e) => { const data = JSON.parse(e.data); progressCurrent.innerHTML = `Completed: ${data.title} (${data.videosCompleted}/${data.totalVideos})`; }); eventSource.addEventListener('complete', (e) => { const data = JSON.parse(e.data); eventSource.close(); progressFill.style.width = '100%'; progressPercent.textContent = '100%'; progressTitle.textContent = 'Download Complete!'; progressEta.textContent = `Total: ${formatTime(data.totalTime)}`; showResult('download-result', true, `
${data.successCount}/${data.totalVideos} videos downloaded
${data.playlistTitle ? `Playlist: ${data.playlistTitle}
` : ''}${errorMsg}
`); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); progressContainer.style.display = 'none'; showResult('download-result', false, `Connection lost
`); setLoading(button, false); }; }); // ==================== CONVERT TAB (Video to MP3) ==================== let convertSelectedFiles = []; const convertDropZone = document.getElementById('convert-drop-zone'); const convertFileInput = document.getElementById('convert-file-input'); const convertSelectedFilesDiv = document.getElementById('convert-selected-files'); const convertFilesList = document.getElementById('convert-files-list'); const convertBtn = document.getElementById('convert-btn'); const convertClearFilesBtn = document.getElementById('convert-clear-files'); function updateConvertFilesList() { if (convertSelectedFiles.length === 0) { convertSelectedFilesDiv.style.display = 'none'; convertBtn.disabled = true; return; } convertSelectedFilesDiv.style.display = 'block'; convertBtn.disabled = false; convertFilesList.innerHTML = convertSelectedFiles.map((file, index) => `${data.successCount}/${data.totalFiles} files converted to MP3
${error.message}
`); } finally { setLoading(button, false); setTimeout(() => { convertProgress.style.display = 'none'; }, 1000); } }); // ==================== TRANSCRIBE TAB (Drag & Drop) ==================== let selectedFiles = []; const dropZone = document.getElementById('drop-zone'); const fileInput = document.getElementById('file-input'); const selectedFilesDiv = document.getElementById('selected-files'); const filesList = document.getElementById('files-list'); const transcribeBtn = document.getElementById('transcribe-btn'); const clearFilesBtn = document.getElementById('clear-files'); function updateFilesList() { if (selectedFiles.length === 0) { selectedFilesDiv.style.display = 'none'; transcribeBtn.disabled = true; return; } selectedFilesDiv.style.display = 'block'; transcribeBtn.disabled = false; filesList.innerHTML = selectedFiles.map((file, index) => `${data.successCount}/${data.totalFiles} files transcribed
${error.message}
`); } finally { setLoading(button, false); } }); // ==================== PROCESS TAB (Download + Transcribe) ==================== const processProgress = document.getElementById('process-progress'); const processProgressFill = document.getElementById('process-progress-fill'); const processProgressTitle = document.getElementById('process-progress-title'); const processProgressPercent = document.getElementById('process-progress-percent'); const processProgressPhase = document.getElementById('process-progress-phase'); const processProgressSpeed = document.getElementById('process-progress-speed'); const processProgressCurrent = document.getElementById('process-progress-current'); const processProgressEta = document.getElementById('process-progress-eta'); function resetProcessProgress() { processProgressFill.style.width = '0%'; processProgressPercent.textContent = '0%'; processProgressTitle.textContent = 'Processing...'; processProgressPhase.textContent = ''; processProgressSpeed.textContent = ''; processProgressCurrent.textContent = ''; processProgressEta.textContent = ''; } document.getElementById('process-form').addEventListener('submit', async (e) => { e.preventDefault(); const button = e.target.querySelector('button[type="submit"]'); const url = document.getElementById('process-url').value; const language = document.getElementById('process-lang').value; const model = document.getElementById('process-model').value; const resultDiv = document.getElementById('process-result'); setLoading(button, true); resetProcessProgress(); processProgress.style.display = 'block'; resultDiv.classList.remove('show'); const params = new URLSearchParams({ url }); if (language) params.append('language', language); params.append('model', model); const eventSource = new EventSource(`${API_URL}/process-stream?${params}`); eventSource.addEventListener('status', (e) => { const data = JSON.parse(e.data); processProgressTitle.textContent = data.message; if (data.phase === 'transcribing') { processProgressPhase.textContent = 'Transcribing'; } }); eventSource.addEventListener('info', (e) => { const data = JSON.parse(e.data); processProgressTitle.textContent = data.totalVideos > 1 ? `Processing playlist: ${data.playlistTitle} (${data.totalVideos} videos)` : `Processing: ${data.title}`; }); eventSource.addEventListener('progress', (e) => { const data = JSON.parse(e.data); processProgressFill.style.width = `${data.percent}%`; processProgressPercent.textContent = `${Math.round(data.percent)}%`; processProgressPhase.textContent = data.phaseLabel || ''; if (data.speed) processProgressSpeed.textContent = data.speed; if (data.title) { processProgressCurrent.innerHTML = `${data.phaseLabel}: ${data.title}`; } if (data.totalVideos > 1) { processProgressCurrent.innerHTML += ` (${data.currentVideo}/${data.totalVideos})`; } }); eventSource.addEventListener('video-complete', (e) => { const data = JSON.parse(e.data); processProgressCurrent.innerHTML = `Downloaded: ${data.title}`; }); eventSource.addEventListener('transcribe-complete', (e) => { const data = JSON.parse(e.data); processProgressCurrent.innerHTML = `Transcribed: ${data.title} (${data.videosCompleted}/${data.totalFiles})`; }); eventSource.addEventListener('complete', (e) => { const data = JSON.parse(e.data); eventSource.close(); processProgressFill.style.width = '100%'; processProgressPercent.textContent = '100%'; processProgressTitle.textContent = 'Processing Complete!'; processProgressPhase.textContent = ''; processProgressEta.textContent = `Total: ${formatTime(data.totalTime)}`; showResult('process-result', true, `Playlist: ${data.playlistTitle}
` : ''}Downloaded: ${data.downloadedCount}/${data.totalVideos}
Transcribed: ${data.transcribedCount}/${data.totalVideos}
${errorMsg}
`); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); processProgress.style.display = 'none'; showResult('process-result', false, `Connection lost
`); setLoading(button, false); }; }); // ==================== TRANSLATE CHECKBOXES (Transcribe & Process tabs) ==================== // Transcribe tab checkbox const transcribeTranslateCheckbox = document.getElementById('transcribe-translate'); const transcribeTranslateLang = document.getElementById('transcribe-translate-lang'); transcribeTranslateCheckbox.addEventListener('change', () => { transcribeTranslateLang.disabled = !transcribeTranslateCheckbox.checked; }); // Process tab checkbox const processTranslateCheckbox = document.getElementById('process-translate'); const processTranslateLang = document.getElementById('process-translate-lang'); processTranslateCheckbox.addEventListener('change', () => { processTranslateLang.disabled = !processTranslateCheckbox.checked; }); // ==================== TRANSLATE TAB ==================== // Mode switching const translateModeBtns = document.querySelectorAll('.mode-btn'); const translateTextMode = document.getElementById('translate-text-mode'); const translateFileMode = document.getElementById('translate-file-mode'); translateModeBtns.forEach(btn => { btn.addEventListener('click', () => { translateModeBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); if (btn.dataset.mode === 'text') { translateTextMode.style.display = 'block'; translateFileMode.style.display = 'none'; } else { translateTextMode.style.display = 'none'; translateFileMode.style.display = 'block'; } }); }); // Text translation form document.getElementById('translate-text-form').addEventListener('submit', async (e) => { e.preventDefault(); const button = document.getElementById('translate-text-btn'); const text = document.getElementById('translate-input').value; const sourceLang = document.getElementById('translate-source').value; const targetLang = document.getElementById('translate-target').value; if (!text.trim()) { showResult('translate-text-result', false, 'Please enter text to translate
'); return; } setLoading(button, true); document.getElementById('translate-text-result').classList.remove('show'); try { const response = await fetch(`${API_URL}/translate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, targetLang, sourceLang: sourceLang || null }) }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Translation failed'); showResult('translate-text-result', true, `From: ${data.sourceLanguage} To: ${data.targetLanguage}
${error.message}
`); } finally { setLoading(button, false); } }); // File translation - Drag & Drop let translateSelectedFiles = []; const translateDropZone = document.getElementById('translate-drop-zone'); const translateFileInput = document.getElementById('translate-file-input'); const translateSelectedFilesDiv = document.getElementById('translate-selected-files'); const translateFilesList = document.getElementById('translate-files-list'); const translateFileBtn = document.getElementById('translate-file-btn'); const translateClearFilesBtn = document.getElementById('translate-clear-files'); function updateTranslateFilesList() { if (translateSelectedFiles.length === 0) { translateSelectedFilesDiv.style.display = 'none'; translateFileBtn.disabled = true; return; } translateSelectedFilesDiv.style.display = 'block'; translateFileBtn.disabled = false; translateFilesList.innerHTML = translateSelectedFiles.map((file, index) => `${data.successCount}/${data.totalFiles} files translated
${error.message}
`); } finally { setLoading(button, false); } }); // ==================== SUMMARIZE TAB ==================== // Mode switching const summarizeModeBtns = document.querySelectorAll('.summarize-mode-selector .mode-btn'); const summarizeTextMode = document.getElementById('summarize-text-mode'); const summarizeFileMode = document.getElementById('summarize-file-mode'); const summarizeLinkMode = document.getElementById('summarize-link-mode'); summarizeModeBtns.forEach(btn => { btn.addEventListener('click', () => { summarizeModeBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); summarizeTextMode.style.display = 'none'; summarizeFileMode.style.display = 'none'; if (summarizeLinkMode) summarizeLinkMode.style.display = 'none'; if (btn.dataset.mode === 'text') { summarizeTextMode.style.display = 'block'; } else if (btn.dataset.mode === 'file') { summarizeFileMode.style.display = 'block'; } else if (btn.dataset.mode === 'link' && summarizeLinkMode) { summarizeLinkMode.style.display = 'block'; } }); }); // Text summarization form document.getElementById('summarize-text-form').addEventListener('submit', async (e) => { e.preventDefault(); const button = document.getElementById('summarize-text-btn'); const text = document.getElementById('summarize-input').value; const style = document.getElementById('summarize-style').value; const language = document.getElementById('summarize-language').value; if (!text.trim()) { showResult('summarize-text-result', false, 'Please enter text to summarize
'); return; } setLoading(button, true); document.getElementById('summarize-text-result').classList.remove('show'); try { const response = await fetch(`${API_URL}/summarize`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, style, language }) }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Summarization failed'); showResult('summarize-text-result', true, `Model: ${data.model} | Style: ${data.style} | Chunks: ${data.chunks}
${error.message}
`); } finally { setLoading(button, false); } }); // File summarization - Drag & Drop let summarizeSelectedFiles = []; const summarizeDropZone = document.getElementById('summarize-drop-zone'); const summarizeFileInput = document.getElementById('summarize-file-input'); const summarizeSelectedFilesDiv = document.getElementById('summarize-selected-files'); const summarizeFilesList = document.getElementById('summarize-files-list'); const summarizeFileBtn = document.getElementById('summarize-file-btn'); const summarizeClearFilesBtn = document.getElementById('summarize-clear-files'); function updateSummarizeFilesList() { if (!summarizeSelectedFilesDiv || !summarizeFileBtn || !summarizeFilesList) return; if (summarizeSelectedFiles.length === 0) { summarizeSelectedFilesDiv.style.display = 'none'; summarizeFileBtn.disabled = true; return; } summarizeSelectedFilesDiv.style.display = 'block'; summarizeFileBtn.disabled = false; summarizeFilesList.innerHTML = summarizeSelectedFiles.map((file, index) => `${data.successCount}/${data.totalFiles} files summarized
${error.message}
`); } finally { setLoading(button, false); } }); // ==================== SUMMARIZE LINK MODE (Full Pipeline) ==================== const summarizeLinkForm = document.getElementById('summarize-link-form'); if (summarizeLinkForm) { const linkProgress = document.getElementById('summarize-link-progress'); const linkProgressFill = document.getElementById('summarize-link-progress-fill'); const linkProgressTitle = document.getElementById('summarize-link-progress-title'); const linkProgressPercent = document.getElementById('summarize-link-progress-percent'); const linkProgressPhase = document.getElementById('summarize-link-progress-phase'); const linkProgressSpeed = document.getElementById('summarize-link-progress-speed'); const linkProgressCurrent = document.getElementById('summarize-link-progress-current'); const linkProgressEta = document.getElementById('summarize-link-progress-eta'); function resetLinkProgress() { if (linkProgressFill) linkProgressFill.style.width = '0%'; if (linkProgressPercent) linkProgressPercent.textContent = '0%'; if (linkProgressTitle) linkProgressTitle.textContent = 'Processing...'; if (linkProgressPhase) linkProgressPhase.textContent = ''; if (linkProgressSpeed) linkProgressSpeed.textContent = ''; if (linkProgressCurrent) linkProgressCurrent.textContent = ''; if (linkProgressEta) linkProgressEta.textContent = ''; } summarizeLinkForm.addEventListener('submit', async (e) => { e.preventDefault(); const button = document.getElementById('summarize-link-btn'); const url = document.getElementById('summarize-url').value; const style = document.getElementById('summarize-link-style').value; const language = document.getElementById('summarize-link-language').value; const resultDiv = document.getElementById('summarize-link-result'); setLoading(button, true); resetLinkProgress(); linkProgress.style.display = 'block'; resultDiv.classList.remove('show'); const params = new URLSearchParams({ url, style, language }); const eventSource = new EventSource(`${API_URL}/summarize-stream?${params}`); eventSource.addEventListener('status', (e) => { const data = JSON.parse(e.data); linkProgressTitle.textContent = data.message; if (data.percent !== undefined) { linkProgressFill.style.width = `${data.percent}%`; linkProgressPercent.textContent = `${Math.round(data.percent)}%`; } }); eventSource.addEventListener('info', (e) => { const data = JSON.parse(e.data); linkProgressTitle.textContent = data.totalVideos > 1 ? `Processing playlist: ${data.playlistTitle} (${data.totalVideos} videos)` : `Processing: ${data.title}`; }); eventSource.addEventListener('progress', (e) => { const data = JSON.parse(e.data); linkProgressFill.style.width = `${data.percent}%`; linkProgressPercent.textContent = `${Math.round(data.percent)}%`; linkProgressPhase.textContent = data.phaseLabel || ''; if (data.speed) linkProgressSpeed.textContent = data.speed; if (data.title) { linkProgressCurrent.innerHTML = `${data.phaseLabel}: ${data.title}`; } if (data.totalVideos > 1) { linkProgressCurrent.innerHTML += ` (${data.currentVideo}/${data.totalVideos})`; } }); eventSource.addEventListener('video-complete', (e) => { const data = JSON.parse(e.data); linkProgressCurrent.innerHTML = `Downloaded: ${data.title}`; }); eventSource.addEventListener('transcribe-complete', (e) => { const data = JSON.parse(e.data); linkProgressCurrent.innerHTML = `Transcribed: ${data.title}`; }); eventSource.addEventListener('summarize-complete', (e) => { const data = JSON.parse(e.data); linkProgressCurrent.innerHTML = `Summarized: ${data.title}`; }); eventSource.addEventListener('complete', (e) => { const data = JSON.parse(e.data); eventSource.close(); linkProgressFill.style.width = '100%'; linkProgressPercent.textContent = '100%'; linkProgressTitle.textContent = 'Complete!'; linkProgressPhase.textContent = ''; linkProgressEta.textContent = `Total: ${formatTime(data.totalTime)}`; showResult('summarize-link-result', true, `Playlist: ${data.playlistTitle}
` : ''}Downloaded: ${data.downloadedCount}/${data.totalVideos} | Transcribed: ${data.transcribedCount} | Summarized: ${data.summarizedCount}
${errorMsg}
`); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); linkProgress.style.display = 'none'; showResult('summarize-link-result', false, `Connection lost
`); setLoading(button, false); }; }); }