Files
abdulive/docs/02-twitch-integration.md
T
ZeasyAlexandClaude Opus 5 d09e2b945a Struktur, Doku und S0-Toolchain fuer das abdulive-Toolkit
Monorepo-Geruest fuer neun Streamer-Tools plus den Ingest-Daemon, der
unter sechs davon liegt. Enthaelt:

- docs/: Ueberblick, Architektur, Twitch-Integration, Datenmodell,
  Roadmap, Setup, Hebel-Analyse und den MVP-Plan "Clip-Knopf"
- docs/projects/: je eine Spezifikation pro Tool
- docs/decisions/: vier ADRs (TS-Monorepo, SQLite, EventSub/IRC, Docker)
- docker/: Image mit ffmpeg und whisper.cpp, abhaengigkeitsfreier
  Entrypoint mit check-env, fetch-model und calibrate
- config/layout.yaml: aus einem VOD-Screenshot eingemessener
  Facecam-Ausschnitt (440x330 oben links, Chat-Overlay ab x=1270)

Noch kein Anwendungscode - Scheibe S0 aus docs/07-mvp-plan.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VTGjd92LsDDWF3frd9FTB
2026-09-09 08:56:09 +02:00

5.6 KiB
Raw Blame History

Twitch-Integration

Zwei Identitäten

Es werden zwei getrennte Twitch-Apps/Tokens gebraucht:

  1. Broadcaster-Token (Konto abdulive_) — für alles, was den eigenen Kanal betrifft: Clips erstellen, Titel ändern, Raids starten, EventSub abonnieren, Analytics lesen.
  2. Bot-Token (eigenes Bot-Konto, z. B. abdulive_bot) — für Chat-Nachrichten. Trennung, damit Bot-Nachrichten als Bot erkennbar sind und der Broadcaster- Token nicht im Chat-Client hängt.

Beide Tokens liegen verschlüsselt in data/db/tokens.json, werden über Refresh Tokens automatisch erneuert (packages/twitch/tokenStore.ts). Client-Secret kommt aus .env, niemals aus der DB, niemals ins Repo.

Scopes

Scope Wofür Token
chat:read IRC lesen Bot
chat:edit IRC schreiben Bot
user:write:chat Chat über Helix senden (Alternative zu IRC) Bot
moderator:read:chatters Zuschauerliste im Chat Broadcaster
clips:edit Clips erstellen Broadcaster
channel:manage:broadcast Titel/Kategorie ändern, Marker setzen Broadcaster
channel:read:subscriptions Sub-Events Broadcaster
moderator:read:followers Follow-Events (EventSub) Broadcaster
channel:manage:raids Raid starten/abbrechen Broadcaster
channel:read:redemptions Channel-Point-Einlösungen Broadcaster
channel:manage:redemptions eigene Punkte-Rewards verwalten Broadcaster
channel:manage:polls Umfragen Broadcaster
analytics:read:games Game-Analytics-Report Broadcaster
bits:read Bits-Events Broadcaster

Der Autorisierungs-Flow läuft einmalig über docker compose run --rm --service-ports toolkit auth → lokaler Redirect auf http://localhost:4780/callback.

EventSub (WebSocket)

Abonniert werden beim Start:

Subscription Version Nutzen für
stream.online 1 Broadcaster, Analytics, Recap
stream.offline 1 Recap, VOD-Kapitel
channel.update 2 VOD-Kapitel (Kategorie/Titel), Analytics
channel.follow 2 Community, Recap
channel.subscribe / .message / .gift 1 Recap, Overlay
channel.cheer 1 Recap, Overlay
channel.raid (to + from) 1 Raid-Tool
channel.channel_points_custom_reward_redemption.add 1 Loyalty, Overlay
channel.poll.begin/progress/end 1 Overlay
channel.chat.message 1 optional, siehe unten
channel.chat.notification 1 Community (Erstzuschauer-Marker)

Chat: IRC oder EventSub? Beides funktioniert. Wir lesen den Chat über IRC (irc-ws.chat.twitch.tv:443), weil die Tags dort (first-msg, returning-chatter, emotes, badges, msg-id) reichhaltiger und stabiler sind als im EventSub-Payload, und weil Rate Limits beim Senden dort klar dokumentiert sind. EventSub übernimmt alles Nicht-Chat.

Verbindungsregeln: Eine WS-Session hält max. 300 Subscriptions. Twitch sendet alle ~10 s ein Keepalive; bleibt es aus, neu verbinden. session_reconnect liefert eine neue URL — dort verbinden, dann erst die alte Verbindung schließen. Nach jedem Reconnect: Lücke per Helix-Polling füllen.

Helix — die relevanten Endpunkte

Endpunkt Tool
GET /helix/streams?user_id= Ingest (Viewer-Sampling, alle 60 s)
GET /helix/streams?game_id=&first=100 Raid-Scout
GET /helix/channels / PATCH /helix/channels Loyalty (Titel-Shop), VOD-Kapitel
POST /helix/clips Clipper
GET /helix/clips?id= Clipper (Metadaten + Thumbnail-URL)
GET /helix/videos?user_id=&type=archive VOD-Kapitel
POST /helix/streams/markers VOD-Kapitel, Clipper
GET /helix/chat/chatters Community (Anwesenheit, alle 5 min)
GET /helix/channels/followers Recap, Community
POST /helix/raids Raid-Tool
GET /helix/users?login= überall (Login → ID)

Rate Limits: 800 Punkte/Minute pro Client-ID (Standard: 1 Punkt pro Request). Der Helix-Client in packages/twitch führt einen Token-Bucket und drosselt selbst; Ratelimit-Remaining aus dem Response-Header wird ausgewertet. Bei 429 → Backoff nach Ratelimit-Reset.

Clips: die Fallstricke

Das ist der Teil, an dem die Clip-Pipeline scheitert, wenn man ihn nicht kennt:

  1. POST /helix/clips schneidet rückwirkend ~30 s ab dem Aufrufzeitpunkt (minus Stream-Delay). Man kann keinen Zeitbereich wählen.
  2. Der Clip ist nicht sofort verfügbar. Nach dem POST bekommt man id und edit_url; die Verarbeitung dauert typischerweise 5–20 s. Danach liefert GET /helix/clips?id= erst Metadaten.
  3. Die MP4-URL steht nirgends in der API. Sie wird aus thumbnail_url abgeleitet: alles ab -preview- abschneiden, .mp4 anhängen.
    https://clips-media-assets2.twitch.tv/AT-cm%7C123-preview-480x272.jpg
    → https://clips-media-assets2.twitch.tv/AT-cm%7C123.mp4
    
    Das ist undokumentiert und kann sich ändern — deshalb im Code an genau einer Stelle (packages/twitch/clips.ts) gekapselt und mit Fallback versehen.
  4. Der Clip enthält das fertig komposite Bild (1920×1080), nicht die Einzelquellen. Facecam und Gameplay lassen sich nur per Crop trennen, und nur wenn das OBS-Layout fest ist.
  5. Bitrate/Qualität entspricht dem Transcode, nicht dem Original.

Deshalb: OBS-Replay-Buffer ist der bessere Primärweg. Details in docs/projects/clip-shorts.md.

OBS-WebSocket

obs-websocket v5 auf ws://127.0.0.1:4455. Genutzt für:

  • CurrentProgramSceneChanged → VOD-Kapitel
  • SaveReplayBuffer → Clipper
  • TriggerHotkeyByName, SetInputSettings → Chat-Steuerung, Loyalty-Shop
  • GetSourceScreenshot → Go-Live-Broadcaster (Screenshot für den Post)