S2: obs-websocket, Wiedergabepuffer und der !clip-Ausloeser

Eigener obs-websocket-v5-Client (das Protokoll ist klein genug, und das
Projekt bleibt ohne Abhaengigkeiten). Anmeldung, Wiederverbindung mit
wachsender Pause, Ereignisse fuer ReplayBufferSaved und Szenenwechsel.

Der Clipper loest zwei Wege gleichzeitig aus: OBS-Wiedergabepuffer fuer die
Rohdatei in Aufnahmequalitaet, Twitch-Clip fuer die teilbare URL. Faellt
einer aus, ist der andere trotzdem etwas wert. Ein direkt in OBS gedrueckter
Hotkey wird ohne unseren Umweg erkannt und als vollwertiger Ausloeser
behandelt.

Die Pfaduebersetzung Windows -> Container hat eine eigene Datei und eigene
Tests - das ist die Stelle, an der die Pipeline am ehesten scheitert. Dazu
Warten auf den abgeschlossenen Schreibvorgang, bevor die Datei uebernommen
wird.

Zum Testvektor der OBS-Anmeldung: die Spezifikation nennt Salt und Challenge,
aber kein erwartetes Ergebnis. Der Erwartungswert wurde unabhaengig mit
Pythons hashlib aus derselben Beschreibung gerechnet; der Test sagt das im
Kommentar, statt einen amtlichen Vektor vorzutaeuschen.

29 Tests gruen. Die Abnahme von S2 braucht ein laufendes OBS und steht aus.

Ausserdem: ADR-0006 haelt fest, dass alles unter einem Dach laeuft - eine
lokale Weboberflaeche plus Kindprozesse fuer Rechenarbeit. Die Prozessliste
in der Architekturdoku ist entsprechend ersetzt. Der Onboarding-Assistent
ist als Teil von S5 aufgenommen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VTGjd92LsDDWF3frd9FTB
This commit is contained in:
ZeasyAlexandClaude Opus 5 committed 2026-09-09 10:10:54 +02:00
1 parent d07c18441b
commit 684f10eb02
13 files changed
+796 -44

No files matched your search

+22 -29
View File
@@ -37,35 +37,28 @@
## Prozesse
Der Verbund besteht aus wenigen langlaufenden Prozessen und mehreren
Kurzläufern, gestartet über `docker compose up -d`.
**Ein Prozess.** Oberflaeche, Ingest und die Werkzeugmodule laufen gemeinsam in
einem Container; schwere Arbeit (ffmpeg, whisper.cpp) laeuft als Kindprozess und
darf abstuerzen, ohne die Chat-Aufnahme mitzureissen. Begruendung und Grenzen:
[ADR-0006](decisions/ADR-0006-ein-dach.md).
> **Im MVP gilt das noch nicht.** Dort laufen Ingest und Clipper als *ein*
> Prozess in *einem* Container; die Aufteilung unten ist der Zielzustand ab
> Meilenstein M3, wenn es mehr als einen Konsumenten gibt. Siehe
> [`07-mvp-plan.md`](07-mvp-plan.md).
| Port | Wofuer |
|------|--------|
| 4792 | Weboberflaeche: Einrichtung, OAuth-Rueckruf, Sichtung, Dashboard, `/health` |
| 4791 | Overlay-Server fuer die OBS-Browser-Quellen (ab M3) |
**Langläufer (laufen immer, auch offline):**
Beide nur auf `127.0.0.1` des Hosts veroeffentlicht.
| Prozess | Port | Aufgabe |
|---------|------|---------|
| `ingest` | 4790 (WS) | EventSub, IRC, OBS-WS, Viewer-Polling → DB + Bus |
| `overlay-server` | 4791 (HTTP+WS) | liefert Overlay-Seiten, nimmt Chat-Commands entgegen |
| `dashboard` | 4792 (HTTP) | Analytics-UI |
Die Werkzeuge sind Module, keine eigenen Dienste:
**Kurzläufer (event- oder zeitgetriggert):**
| Prozess | Trigger |
|---------|---------|
| `clipper` | Clip-Event vom Bus, oder manuell über CLI |
| `broadcaster` | `stream.online` |
| `recap` | `stream.offline` (+ 5 min Karenz) |
| `vod-chapters` | `stream.offline` |
| `raid-scout` | manuell / Chat-Command `!raidziel` |
`community` und `loyalty` sind keine eigenen Prozesse, sondern Module, die im
`overlay-server` mitlaufen — sie brauchen dieselbe Chat-Verbindung und
denselben WS-Kanal zum Overlay.
| Modul | laeuft | Ausloeser |
|-------|--------|-----------|
| Ingest | dauernd | IRC, EventSub, Helix-Polling |
| Community, Loyalty | dauernd | Chat-Ereignisse |
| Clipper | bei Bedarf | `!clip`, OBS-Hotkey, spaeter Chat-Spitze |
| Broadcaster | bei Bedarf | `stream.online` |
| Recap, VOD-Kapitel | bei Bedarf | `stream.offline` |
| Raid-Scout | bei Bedarf | `!raidziel`, Oberflaeche |
## Event-Bus
@@ -82,10 +75,10 @@ type StreamEvent = {
};
```
Transport: In-Prozess ein `EventEmitter`, prozessübergreifend ein
WebSocket-Broadcast auf `ws://127.0.0.1:4790`. Kein Kafka, kein Redis. Wenn ein
Konsument abstürzt, holt er sich beim Neustart die verpassten Events aus der
`events`-Tabelle über `ts > lastSeen`.
Transport: ein `EventEmitter` im Prozess. Solange alles unter einem Dach laeuft
(ADR-0006), braucht es keinen Netzwerk-Bus. Die `events`-Tabelle ist trotzdem
die Wahrheit: sie wird immer geschrieben, und ein spaeter herausgeloester
Prozess kann seinen Rueckstand ueber `ts > lastSeen` aufholen.
Vollständige Event-Liste: siehe [`03-data-model.md`](03-data-model.md).