// 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, `

Download Complete!

${data.successCount}/${data.totalVideos} videos downloaded

${data.playlistTitle ? `

Playlist: ${data.playlistTitle}

` : ''} `); setLoading(button, false); }); eventSource.addEventListener('error', (e) => { let errorMsg = 'Download failed'; try { errorMsg = JSON.parse(e.data).message || errorMsg; } catch {} eventSource.close(); progressContainer.style.display = 'none'; showResult('download-result', false, `

Error

${errorMsg}

`); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); progressContainer.style.display = 'none'; showResult('download-result', false, `

Error

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) => `
  • ${file.name} ${formatSize(file.size)}
  • `).join(''); // Add remove handlers convertFilesList.querySelectorAll('.remove-file').forEach(btn => { btn.addEventListener('click', () => { convertSelectedFiles.splice(parseInt(btn.dataset.index), 1); updateConvertFilesList(); }); }); } function addConvertFiles(files) { const videoAudioFiles = Array.from(files).filter(f => f.type.startsWith('video/') || f.type.startsWith('audio/') || f.name.match(/\.(mp4|avi|mkv|mov|m4a|wav|flac|ogg|webm|wmv|flv)$/i) ); convertSelectedFiles = [...convertSelectedFiles, ...videoAudioFiles]; updateConvertFilesList(); } // Drag & Drop events for convert convertDropZone.addEventListener('click', () => convertFileInput.click()); convertDropZone.addEventListener('dragover', (e) => { e.preventDefault(); convertDropZone.classList.add('drag-over'); }); convertDropZone.addEventListener('dragleave', () => { convertDropZone.classList.remove('drag-over'); }); convertDropZone.addEventListener('drop', (e) => { e.preventDefault(); convertDropZone.classList.remove('drag-over'); addConvertFiles(e.dataTransfer.files); }); convertFileInput.addEventListener('change', () => { addConvertFiles(convertFileInput.files); convertFileInput.value = ''; }); convertClearFilesBtn.addEventListener('click', () => { convertSelectedFiles = []; updateConvertFilesList(); }); // Convert form submit document.getElementById('convert-form').addEventListener('submit', async (e) => { e.preventDefault(); if (convertSelectedFiles.length === 0) return; const button = convertBtn; const bitrate = document.getElementById('convert-bitrate').value; const quality = document.getElementById('convert-quality').value; const convertProgress = document.getElementById('convert-progress'); const convertProgressFill = document.getElementById('convert-progress-fill'); const convertProgressTitle = document.getElementById('convert-progress-title'); const convertProgressPercent = document.getElementById('convert-progress-percent'); const convertProgressInfo = document.getElementById('convert-progress-info'); const convertProgressCurrent = document.getElementById('convert-progress-current'); setLoading(button, true); convertProgress.style.display = 'block'; convertProgressFill.style.width = '0%'; convertProgressTitle.textContent = 'Converting to MP3...'; convertProgressPercent.textContent = '0%'; convertProgressInfo.textContent = `0/${convertSelectedFiles.length} files`; document.getElementById('convert-result').classList.remove('show'); const formData = new FormData(); convertSelectedFiles.forEach(file => formData.append('files', file)); formData.append('bitrate', bitrate); formData.append('quality', quality); try { const response = await fetch(`${API_URL}/convert-to-mp3`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Conversion failed'); convertProgressFill.style.width = '100%'; convertProgressPercent.textContent = '100%'; convertProgressTitle.textContent = 'Conversion Complete!'; convertProgressInfo.textContent = `${data.successCount}/${data.totalFiles} files`; showResult('convert-result', true, `

    Conversion Complete!

    ${data.successCount}/${data.totalFiles} files converted to MP3

    ${data.results.map(r => r.success ? `
    ${r.fileName} ${r.size}
    Download MP3
    ` : `
    ${r.fileName} ${r.error}
    `).join('')}
    `); // Clear selected files after successful conversion convertSelectedFiles = []; updateConvertFilesList(); } catch (error) { showResult('convert-result', false, `

    Error

    ${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) => `
  • ${file.name} ${formatSize(file.size)}
  • `).join(''); // Add remove handlers filesList.querySelectorAll('.remove-file').forEach(btn => { btn.addEventListener('click', () => { selectedFiles.splice(parseInt(btn.dataset.index), 1); updateFilesList(); }); }); } function addFiles(files) { const audioFiles = Array.from(files).filter(f => f.type.startsWith('audio/') || f.name.match(/\.(mp3|wav|m4a|ogg|flac)$/i) ); selectedFiles = [...selectedFiles, ...audioFiles]; updateFilesList(); } // Drag & Drop events dropZone.addEventListener('click', () => fileInput.click()); dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); addFiles(e.dataTransfer.files); }); fileInput.addEventListener('change', () => { addFiles(fileInput.files); fileInput.value = ''; }); clearFilesBtn.addEventListener('click', () => { selectedFiles = []; updateFilesList(); }); // Transcribe form submit document.getElementById('transcribe-form').addEventListener('submit', async (e) => { e.preventDefault(); if (selectedFiles.length === 0) return; const button = transcribeBtn; const language = document.getElementById('transcribe-lang').value; const model = document.getElementById('transcribe-model').value; const transcribeProgress = document.getElementById('transcribe-progress'); const transcribeProgressFill = document.getElementById('transcribe-progress-fill'); const transcribeProgressTitle = document.getElementById('transcribe-progress-title'); const transcribeProgressPercent = document.getElementById('transcribe-progress-percent'); const transcribeProgressInfo = document.getElementById('transcribe-progress-info'); const transcribeProgressCurrent = document.getElementById('transcribe-progress-current'); setLoading(button, true); transcribeProgress.style.display = 'block'; transcribeProgressFill.style.width = '0%'; transcribeProgressTitle.textContent = 'Uploading and transcribing...'; transcribeProgressPercent.textContent = '0%'; transcribeProgressInfo.textContent = `0/${selectedFiles.length} files`; document.getElementById('transcribe-result').classList.remove('show'); const formData = new FormData(); selectedFiles.forEach(file => formData.append('files', file)); if (language) formData.append('language', language); formData.append('model', model); try { const response = await fetch(`${API_URL}/upload-transcribe`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Transcription failed'); transcribeProgressFill.style.width = '100%'; transcribeProgressPercent.textContent = '100%'; transcribeProgressTitle.textContent = 'Transcription Complete!'; transcribeProgressInfo.textContent = `${data.successCount}/${data.totalFiles} files`; showResult('transcribe-result', true, `

    Transcription Complete!

    ${data.successCount}/${data.totalFiles} files transcribed

    ${data.results[0]?.text ? `

    Preview (first file):

    ${data.results[0].text.substring(0, 1000)}${data.results[0].text.length > 1000 ? '...' : ''}
    ` : ''} `); selectedFiles = []; updateFilesList(); } catch (error) { transcribeProgress.style.display = 'none'; showResult('transcribe-result', false, `

    Error

    ${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, `

    Processing Complete!

    ${data.playlistTitle ? `

    Playlist: ${data.playlistTitle}

    ` : ''}

    Downloaded: ${data.downloadedCount}/${data.totalVideos}

    Transcribed: ${data.transcribedCount}/${data.totalVideos}

    ${data.results[0]?.text ? `

    Preview (first file):

    ${data.results[0].text.substring(0, 1000)}${data.results[0].text.length > 1000 ? '...' : ''}
    ` : ''} `); setLoading(button, false); }); eventSource.addEventListener('error', (e) => { let errorMsg = 'Processing failed'; try { errorMsg = JSON.parse(e.data).message || errorMsg; } catch {} eventSource.close(); processProgress.style.display = 'none'; showResult('process-result', false, `

    Error

    ${errorMsg}

    `); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); processProgress.style.display = 'none'; showResult('process-result', false, `

    Error

    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, '

    Error

    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, `

    Translation Complete!

    From: ${data.sourceLanguage} To: ${data.targetLanguage}

    ${data.translatedText}
    `); } catch (error) { showResult('translate-text-result', false, `

    Error

    ${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) => `
  • ${file.name} ${formatSize(file.size)}
  • `).join(''); translateFilesList.querySelectorAll('.remove-file').forEach(btn => { btn.addEventListener('click', () => { translateSelectedFiles.splice(parseInt(btn.dataset.index), 1); updateTranslateFilesList(); }); }); } function addTranslateFiles(files) { const textFiles = Array.from(files).filter(f => f.type === 'text/plain' || f.name.endsWith('.txt') ); translateSelectedFiles = [...translateSelectedFiles, ...textFiles]; updateTranslateFilesList(); } translateDropZone.addEventListener('click', () => translateFileInput.click()); translateDropZone.addEventListener('dragover', (e) => { e.preventDefault(); translateDropZone.classList.add('drag-over'); }); translateDropZone.addEventListener('dragleave', () => { translateDropZone.classList.remove('drag-over'); }); translateDropZone.addEventListener('drop', (e) => { e.preventDefault(); translateDropZone.classList.remove('drag-over'); addTranslateFiles(e.dataTransfer.files); }); translateFileInput.addEventListener('change', () => { addTranslateFiles(translateFileInput.files); translateFileInput.value = ''; }); translateClearFilesBtn.addEventListener('click', () => { translateSelectedFiles = []; updateTranslateFilesList(); }); // File translation form submit document.getElementById('translate-file-form').addEventListener('submit', async (e) => { e.preventDefault(); if (translateSelectedFiles.length === 0) return; const button = translateFileBtn; const sourceLang = document.getElementById('translate-file-source').value; const targetLang = document.getElementById('translate-file-target').value; setLoading(button, true); document.getElementById('translate-file-result').classList.remove('show'); const formData = new FormData(); translateSelectedFiles.forEach(file => formData.append('files', file)); formData.append('targetLang', targetLang); if (sourceLang) formData.append('sourceLang', sourceLang); try { const response = await fetch(`${API_URL}/translate-file`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Translation failed'); showResult('translate-file-result', true, `

    Translation Complete!

    ${data.successCount}/${data.totalFiles} files translated

    ${data.results[0]?.translatedText ? `

    Preview (first file):

    ${data.results[0].translatedText.substring(0, 1000)}${data.results[0].translatedText.length > 1000 ? '...' : ''}
    ` : ''} `); translateSelectedFiles = []; updateTranslateFilesList(); } catch (error) { showResult('translate-file-result', false, `

    Error

    ${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, '

    Error

    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, `

    Summary Complete!

    Model: ${data.model} | Style: ${data.style} | Chunks: ${data.chunks}

    ${data.summary}
    `); } catch (error) { showResult('summarize-text-result', false, `

    Error

    ${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) => `
  • ${file.name} ${formatSize(file.size)}
  • `).join(''); summarizeFilesList.querySelectorAll('.remove-file').forEach(btn => { btn.addEventListener('click', () => { summarizeSelectedFiles.splice(parseInt(btn.dataset.index), 1); updateSummarizeFilesList(); }); }); } function addSummarizeFiles(files) { console.log('Adding files:', files); const textFiles = Array.from(files).filter(f => f.type === 'text/plain' || f.name.endsWith('.txt') ); console.log('Filtered text files:', textFiles); summarizeSelectedFiles = [...summarizeSelectedFiles, ...textFiles]; updateSummarizeFilesList(); } if (summarizeDropZone) { summarizeDropZone.addEventListener('click', () => { console.log('Drop zone clicked'); summarizeFileInput.click(); }); summarizeDropZone.addEventListener('dragover', (e) => { e.preventDefault(); e.stopPropagation(); summarizeDropZone.classList.add('drag-over'); }); summarizeDropZone.addEventListener('dragleave', (e) => { e.preventDefault(); e.stopPropagation(); summarizeDropZone.classList.remove('drag-over'); }); summarizeDropZone.addEventListener('drop', (e) => { e.preventDefault(); e.stopPropagation(); console.log('Files dropped:', e.dataTransfer.files); summarizeDropZone.classList.remove('drag-over'); addSummarizeFiles(e.dataTransfer.files); }); } if (summarizeFileInput) { summarizeFileInput.addEventListener('change', () => { console.log('File input changed:', summarizeFileInput.files); addSummarizeFiles(summarizeFileInput.files); summarizeFileInput.value = ''; }); } if (summarizeClearFilesBtn) { summarizeClearFilesBtn.addEventListener('click', () => { summarizeSelectedFiles = []; updateSummarizeFilesList(); }); } // File summarization form submit document.getElementById('summarize-file-form').addEventListener('submit', async (e) => { e.preventDefault(); if (summarizeSelectedFiles.length === 0) return; const button = summarizeFileBtn; const style = document.getElementById('summarize-file-style').value; const language = document.getElementById('summarize-file-language').value; setLoading(button, true); document.getElementById('summarize-file-result').classList.remove('show'); const formData = new FormData(); summarizeSelectedFiles.forEach(file => formData.append('files', file)); formData.append('style', style); formData.append('language', language); try { const response = await fetch(`${API_URL}/summarize-file`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Summarization failed'); showResult('summarize-file-result', true, `

    Summarization Complete!

    ${data.successCount}/${data.totalFiles} files summarized

    ${data.results[0]?.summary ? `

    Preview (first file):

    ${data.results[0].summary.substring(0, 1000)}${data.results[0].summary.length > 1000 ? '...' : ''}
    ` : ''} `); summarizeSelectedFiles = []; updateSummarizeFilesList(); } catch (error) { showResult('summarize-file-result', false, `

    Error

    ${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, `

    Pipeline Complete!

    ${data.playlistTitle ? `

    Playlist: ${data.playlistTitle}

    ` : ''}

    Downloaded: ${data.downloadedCount}/${data.totalVideos} | Transcribed: ${data.transcribedCount} | Summarized: ${data.summarizedCount}

    ${data.results[0]?.summary ? `

    Summary Preview:

    ${data.results[0].summary.substring(0, 2000)}${data.results[0].summary.length > 2000 ? '...' : ''}
    ` : ''} `); setLoading(button, false); }); eventSource.addEventListener('error', (e) => { let errorMsg = 'Processing failed'; try { errorMsg = JSON.parse(e.data).message || errorMsg; } catch {} eventSource.close(); linkProgress.style.display = 'none'; showResult('summarize-link-result', false, `

    Error

    ${errorMsg}

    `); setLoading(button, false); }); eventSource.onerror = () => { eventSource.close(); linkProgress.style.display = 'none'; showResult('summarize-link-result', false, `

    Error

    Connection lost

    `); setLoading(button, false); }; }); }