7.8 KiB
Twitch4SteamDeck — Architekturplan
Spiegel von
C:\Users\andre\.claude\plans\smooth-shimmying-parasol.md. Fortschritt steht intodo.md, aktueller Stand inCLAUDE_PROGRESS.md.
Ziel
Steam-Deck-App zur werbefreien Wiedergabe von Twitch-Livestreams und -VODs. Big-Screen-/10-Foot-UI, vollständig per Gamepad bedienbar. Lokaler VOD-Verlauf mit Resume. Verteilung als Flatpak.
Tech-Stack (entschieden)
| Bereich | Wahl |
|---|---|
| Sprache/Runtime | TypeScript + Electron (Main + Renderer) |
| UI | React + CSS, Spatial-Navigation für Gamepad |
| Wiedergabe | externer mpv-Prozess via JSON-IPC-Socket |
| Stream-Quelle / Ad-Bypass | Streamlink (Subprozess) + --twitch-disable-ads, optional TTV.LOL |
| Twitch-Auth | OAuth 2.0 Device Code Flow |
| Twitch-Daten | Helix-API (api.twitch.tv/helix) |
| Token-Storage | Electron safeStorage → userData/twitch-tokens.bin |
| Lokale Persistenz | SQLite via better-sqlite3 (VOD-Verlauf, Settings) |
| Packaging | electron-builder → Flatpak |
MVP-Scope
- Twitch-Login via Device Code Flow
- Followed Channels (Liste, Live-Indicator)
- Live-Stream-Wiedergabe werbefrei (Streamlink → mpv)
- VOD-Browsing pro Kanal
- VOD-Wiedergabe werbefrei
- Lokaler VOD-Verlauf + Resume + „Continue Watching"-Reihe
Bewusst nicht im MVP: Chat/Emotes, Suche, Kategorien, PiP, Themes.
Architektur
┌─────────────────────────────────────────────────┐
│ Electron Main Process (Node) │
│ - AuthService (Device Code Flow, Refresh) │
│ - TwitchApiClient (Helix) │
│ - StreamlinkRunner (spawn streamlink) │
│ - MpvController (spawn mpv, JSON-IPC) │
│ - HistoryStore (better-sqlite3) │
│ - SettingsStore │
└──────────────┬──────────────────────────────────┘
│ IPC (contextBridge / preload)
┌──────────────┴──────────────────────────────────┐
│ Electron Renderer (React + TS) │
│ - Screens: Login, Home, Channel, Player │
│ - SpatialNavigation (Gamepad-Fokus) │
│ - GamepadInputService │
└─────────────────────────────────────────────────┘
Wiedergabe läuft außerhalb Electron in einem mpv-Vollbildfenster.
Electron startet/stoppt mpv, sendet Befehle, lauscht auf time-pos für Verlaufsspeicherung.
Verzeichnisstruktur (Soll)
twitch4steamdeck/
├── package.json
├── electron.vite.config.ts
├── electron-builder.yml
├── flatpak/
│ └── tv.twitch4steamdeck.App.yml
├── src/
│ ├── main/
│ │ ├── index.ts ✅
│ │ ├── auth/
│ │ │ ├── deviceCodeFlow.ts ✅
│ │ │ ├── tokenStore.ts ✅
│ │ │ └── authService.ts ✅
│ │ ├── twitch/
│ │ │ ├── helixClient.ts ⏳ Phase 2
│ │ │ └── types.ts ⏳ Phase 2
│ │ ├── playback/
│ │ │ ├── streamlink.ts ⏳ Phase 3
│ │ │ └── mpvController.ts ⏳ Phase 3
│ │ ├── store/
│ │ │ ├── db.ts ⏳ Phase 5
│ │ │ ├── historyRepo.ts ⏳ Phase 5
│ │ │ └── settingsRepo.ts ⏳ Phase 5
│ │ └── ipc/
│ │ └── handlers.ts ✅ (nur Auth)
│ ├── preload/
│ │ └── index.ts ✅
│ └── renderer/
│ ├── index.html ✅
│ └── src/
│ ├── main.tsx ✅
│ ├── App.tsx ✅
│ ├── screens/
│ │ ├── LoginScreen.tsx ✅
│ │ ├── HomeScreen.tsx ⏳ Phase 2
│ │ ├── ChannelScreen.tsx ⏳ Phase 3/4
│ │ └── PlayerLaunchScreen.tsx ⏳ Phase 3
│ ├── components/
│ │ ├── FocusableCard.tsx ⏳ Phase 2
│ │ └── SpatialNav/ ⏳ Phase 2
│ ├── input/
│ │ └── gamepad.ts ⏳ Phase 2
│ ├── types/
│ │ └── t4sd.d.ts ✅
│ └── styles/
│ └── global.css ✅
└── resources/
└── icons/
Kernkomponenten — Spezifikation
Auth (✅ implementiert)
- Endpoint
POST https://id.twitch.tv/oauth2/devicemitclient_id+scopes(space-sep). - Anzeige
user_code+verification_uri+ QR. - Polling
POST https://id.twitch.tv/oauth2/tokenmitgrant_type=urn:ietf:params:oauth:grant-type:device_code. - Refresh-Token via Electron
safeStorage. - Auto-Refresh wenn Helix 401.
- Client-ID via
MAIN_VITE_TWITCH_CLIENT_ID(.envim Projektroot, electron-vite lädt sie). - Scope MVP:
user:read:follows.
Helix-Client (⏳ Phase 2)
MVP-Endpoints:
GET /users(eigener User)GET /channels/followed?user_id=…(paginiert viaafter)GET /streams?user_id=…(batched, max 100 IDs/Call)GET /videos?user_id=…&type=archive
Header: Client-Id: <id>, Authorization: Bearer <access_token>.
Streamlink + mpv (⏳ Phase 3)
streamlink --twitch-disable-ads --twitch-api-header=Authorization=OAuth <token> --stdout twitch.tv/<channel> best- Pipe an
mpv -mit--input-ipc-server=/tmp/twitch4sd-mpv.sock --fullscreen --hwdec=vaapi - Auf Windows: Named Pipe statt Unix-Socket.
VOD-History-DB (⏳ Phase 5)
CREATE TABLE vod_history (
vod_id TEXT PRIMARY KEY,
channel_login TEXT NOT NULL,
channel_display TEXT,
title TEXT,
thumbnail_url TEXT,
duration_seconds INTEGER,
resume_position_seconds INTEGER NOT NULL DEFAULT 0,
watched_at INTEGER NOT NULL,
completed INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_history_watched ON vod_history(watched_at DESC);
„Continue Watching": SELECT … WHERE completed=0 ORDER BY watched_at DESC LIMIT 20.
Complete-Threshold: resume_position / duration > 0.95.
Big-Screen-UI / Gamepad
- Spatial-Navigation-Lib oder eigene Mini-Implementierung.
- Fokus-Ring deutlich sichtbar.
- Schrift: min. 22px Body, 36px+ Headlines.
- Mapping: D-Pad/Stick = Navigation, A = Aktivieren, B = Zurück, Y = Refresh, Start = Menü, L1/R1 = Tab.
Flatpak (⏳ Phase 6)
- Runtime:
org.freedesktop.Platform//23.08,org.electronjs.Electron2.BaseApp - Module: app-bundle,
mpv,streamlink(pip), evtl.streamlink-ttvlol - Permissions:
--share=network,--share=ipc,--socket=wayland,--socket=fallback-x11,--socket=pulseaudio,--device=dri
Verifikation
End-to-End auf Steam Deck oder Linux-Box:
- Build:
npm install && npm run build && flatpak-builder --user --install build-dir flatpak/…yml - Login-Flow mit Device-Code + Smartphone
- Live-Stream 5+ Min ohne Werbe-Unterbrechung (Streamlink-Debug-Logs gegenchecken)
- VOD ansehen, mpv beenden, neu starten → springt an gespeicherte Position
- Komplette Bedienung nur mit Steam-Deck-Controller
- Im Gaming Mode als Non-Steam-Game starten und alles wiederholen
Offene Punkte (Post-MVP)
- Eigene
client_idim Build vor Release. - Chat / Emotes (BTTV/FFZ/7TV).
- Suche, Kategorien, Top-Streams.
- README + ToS-Disclaimer (Twitch-Werbeumgehung kann gegen ToS verstoßen).