Claude: Phase 2 button
This commit is contained in:
@@ -351,19 +351,80 @@ function showNotice() {
|
||||
bar.style.display = 'flex';
|
||||
}
|
||||
|
||||
// ── Snapshot aller Kameras ───────────────────────────────────────────────────
|
||||
function snapshotAll() {
|
||||
const ts = Date.now();
|
||||
const ids = cameras.map(c => c.id);
|
||||
log('snap', `Snapshot alle: ${ids.join(', ')}`);
|
||||
ids.forEach(id => {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/api/snapshot/${id}`;
|
||||
a.download = `${id}_${ts}.jpg`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
});
|
||||
// ── HD-Snapshot aller Kameras (parallel) ─────────────────────────────────────
|
||||
// cam0 und cam1 liegen auf getrennten Geräten → gleichzeitiger Grab sicher.
|
||||
// Alle Live-Streams werden synchron eingefroren und losgelassen, dann beide
|
||||
// /hires-Requests parallel gefeuert. finally stellt immer alle zurück.
|
||||
async function snapshotAllHires() {
|
||||
if (cameras.some(c => c.testing)) return;
|
||||
|
||||
const snapBtn = document.getElementById('snapAllBtn');
|
||||
if (snapBtn) snapBtn.disabled = true;
|
||||
cameras.forEach(c => { c.testing = true; c.hdBtn.disabled = true; });
|
||||
log('snap', `HD-Grab alle: ${cameras.map(c => c.id).join(', ')}`);
|
||||
|
||||
try {
|
||||
// 1. Alle Freeze-Canvases gleichzeitig aufbauen (je ein /api/snapshot-Fetch)
|
||||
await Promise.all(cameras.map(c => showFreezeCanvas(c, 'Capturing HD…')));
|
||||
|
||||
// 2. Alle Live-Streams synchron loslassen → alle Consumer fallen gleichzeitig auf 0
|
||||
cameras.forEach(c => stopStream(c));
|
||||
|
||||
const ts = Date.now();
|
||||
|
||||
// 3. Alle /hires-Grabs parallel – Fehler einer Kamera blockieren die andere nicht
|
||||
await Promise.allSettled(cameras.map(async c => {
|
||||
try {
|
||||
const r = await fetch(
|
||||
`/api/snapshot/${encodeURIComponent(c.id)}/hires`,
|
||||
{ signal: AbortSignal.timeout(20000) }
|
||||
);
|
||||
if (!r.ok) {
|
||||
const body = await r.json().catch(() => ({}));
|
||||
throw new Error(body.error ?? `HTTP ${r.status}`);
|
||||
}
|
||||
const blob = await r.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
if (c.freezeCanvas) {
|
||||
const ctx = c.freezeCanvas.getContext('2d');
|
||||
await new Promise(resolve => {
|
||||
const img = new Image();
|
||||
img.onload = () => { ctx.drawImage(img, 0, 0, 640, 480); resolve(); };
|
||||
img.onerror = resolve;
|
||||
img.src = blobUrl;
|
||||
});
|
||||
updateBadge(c, 'HD ✓', '#8f8');
|
||||
}
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = blobUrl;
|
||||
a.download = `${c.id}_hires_${ts}.jpg`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
|
||||
setInfo(c, 'HD gespeichert', 'ok');
|
||||
log(c.id, `HD-Grab OK – ${blob.size} bytes`);
|
||||
} catch (e) {
|
||||
logErr(c.id, 'HD-Grab fehlgeschlagen', e);
|
||||
setInfo(c, `HD Fehler: ${e.message}`, 'crit');
|
||||
}
|
||||
}));
|
||||
|
||||
} finally {
|
||||
// 4. Immer: alle zurück auf Live
|
||||
await sleep(600);
|
||||
cameras.forEach(c => {
|
||||
removeFreezeCanvas(c);
|
||||
startStream(c);
|
||||
c.testing = false;
|
||||
c.hdBtn.disabled = false;
|
||||
});
|
||||
if (snapBtn) snapBtn.disabled = false;
|
||||
log('snap', '── HD-Grab alle beendet, alle zurück auf Live ──');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Kamera-View aufbauen ─────────────────────────────────────────────────────
|
||||
@@ -443,7 +504,7 @@ async function init() {
|
||||
if (camIds.length === 0) { warn('init', 'Fallback cam0, cam1'); camIds = ['cam0', 'cam1']; }
|
||||
|
||||
const snapBtn = document.getElementById('snapAllBtn');
|
||||
if (snapBtn) { snapBtn.onclick = snapshotAll; snapBtn.disabled = false; }
|
||||
if (snapBtn) { snapBtn.onclick = snapshotAllHires; snapBtn.disabled = false; }
|
||||
|
||||
camIds.forEach(id => buildCamera(id, container));
|
||||
statusText.textContent = `${camIds.length} Kamera${camIds.length !== 1 ? 's' : ''} · WebRTC`;
|
||||
|
||||
Reference in New Issue
Block a user