video_press/static/js/modules/compress.js

195 lines
6.6 KiB
JavaScript

/**
* 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 =
'<span class="btn-icon-prefix" aria-hidden="true">⚡</span> 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.');
});
}