/** * compress.js * ----------- * Compression job lifecycle: start, notification opt-in, cancel, and restart. * * Exports * ------- * initCompress() — attach all event listeners; call once at startup */ import { state, els, announce } from './state.js'; import { setupProgressSection, showResults } from './progress.js'; import { startProgressStream } from './stream.js'; import { smtpIsConfigured } from './settings.js'; // ─── Public init ───────────────────────────────────────────────────────────── /** * Attach event listeners for: * - Notification checkbox toggle * - "Compress Selected Files" button * - "Cancel Compression" button * - "Start New Session" (restart) button * * Call once during app initialisation. */ export function initCompress() { _initNotifyToggle(); _initCompressButton(); _initCancelButton(); _initRestartButton(); } // ─── Notification opt-in ───────────────────────────────────────────────────── function _initNotifyToggle() { els.notifyChk.addEventListener('change', () => { const show = els.notifyChk.checked; els.notifyEmailRow.hidden = !show; els.notifyEmail.setAttribute('aria-required', show ? 'true' : 'false'); const warn = document.getElementById('smtp-not-configured-warn'); if (show) { els.notifyEmail.focus(); if (warn) warn.hidden = smtpIsConfigured(); } else { els.notifyEmail.value = ''; if (warn) warn.hidden = true; } }); } // ─── Start compression ──────────────────────────────────────────────────────── function _initCompressButton() { els.compressBtn.addEventListener('click', async () => { const selectedFiles = state.scannedFiles.filter( f => state.selectedPaths.has(f.path), ); if (!selectedFiles.length) return; const suffix = els.suffixInput.value.trim() || '_new'; const notifyEmail = els.notifyChk.checked ? els.notifyEmail.value.trim() : ''; // Client-side email validation if (els.notifyChk.checked) { if (!notifyEmail) { els.notifyEmail.setCustomValidity('Please enter your email address.'); els.notifyEmail.reportValidity(); return; } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(notifyEmail)) { els.notifyEmail.setCustomValidity('Please enter a valid email address.'); els.notifyEmail.reportValidity(); return; } els.notifyEmail.setCustomValidity(''); } els.compressBtn.disabled = true; els.compressBtn.textContent = 'Starting…'; try { const resp = await fetch('/api/compress/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ files: selectedFiles.map(f => ({ path: f.path, size_bytes: f.size_bytes, target_bit_rate_bps: f.target_bit_rate_bps || 1_000_000, codec: f.codec || 'unknown', })), suffix, notify_email: notifyEmail, }), }); const data = await resp.json(); if (!resp.ok) { alert(`Failed to start compression: ${data.error}`); _resetCompressBtn(); return; } state.currentJobId = data.job_id; state.seenEventCount = 0; state.compressionResults = []; sessionStorage.setItem('vp-job-id', data.job_id); setupProgressSection(selectedFiles); els.sectionProgress.hidden = false; els.sectionProgress.scrollIntoView({ behavior: 'smooth', block: 'start' }); announce(`Compression started for ${selectedFiles.length} file(s).`); startProgressStream(data.job_id, selectedFiles); } catch (err) { alert(`Error: ${err.message}`); _resetCompressBtn(); } }); } function _resetCompressBtn() { els.compressBtn.disabled = false; els.compressBtn.innerHTML = ' Compress Selected Files'; } // ─── Cancel ─────────────────────────────────────────────────────────────────── function _initCancelButton() { els.cancelBtn.addEventListener('click', async () => { if (!state.currentJobId) return; if (!confirm( 'Cancel all compression operations? ' + 'Any file currently being processed will be deleted.', )) return; els.cancelBtn.disabled = true; els.cancelBtn.textContent = 'Cancelling…'; try { await fetch(`/api/compress/cancel/${state.currentJobId}`, { method: 'POST' }); announce('Cancellation requested.'); } catch (err) { console.error('Cancel error:', err); } }); } // ─── Restart (new session) ──────────────────────────────────────────────────── function _initRestartButton() { els.restartBtn.addEventListener('click', () => { // Clear state state.scannedFiles = []; state.selectedPaths.clear(); state.currentJobId = null; state.compressionResults = []; state.seenEventCount = 0; if (state.eventSource) { state.eventSource.close(); state.eventSource = null; } if (state.reconnectTimer) { clearTimeout(state.reconnectTimer); state.reconnectTimer = null; } sessionStorage.removeItem('vp-job-id'); // Reset UI els.sectionFiles.hidden = true; els.sectionProgress.hidden = true; els.sectionResults.hidden = true; els.fileTbody.innerHTML = ''; els.fileProgressList.innerHTML = ''; els.scanStatus.textContent = ''; els.notifyChk.checked = false; els.notifyEmailRow.hidden = true; els.notifyEmail.value = ''; els.notifyStatus.hidden = true; els.notifyStatus.textContent = ''; els.streamLostBanner.hidden = true; els.reconnectBtn.hidden = true; els.cancelBtn.disabled = false; els.cancelBtn.textContent = '✕ Cancel Compression'; _resetCompressBtn(); document.getElementById('section-config') .scrollIntoView({ behavior: 'smooth' }); els.dirInput.focus(); announce('Session reset. Ready to scan again.'); }); }