Files
abdulive/docs/01-architecture.md
T
ZeasyAlexandClaude Opus 5 684f10eb02 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
2026-09-09 10:10:54 +02:00

6.0 KiB

Architektur

Bild

                    Twitch                     OBS
              ┌──────────────────┐        ┌───────────┐
              │ EventSub (WS)    │        │ obs-      │
              │ IRC / Chat       │        │ websocket │
              │ Helix REST       │        └─────┬─────┘
              └────────┬─────────┘              │
                       │                        │
              ┌────────▼────────────────────────▼────────┐
              │           apps/ingest  (Daemon)          │
              │  normalisiert alles zu Events            │
              └────────┬─────────────────────┬───────────┘
                       │                     │
             schreibt  │                     │  publiziert
                       ▼                     ▼
              ┌─────────────────┐   ┌──────────────────────┐
              │  SQLite         │   │  Event-Bus           │
              │  data/db/       │   │  (in-proc + WS 4790) │
              └────────┬────────┘   └──────────┬───────────┘
                       │                       │
   ┌───────────┬───────┼───────────┬───────────┼───────────┬──────────┐
   ▼           ▼       ▼           ▼           ▼           ▼          ▼
 dashboard  recap  raid-scout  vod-chapters community  overlay-   clipper
                                                        server       │
                                                          │          │
                                                    overlay-web   ffmpeg
                                                    (Browser-      whisper
                                                     Source)          │
                                                                 data/clips
                                                                      │
                                                                 broadcaster

Prozesse

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.

Port Wofuer
4792 Weboberflaeche: Einrichtung, OAuth-Rueckruf, Sichtung, Dashboard, /health
4791 Overlay-Server fuer die OBS-Browser-Quellen (ab M3)

Beide nur auf 127.0.0.1 des Hosts veroeffentlicht.

Die Werkzeuge sind Module, keine eigenen Dienste:

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

Der Ingest normalisiert alles auf ein einheitliches Event-Format:

type StreamEvent = {
  id: string;            // ULID
  ts: string;            // ISO-8601, UTC
  type: EventType;       // 'chat.message' | 'stream.online' | ...
  streamId: string | null;
  userId: string | null;
  payload: unknown;      // typisiert pro EventType
};

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.

Pakete

Paket Inhalt
packages/shared Ereignistypen, ULID
packages/core Config, Layout, Logger mit Maskierung, Retry, YAML-Leser
packages/twitch Secret-Speicher, OAuth, Helix-Client, IRC-Client
packages/db SQLite-Verbindung, Migrationen, Repositories

Regel: Apps dürfen von Paketen abhängen, Pakete nie von Apps. Apps hängen nicht voneinander ab — Kommunikation läuft über Bus und DB.

Importiert wird über die imports-Map der Wurzel-package.json (#core/config.ts, #db/repos.ts). Das ist eine Node-Funktion, kein Build-Werkzeug — es gibt keinen Bundler und keine Pfad-Aliasse im Compiler.

Technologie

  • TypeScript auf Node 24, ohne Build-Schritt und ohne Laufzeitabhängigkeiten — Node führt die .ts-Dateien direkt aus. Siehe ADR-0001
  • SQLite über das eingebaute node:sqlite — siehe ADR-0002
  • EventSub über WebSocket, nicht Webhooks — siehe ADR-0003
  • ffmpeg (CLI) für Videoverarbeitung
  • whisper.cpp (CLI) für Untertitel — kein Python, keine GPU
  • Serverseitiges HTML ohne Framework für Einrichtung und Sichtungsseite; für Dashboard und Overlays ab M3 ggf. Vite + Preact
  • Docker Compose für den Betrieb — siehe ADR-0004

Fehlerverhalten

  • Jeder Netzwerkaufruf hat Retry mit exponentiellem Backoff (packages/core/retry).
  • Der Ingest darf niemals wegen eines Konsumentenfehlers sterben. Konsumenten laufen in eigenen Prozessen; ein Fehler dort schreibt in logs/ und wird im Dashboard als Warnung angezeigt.
  • Wenn EventSub die Verbindung verliert: Reconnect mit Session-Resume, danach Lücke über Helix-Polling schließen (stream.online-Status, Follower-Liste).