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;