Standbild
This commit is contained in:
@@ -79,6 +79,15 @@
|
||||
color: #444; font-size: 0.82rem; letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
/* Snapshot-Modus: gross+fett über dem Einzelbild */
|
||||
.single-pic-banner {
|
||||
position: absolute; top: 0; left: 0; right: 0; z-index: 3;
|
||||
text-align: center; padding: 8px 4px;
|
||||
font-size: 1.15rem; font-weight: bold; letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: #fc6; background: rgba(0,0,0,.72);
|
||||
}
|
||||
|
||||
/* Hi-Res-Test-Button (Phase 1) – links neben dem Ein/Aus-Schalter */
|
||||
.cam-hdtest {
|
||||
position: absolute; top: 5px; right: 40px; z-index: 2;
|
||||
|
||||
@@ -38,6 +38,37 @@ function stopStream(cam) {
|
||||
log(cam.id, 'Live aus');
|
||||
}
|
||||
|
||||
// ── Snapshot-Modus (stream:false): alle 5 s ein Einzelbild ──────────────────
|
||||
// Kein Video – der Server öffnet das Gerät pro Snapshot kurz (one-shot). Fehler
|
||||
// (z. B. Gerät gerade durch HD-Grab belegt) werden still übersprungen, das letzte
|
||||
// gute Bild bleibt stehen.
|
||||
const SNAPSHOT_INTERVAL_MS = 5000;
|
||||
|
||||
async function fetchSnapshot(cam) {
|
||||
if (!cam.snapshotActive) return;
|
||||
try {
|
||||
const r = await fetch(`/api/snapshot/${encodeURIComponent(cam.id)}?t=${Date.now()}`,
|
||||
{ signal: AbortSignal.timeout(8000) });
|
||||
if (!r.ok) { setInfo(cam, `Snapshot-Fehler (HTTP ${r.status})`, 'warn'); return; }
|
||||
const blob = await r.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
cam.img.src = url;
|
||||
if (cam.lastBlobUrl) URL.revokeObjectURL(cam.lastBlobUrl);
|
||||
cam.lastBlobUrl = url;
|
||||
setInfo(cam, `Einzelbild · ${new Date().toLocaleTimeString()}`, 'ok');
|
||||
} catch (_e) {
|
||||
setInfo(cam, 'Snapshot-Timeout', 'warn');
|
||||
}
|
||||
}
|
||||
|
||||
function startSnapshotMode(cam) {
|
||||
cam.snapshotActive = true;
|
||||
setInfo(cam, 'Einzelbild lädt…', '');
|
||||
fetchSnapshot(cam); // sofort das erste Bild
|
||||
cam.snapTimer = setInterval(() => fetchSnapshot(cam), SNAPSHOT_INTERVAL_MS);
|
||||
log(cam.id, `Snapshot-Modus (alle ${SNAPSHOT_INTERVAL_MS / 1000} s)`);
|
||||
}
|
||||
|
||||
// ── HD-Snapshot ───────────────────────────────────────────────────────────────
|
||||
async function runHiresGrab(cam) {
|
||||
if (cam.busy) return;
|
||||
@@ -144,11 +175,21 @@ function buildCamera(camMeta, container) {
|
||||
box.appendChild(toggle);
|
||||
startStream(cam);
|
||||
} else {
|
||||
const placeholder = document.createElement('div');
|
||||
placeholder.className = 'cam-img cam-placeholder';
|
||||
placeholder.textContent = 'Kein Live-Stream';
|
||||
hd.title = 'Hi-Res-Snapshot (1280×960) – Download';
|
||||
box.appendChild(placeholder);
|
||||
// Snapshot-Modus: grosser Banner + Einzelbild, das alle 5 s aktualisiert wird.
|
||||
const banner = document.createElement('div');
|
||||
banner.className = 'single-pic-banner';
|
||||
banner.textContent = 'Single Picture no Video';
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.className = 'cam-img';
|
||||
img.alt = labelText;
|
||||
|
||||
cam.img = img;
|
||||
hd.title = 'Hi-Res-Snapshot – Download';
|
||||
|
||||
box.appendChild(banner);
|
||||
box.appendChild(img);
|
||||
startSnapshotMode(cam);
|
||||
}
|
||||
|
||||
box.appendChild(label);
|
||||
|
||||
Reference in New Issue
Block a user