# Betrieb [← zurück zum Überblick](../README.md) · [alle Kapitel](README.md) Live zuschauen, mehrere Kameras, der Detector auf einem eigenen Host und das Deployen der Web-App. Also alles, was nach dem ersten erkannten Vogel kommt. ## Live zuschauen Eine Kamera liefert **einen** Stream. Wer direkt bei ihr zuschaut, nimmt ihn der Erkennung weg — auf einem Pi Zero W ist das keine Einstellung, sondern Physik: die Funkstrecke trägt rund 2 Mbit/s, ein zweiter Client sättigt sie. Der Detector liest den Stream aber ohnehin rund um die Uhr. Also verteilt er ihn weiter: ```bash python -m detector watch --source http://kamera:8080/stream --live-port 8131 ``` Damit schauen beliebig viele Leute zu, und die Kamera merkt nichts davon — sie hat weiterhin genau einen Zuschauer. Solange niemand hinsieht, wird auch nichts umkodiert: bei MJPEG-Quellen gehen die JPEG-Bytes unverändert durch, und das Kodieren für andere Quellen passiert im HTTP-Thread, einmal je Bild statt einmal je Zuschauer. Die Erkennung wird davon nicht langsamer. | Adresse | wozu | |---|---| | `http://:8131/live` | MJPEG, für mehrere Zuschauer | | `http://:8131/bild.jpg` | Einzelbild | | `http://:8131/` | Seite mit Livebild | ### In der Web-App Eingetragen wird das **im Django-Admin bei der Kamera**: `Live-Adresse` auf `http://:8131/live`, dann `Live-Bild zeigen` ankreuzen. Danach gibt es die Seite `/live/` und einen Eintrag in der Navigation. Ohne Freigabe existiert beides nicht — kein Link, keine Seite, 404. Das Bild lädt der Browser direkt beim Verteiler; die Web-App reicht nichts durch. Sonst hinge an jedem Zuschauer eine offene Verbindung im Webserver. > **Für eine öffentliche Instanz: `VOGELALBUM_LIVE_ERLAUBT=0`.** Dann ist Live > nicht einschaltbar, auch nicht mit einem Fehlklick im Admin, und die > Admin-Liste zeigt „gesperrt". Ein Livebild aus einem Privatgarten soll nicht > an einem Häkchen hängen — für einen Showcase ist die Sammlung ohnehin das > Interessantere. Der Verteiler selbst kennt keine Anmeldung. Er gehört ins Heimnetz. ## Mehrere Kameras Jede Kamera bekommt einen eigenen Token. Der Token identifiziert sie — die Web-App muss nie wissen, wo sie steht, und ein kompromittierter Standort lässt sich einzeln stilllegen, ohne die anderen anzufassen. ```bash manage.py camera add futterhaus --name "Futterhaus Garten" --location "Garten" manage.py camera add nistkasten --name "Nistkasten Apfelbaum" manage.py camera list manage.py camera rotate futterhaus # Token erneuern manage.py camera disable nistkasten ``` Der Token wird **genau einmal** ausgegeben; gespeichert ist nur sein SHA-256. Bei 256 Bit Entropie braucht es kein Salt, und wer die Datenbank in die Hand bekommt, kann damit trotzdem nichts einliefern. Pro Kamera läuft eine eigene `detector watch`-Instanz. Sie können auf demselben Host laufen; rechne mit rund 40 % eines Kerns je Kamera bei `--classify-every 2` und 4 fps. Die Sammlung ist dabei **gemeinsam**: Findet die Gartenkamera die erste Amsel, ist sie im Dex freigeschaltet — die Nistkastenkamera bekommt für dieselbe Art keinen zweiten Erstfang. Das ist Absicht, es ist die Sammlung eines Menschen und nicht die einer Kamera. Die Sichtungsliste lässt sich trotzdem nach Kamera filtern. `manage.py camera list` markiert Kameras, die sich über eine Stunde nicht gemeldet haben — der schnellste Weg zu „warum kommt da nichts mehr". ## Betrieb ### Detector auf einem eigenen Host Ein frischer Debian- oder Ubuntu-Container, dann: ```bash sudo ./scripts/setup_detector_host.sh ``` Das Skript legt den Dienstbenutzer `vogelalbum` an, klont nach `/opt/vogelalbum`, baut das venv, holt die Modelle, schreibt `/etc/vogelalbum.env` und installiert die systemd-Unit. Idempotent — ein zweiter Lauf aktualisiert nur. Danach `/etc/vogelalbum.env` ausfüllen und `systemctl enable --now vogelalbum-detector`. **Dimensionierung** | | | |---|---| | CPU | 1 Kern je Kamera (bei `--classify-every 2` und ~4 fps rund 40 % Last) | | RAM | 512 MB je Instanz, 1 GB für Luft | | Platte | 4 GB fürs System, plus ~150 kB je gespeicherter Sichtung | **Netz:** ausgehend zur Kamera und zur Web-App. **Eingehend nichts.** Achtung, wenn Kamera und Erkennungs-Host in verschiedenen Netzsegmenten liegen: Die Route muss stehen. Bei uns hängt die Kamera im WLAN-Segment und der Server im LAN — ein Scan des falschen Segments findet sie schlicht nicht. **Zweite Kamera:** eigene `/etc/vogelalbum-.env`, die Unit als `vogelalbum-detector@.service` ablegen, dann `systemctl enable --now vogelalbum-detector@nistkasten`. ## Die Web-App deployen Nur die Web-App wird deployt. Der Detector bleibt im Heimnetz — er braucht Sichtverbindung zur Kamera. 1. DNS auf den Host zeigen lassen. 2. Anwendung aus dem Repo anlegen, Build über `docker-compose.yml`. ⚠️ **Der Eingang ist `medien` auf Port 8080, nicht `web` auf 8000.** Das compose startet zwei Dienste: Gunicorn und einen nginx davor, der die Vogelfotos vom Volume ausliefert. Zeigt der Reverse Proxy direkt auf `web`, lädt die Seite — aber **jede Karte der Sammlung hat ein kaputtes Bild**, denn Django liefert unter `DJANGO_DEBUG=0` keine Medien aus. Begründung ausführlich in [deploy/nginx-media.conf](../deploy/nginx-media.conf). 3. Persistentes Volume auf `/data` mounten. **Das ist der Schritt, den man vergisst** — ohne ihn löscht jeder Deploy Sammlung und Fotos. 4. Umgebungsvariablen setzen (siehe [Einstellungen](einstellungen.md)). 5. Denselben Token auf dem Erkennungs-Host eintragen und `VOGELALBUM_API` auf die öffentliche Adresse setzen. 6. Auf einer öffentlichen Instanz `VOGELALBUM_LIVE_ERLAUBT=0` setzen — steht im `docker-compose.yml` schon drin. Ohne die Angabe wäre das Livebild aus dem eigenen Garten erlaubt, denn der Standard im Code ist `1`. Der erste Start dauert ein paar Minuten: Der Entrypoint holt einmalig die Referenzfotos. Danach kostet der Schritt nichts mehr. `migrate`, `collectstatic` und `seed_dex` erledigt er ebenfalls — neue Arten im Katalog landen also mit dem nächsten Deploy automatisch in der Datenbank. 📘 **Schritt für Schritt mit Coolify, inklusive Umzug einer bestehenden Sammlung: [deploy/COOLIFY.md](../deploy/COOLIFY.md).** Dort steht auch, wie Datenbank und Fotos als 1:1-Kopie auf das Volume kommen — Kamera-Tokens, Handbestimmungen und Abzeichen bleiben dabei erhalten, und die Kameras zu Hause liefern mit ihrem bisherigen Token weiter. ### Einbinden auf einer anderen Seite `/widget/` ist dafür da — keine Navigation, transparenter Hintergrund: ```html ``` Wer lieber selbst gestaltet, nimmt `/api/stats/` — Level, Artenzahl und die letzte Sichtung als JSON.