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 <noreply@anthropic.com>
This commit is contained in:
1 parent
48ac3dfa85
commit
19aab4e61e
3 files changed
+21
-5
No files matched your search
@@ -320,6 +320,14 @@
|
|||||||
<option value="1080x1440">3:4 verticale (1080×1440)</option>
|
<option value="1080x1440">3:4 verticale (1080×1440)</option>
|
||||||
<option value="2560x1080">21:9 cinema (2560×1080)</option>
|
<option value="2560x1080">21:9 cinema (2560×1080)</option>
|
||||||
</select>
|
</select>
|
||||||
|
<label>Durata (Reel: si ferma da solo)</label>
|
||||||
|
<select id="rec-dur">
|
||||||
|
<option value="0">∞ manuale</option>
|
||||||
|
<option value="15000">⏱ 15 secondi</option>
|
||||||
|
<option value="30000">⏱ 30 secondi</option>
|
||||||
|
<option value="60000">⏱ 60 secondi</option>
|
||||||
|
<option value="90000">⏱ 90 secondi</option>
|
||||||
|
</select>
|
||||||
<button id="btn-rec" class="wide">🔴 Avvia registrazione</button>
|
<button id="btn-rec" class="wide">🔴 Avvia registrazione</button>
|
||||||
<button id="btn-rec-folder" class="wide">📁 Apri cartella registrazioni</button>
|
<button id="btn-rec-folder" class="wide">📁 Apri cartella registrazioni</button>
|
||||||
<div class="hint-mini">Registra ciò che vedi sull'output + l'audio (file o input live). Salvataggio in <b>Filmati ▸ DJ Visualizer</b>. La conversione in MP4 avviene allo stop.</div>
|
<div class="hint-mini">Registra ciò che vedi sull'output + l'audio (file o input live). Salvataggio in <b>Filmati ▸ DJ Visualizer</b>. La conversione in MP4 avviene allo stop.</div>
|
||||||
|
|||||||
+3
-1
@@ -1105,7 +1105,9 @@ function stopRecTimer() { if (recTimer) { clearInterval(recTimer); recTimer = nu
|
|||||||
|
|
||||||
$('#btn-rec').addEventListener('click', () => {
|
$('#btn-rec').addEventListener('click', () => {
|
||||||
if (!recOn) {
|
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 {
|
} else {
|
||||||
const [w, h] = $('#rec-aspect').value.split('x').map(Number);
|
const [w, h] = $('#rec-aspect').value.split('x').map(Number);
|
||||||
send({ type: 'recStop', w, h });
|
send({ type: 'recStop', w, h });
|
||||||
|
|||||||
+10
-4
@@ -58,7 +58,7 @@ function restartSlideshow() {
|
|||||||
// ---------------------------------------------------------------- recording
|
// ---------------------------------------------------------------- recording
|
||||||
// Records the output canvas (video) + the audio tap into a WebM stream that
|
// Records the output canvas (video) + the audio tap into a WebM stream that
|
||||||
// main transcodes to MP4 at the chosen aspect ratio.
|
// 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;
|
let recCanvas = null, recCtx = null, composeRAF = null;
|
||||||
|
|
||||||
function pickMime() {
|
function pickMime() {
|
||||||
@@ -152,7 +152,7 @@ function stopCompose() {
|
|||||||
if (composeRAF) { cancelAnimationFrame(composeRAF); composeRAF = null; }
|
if (composeRAF) { cancelAnimationFrame(composeRAF); composeRAF = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startRecording() {
|
async function startRecording(opts) {
|
||||||
if (recording) return;
|
if (recording) return;
|
||||||
try {
|
try {
|
||||||
await djv.recStart();
|
await djv.recStart();
|
||||||
@@ -178,11 +178,16 @@ async function startRecording() {
|
|||||||
const res = await djv.recStop(pendingRecOpts || {});
|
const res = await djv.recStop(pendingRecOpts || {});
|
||||||
recording = false;
|
recording = false;
|
||||||
djv.report({ type: 'recState', 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' });
|
else djv.report({ type: 'recError', message: (res && res.error) || 'errore sconosciuto' });
|
||||||
};
|
};
|
||||||
recorder.start(2000); // emit a chunk every 2s
|
recorder.start(2000); // emit a chunk every 2s
|
||||||
recording = true;
|
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 });
|
djv.report({ type: 'recState', recording: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
stopCompose();
|
stopCompose();
|
||||||
@@ -191,6 +196,7 @@ async function startRecording() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stopRecording(opts) {
|
function stopRecording(opts) {
|
||||||
|
clearTimeout(recAutoStop); recAutoStop = null;
|
||||||
if (!recording || !recorder) return;
|
if (!recording || !recorder) return;
|
||||||
pendingRecOpts = { w: opts && opts.w, h: opts && opts.h };
|
pendingRecOpts = { w: opts && opts.w, h: opts && opts.h };
|
||||||
recorder.stop();
|
recorder.stop();
|
||||||
@@ -312,7 +318,7 @@ djv.onControl(async (m) => {
|
|||||||
djv.report({ type: 'autoVj', on: autoVj });
|
djv.report({ type: 'autoVj', on: autoVj });
|
||||||
break;
|
break;
|
||||||
case 'svg': loadCustomTexture(m.dataUrl); break;
|
case 'svg': loadCustomTexture(m.dataUrl); break;
|
||||||
case 'recStart': startRecording(); break;
|
case 'recStart': startRecording(m); break;
|
||||||
case 'recStop': stopRecording(m); break;
|
case 'recStop': stopRecording(m); break;
|
||||||
case 'gain': audio.gain = m.value; break;
|
case 'gain': audio.gain = m.value; break;
|
||||||
case 'speed': speed = m.value; audio.scrollRate = m.value; break;
|
case 'speed': speed = m.value; audio.scrollRate = m.value; break;
|
||||||
|
|||||||
Reference in new issue
Block a user