3ac7f836c4
Replaces the Windows port with a Raspberry Pi / Linux implementation on Raspberry Pi OS "Trixie" (Debian 13, aarch64, Wayland/labwc). The Windows code is removed here but preserved on the Windows-Player branch. Entry point ----------- linux/run_linux.py replaces windows/run_win.py. src/main.py stays platform-neutral; all Pi-specific behaviour is injected from linux/. Five bugs that prevented the port (all measured on real hardware) ---------------------------------------------------------------- 1. Kivy's PyPI wheel bundles an SDL2 built WITHOUT the wayland driver, so no window could be created (Trixie has no X server). linux/fix_kivy_sdl2.sh symlinks the system SDL2 over the bundled filename. 2. SDL2 requires WAYLAND_DISPLAY to be *set* - the socket alone is not enough, unlike wlopm. This broke every systemd/cron/autostart launch. linux_display.ensure_session_environment() detects and exports it. 3. Kivy's Clock resolves callbacks via func.__name__; a patch assigned under a different name crashed the player ~20s after a successful start. 4. The inherited signal_screen_activity() shelled out to tvservice, xdotool and ydotool - none exist on Trixie - and mis-escaped 'wlopm --on \*', so the display blanked after 10 minutes. 5. The launchers ran src/main.py directly, bypassing every platform patch and resolving the data directory one level too high. Web links --------- - --ozone-platform-hint=auto does NOT fall back to Wayland on Chromium 152; it aborts. The platform is now chosen explicitly. - The keyring password prompt is suppressed via the ENVIRONMENT, not the flags: launch_env() strips DBUS_SESSION_BUS_ADDRESS for the child so Chromium cannot reach gnome-keyring-daemon. - Teardown kills the whole process group (needs start_new_session=True); previously it silently fell back to terminate() and orphaned children. Video normalisation ------------------- A 4K video cannot play on a Pi 4: ffpyplayer decodes in software, measured at 0.90x realtime (1080p is 3.03x). Oversized media is downscaled to 1920x1080 at sync time using the hardware h264_v4l2m2m encoder (~31s for an 18s clip), triggered by resolution only so already-playable files are untouched. src/media_state.py owns the shared on-disk contract: a .kiwy-converting marker makes the player skip the item while it is being rebuilt, then the converted file is played instead. If nothing is playable at all (a single-item playlist whose only video is converting), the player loops the intro video rather than leaving a blank screen. Also fixed ---------- - network_monitor: replaced netsh/ifconfig/dhclient with nmcli (Trixie uses NetworkManager; ifconfig and dhclient are not even installed). - Removed the Windows-only focus keeper/guardian from main.py. - main.py: duplicate SDL_AUDIODRIVER setdefault (a silent no-op); Settings "Test connection" now uses tempfile.gettempdir(). - config/app_config.json: credentials blanked so a fresh clone runs the first-run setup flow. Verification ------------ linux/test_media_state.py 18/18, test_linux_patches.py 21/21, test_linux_browser_flags.py 27/27. Verified live against a real DigiServer: image -> weblink -> image -> video with correct durations, zero leaked Chromium processes, and no throttling over a 10 minute monitored run.
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
"""
|
|
playback_trace.py — Always-on playback transition logger.
|
|
|
|
Kivy's log level is forced to 'warning' in main.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
|