Updates galore. Improved folder structure, componentized, and notifications upon completion.
This commit is contained in:
parent
b48784e2ad
commit
7e0502ca40
33 changed files with 3565 additions and 728 deletions
195
static/js/modules/compress.js
Normal file
195
static/js/modules/compress.js
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* 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.');
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue