Spaces:
Sleeping
Sleeping
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- DOM Elements --- | |
| const verifyBtn = document.getElementById('verifyButton'); | |
| const loader = document.getElementById('loader'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const progressPercent = document.getElementById('progressPercent'); | |
| const loaderText = document.getElementById('loaderText'); | |
| const resultsWrapper = document.getElementById('resultsWrapper'); | |
| const resultsArea = document.getElementById('results'); | |
| const correctedSrtOutput = document.getElementById('correctedSrtOutput'); | |
| const ocrErrorOutput = document.getElementById('ocrErrorOutput'); | |
| const errorMessage = document.getElementById('errorMessage'); | |
| // Stats | |
| const statChunks = document.getElementById('statChunks'); | |
| const statCorrections = document.getElementById('statCorrections'); | |
| const statErrors = document.getElementById('statErrors'); | |
| const statOcrErrors = document.getElementById('statOcrErrors'); | |
| // File Inputs | |
| const pdfUpload = document.getElementById('pdfUpload'); | |
| const srtUpload = document.getElementById('srtUpload'); | |
| // Global Store for Original SRT Content | |
| let originalSrtContent = ""; | |
| // --- Event Listeners --- | |
| if (pdfUpload) { | |
| pdfUpload.addEventListener('change', function() { | |
| if(this.files[0]) document.getElementById('pdfFileName').textContent = this.files[0].name; | |
| }); | |
| } | |
| if (srtUpload) { | |
| srtUpload.addEventListener('change', function() { | |
| if(this.files[0]) { | |
| document.getElementById('srtFileName').textContent = this.files[0].name; | |
| // Read content immediately for later patching | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { originalSrtContent = e.target.result; }; | |
| reader.readAsText(this.files[0]); | |
| } | |
| }); | |
| } | |
| if (verifyBtn) { | |
| verifyBtn.addEventListener('click', async () => { | |
| const apiKeys = document.getElementById('apiKeyInput').value.trim(); | |
| const pdfFile = pdfUpload ? pdfUpload.files[0] : null; | |
| const srtFile = srtUpload ? srtUpload.files[0] : null; | |
| const pagesPerRequest = document.getElementById('pagesPerRequestInput').value; | |
| const modelName = document.getElementById('modelSelect').value; | |
| if (errorMessage) errorMessage.classList.add('hidden'); | |
| if (!apiKeys) return showError("Please enter API Keys."); | |
| if (!pdfFile) return showError("Please upload a PDF."); | |
| if (!srtFile) return showError("Please upload an SRT."); | |
| // Reset UI | |
| verifyBtn.disabled = true; | |
| verifyBtn.classList.add('opacity-50', 'cursor-not-allowed'); | |
| if (loader) loader.classList.remove('hidden'); | |
| if (resultsWrapper) resultsWrapper.classList.add('hidden'); | |
| const formData = new FormData(); | |
| formData.append('api_keys', apiKeys); | |
| formData.append('pdf', pdfFile); | |
| formData.append('srt', srtFile); | |
| formData.append('pages_per_request', pagesPerRequest); | |
| formData.append('model_name', modelName); | |
| // Fake Progress | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| if (progress < 90) { | |
| progress += Math.random() * 5; | |
| if (progress > 90) progress = 90; | |
| if (progressBar) progressBar.style.width = `${progress}%`; | |
| if (progressPercent) progressPercent.textContent = `${Math.round(progress)}%`; | |
| } | |
| }, 800); | |
| try { | |
| const response = await fetch('/verify_batch', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| clearInterval(interval); | |
| const data = await response.json(); | |
| if (!response.ok) throw new Error(data.detail || data.error || "Server Error"); | |
| if (progressBar) progressBar.style.width = '100%'; | |
| if (progressPercent) progressPercent.textContent = '100%'; | |
| setTimeout(() => { | |
| if (loader) loader.classList.add('hidden'); | |
| displayResults(data); | |
| verifyBtn.disabled = false; | |
| verifyBtn.classList.remove('opacity-50', 'cursor-not-allowed'); | |
| }, 500); | |
| } catch (error) { | |
| clearInterval(interval); | |
| if (loader) loader.classList.add('hidden'); | |
| verifyBtn.disabled = false; | |
| verifyBtn.classList.remove('opacity-50', 'cursor-not-allowed'); | |
| showError(error.message); | |
| } | |
| }); | |
| } | |
| // --- Helper Functions --- | |
| function showError(msg) { | |
| if (errorMessage) { | |
| errorMessage.textContent = `Error: ${msg}`; | |
| errorMessage.classList.remove('hidden'); | |
| } else { | |
| alert(msg); | |
| } | |
| } | |
| function displayResults(data) { | |
| if (resultsWrapper) resultsWrapper.classList.remove('hidden'); | |
| // 1. Stats | |
| if (statChunks) statChunks.textContent = data.total_chunks || 0; | |
| if (statErrors) statErrors.textContent = data.system_errors ? data.system_errors.length : 0; | |
| const corrections = data.corrections || []; | |
| let validCorrections = []; | |
| let ocrReports = ""; | |
| // 2. Filter Data | |
| corrections.forEach((item, index) => { | |
| // Only count if it has a correctedSrt field | |
| if (item.correctedSrt) { | |
| validCorrections.push(item); | |
| } | |
| // Collect significant error reports | |
| if (item.errorReport && !item.errorReport.includes("No significant errors")) { | |
| ocrReports += `[Report ${index+1}]\n${item.errorReport}\n\n`; | |
| } | |
| }); | |
| if (statCorrections) statCorrections.textContent = validCorrections.length; | |
| if (statOcrErrors) statOcrErrors.textContent = ocrReports ? "!" : "0"; | |
| // 3. Fill Textareas | |
| if (resultsArea) resultsArea.value = JSON.stringify(data, null, 2); | |
| if (ocrErrorOutput) ocrErrorOutput.value = ocrReports || "No significant OCR errors reported."; | |
| if (correctedSrtOutput) correctedSrtOutput.value = JSON.stringify(validCorrections, null, 2); | |
| } | |
| // --- ROBUST SRT DOWNLOAD LOGIC --- | |
| const downloadSrtBtn = document.getElementById('downloadSrtBtn'); | |
| if (downloadSrtBtn) { | |
| downloadSrtBtn.addEventListener('click', () => { | |
| if (!originalSrtContent) { | |
| alert("Original SRT content is missing. Please re-upload the SRT file to enable patching."); | |
| return; | |
| } | |
| // Parse results from the text area | |
| let corrections = []; | |
| try { | |
| // We use the filtered list we displayed in correctedSrtOutput | |
| corrections = JSON.parse(correctedSrtOutput.value); | |
| } catch (e) { | |
| alert("No valid corrections data found to apply."); | |
| return; | |
| } | |
| if (!corrections || corrections.length === 0) { | |
| alert("There are no corrections to apply."); | |
| return; | |
| } | |
| try { | |
| const patchedSrt = patchSrtFile(originalSrtContent, corrections); | |
| downloadFile(patchedSrt, 'corrected_subtitles.srt'); | |
| } catch (e) { | |
| alert("Error applying patches: " + e.message); | |
| } | |
| }); | |
| } | |
| /** | |
| * Replaces blocks in the original SRT with the corrected blocks. | |
| */ | |
| function patchSrtFile(originalText, corrections) { | |
| // 1. Split original SRT into blocks (Double newline separator) | |
| // Normalize line endings to \n first | |
| const normalizedText = originalText.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); | |
| let blocks = normalizedText.split(/\n\n+/); | |
| // 2. Create a Map for faster updates | |
| // We map "ID" -> Index in the blocks array | |
| let blockMap = new Map(); | |
| blocks.forEach((block, index) => { | |
| const lines = block.trim().split('\n'); | |
| if (lines.length > 0) { | |
| const id = lines[0].trim(); | |
| // Store the index so we can update the array directly | |
| blockMap.set(id, index); | |
| } | |
| }); | |
| // 3. Apply Corrections | |
| let appliedCount = 0; | |
| corrections.forEach(fix => { | |
| // Extract ID from the CORRECTION block (it's usually the first line) | |
| // Example fix.correctedSrt: "10\n00:00:10 --> ... \nHello" | |
| if (!fix.correctedSrt) return; | |
| const fixLines = fix.correctedSrt.trim().split('\n'); | |
| const fixId = fixLines[0].trim(); | |
| if (blockMap.has(fixId)) { | |
| const originalIndex = blockMap.get(fixId); | |
| // REPLACE the entire original block with the new corrected block | |
| blocks[originalIndex] = fix.correctedSrt.trim(); | |
| appliedCount++; | |
| } | |
| }); | |
| console.log(`Applied ${appliedCount} corrections.`); | |
| return blocks.join('\n\n'); | |
| } | |
| function downloadFile(content, filename) { | |
| const blob = new Blob([content], { type: 'text/plain' }); | |
| 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); | |
| } | |
| }); |