diff --git a/app.py b/app.py index dced2ae..6f90b67 100644 --- a/app.py +++ b/app.py @@ -437,6 +437,7 @@ class WebcamGrabber: self.latest_jpeg: bytes | None = None self.frame_seq = 0 + self.last_frame_ts = 0.0 # time.time() des letzten gelieferten Frames self.viewers = 0 self.reset_flag = Event() self.line = dict(SAVED_LINE) @@ -511,6 +512,7 @@ class WebcamGrabber: with self.frame_cond: self.latest_jpeg = jpeg self.frame_seq += 1 + self.last_frame_ts = time.time() self.frame_cond.notify_all() def _clear(self): @@ -519,6 +521,10 @@ class WebcamGrabber: self.frame_seq += 1 self.frame_cond.notify_all() + def is_online(self) -> bool: + """True, wenn zuletzt vor < 5s ein Frame kam (Kamera liefert).""" + return self.latest_jpeg is not None and (time.time() - self.last_frame_ts) < 5.0 + # -- Hintergrund-Thread (laeuft die ganze Prozess-Lebensdauer) ---------- def _run(self): state = new_state() @@ -755,6 +761,12 @@ def get_counting_line(): return jsonify(get_line_from_session()) +@app.route("/api/webcam_status", methods=["GET"]) +def webcam_status(): + """Liefert, ob der Webcam-Grabber gerade Frames bekommt (Kamera online).""" + return jsonify({"online": webcam.is_online()}) + + @app.route("/api/reset_count", methods=["POST"]) def reset_count(): data = request.get_json(silent=True) or {} diff --git a/templates/webcam.html b/templates/webcam.html index 8747680..672b975 100644 --- a/templates/webcam.html +++ b/templates/webcam.html @@ -46,6 +46,27 @@ .theme-toggle:hover { border-color: var(--muted); } + .offline-overlay { + position: absolute; + top: 0; + left: 0; + width: 1020px; + height: 600px; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + background: rgba(0, 0, 0, 0.78); + color: #fff; + border-radius: 8px; + text-align: center; + z-index: 5; + } + .offline-overlay.show { display: flex; } + .offline-overlay .icon { font-size: 54px; } + .offline-overlay .msg { font-size: 22px; font-weight: 600; } + .offline-overlay .sub { font-size: 14px; opacity: 0.8; } .video-container { position: relative; width: 1020px; @@ -122,6 +143,11 @@
+
+
📷
+
Kamera offline
+
Versuche neu zu verbinden…
+
@@ -258,6 +284,28 @@ drawLine(); } }, 100); + + // --- Kamera-Online-Status pollen + Auto-Recovery des Streams --- + const videoFeed = document.getElementById('videoFeed'); + const offlineOverlay = document.getElementById('offlineOverlay'); + let camWasOffline = false; + async function checkCamStatus() { + let online = false; + try { + const res = await fetch('/api/webcam_status', { cache: 'no-store' }); + online = (await res.json()).online; + } catch (e) { + online = false; + } + if (online && camWasOffline) { + // MJPEG- reconnectet nach Stream-Abbruch nicht von selbst -> neu anstossen + videoFeed.src = "{{ url_for('webcam_feed') }}?t=" + Date.now(); + } + camWasOffline = !online; + offlineOverlay.classList.toggle('show', !online); + } + checkCamStatus(); + setInterval(checkCamStatus, 2000);