Zaehllinie persistent speichern (ueberlebt Neustart)

- Linie wird beim Setzen atomar in counting_line.json gespeichert
- beim Start geladen -> Default fuer Session UND Webcam-Grabber
- Pfad per LINE_FILE-Env ueberschreibbar, Datei via .gitignore ausgeschlossen
- set_line-Route nutzt jetzt _valid_line (weniger Doppelcode)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 14:41:29 +02:00
parent 394c72d6d9
commit b20f4c582c
2 changed files with 50 additions and 11 deletions

3
.gitignore vendored
View File

@@ -4,6 +4,9 @@
# Hochgeladene Videos # Hochgeladene Videos
uploads/ uploads/
# Persistierte Zaehllinie (maschinenspezifisch)
counting_line.json
# Python # Python
__pycache__/ __pycache__/
*.py[cod] *.py[cod]

58
app.py
View File

@@ -136,6 +136,45 @@ UPLOAD_DIR = "uploads"
DEFAULT_LINE = {"x1": 0, "y1": 300, "x2": 1020, "y2": 300} DEFAULT_LINE = {"x1": 0, "y1": 300, "x2": 1020, "y2": 300}
FRAME_SIZE = (1020, 600) FRAME_SIZE = (1020, 600)
# Persistente Zaehllinie: wird als JSON gespeichert und beim Start geladen,
# damit sie einen Neustart ueberlebt (Pfad per Env ueberschreibbar).
LINE_FILE = os.environ.get("LINE_FILE", "counting_line.json")
def _valid_line(d):
"""Validiert/normalisiert ein Linien-Dict zu int-Koordinaten oder None."""
try:
return {k: int(d[k]) for k in ("x1", "y1", "x2", "y2")}
except (KeyError, ValueError, TypeError):
return None
def load_saved_line() -> dict:
"""Laedt die gespeicherte Linie oder faellt auf DEFAULT_LINE zurueck."""
try:
with open(LINE_FILE) as fh:
line = _valid_line(json.load(fh))
if line:
return line
except (OSError, json.JSONDecodeError):
pass
return dict(DEFAULT_LINE)
def save_line(line: dict) -> None:
"""Speichert die Linie atomar als JSON (ueberlebt App-Neustart)."""
try:
tmp = f"{LINE_FILE}.tmp"
with open(tmp, "w") as fh:
json.dump(line, fh)
os.replace(tmp, LINE_FILE)
except OSError as exc:
print(f"[line] save failed: {exc}", flush=True)
# Beim Start einmal laden -> Default fuer Session UND Webcam-Grabber.
SAVED_LINE = load_saved_line()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Hilfsfunktionen # Hilfsfunktionen
@@ -151,7 +190,7 @@ def ensure_upload_dir() -> None:
def get_line_from_session(): def get_line_from_session():
if "counting_line" not in session: if "counting_line" not in session:
session["counting_line"] = dict(DEFAULT_LINE) session["counting_line"] = dict(SAVED_LINE)
return session["counting_line"] return session["counting_line"]
@@ -314,7 +353,7 @@ class WebcamGrabber:
self.frame_seq = 0 self.frame_seq = 0
self.viewers = 0 self.viewers = 0
self.reset_flag = Event() self.reset_flag = Event()
self.line = dict(DEFAULT_LINE) self.line = dict(SAVED_LINE)
# Eigenes Modell -> isolierter Tracker, getrennt vom Video-Pfad. # Eigenes Modell -> isolierter Tracker, getrennt vom Video-Pfad.
# Lazy: wird erst beim ersten aktiven Stream geladen. # Lazy: wird erst beim ersten aktiven Stream geladen.
@@ -612,18 +651,15 @@ def webcam_feed():
def set_counting_line(): def set_counting_line():
"""Setzt die Zaehllinie (gilt fuer Video-Session UND Webcam-Grabber).""" """Setzt die Zaehllinie (gilt fuer Video-Session UND Webcam-Grabber)."""
data = request.get_json(silent=True) or {} data = request.get_json(silent=True) or {}
try: line = _valid_line(data)
line = { if line is None:
"x1": int(data["x1"]),
"y1": int(data["y1"]),
"x2": int(data["x2"]),
"y2": int(data["y2"]),
}
except (KeyError, ValueError, TypeError):
abort(400, description="Ungueltige Linienkoordinaten") abort(400, description="Ungueltige Linienkoordinaten")
global SAVED_LINE
SAVED_LINE = line # Default fuer kuenftige Sessions
save_line(line) # persistent -> ueberlebt Neustart
session["counting_line"] = line session["counting_line"] = line
webcam.set_line(line) # Webcam nutzt eine globale Linie (eine Kamera) webcam.set_line(line) # Webcam nutzt eine globale Linie (eine Kamera)
return jsonify({"status": "success", "line": line}) return jsonify({"status": "success", "line": line})