""" playback_trace.py — Always-on playback transition logger. Kivy's log level is forced to 'warning' in main.py / run_win.py, which suppresses every Logger.info()/Logger.debug() line. That made it impossible to see why the player skips/crashes at the weblink->image and video->next transitions. This module writes a plain-text trace file (logs/playback_trace.log) with timestamps, INDEPENDENT of Kivy's log level, so we can always see exactly what the player is doing. It is thread-safe (a lock guards the append) and never throws (all failures are swallowed) so it can never break playback. Usage: from playback_trace import trace trace("play_current_media", index=3, name="foo.jpg", type="image") """ import os import threading import time _LOCK = threading.Lock() _LOG_PATH = None _OPENED = False def _ensure_path(): global _LOG_PATH, _OPENED if _OPENED: return _LOG_PATH _OPENED = True try: # Respect the local data dir the launcher set (same place as logs/). base = os.environ.get('KIWY_DATA_DIR') or os.getcwd() log_dir = os.path.join(base, 'logs') os.makedirs(log_dir, exist_ok=True) _LOG_PATH = os.path.join(log_dir, 'playback_trace.log') except Exception: _LOG_PATH = None return _LOG_PATH def trace(event, **kwargs): """Append one line to the playback trace log. Args: event: short event name, e.g. 'next_media', 'eos', 'web_open'. **kwargs: key=value context, e.g. index=3, name='foo.jpg'. """ try: path = _ensure_path() if not path: return t = time.strftime('%H:%M:%S') ms = int((time.time() % 1) * 1000) parts = [f"{t}.{ms:03d}", event] for k, v in kwargs.items(): parts.append(f"{k}={v}") with _LOCK: with open(path, 'a', encoding='utf-8') as f: f.write(" ".join(parts) + "\n") except Exception: pass # tracing must never break the player