From 19aab4e61e994537533927ce643ca880c302d4c6 Mon Sep 17 00:00:00 2001 From: luciano Date: Fri, 17 Jul 2026 15:51:51 +0200 Subject: [PATCH] Reels: timed recording with auto-stop recStart now carries the format and an optional maxMs; the output stops itself when the time is up (15/30/60/90s picker in the control UI), so a reel doesn't depend on a manual stop. Co-Authored-By: Claude Fable 5 --- src/control.html | 8 ++++++++ src/control.js | 4 +++- src/output.js | 14 ++++++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/control.html b/src/control.html index 2b6df82..b47c17a 100644 --- a/src/control.html +++ b/src/control.html @@ -320,6 +320,14 @@ + +
Registra ciò che vedi sull'output + l'audio (file o input live). Salvataggio in Filmati ▸ DJ Visualizer. La conversione in MP4 avviene allo stop.
diff --git a/src/control.js b/src/control.js index 5c0fde7..1250836 100644 --- a/src/control.js +++ b/src/control.js @@ -1105,7 +1105,9 @@ function stopRecTimer() { if (recTimer) { clearInterval(recTimer); recTimer = nu $('#btn-rec').addEventListener('click', () => { if (!recOn) { - send({ type: 'recStart' }); + // Pass format + optional max duration so the output can auto-stop (Reel mode). + const [w, h] = $('#rec-aspect').value.split('x').map(Number); + send({ type: 'recStart', w, h, maxMs: parseInt($('#rec-dur').value, 10) || 0 }); } else { const [w, h] = $('#rec-aspect').value.split('x').map(Number); send({ type: 'recStop', w, h }); diff --git a/src/output.js b/src/output.js index ffbc2dc..469ed2b 100644 --- a/src/output.js +++ b/src/output.js @@ -58,7 +58,7 @@ function restartSlideshow() { // ---------------------------------------------------------------- recording // Records the output canvas (video) + the audio tap into a WebM stream that // main transcodes to MP4 at the chosen aspect ratio. -let recorder = null, recording = false, pendingRecOpts = null; +let recorder = null, recording = false, pendingRecOpts = null, recAutoStop = null; let recCanvas = null, recCtx = null, composeRAF = null; function pickMime() { @@ -152,7 +152,7 @@ function stopCompose() { if (composeRAF) { cancelAnimationFrame(composeRAF); composeRAF = null; } } -async function startRecording() { +async function startRecording(opts) { if (recording) return; try { await djv.recStart(); @@ -178,11 +178,16 @@ async function startRecording() { const res = await djv.recStop(pendingRecOpts || {}); recording = false; djv.report({ type: 'recState', recording: false }); - if (res && res.ok) djv.report({ type: 'recSaved', path: res.path }); + if (res && res.ok) djv.report({ type: 'recSaved', path: res.path, name: res.name, url: res.url }); else djv.report({ type: 'recError', message: (res && res.error) || 'errore sconosciuto' }); }; recorder.start(2000); // emit a chunk every 2s recording = true; + // Reel mode: auto-stop after maxMs using the format chosen at start. + if (opts && opts.maxMs > 0) { + clearTimeout(recAutoStop); + recAutoStop = setTimeout(() => stopRecording({ w: opts.w, h: opts.h }), opts.maxMs); + } djv.report({ type: 'recState', recording: true }); } catch (e) { stopCompose(); @@ -191,6 +196,7 @@ async function startRecording() { } function stopRecording(opts) { + clearTimeout(recAutoStop); recAutoStop = null; if (!recording || !recorder) return; pendingRecOpts = { w: opts && opts.w, h: opts && opts.h }; recorder.stop(); @@ -312,7 +318,7 @@ djv.onControl(async (m) => { djv.report({ type: 'autoVj', on: autoVj }); break; case 'svg': loadCustomTexture(m.dataUrl); break; - case 'recStart': startRecording(); break; + case 'recStart': startRecording(m); break; case 'recStop': stopRecording(m); break; case 'gain': audio.gain = m.value; break; case 'speed': speed = m.value; audio.scrollRate = m.value; break;