2 Commits
Author SHA1 Message Date
Fojadrachi fbf5f3b93f Release v2.1.4
Release / build-windows (push) Waiting to run
Release / build-linux (push) Waiting to run
Release / publish-release (push) Blocked by required conditions
2026-09-12 12:05:34 +02:00
FojadrachiandClaude Sonnet 5 9bd4b360cd Debug-Log fuer Discord-RPC-Sends (gepackte .exe laeuft ohne Konsole)
console=False im PyInstaller-Build bedeutet: print()/PLAYTUBE_DEBUG-Ausgaben
sind fuer den Nutzer komplett unsichtbar - alle bisherigen Fixes basierten
daher auf Symptombeschreibungen statt echten Daten. Schreibt jetzt bei jedem
RPC-Send (Titel, Thumbnail, large_image, start/end, Track-Wechsel-Erkennung)
eine Zeile in %APPDATA%/Playtube/logs/discord_rpc.log (auf 500 Zeilen
gekappt), damit sich das Bild-Problem anhand echter Daten diagnostizieren
laesst statt weiter zu raten.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 12:05:29 +02:00
4 changed files with 68 additions and 1 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
"""Playtube - ein eigenstaendiger YouTube- & YouTube-Music-Player mit Discord Rich Presence."""
__app_name__ = "Playtube"
__version__ = "2.1.3"
__version__ = "2.1.4"
+8
View File
@@ -48,6 +48,14 @@ def _app_data_dir() -> Path:
return d
def app_data_dir() -> Path:
"""Oeffentlicher Zugriff auf den App-Datenordner, z.B. fuer Debug-Logs - die
gepackte .exe laeuft ohne Konsolenfenster (console=False), print()-Debugging ist
dort also unsichtbar; ein Log-File ist die einzige Moeglichkeit, dort etwas
nachtraeglich einzusehen."""
return _app_data_dir()
def _config_path() -> Path:
# Im Entwicklungsmodus liegt config.json direkt im Projektordner (leicht editierbar),
# im gepackten Build im APPDATA-Ordner.
+44
View File
@@ -0,0 +1,44 @@
"""Sehr einfaches, immer aktives Datei-Log fuer schwer reproduzierbare Bugs.
Die gepackte .exe laeuft ohne Konsolenfenster (console=False in packaging/playtube.spec),
also sind print()-Debugausgaben dort unsichtbar - selbst mit PLAYTUBE_DEBUG=1 sieht der
Nutzer nichts. Dieses Modul schreibt stattdessen in eine kleine, automatisch gekappte
Log-Datei im App-Datenordner, die sich jederzeit nachtraeglich auslesen laesst.
"""
from __future__ import annotations
import time
import traceback
from pathlib import Path
from .config import app_data_dir
_MAX_LINES = 500
def _log_path() -> Path:
d = app_data_dir() / "logs"
d.mkdir(parents=True, exist_ok=True)
return d / "discord_rpc.log"
def log_line(msg: str) -> None:
"""Haengt eine Zeile mit Zeitstempel an - haelt die Datei klein, indem bei
Ueberlaenge nur die letzten _MAX_LINES Zeilen behalten werden."""
try:
path = _log_path()
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
line = f"[{timestamp}] {msg}\n"
with open(path, "a", encoding="utf-8") as f:
f.write(line)
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
if len(lines) > _MAX_LINES:
path.write_text("\n".join(lines[-_MAX_LINES:]) + "\n", encoding="utf-8")
except Exception:
# Logging darf niemals die App zum Absturz bringen.
pass
def log_exception(context: str, exc: BaseException) -> None:
log_line(f"{context}: {exc!r}\n{''.join(traceback.format_exception(exc))}")
+15
View File
@@ -16,6 +16,7 @@ from typing import Any
from PySide6.QtCore import QThread
from .config import APP_NAME
from .debug_log import log_line
try:
from pypresence.types import ActivityType
@@ -199,6 +200,7 @@ class DiscordRPCWorker(QThread):
self._presence = Presence(self._client_id)
self._presence.connect()
self._connected = True
log_line("[connect] verbunden")
if os.environ.get("PLAYTUBE_DEBUG"):
print("[discord-rpc] verbunden", flush=True)
except Exception as exc:
@@ -224,6 +226,7 @@ class DiscordRPCWorker(QThread):
now = time.time()
if key != self._track_key:
old_key = self._track_key
self._track_key = key
# current_time nur als grobe Anfangs-Schaetzung verwenden (z.B. Programm
# startet waehrend ein Titel schon laeuft) - plausibilisiert, damit ein
@@ -231,6 +234,7 @@ class DiscordRPCWorker(QThread):
# rein ueber die Systemzeit weiterlaeuft statt ueber currentTime.
offset = current_time if (duration <= 0 or 0 <= current_time <= duration) else 0
self._track_start_ts = int(now - offset)
log_line(f"[track-change] alt={old_key!r} neu={key!r} offset={offset:.1f}s")
start_ts = self._track_start_ts if self._track_start_ts is not None else int(now)
end_ts = start_ts + int(duration) if duration and duration > 0 else None
@@ -254,11 +258,22 @@ class DiscordRPCWorker(QThread):
start_ts, end_ts = self._track_timestamps(item)
payload = build_presence_payload(item, self._session_start, start_ts, end_ts)
self._presence.update(**payload)
log_line(
"[send] title=%r thumbnail=%r large_image=%r start=%s end=%s"
% (
item.get("title"),
item.get("thumbnail"),
payload.get("large_image"),
start_ts,
end_ts,
)
)
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.
self._connected = False
log_line(f"[send-error] {exc!r}")
if os.environ.get("PLAYTUBE_DEBUG"):
print(f"[discord-rpc] Senden fehlgeschlagen: {exc!r}", flush=True)