Raid-Scout, Auswertung, VOD-Kapitel, Community-Gedaechtnis, Punkteshop und Recap samt Platzhaltern, Spezifikationen und Konfiguration. Der Hub schickt am Chat zusaetzlich die Sendezeit mit, fuer die Uhrzeit im Pult. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
5.7 KiB
Markdown
123 lines
5.7 KiB
Markdown
# Architektur
|
|
|
|
## Bild
|
|
|
|
Stand vor dem Umzug in den Hub (ADR-0008). Raid-Scout, Auswertung,
|
|
VOD-Kapitel, Community-Gedaechtnis und Recap sind seit 2026-10-27 gestrichen.
|
|
|
|
```
|
|
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) │
|
|
└─────────────────┘ └──────────┬───────────┘
|
|
│
|
|
┌─────────┴─────────┐
|
|
▼ ▼
|
|
overlay-server clipper-Kette
|
|
(Pult, Sounds) ffmpeg, whisper
|
|
│ │
|
|
overlay-web data/clips
|
|
(Browser-Source)
|
|
```
|
|
|
|
## 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](decisions/ADR-0006-ein-dach.md).
|
|
|
|
| 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:
|
|
|
|
```ts
|
|
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`](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](decisions/ADR-0001-monorepo-typescript.md)
|
|
- **SQLite** über das eingebaute `node:sqlite` — siehe
|
|
[ADR-0002](decisions/ADR-0002-sqlite.md)
|
|
- **EventSub über WebSocket**, nicht Webhooks — siehe
|
|
[ADR-0003](decisions/ADR-0003-eventsub-websocket.md)
|
|
- **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](decisions/ADR-0004-docker.md)
|
|
|
|
## 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).
|