From ac9f9ea94fc53e2d0cb6733fe04c00ab5490bb59 Mon Sep 17 00:00:00 2001 From: Andre <200930841+Andre-Bzk@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:43:37 +0200 Subject: [PATCH] Phase 6 Fix 2 -Wiedergabe Livestream und VOD im Desktopmodus --- flatpak/build-flatpak.sh | 17 ++++++++ flatpak/tv.twitch4steamdeck.App.yml | 48 +++++++++------------- src/main/playback/playbackService.ts | 48 ++++++++++++++++------ src/main/playback/streamlink.ts | 16 +++++--- src/renderer/src/screens/ChannelScreen.tsx | 2 +- todo.md | 5 ++- 6 files changed, 89 insertions(+), 47 deletions(-) diff --git a/flatpak/build-flatpak.sh b/flatpak/build-flatpak.sh index 6c9e659..fbec135 100644 --- a/flatpak/build-flatpak.sh +++ b/flatpak/build-flatpak.sh @@ -101,6 +101,23 @@ if [ -f "$NATIVE_NODE" ]; then fi fi +# ───────────────────────────────────────────────────────────────────────────── +# Schritt 4b: Python-Deps für streamlink via flatpak-pip-generator +# Erzeugt flatpak/python3-streamlink.json mit allen pip-Deps + sha256-Hashes. +# Die Datei wird von tv.twitch4steamdeck.App.yml per !include eingebunden. +# ───────────────────────────────────────────────────────────────────────────── +echo "" +echo "=== [4b] Python-Deps für streamlink generieren ===" +if ! python3 -c "import flatpak_pip_generator" &>/dev/null; then + echo " → Installiere flatpak-pip-generator ..." + # Ubuntu 24.04: externally-managed-environment → --break-system-packages nötig + pip3 install --quiet --break-system-packages flatpak-pip-generator +fi +python3 -m flatpak_pip_generator \ + streamlink==6.11.0 \ + --output flatpak/python3-streamlink +echo " ✓ flatpak/python3-streamlink.json generiert ($(wc -l < flatpak/python3-streamlink.json) Zeilen)" + # ───────────────────────────────────────────────────────────────────────────── # Schritt 5: Manifest-SHA256-Werte füllen (interaktiv) # ───────────────────────────────────────────────────────────────────────────── diff --git a/flatpak/tv.twitch4steamdeck.App.yml b/flatpak/tv.twitch4steamdeck.App.yml index 0d97019..d0b22cf 100644 --- a/flatpak/tv.twitch4steamdeck.App.yml +++ b/flatpak/tv.twitch4steamdeck.App.yml @@ -64,14 +64,26 @@ modules: sha256: 7f5da41b872302208f0e10c22d3e108ab9fdb1f253eb47c18651698c65d6b426 # ───────────────────────────────────────────────────────────────────────────── - # 1c) mpv — Media-Player für Live-Streams und VODs - # ffmpeg, libass, libplacebo, libva (VAAPI) verfügbar. + # 1c) libXpresent — X11-Extension für VSync/Frame-Timing (mpv X11-Dependency) + # ───────────────────────────────────────────────────────────────────────────── + - name: libxpresent + buildsystem: autotools + sources: + - type: archive + url: https://xorg.freedesktop.org/releases/individual/lib/libXpresent-1.0.1.tar.xz + sha256: b964df9e5a066daa5e08d2dc82692c57ca27d00b8cc257e8e960c9f1cf26231b + + # ───────────────────────────────────────────────────────────────────────────── + # 1d) mpv — Media-Player für Live-Streams und VODs + # ffmpeg, libass, libplacebo, libva (VAAPI), X11 verfügbar. # ───────────────────────────────────────────────────────────────────────────── - name: mpv buildsystem: meson config-opts: - -Dvaapi=enabled - -Dpulse=enabled + - -Dx11=enabled + - -Degl-x11=enabled - -Dalsa=disabled - -Dlua=disabled - -Djavascript=disabled @@ -85,33 +97,13 @@ modules: # ───────────────────────────────────────────────────────────────────────────── # 2) streamlink (Python) — liefert HLS-URLs aus Twitch-Streams - # Python 3 kommt aus dem freedesktop SDK. - # - # Vorbereitung (einmalig, in WSL2 Ubuntu): - # pip3 install flatpak-pip-generator - # python3 flatpak-pip-generator streamlink --output flatpak/python-streamlink-deps - # → erzeugt flatpak/python-streamlink-deps.json mit allen pinned Deps + sha256. - # Dann dieses Modul hier durch die generierte Datei ersetzen: - # - name: python3-streamlink-deps - # buildsystem: simple - # build-commands: [...] # generiert durch flatpak-pip-generator - # sources: [...] # alle .whl/.tar.gz mit echten sha256 - # - # Vereinfachte Variante (erfordert Netzwerk-Build — nur für lokale Tests): + # Wird von build-flatpak.sh via flatpak-pip-generator generiert: + # python3 -m flatpak_pip_generator streamlink==6.11.0 \ + # --output flatpak/python3-streamlink + # Das erzeugt flatpak/python3-streamlink.json mit allen Deps + sha256-Hashes. + # flatpak-builder lädt alle Sources offline herunter und installiert sie sauber. # ───────────────────────────────────────────────────────────────────────────── - - name: python3-streamlink - buildsystem: simple - build-commands: - # --no-deps: Dependencies separat via flatpak-pip-generator auflösen - - pip3 install --prefix=/app --no-build-isolation --no-deps . - sources: - - type: archive - url: https://github.com/streamlink/streamlink/archive/refs/tags/6.11.0.tar.gz - # sha256 generieren: sha256sum streamlink-6.11.0.tar.gz - sha256: fbed984049b33f176e3a63e4bce302180d1ef24feffa24ea7306aad1a24bff40 - # ACHTUNG: Vor dem echten Release-Build alle Python-Dependencies (requests, - # websocket-client, pycountry, pycryptodome, ...) mit flatpak-pip-generator - # als separate file-Sources einbinden. Sonst ist Streamlink zur Laufzeit unvollständig. + - !include python3-streamlink.json # ───────────────────────────────────────────────────────────────────────────── # 3) Haupt-App diff --git a/src/main/playback/playbackService.ts b/src/main/playback/playbackService.ts index e6274b7..b83a9d3 100644 --- a/src/main/playback/playbackService.ts +++ b/src/main/playback/playbackService.ts @@ -24,20 +24,34 @@ export class PlaybackService extends EventEmitter { if (this.current) await this.stop() const proc = spawnStreamlink(`twitch.tv/${channelLogin}`, quality) - this._attachLogs(proc) + + let stderrBuf = '' + proc.stdout?.on('data', (d: Buffer) => console.log('[streamlink]', d.toString().trim())) + proc.stderr?.on('data', (d: Buffer) => { + const s = d.toString() + console.log('[streamlink stderr]', s.trim()) + stderrBuf += s + }) proc.on('error', (e: Error) => { if (this.current?.process === proc) this.current = null this.emit('playback-event', { kind: 'error', - message: `streamlink Fehler: ${e.message}` + message: `streamlink nicht gefunden: ${e.message}` } satisfies PlaybackEvent) }) proc.on('exit', (code) => { if (this.current?.process === proc) this.current = null - if (code !== 0 && code !== null) console.warn('[streamlink] exit code', code) - this.emit('playback-event', { kind: 'stopped' } satisfies PlaybackEvent) + if (code !== 0 && code !== null) { + const snippet = stderrBuf.trim().split('\n').slice(-6).join('\n').substring(0, 500) + this.emit('playback-event', { + kind: 'error', + message: `streamlink Exit ${code}:\n${snippet || '(kein Output)'}` + } satisfies PlaybackEvent) + } else { + this.emit('playback-event', { kind: 'stopped' } satisfies PlaybackEvent) + } }) // Für Live: mpv wird von streamlink gestartet, kein direkter IPC nötig @@ -75,20 +89,34 @@ export class PlaybackService extends EventEmitter { } const proc = spawnMpv(hlsUrl, { ipcPath: this.ipcPath, startSeconds: resumePos }) - this._attachLogs(proc) + + let mpvStderrBuf = '' + proc.stdout?.on('data', (d: Buffer) => console.log('[mpv]', d.toString().trim())) + proc.stderr?.on('data', (d: Buffer) => { + const s = d.toString() + console.log('[mpv stderr]', s.trim()) + mpvStderrBuf += s + }) proc.on('error', (e: Error) => { if (this.current?.process === proc) this.current = null this.emit('playback-event', { kind: 'error', - message: `mpv Fehler: ${e.message}` + message: `mpv nicht gefunden: ${e.message}` } satisfies PlaybackEvent) }) proc.on('exit', (code) => { if (this.current?.process === proc) this.current = null - if (code !== 0 && code !== null) console.warn('[mpv] exit code', code) - this.emit('playback-event', { kind: 'stopped' } satisfies PlaybackEvent) + if (code !== 0 && code !== null) { + const snippet = mpvStderrBuf.trim().split('\n').slice(-6).join('\n').substring(0, 500) + this.emit('playback-event', { + kind: 'error', + message: `mpv Exit ${code}:\n${snippet || '(kein Output)'}` + } satisfies PlaybackEvent) + } else { + this.emit('playback-event', { kind: 'stopped' } satisfies PlaybackEvent) + } }) const mpv = new MpvController(this.ipcPath) @@ -129,8 +157,4 @@ export class PlaybackService extends EventEmitter { this.stop().catch(() => {}) } - private _attachLogs(proc: ChildProcess): void { - proc.stdout?.on('data', (d: Buffer) => console.log('[player stdout]', d.toString().trim())) - proc.stderr?.on('data', (d: Buffer) => console.log('[player stderr]', d.toString().trim())) - } } diff --git a/src/main/playback/streamlink.ts b/src/main/playback/streamlink.ts index 67cebb8..8527b16 100644 --- a/src/main/playback/streamlink.ts +++ b/src/main/playback/streamlink.ts @@ -35,9 +35,14 @@ const MPV_BIN = resolveMpvBin() /** Startet streamlink so dass es mpv als Player verwendet (für Live-Streams). */ export function spawnStreamlink(url: string, quality: string): ChildProcess { - return spawn(STREAMLINK_BIN, ['--player', MPV_BIN, '--player-args', '--fullscreen', url, quality], { - stdio: ['ignore', 'pipe', 'pipe'] - }) + // --vo=gpu: Legacy-OpenGL-Renderer (gpu-next/libplacebo scheitert in Flatpak ohne Vulkan) + // --hwdec=auto: VAAPI-Fallback auf SW-Dekodierung wenn nötig + // --force-window=yes: Fenster auch bei vo-Init-Problemen erzwingen + return spawn( + STREAMLINK_BIN, + ['--player', MPV_BIN, '--player-args', '--fullscreen --vo=gpu --hwdec=auto --force-window=yes', url, quality], + { stdio: ['ignore', 'pipe', 'pipe'] } + ) } /** @@ -70,12 +75,13 @@ export interface MpvOptions { /** Startet mpv direkt mit einer URL (HLS oder lokal). IPC-Socket wird gesetzt. */ export function spawnMpv(url: string, { ipcPath, startSeconds }: MpvOptions): ChildProcess { - const hwdec = process.platform === 'win32' ? 'auto' : 'vaapi' const args = [ url, `--input-ipc-server=${ipcPath}`, '--fullscreen', - `--hwdec=${hwdec}` + '--vo=gpu', + '--hwdec=auto', + '--force-window=yes' ] if (startSeconds && startSeconds > 0) { args.push(`--start=${startSeconds}`) diff --git a/src/renderer/src/screens/ChannelScreen.tsx b/src/renderer/src/screens/ChannelScreen.tsx index e1cb734..4753cf9 100644 --- a/src/renderer/src/screens/ChannelScreen.tsx +++ b/src/renderer/src/screens/ChannelScreen.tsx @@ -257,7 +257,7 @@ export default function ChannelScreen({ channel, onBack }: Props): JSX.Element { )} {playState === 'error' && ( -

