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.
Removes the tracked credential files. They are kept on disk, but they must not
be in the repository: player_auth.json holds a live auth_code, player_id and
server_url.
removed from tracking: player_auth.json, src/player_auth.json,
working_files/player_auth.json
Also fixes the ignore rules for the player's runtime state, which were being
reported as untracked and would have been committed:
- .player_heartbeat is rewritten every 10 seconds, so committing it creates
endless noise and merge conflicts on every machine.
- .player_stop_requested is transient session state.
- logs/startup_marker.txt and the various log outputs are runtime artifacts.
While doing this I found that the existing ignore entry was spelled
".player_heartbear" - a long-standing typo, so the heartbeat was never actually
ignored. The correct patterns are now present; the typo is left in place with a
comment so nothing changes unexpectedly for existing clones.
Note the deliberate exceptions, both tracked on purpose:
- windows/webview2_sdk/ - build.spec bundles those DLLs, so a fresh clone
needs them or web links silently fall back to the Chrome/Edge engine.
- windows/webview2_runtime/MicrosoftEdgeWebview2Setup.exe (~1.7 MB) - so a
machine without the WebView2 Runtime can install it on first start. The
~203 MB offline standalone installer stays ignored; fetch it with
webview2_runtime/download_runtime_installers.ps1 -Offline when building for
machines that have no internet.
- New windows/ directory with build scripts, specs, and configuration
- Windows-specific requirements (requirements_win.txt)
- Launch and runtime scripts for Windows (run_win.py, launch_player.bat)
- PyInstaller build configuration (build.spec)
- Updated .gitignore to exclude windows/venv312/
- Updated config and source files for Windows compatibility
- Moved working_files to proper directory