From b5b56e4c1854314fb519d13af9dda80a10ee0b8a Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 10 Nov 2026 10:46:04 +0100 Subject: feat(k1c): live camera stream instead of snapshots The camera was a snapshot reloaded on every poll. It is now the webcam's MJPEG stream_url, which an plays by itself at the camera's own rate (15 fps on the K1C). The stream connects only while the K1C tab is on screen and the page visible, and disconnects otherwise, so a hidden tab does not keep the printer streaming. A dropped stream hides the image and reconnects on the next poll. The image is shown as soon as its source is set, since a never-ending stream may never fire load. Co-Authored-By: Claude Opus 5.5 --- static/modules/k1c.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'static/modules') diff --git a/static/modules/k1c.js b/static/modules/k1c.js index 4cd0358..d8211fa 100644 --- a/static/modules/k1c.js +++ b/static/modules/k1c.js @@ -184,16 +184,24 @@ export default { tile("Filament", st.filament ? `${(st.filament / 1000).toFixed(2)} m` : "—"), ); - // A fresh snapshot each poll, fetched by the tablet from the printer - // directly. ponytail: needs the tablet on the printer's LAN; proxy it - // through the server if it ever is not. + // The live MJPEG stream, which an plays by itself, fetched by the + // tablet from the printer directly. Connected only while this tab is on + // screen: a hidden tab would keep the printer streaming for nothing. + // ponytail: needs the tablet on the printer's LAN; proxy it through the + // server if it ever is not. const cam = el.querySelector(".k1c-cam"); - if (st.snapshot && !cam._loading) { - cam._loading = true; - cam.onload = () => { cam._loading = false; cam.hidden = false; }; - cam.onerror = () => { cam._loading = false; cam.hidden = true; }; - cam.src = `${st.snapshot}${st.snapshot.includes("?") ? "&" : "?"}t=${Date.now()}`; + const shown = st.camera && !document.hidden && el.offsetParent !== null; + if (shown && cam.dataset.src !== st.camera) { + cam.dataset.src = st.camera; + // Shown at once: a never-ending stream may never fire load. Dropped + // (camera restarted): hide it and forget it, so the next poll reconnects. + cam.onerror = () => { cam.hidden = true; delete cam.dataset.src; }; + cam.src = st.camera; + cam.hidden = false; + } else if (!shown && cam.dataset.src) { + delete cam.dataset.src; + cam.removeAttribute("src"); + cam.hidden = true; } - if (!st.snapshot) cam.hidden = true; }, }; -- cgit v1.2.3