{errorMsg}

+

{errorMsg}

)} diff --git a/todo.md b/todo.md index 23969a4..dc037cc 100644 --- a/todo.md +++ b/todo.md @@ -126,8 +126,11 @@ - [x] `flatpak/build-flatpak.sh` Build-Skript für WSL2 (inkl. SHA256-Auflösung) - [x] `resources/icons/icon.svg` App-Icon (Platzhalter) - [x] mpv + streamlink als Flatpak-Module im Manifest definiert +- [x] **Fix (Phase 6.1):** streamlink Python-Deps via `flatpak-pip-generator` eingebunden + (`build-flatpak.sh` Schritt 4b + `!include python3-streamlink.json` im Manifest) +- [x] **Fix (Phase 6.1):** mpv-Patch `patches/mpv-ffmpeg7-avio-const.patch` ins Manifest eingebunden - [ ] **User:** WSL2-Setup (flatpak, flatpak-builder, Runtimes) — einmalig -- [ ] **User:** `bash flatpak/build-flatpak.sh` in WSL2 ausführen +- [ ] **User:** `bash flatpak/build-flatpak.sh` in WSL2 neu ausführen (nach 6.1-Fix) - [ ] **User:** Auf Steam Deck installieren und Login + Live + VOD durchspielen - [ ] **User:** Als Non-Steam-Game in Gaming Mode hinzufügen, dort testen