From 901edcf594f529fa77766b60ae66d08a7657d6f3 Mon Sep 17 00:00:00 2001 From: cclohmar Date: Wed, 5 Aug 2026 13:06:36 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20focus=20mode=20uses=20fast=20JPEG=20refr?= =?UTF-8?q?esh=20(200ms)=20=E2=80=94=20no=20codec,=20works=20everywhere?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/app.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/public/app.js b/public/app.js index dd0366d..9538113 100644 --- a/public/app.js +++ b/public/app.js @@ -94,21 +94,23 @@ function renderLiveGrid() { // ── Focus Overlay ── let focusIdx = -1; +let focusTimer = null; function openFocus(cam) { focusIdx = cameras.indexOf(cam); const overlay = document.getElementById('focus-overlay'); overlay.classList.remove('hidden'); - updateFocus(); + refreshFocus(); renderThumbs(); + // Fast JPEG refresh — 200ms = ~5fps, smooth enough for monitoring. + focusTimer = setInterval(refreshFocus, 200); } -function updateFocus() { +function refreshFocus() { if (focusIdx < 0 || focusIdx >= cameras.length) return; const cam = cameras[focusIdx]; document.getElementById('focus-title').textContent = cam.name; document.getElementById('focus-time').textContent = new Date().toLocaleTimeString() + ' live'; - // go2rtc MJPEG stream proxied through NextNVR — same origin, no CORS issues. const img = document.getElementById('focus-video'); - img.src = `/stream/${cam.id}?type=sub&t=${Date.now()}`; + img.src = `/recordings/${cam.name || cam.id}/latest.jpg?t=${Date.now()}`; } function renderThumbs() { const strip = document.getElementById('focus-thumbs'); @@ -124,20 +126,22 @@ function renderThumbs() { } document.getElementById('focus-close').addEventListener('click', () => { document.getElementById('focus-overlay').classList.add('hidden'); + if (focusTimer) { clearInterval(focusTimer); focusTimer = null; } }); document.getElementById('focus-prev').addEventListener('click', () => { - if (focusIdx > 0) { focusIdx--; updateFocus(); renderThumbs(); } + if (focusIdx > 0) { focusIdx--; refreshFocus(); renderThumbs(); } }); document.getElementById('focus-next').addEventListener('click', () => { - if (focusIdx < cameras.length - 1) { focusIdx++; updateFocus(); renderThumbs(); } + if (focusIdx < cameras.length - 1) { focusIdx++; refreshFocus(); renderThumbs(); } }); document.addEventListener('keydown', e => { if (document.getElementById('focus-overlay').classList.contains('hidden')) return; if (e.key === 'Escape') { document.getElementById('focus-overlay').classList.add('hidden'); + if (focusTimer) { clearInterval(focusTimer); focusTimer = null; } } - if (e.key === 'ArrowLeft' && focusIdx > 0) { focusIdx--; updateFocus(); renderThumbs(); } - if (e.key === 'ArrowRight' && focusIdx < cameras.length - 1) { focusIdx++; updateFocus(); renderThumbs(); } + if (e.key === 'ArrowLeft' && focusIdx > 0) { focusIdx--; refreshFocus(); renderThumbs(); } + if (e.key === 'ArrowRight' && focusIdx < cameras.length - 1) { focusIdx++; refreshFocus(); renderThumbs(); } }); // ── Playback ──