mirror of
https://github.com/Andre-Bzk/twitch4steamdeck.git
synced 2026-11-15 15:43:08 +01:00
Vorbereitung Phase 3
- Plan und Todo überarbeitet
This commit is contained in:
+94
-54
@@ -1,8 +1,10 @@
|
|||||||
# Twitch4SteamDeck — Fortschritts-Snapshot
|
# Twitch4SteamDeck — Fortschritts-Snapshot
|
||||||
|
|
||||||
> Stand: 2026-04-07. Diese Datei dient als Übergabe zwischen Sessions.
|
> Stand: **Ende 2026-04-07**. Diese Datei dient als Übergabe zwischen Sessions.
|
||||||
> **Lies zuerst:** diese Datei + `todo.md`. Der vollständige Architekturplan liegt unter
|
> **Lies zuerst:** diese Datei + `todo.md` + `plan.md`.
|
||||||
> `C:\Users\andre\.claude\plans\smooth-shimmying-parasol.md` (im Repo gespiegelt als `plan.md`).
|
> Architekturplan-Quelle: `C:\Users\andre\.claude\plans\smooth-shimmying-parasol.md` (im Repo gespiegelt).
|
||||||
|
>
|
||||||
|
> **Aktueller Stand:** Phase 0–2 abgeschlossen & vom User verifiziert. Als nächstes Phase 3 (Live-Wiedergabe).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -53,63 +55,101 @@
|
|||||||
- `gamepad.ts` — Gamepad-Polling → synthetische KeyboardEvents (DPad + linker Stick + A/B)
|
- `gamepad.ts` — Gamepad-Polling → synthetische KeyboardEvents (DPad + linker Stick + A/B)
|
||||||
- CSS komplett überarbeitet: `.app-layout` (Grid 260px / 1fr), `.sidebar`, `.screen`, `.card-grid` mit `grid-auto-rows: max-content`, `.card` mit Block-Layout
|
- CSS komplett überarbeitet: `.app-layout` (Grid 260px / 1fr), `.sidebar`, `.screen`, `.card-grid` mit `grid-auto-rows: max-content`, `.card` mit Block-Layout
|
||||||
- **Entfernt:** alte `HomeScreen.tsx` (ersetzt durch FollowingScreen)
|
- **Entfernt:** alte `HomeScreen.tsx` (ersetzt durch FollowingScreen)
|
||||||
- **Status:** noch nicht vom User getestet
|
- **Status:** ✅ End-to-End vom User verifiziert (2026-04-07). Karten-Grid sauber, Tastatur-Navigation funktioniert, Sidebar-Switching ok.
|
||||||
|
|
||||||
## Wo wir gerade stehen
|
**Offener Rest aus Phase 2 (nicht blockierend):**
|
||||||
|
- Logout-Test (`userData/twitch-tokens.bin` wird gelöscht) — kommt später beim nächsten vollständigen Durchlauf
|
||||||
**Mitten in:** Phase 2 ist code-complete, wartet auf Verifikation durch den User.
|
- Gamepad-Test mit echtem Steam-Deck-Controller steht noch aus (Dev auf Windows)
|
||||||
|
|
||||||
**Konkret offen:**
|
|
||||||
1. User muss eine Twitch-App auf https://dev.twitch.tv/console/apps registrieren
|
|
||||||
(Public Client, Redirect `http://localhost`) und die Client-ID in `.env` eintragen:
|
|
||||||
```
|
|
||||||
MAIN_VITE_TWITCH_CLIENT_ID=<id>
|
|
||||||
```
|
|
||||||
2. `npm install` muss erneut laufen (neue Deps: `qrcode`, `@types/qrcode`).
|
|
||||||
3. `npm run dev` starten und den kompletten Login-Flow durchspielen:
|
|
||||||
- Button „Mit Twitch verbinden" → QR + Code erscheinen
|
|
||||||
- Auf Handy `twitch.tv/activate` öffnen, Code eingeben
|
|
||||||
- App sollte automatisch zu „Eingeloggt"-Platzhalter wechseln
|
|
||||||
- Neustart der App → sollte eingeloggt bleiben (Tokens in `userData/twitch-tokens.bin`)
|
|
||||||
|
|
||||||
**Mögliche Stolpersteine, die morgen früh zu prüfen sind:**
|
|
||||||
- `import.meta.env.MAIN_VITE_TWITCH_CLIENT_ID` im Main-Prozess: setzt voraus, dass electron-vite die
|
|
||||||
`.env` lädt — falls nicht, alternativ `process.env.MAIN_VITE_TWITCH_CLIENT_ID` oder via
|
|
||||||
`define`-Plugin in `electron.vite.config.ts` injizieren.
|
|
||||||
- TypeScript könnte über `import.meta.env` im Main-Prozess meckern. tsconfig.node.json hat
|
|
||||||
`"types": ["node", "electron-vite/node"]` — sollte reichen.
|
|
||||||
- Renderer-Types: `src/renderer/src/types/t4sd.d.ts` muss von `tsconfig.web.json` erfasst werden
|
|
||||||
(ist über `src/renderer/**/*.ts` abgedeckt).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Nächster Schritt morgen (in dieser Reihenfolge)
|
## Wo wir gerade stehen — Start Phase 3
|
||||||
|
|
||||||
1. **Fragen:** „Hat der Login-Flow gestern durchgelaufen? Falls Fehler, welche Logs?"
|
**Ziel Phase 3:** Live-Wiedergabe werbefrei via `streamlink | mpv` starten, vom `FollowingScreen` aus
|
||||||
2. **Falls Fehler:** Erst diese fixen, bevor neue Features dazukommen.
|
per Kanal-Karte → `ChannelScreen` (Detail) → Button „Live ansehen" → externes mpv-Fenster.
|
||||||
- Häufiger Verdächtiger: `import.meta.env`-Loading im Main → Workaround dokumentiert oben.
|
|
||||||
3. **Falls grün:** Direkt in **Schritt 2: Twitch Helix-Client + Followed-Channels-Home-Screen** einsteigen.
|
### Was morgen zuerst passieren muss
|
||||||
- Neue Dateien planen: `src/main/twitch/helixClient.ts`, `src/main/twitch/types.ts`,
|
|
||||||
IPC-Kanäle `twitch:get-followed`, `twitch:get-live-status`,
|
**1. Dev-Tooling verifizieren (User-Aktion nötig):**
|
||||||
Renderer-Screen `src/renderer/src/screens/HomeScreen.tsx` mit Spatial-Navigation-Karten.
|
Wir entwickeln auf Windows, deployen später nach Linux/Flatpak. Für Phase 3 muss der User lokal:
|
||||||
- Helix-Endpoints für MVP: `GET /users` (own), `GET /channels/followed`, `GET /streams` (batched).
|
- `mpv` installieren: `winget install mpv` (danach `mpv --version` in neuer Shell prüfen)
|
||||||
|
- `streamlink` installieren: `winget install streamlink` oder `pip install streamlink`
|
||||||
|
- **Baseline-Test** ohne unseren Code:
|
||||||
|
```
|
||||||
|
streamlink --twitch-disable-ads twitch.tv/<live-kanal> best --player mpv
|
||||||
|
```
|
||||||
|
→ öffnet mpv-Fenster mit dem Stream. Wenn das klappt, wissen wir, dass die Pipeline auf der
|
||||||
|
Windows-Dev-Umgebung grundsätzlich funktioniert und wir können im Code darauf aufbauen.
|
||||||
|
|
||||||
|
**Wenn der Baseline-Test scheitert**, haben wir entweder:
|
||||||
|
- fehlende Binaries (nicht im PATH)
|
||||||
|
- Twitch hat `--twitch-disable-ads` gebrochen → Fallback auf `streamlink-ttvlol`-Plugin
|
||||||
|
- Region-/Geo-Blocking
|
||||||
|
|
||||||
|
→ In diesem Fall **erst den Baseline fixen**, bevor Code geschrieben wird.
|
||||||
|
|
||||||
|
**2. Architektur-Entscheidung bestätigen (kurz abfragen):**
|
||||||
|
- Direkter Sprung von Karte → mpv **oder** dazwischen `ChannelScreen` als Detail-View?
|
||||||
|
- Plan sagt: `ChannelScreen` mit Detail + „Live ansehen"-Button. Das ist auch sinnvoll, weil
|
||||||
|
in Phase 4 derselbe Screen die VOD-Liste hostet. Default: so umsetzen.
|
||||||
|
- Quality-Picker im MVP erstmal mit Fixed-Default `best`, UI-Dropdown nachziehen wenn Rest steht.
|
||||||
|
|
||||||
|
**3. Dateien, die in Phase 3 neu entstehen (siehe `todo.md` Phase 3 für Details):**
|
||||||
|
|
||||||
|
Main-Prozess:
|
||||||
|
- `src/main/playback/types.ts`
|
||||||
|
- `src/main/playback/streamlink.ts` — `spawnStreamlink({channelLogin, quality, oauthToken})`
|
||||||
|
- `src/main/playback/mpvController.ts` — Plattform-Weiche Named Pipe / Unix Socket, JSON-IPC
|
||||||
|
- `src/main/playback/playbackService.ts` — orchestriert streamlink + mpv, räumt auf
|
||||||
|
- Erweiterung `src/main/ipc/handlers.ts`: `playback:start-live`, `playback:stop`, `playback:event`
|
||||||
|
|
||||||
|
Renderer:
|
||||||
|
- `src/renderer/src/screens/ChannelScreen.tsx` — Detail-Screen mit „Live ansehen"-Button
|
||||||
|
- Erweiterung `AppShell.tsx`: `selectedChannel`-State, Routing FollowingScreen ↔ ChannelScreen
|
||||||
|
- Erweiterung Preload + `t4sd.d.ts`: `window.t4sd.playback.{startLive, stop, onEvent}`
|
||||||
|
- `FocusableCard.onSelect` verkabeln (aktuell nur `console.log`)
|
||||||
|
|
||||||
|
### Windows-spezifische Stolpersteine, die Claude morgen wissen muss
|
||||||
|
|
||||||
|
- **mpv IPC-Socket:** auf Windows Named Pipe im Format `\\.\pipe\twitch4sd-mpv-<pid>`, NICHT
|
||||||
|
`/tmp/...sock`. In `mpvController.ts` mit `process.platform === 'win32'` verzweigen.
|
||||||
|
- **`net.createConnection` unter Windows mit Named Pipes:** funktioniert, erwartet aber den Pfad
|
||||||
|
als `{path: '\\\\.\\pipe\\twitch4sd-mpv-...'}`.
|
||||||
|
- **hwdec-Flag:** auf Windows `--hwdec=auto` oder `--hwdec=d3d11va`, nicht `vaapi` (das ist Linux).
|
||||||
|
- **Pipe streamlink → mpv:** funktioniert unter Windows mit Node `spawn` und `stdio: ['pipe', ...]`
|
||||||
|
und `streamlink.stdout.pipe(mpv.stdin)`. Oder alternativ: streamlink mit `--player mpv
|
||||||
|
--player-args "..."` aufrufen und mpv selbst die Pipe machen lassen. Variante 1 gibt uns mehr
|
||||||
|
Kontrolle, Variante 2 ist einfacher — **Vorschlag: mit Variante 2 starten**, später
|
||||||
|
refactoren falls wir mpv-IPC direkt aus unserem Code brauchen.
|
||||||
|
- **Achtung bei Variante 2:** mpv-IPC-Socket muss als `--player-args` durchgereicht werden,
|
||||||
|
z. B. `--player-args="--input-ipc-server=\\\\.\\pipe\\twitch4sd-mpv --fullscreen"`.
|
||||||
|
- **Prozess-Cleanup:** Beim App-Quit alle spawned Subprozesse killen (sonst Zombie-mpv-Fenster).
|
||||||
|
`app.on('before-quit', () => playbackService.stopAll())`.
|
||||||
|
|
||||||
|
### Technische Baseline aus Phase 0–2 (Wiederholung für Sanity-Check)
|
||||||
|
|
||||||
|
- Electron 33, electron-vite, TypeScript 5.5, React 18
|
||||||
|
- Auth: `authService.getValidAccessToken()` liefert einen frischen OAuth-Token
|
||||||
|
(Auto-Refresh via Refresh-Token, 60s Safety-Margin) — für `--twitch-api-header` direkt nutzbar
|
||||||
|
- HelixClient liefert `FollowedChannelInfo[]` mit `broadcasterLogin`, was als Channel-Name für
|
||||||
|
`twitch.tv/<login>` passt
|
||||||
|
- IPC-Pattern: `ipcMain.handle` im Main, `ipcRenderer.invoke` im Preload, `contextBridge` exponiert
|
||||||
|
`window.t4sd.*`. Main → Renderer Events via `webContents.send` + `ipcRenderer.on`
|
||||||
|
|
||||||
|
### Wichtiges Projekt-Memo
|
||||||
|
|
||||||
|
- Bei **allen Architekturänderungen** `plan.md` updaten (nicht nur `~/.claude/plans/...`).
|
||||||
|
- Nach **jeder Subaufgabe** Checkbox in `todo.md` setzen und 1-Zeilen-Update in dieser Datei.
|
||||||
|
- Dev-Host ist **Windows** (siehe Shell bash/Unix-Syntax in `/dev/null` etc.), Target ist
|
||||||
|
**Steam Deck (Linux / Flatpak)** — plattform-spezifische Weichen einbauen, wo es um Pfade,
|
||||||
|
Sockets und Hardware-Decoding geht.
|
||||||
|
- User akzeptiert „gut genug für MVP"-Entscheidungen (Quality-Picker später, kein Chat, etc.).
|
||||||
|
**Nicht** mit Speculative Abstractions aufblähen.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Wie morgen effektiv weiterarbeiten?
|
## Erste Anweisung morgen an Claude (copy-paste)
|
||||||
|
|
||||||
**Empfehlung:** Beim Sessionstart **diese drei Dateien in dieser Reihenfolge** lesen lassen:
|
> „Lies CLAUDE_PROGRESS.md, todo.md und plan.md. Wir wollen Phase 3 (Live-Wiedergabe via
|
||||||
|
> streamlink + mpv) starten. Frag mich zuerst, ob ich streamlink und mpv lokal installiert habe
|
||||||
1. `CLAUDE_PROGRESS.md` (diese Datei) — Snapshot
|
> und ob der Baseline-Test aus CLAUDE_PROGRESS.md funktioniert, dann leg los."
|
||||||
2. `todo.md` — Checkboxen-Tracker (jeder erledigte Schritt wird dort sofort abgehakt)
|
|
||||||
3. `plan.md` — gespiegelter Architekturplan (Quelle: `~/.claude/plans/smooth-shimmying-parasol.md`)
|
|
||||||
|
|
||||||
**Workflow:**
|
|
||||||
- Vor jeder Subaufgabe: relevanten Abschnitt in `todo.md` checken.
|
|
||||||
- Nach Abschluss: Checkbox in `todo.md` setzen + 1-Zeilen-Update in `CLAUDE_PROGRESS.md`
|
|
||||||
unter „Bereits abgeschlossen".
|
|
||||||
- Bei Architekturänderungen: `plan.md` updaten (nicht nur die Kopie im `~/.claude/plans/`).
|
|
||||||
|
|
||||||
**Erste Anweisung morgen an Claude (copy-paste):**
|
|
||||||
> „Lies CLAUDE_PROGRESS.md, todo.md und plan.md. Frag mich, ob der Login-Flow gestern durchgelaufen ist,
|
|
||||||
> und mach dann mit dem nächsten offenen Schritt weiter."
|
|
||||||
|
|||||||
@@ -58,59 +58,73 @@ mit Resume. Verteilung als Flatpak.
|
|||||||
Wiedergabe läuft **außerhalb** Electron in einem mpv-Vollbildfenster.
|
Wiedergabe läuft **außerhalb** Electron in einem mpv-Vollbildfenster.
|
||||||
Electron startet/stoppt mpv, sendet Befehle, lauscht auf `time-pos` für Verlaufsspeicherung.
|
Electron startet/stoppt mpv, sendet Befehle, lauscht auf `time-pos` für Verlaufsspeicherung.
|
||||||
|
|
||||||
## Verzeichnisstruktur (Soll)
|
## Verzeichnisstruktur (Ist-Stand + Soll)
|
||||||
|
|
||||||
```
|
```
|
||||||
twitch4steamdeck/
|
twitch4steamdeck/
|
||||||
├── package.json
|
├── package.json ✅
|
||||||
├── electron.vite.config.ts
|
├── electron.vite.config.ts ✅
|
||||||
├── electron-builder.yml
|
├── electron-builder.yml ✅
|
||||||
|
├── .env.example ✅
|
||||||
├── flatpak/
|
├── flatpak/
|
||||||
│ └── tv.twitch4steamdeck.App.yml
|
│ └── tv.twitch4steamdeck.App.yml ⏳ Phase 6
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── main/
|
│ ├── main/
|
||||||
│ │ ├── index.ts ✅
|
│ │ ├── index.ts ✅ (Auth + Helix instanziiert)
|
||||||
│ │ ├── auth/
|
│ │ ├── auth/
|
||||||
│ │ │ ├── deviceCodeFlow.ts ✅
|
│ │ │ ├── deviceCodeFlow.ts ✅
|
||||||
│ │ │ ├── tokenStore.ts ✅
|
│ │ │ ├── tokenStore.ts ✅
|
||||||
│ │ │ └── authService.ts ✅
|
│ │ │ └── authService.ts ✅
|
||||||
│ │ ├── twitch/
|
│ │ ├── twitch/
|
||||||
│ │ │ ├── helixClient.ts ⏳ Phase 2
|
│ │ │ ├── helixClient.ts ✅
|
||||||
│ │ │ └── types.ts ⏳ Phase 2
|
│ │ │ └── types.ts ✅
|
||||||
│ │ ├── playback/
|
│ │ ├── playback/
|
||||||
│ │ │ ├── streamlink.ts ⏳ Phase 3
|
│ │ │ ├── streamlink.ts ⏳ Phase 3
|
||||||
│ │ │ └── mpvController.ts ⏳ Phase 3
|
│ │ │ └── mpvController.ts ⏳ Phase 3
|
||||||
│ │ ├── store/
|
│ │ ├── store/
|
||||||
│ │ │ ├── db.ts ⏳ Phase 5
|
│ │ │ ├── db.ts ⏳ Phase 5
|
||||||
│ │ │ ├── historyRepo.ts ⏳ Phase 5
|
│ │ │ ├── historyRepo.ts ⏳ Phase 5
|
||||||
│ │ │ └── settingsRepo.ts ⏳ Phase 5
|
│ │ │ └── settingsRepo.ts ⏳ Phase 5
|
||||||
│ │ └── ipc/
|
│ │ └── ipc/
|
||||||
│ │ └── handlers.ts ✅ (nur Auth)
|
│ │ └── handlers.ts ✅ (Auth + twitch:get-followed)
|
||||||
│ ├── preload/
|
│ ├── preload/
|
||||||
│ │ └── index.ts ✅
|
│ │ └── index.ts ✅ (auth + twitch APIs)
|
||||||
│ └── renderer/
|
│ └── renderer/
|
||||||
│ ├── index.html ✅
|
│ ├── index.html ✅
|
||||||
│ └── src/
|
│ └── src/
|
||||||
│ ├── main.tsx ✅
|
│ ├── main.tsx ✅
|
||||||
│ ├── App.tsx ✅
|
│ ├── App.tsx ✅ (Routing: LoginScreen / AppShell)
|
||||||
│ ├── screens/
|
│ ├── screens/
|
||||||
│ │ ├── LoginScreen.tsx ✅
|
│ │ ├── LoginScreen.tsx ✅
|
||||||
│ │ ├── HomeScreen.tsx ⏳ Phase 2
|
│ │ ├── AppShell.tsx ✅ (Sidebar + Main-Content, Region-Nav)
|
||||||
│ │ ├── ChannelScreen.tsx ⏳ Phase 3/4
|
│ │ ├── FollowingScreen.tsx ✅ (ersetzt die ursprünglich geplante HomeScreen.tsx)
|
||||||
|
│ │ ├── BrowseScreen.tsx ✅ (Platzhalter)
|
||||||
|
│ │ ├── AccountScreen.tsx ✅ (Platzhalter + Logout)
|
||||||
|
│ │ ├── ChannelScreen.tsx ⏳ Phase 3/4 (Detail-Screen mit „Live ansehen")
|
||||||
│ │ └── PlayerLaunchScreen.tsx ⏳ Phase 3
|
│ │ └── PlayerLaunchScreen.tsx ⏳ Phase 3
|
||||||
│ ├── components/
|
│ ├── components/
|
||||||
│ │ ├── FocusableCard.tsx ⏳ Phase 2
|
│ │ ├── Sidebar.tsx ✅
|
||||||
│ │ └── SpatialNav/ ⏳ Phase 2
|
│ │ ├── Icons.tsx ✅ (inline SVG: Heart/Compass/User)
|
||||||
|
│ │ └── FocusableCard.tsx ✅ (Block-Layout)
|
||||||
│ ├── input/
|
│ ├── input/
|
||||||
│ │ └── gamepad.ts ⏳ Phase 2
|
│ │ └── gamepad.ts ✅
|
||||||
│ ├── types/
|
│ ├── types/
|
||||||
│ │ └── t4sd.d.ts ✅
|
│ │ └── t4sd.d.ts ✅
|
||||||
│ └── styles/
|
│ └── styles/
|
||||||
│ └── global.css ✅
|
│ └── global.css ✅
|
||||||
└── resources/
|
└── resources/
|
||||||
└── icons/
|
└── icons/ ⏳
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Architektur-Abweichungen vom Original-Plan (bewusst):**
|
||||||
|
- `HomeScreen.tsx` wurde durch `FollowingScreen.tsx` ersetzt und in ein `AppShell.tsx`-Layout
|
||||||
|
mit linker `Sidebar.tsx` (3 Tabs: Du folgst / Durchsuchen / Mein Account) eingebettet.
|
||||||
|
- Spatial-Navigation ist eine **eigene Implementierung** (Region-basiert: `sidebar` ↔ `main`),
|
||||||
|
kein `@noriginmedia/norigin-spatial-navigation`. Grund: einfacher, keine externe Dependency,
|
||||||
|
volle Kontrolle über das Region-Switch-Verhalten.
|
||||||
|
- `FocusableCard` nutzt **Block-Layout** (nicht Flex), weil Flex im Zusammenspiel mit
|
||||||
|
`aspect-ratio` und Grid-Zellen Schrumpf-Artefakte hatte.
|
||||||
|
|
||||||
## Kernkomponenten — Spezifikation
|
## Kernkomponenten — Spezifikation
|
||||||
|
|
||||||
### Auth (✅ implementiert)
|
### Auth (✅ implementiert)
|
||||||
@@ -123,19 +137,54 @@ twitch4steamdeck/
|
|||||||
- Client-ID via `MAIN_VITE_TWITCH_CLIENT_ID` (`.env` im Projektroot, electron-vite lädt sie).
|
- Client-ID via `MAIN_VITE_TWITCH_CLIENT_ID` (`.env` im Projektroot, electron-vite lädt sie).
|
||||||
- Scope MVP: `user:read:follows`.
|
- Scope MVP: `user:read:follows`.
|
||||||
|
|
||||||
### Helix-Client (⏳ Phase 2)
|
### Helix-Client (✅ implementiert)
|
||||||
MVP-Endpoints:
|
Implementierte Endpoints (in `src/main/twitch/helixClient.ts`):
|
||||||
- `GET /users` (eigener User)
|
- `GET /users` (eigener User → `cachedUserId`)
|
||||||
- `GET /channels/followed?user_id=…` (paginiert via `after`)
|
- `GET /users?id=…` (batched, für Avatare der Followed-Kanäle)
|
||||||
|
- `GET /channels/followed?user_id=…` (paginiert via `after`, safety-cap 500)
|
||||||
- `GET /streams?user_id=…` (batched, max 100 IDs/Call)
|
- `GET /streams?user_id=…` (batched, max 100 IDs/Call)
|
||||||
- `GET /videos?user_id=…&type=archive`
|
- ⏳ `GET /videos?user_id=…&type=archive` (noch offen für Phase 4)
|
||||||
|
|
||||||
Header: `Client-Id: <id>`, `Authorization: Bearer <access_token>`.
|
Header: `Client-Id: <id>`, `Authorization: Bearer <access_token>`.
|
||||||
|
`getFollowedWithLiveStatus()` merged Followed + Live + Avatare zu `FollowedChannelInfo[]` und
|
||||||
|
sortiert live-zuerst, dann alphabetisch.
|
||||||
|
|
||||||
### Streamlink + mpv (⏳ Phase 3)
|
### 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`
|
**Kommandos (Linux / Flatpak-Target):**
|
||||||
- Auf Windows: Named Pipe statt Unix-Socket.
|
```
|
||||||
|
streamlink \
|
||||||
|
--twitch-disable-ads \
|
||||||
|
--twitch-api-header=Authorization=OAuth <token> \
|
||||||
|
--stdout twitch.tv/<channel> best
|
||||||
|
| mpv - \
|
||||||
|
--input-ipc-server=/tmp/twitch4sd-mpv.sock \
|
||||||
|
--fullscreen --hwdec=vaapi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows-Entwicklung (Ist-Umgebung):**
|
||||||
|
- mpv: `winget install mpv` oder Binary von mpv.io ins PATH
|
||||||
|
- streamlink: `winget install streamlink` oder `pip install streamlink`
|
||||||
|
- IPC-Socket: Named Pipe `\\.\pipe\twitch4sd-mpv` (nicht `/tmp/...sock`)
|
||||||
|
- `--hwdec=vaapi` → auf Windows `--hwdec=d3d11va` oder `auto`
|
||||||
|
- Pipe stdout → stdin funktioniert auch unter Windows (Node `spawn` mit stdio pipe)
|
||||||
|
|
||||||
|
**Architektur-Entscheidung für `mpvController`:**
|
||||||
|
- `process.platform === 'win32'` → Named Pipe path, sonst Unix Socket
|
||||||
|
- Nach `spawn` kurz warten (100–200 ms) bis der IPC-Endpoint bereit ist, dann `net.createConnection`
|
||||||
|
- JSON-IPC: pro Zeile ein Command `{"command": ["set_property", "pause", true]}\n`
|
||||||
|
- Property-Observer für `time-pos` → Events in Main-Prozess → IPC zum Renderer (für Phase 5 Verlauf)
|
||||||
|
|
||||||
|
**Spezielle Streamlink-Flags zu prüfen:**
|
||||||
|
- `--twitch-low-latency` (reduziert Buffer, kann aber stottern verursachen)
|
||||||
|
- `--twitch-disable-hosting`
|
||||||
|
- TTV.LOL-Plugin: `pip install streamlink-ttvlol` — Fallback falls `--twitch-disable-ads` allein
|
||||||
|
keine Werbe-Umgehung mehr leistet.
|
||||||
|
|
||||||
|
**IPC zwischen Renderer und Main (Phase 3):**
|
||||||
|
- `playback:start-live` → `{channelLogin, quality}` → startet Prozess-Kette, gibt `playbackId` zurück
|
||||||
|
- `playback:stop` → sendet `quit` an mpv, killt streamlink
|
||||||
|
- `playback:event` (main → renderer) → `{kind: 'started' | 'stopped' | 'error', message?}`
|
||||||
|
|
||||||
### VOD-History-DB (⏳ Phase 5)
|
### VOD-History-DB (⏳ Phase 5)
|
||||||
```sql
|
```sql
|
||||||
|
|||||||
@@ -39,18 +39,57 @@
|
|||||||
- [x] `AppShell.tsx` + `Sidebar.tsx` — Layout mit linker Navigation (Du folgst / Durchsuchen / Mein Account)
|
- [x] `AppShell.tsx` + `Sidebar.tsx` — Layout mit linker Navigation (Du folgst / Durchsuchen / Mein Account)
|
||||||
- [x] `Icons.tsx` — inline SVG-Icons (Heart, Compass, User)
|
- [x] `Icons.tsx` — inline SVG-Icons (Heart, Compass, User)
|
||||||
- [x] `BrowseScreen.tsx` + `AccountScreen.tsx` — Platzhalter-Screens mit Logout
|
- [x] `BrowseScreen.tsx` + `AccountScreen.tsx` — Platzhalter-Screens mit Logout
|
||||||
- [ ] **End-to-End-Verifikation durch User** (Sidebar-Navigation + Karten-Grid mit echten Daten testen)
|
- [x] **End-to-End-Verifikation durch User** (2026-04-07: Karten + Tastatur-Nav bestätigt)
|
||||||
|
|
||||||
## Phase 3 — Streamlink + mpv (Live-Wiedergabe)
|
## Phase 3 — Streamlink + mpv (Live-Wiedergabe)
|
||||||
- [ ] `src/main/playback/streamlink.ts` — Subprozess-Spawning, `--twitch-disable-ads`,
|
|
||||||
OAuth-Header für Sub-only Inhalte, optional TTV.LOL-Plugin
|
### Vorab (Dev-Setup, Windows)
|
||||||
- [ ] `src/main/playback/mpvController.ts` — Spawn mpv mit `--input-ipc-server`,
|
- [ ] **User:** `mpv` installieren und im PATH verfügbar machen (`winget install mpv` oder
|
||||||
Unix-Socket JSON-IPC (Win: Named Pipe), `pause`/`seek`/`quit`-Befehle,
|
Binary von mpv.io) — `mpv --version` muss in Shell funktionieren
|
||||||
`observe_property time-pos`
|
- [ ] **User:** `streamlink` installieren (`winget install streamlink` oder
|
||||||
- [ ] IPC: `playback:start-live`, `playback:stop`
|
`pip install streamlink`) — `streamlink --version` muss funktionieren
|
||||||
- [ ] Channel-Detail-Screen mit „Live ansehen"-Button
|
- [ ] **User:** Kurz manuell testen:
|
||||||
- [ ] Quality-Picker
|
`streamlink --twitch-disable-ads twitch.tv/<live-channel> best --player mpv`
|
||||||
- [ ] End-to-End: 5 min Live-Stream ohne Werbe-Unterbrechung
|
→ soll ein mpv-Fenster mit Live-Stream öffnen. Wenn das klappt, ist der Pipeline-Baseline
|
||||||
|
verifiziert und wir können im Code darauf aufbauen.
|
||||||
|
|
||||||
|
### Backend (Main-Prozess)
|
||||||
|
- [ ] `src/main/playback/types.ts` — `PlaybackState`, `PlaybackEvent`, `Quality`
|
||||||
|
- [ ] `src/main/playback/streamlink.ts` — `spawnStreamlink({channelLogin, quality, oauthToken})`,
|
||||||
|
gibt `ChildProcess` mit stdout-Stream zurück, `--twitch-disable-ads`,
|
||||||
|
`--twitch-api-header=Authorization=OAuth <token>`, stderr in Logger
|
||||||
|
- [ ] `src/main/playback/mpvController.ts`:
|
||||||
|
- [ ] Plattform-Weiche: `win32` → Named Pipe `\\.\pipe\twitch4sd-mpv-<pid>`,
|
||||||
|
sonst Unix Socket unter `os.tmpdir()`
|
||||||
|
- [ ] `start(stdin)` — spawnt mpv mit `--input-ipc-server=<path> --fullscreen --hwdec=auto -`
|
||||||
|
- [ ] IPC-Client verbindet mit Retry (100–200 ms Delay nach spawn)
|
||||||
|
- [ ] Commands: `pause(bool)`, `seek(sec)`, `quit()`, `observeProperty(name)`
|
||||||
|
- [ ] `EventEmitter`-Events: `'started'`, `'time-pos'`, `'ended'`, `'error'`
|
||||||
|
- [ ] `src/main/playback/playbackService.ts` — bindet streamlink + mpv zusammen,
|
||||||
|
verwaltet `currentPlayback: { streamlink, mpv, channelLogin }`, räumt beim Stop auf
|
||||||
|
- [ ] IPC-Kanäle (in `handlers.ts`):
|
||||||
|
- [ ] `playback:start-live` → `{channelLogin, quality}` → `Promise<{playbackId}>`
|
||||||
|
- [ ] `playback:stop`
|
||||||
|
- [ ] `playback:event` (main → renderer, `{kind, payload?}`)
|
||||||
|
|
||||||
|
### Renderer (UI)
|
||||||
|
- [ ] Preload + `t4sd.d.ts` erweitern: `window.t4sd.playback.{startLive, stop, onEvent}`
|
||||||
|
- [ ] `src/renderer/src/screens/ChannelScreen.tsx` — Detail-Screen:
|
||||||
|
- [ ] Hero mit Thumbnail/Avatar + Kanalname + Titel + Spiel + Viewer
|
||||||
|
- [ ] Button „Live ansehen" (default `best`)
|
||||||
|
- [ ] Quality-Picker als Dropdown/Liste (`best`, `720p`, `480p`, `audio_only`) — MVP optional,
|
||||||
|
kann mit Default `best` beginnen und später nachgezogen werden
|
||||||
|
- [ ] „Zurück"-Button (B / Escape)
|
||||||
|
- [ ] Routing in `AppShell.tsx` erweitern: `selectedChannel: FollowedChannelInfo | null` State,
|
||||||
|
wenn gesetzt → `ChannelScreen` statt `FollowingScreen` anzeigen
|
||||||
|
- [ ] `FocusableCard.onSelect` → setzt `selectedChannel`
|
||||||
|
- [ ] Spatial-Nav: B-Taste / Escape im ChannelScreen → zurück zu FollowingScreen
|
||||||
|
|
||||||
|
### Verifikation
|
||||||
|
- [ ] 5 min Live-Stream ohne Werbe-Unterbrechung auf Windows (Dev-Setup)
|
||||||
|
- [ ] Beim Stop: mpv-Fenster schließt sich sauber, streamlink-Prozess terminiert,
|
||||||
|
keine Zombie-Prozesse im Task-Manager
|
||||||
|
- [ ] Logout/Login und erneutes Starten funktioniert ohne App-Neustart
|
||||||
|
|
||||||
## Phase 4 — VOD-Browsing + -Wiedergabe
|
## Phase 4 — VOD-Browsing + -Wiedergabe
|
||||||
- [ ] Helix `GET /videos?user_id=…&type=archive` (paginiert)
|
- [ ] Helix `GET /videos?user_id=…&type=archive` (paginiert)
|
||||||
|
|||||||
Reference in New Issue
Block a user