2 Commits
Author SHA1 Message Date
Fojadrachi 04a12c2426 Release v1.0.1
Release / build-windows (push) Waiting to run
Release / build-linux (push) Waiting to run
Release / publish-release (push) Blocked by required conditions
2026-09-11 19:46:45 +02:00
FojadrachiandClaude Sonnet 5 cb1e13b0fc Fix: Discord RPC hat nie Daten empfangen (runJavaScript-Bug)
Wurzelursache gefunden: page().runJavaScript() liefert bei einem direkt
zurueckgegebenen JS-Objekt in dieser PySide6/QtWebEngine-Version zuverlaessig nur
einen leeren String statt das Objekt (Zahlen/Strings funktionieren, Objekte nicht).
Dadurch schlug 'isinstance(result, dict)' in BrowserTab._on_media_probe_result IMMER
fehl, mediaInfoChanged wurde nie emittiert, MainWindow._on_media_info nie aufgerufen
und der DiscordRPCWorker damit nie mit echten Daten gefuettert (Warteschlange blieb
fuer immer leer, der Worker hat sich nie mal mit Discord verbunden).

Fix: media_probe.py gibt jetzt JSON.stringify(...) zurueck, browser.py parst den
String mit json.loads(). Per isoliertem Test verifiziert (Songtitel erscheint jetzt
korrekt im Fenstertitel, Discord-Update wird erfolgreich gesendet).

Ausserdem: PLAYTUBE_DEBUG=1 Env-Var fuer Debug-Logging in browser.py/discord_rpc.py,
Exceptions in discord_rpc.py werden jetzt sichtbar geloggt statt verschluckt,
neue Discord Client-ID.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-11 19:46:32 +02:00
5 changed files with 44 additions and 10 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
"""Playtube - ein eigenstaendiger YouTube- & YouTube-Music-Player mit Discord Rich Presence.""" """Playtube - ein eigenstaendiger YouTube- & YouTube-Music-Player mit Discord Rich Presence."""
__app_name__ = "Playtube" __app_name__ = "Playtube"
__version__ = "1.0.0" __version__ = "1.0.1"
+19 -2
View File
@@ -3,6 +3,8 @@ Profil (eigener Datenordner, kein System-Browser-Profil) und periodischem Ausles
der aktuellen Wiedergabe fuer Discord Rich Presence.""" der aktuellen Wiedergabe fuer Discord Rich Presence."""
from __future__ import annotations from __future__ import annotations
import json
import os
import sys import sys
from PySide6.QtCore import QTimer, Signal, QUrl from PySide6.QtCore import QTimer, Signal, QUrl
@@ -162,5 +164,20 @@ class BrowserTab(QWebEngineView):
self.page().runJavaScript(MEDIA_PROBE_JS, self._on_media_probe_result) self.page().runJavaScript(MEDIA_PROBE_JS, self._on_media_probe_result)
def _on_media_probe_result(self, result) -> None: def _on_media_probe_result(self, result) -> None:
if isinstance(result, dict): # runJavaScript() liefert JS-Objekte ueber diese Bruecke nicht zuverlaessig
self.mediaInfoChanged.emit(result) # als dict (siehe media_probe.py) - das Probe-Skript gibt daher einen
# JSON-String zurueck, der hier geparst wird.
info = None
if isinstance(result, str) and result:
try:
info = json.loads(result)
except json.JSONDecodeError:
info = None
elif isinstance(result, dict):
info = result
if os.environ.get("PLAYTUBE_DEBUG"):
print(f"[media-probe:{self._home_url}] roh={result!r} geparst={info!r}", flush=True)
if isinstance(info, dict):
self.mediaInfoChanged.emit(info)
+1 -1
View File
@@ -15,7 +15,7 @@ DEFAULT_CONFIG: dict[str, Any] = {
"discord": { "discord": {
"enabled": True, "enabled": True,
# Deine Discord Application Client-ID (discord.com/developers/applications). # Deine Discord Application Client-ID (discord.com/developers/applications).
"client_id": "1544058029190938774", "client_id": "1548023494976086127",
"update_interval_seconds": 15, "update_interval_seconds": 15,
# Wenn nichts laeuft: Idle-Status anzeigen statt Presence komplett zu leeren. # Wenn nichts laeuft: Idle-Status anzeigen statt Presence komplett zu leeren.
"show_idle_presence": True, "show_idle_presence": True,
+16 -4
View File
@@ -7,6 +7,7 @@ versucht, ohne die App zu beeintraechtigen.
""" """
from __future__ import annotations from __future__ import annotations
import os
import queue import queue
import time import time
from typing import Any from typing import Any
@@ -156,8 +157,12 @@ class DiscordRPCWorker(QThread):
self._presence = Presence(self._client_id) self._presence = Presence(self._client_id)
self._presence.connect() self._presence.connect()
self._connected = True self._connected = True
except Exception: if os.environ.get("PLAYTUBE_DEBUG"):
print("[discord-rpc] verbunden", flush=True)
except Exception as exc:
self._connected = False self._connected = False
if os.environ.get("PLAYTUBE_DEBUG"):
print(f"[discord-rpc] Verbindung fehlgeschlagen: {exc!r}", flush=True)
def _send(self, item) -> None: def _send(self, item) -> None:
if self._presence is None: if self._presence is None:
@@ -165,14 +170,21 @@ class DiscordRPCWorker(QThread):
try: try:
if item is _IDLE_SENTINEL: if item is _IDLE_SENTINEL:
if self._show_idle: if self._show_idle:
self._presence.update(**build_idle_payload(self._session_start)) payload = build_idle_payload(self._session_start)
self._presence.update(**payload)
else: else:
payload = None
self._presence.clear() self._presence.clear()
else: else:
self._presence.update(**build_presence_payload(item, self._session_start)) payload = build_presence_payload(item, self._session_start)
except Exception: self._presence.update(**payload)
if os.environ.get("PLAYTUBE_DEBUG"):
print(f"[discord-rpc] gesendet: {payload!r}", flush=True)
except Exception as exc:
# Discord evtl. geschlossen worden -> beim naechsten Mal neu verbinden. # Discord evtl. geschlossen worden -> beim naechsten Mal neu verbinden.
self._connected = False self._connected = False
if os.environ.get("PLAYTUBE_DEBUG"):
print(f"[discord-rpc] Senden fehlgeschlagen: {exc!r}", flush=True)
def _cleanup(self) -> None: def _cleanup(self) -> None:
if self._presence is not None: if self._presence is not None:
+7 -2
View File
@@ -27,7 +27,12 @@ MEDIA_PROBE_JS = r"""
thumbnail = imgY ? imgY.href : null; thumbnail = imgY ? imgY.href : null;
} }
return { // WICHTIG: QtWebEngine's runJavaScript()-Bruecke liefert bei einem direkt
// zurueckgegebenen JS-Objekt zuverlaessig nur einen leeren String statt des
// Objekts (Zahlen/Strings funktionieren, Objekte nicht) - deshalb hier als
// JSON-String zurueckgeben und in Python mit json.loads() wieder parsen
// (siehe BrowserTab._on_media_probe_result in browser.py).
return JSON.stringify({
isMusic: isMusic, isMusic: isMusic,
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,
@@ -37,7 +42,7 @@ MEDIA_PROBE_JS = r"""
currentTime: video ? video.currentTime : 0, currentTime: video ? video.currentTime : 0,
duration: (video && isFinite(video.duration)) ? video.duration : 0, duration: (video && isFinite(video.duration)) ? video.duration : 0,
hasVideo: !!video hasVideo: !!video
}; });
})(); })();
""" """