Port the player to Raspberry Pi OS Trixie 64-bit (Linux-only branch)

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.
This commit is contained in:
ske087
2026-09-13 21:57:49 +03:00
parent f437aba1fc
commit 3ac7f836c4
68 changed files with 5604 additions and 9326 deletions
@@ -1,108 +0,0 @@
# Kiwy Signage Player — Code Signing & Smart App Control (Production)
> **TL;DR:** If production PCs have **Smart App Control (SAC) ON** and you
> cannot disable it, the player `.exe` **must be signed by a certificate from a
> reputable public CA**. There is no other way — SAC blocks unsigned binaries at
> the kernel level (no "Run anyway" button). Self-signed certs and Defender
> exclusions do **not** satisfy SAC.
---
## 1. Why Smart App Control blocks the app
- SAC (Windows 11 22H2+, "Smart App Control" in **Windows Security → App &
browser control**) only runs apps that are **signed by a reputable publisher**.
- Your locally-built `KiwySignagePlayer.exe` is **unsigned**
(`Get-AuthenticodeSignature``NotSigned`), so SAC refuses to launch it and
shows "An Application Control policy has blocked this file."
- Unlike classic SmartScreen, SAC has **no "Run anyway" button** and cannot be
bypassed per-file. Disabling SAC is **permanent** and only possible with admin
rights — so it is not viable for locked-down production PCs.
---
## 2. The solution for production: a real code-signing certificate
1. **Buy an OV code-signing certificate** from a reputable CA, e.g.:
- Sectigo Code Signing
- SSL.com Code Signing
- DigiCert Code Signing
- GlobalSign Code Signing
OV is sufficient for SAC; EV gives the highest trust level. Cost is roughly
USD 100300/yr. The CA will issue a `.pfx`/`.p12` (or `.cer`+key).
2. **Sign the exe** after each build. Place your pfx at
`windows\kiwy_signing.pfx` (or set `KIWY_SIGN_PFX` env var) — `build_win.bat`
will then auto-sign via `sign_exe.ps1`:
```powershell
# One-off, from the windows\ folder:
.\sign_exe.ps1 -CertPath "C:\certs\mycodesign.pfx" -CertPassword "yourpwd"
```
The script:
- locates `signtool.exe` (Windows SDK) — install with
`winget install Microsoft.WindowsSDK.10.0.26100` if missing,
- signs with **SHA256** + **RFC3161 timestamp** (required for SAC and to
keep the signature valid after the cert expires),
- verifies the result with `Get-AuthenticodeSignature`.
3. **Test** — confirm on one production PC:
```powershell
Get-AuthenticodeSignature "dist\KiwySignagePlayer\KiwySignagePlayer.exe"
# Status must be: Valid
```
---
## 3. Dev / test machines (where you have admin rights)
If a test PC has SAC **off**, you can make the app trusted locally without
buying a cert:
```powershell
# Run as Administrator
.\create_self_signed_cert.ps1
```
This creates a self-signed code-signing cert, exports `kiwy_dev_signing.pfx`,
and installs it into **Trusted Root + Trusted Publisher + Trusted People** for
the current user, so the player runs without SmartScreen/Defender prompts on
that dev PC.
⚠️ **This does NOT satisfy SAC.** It is only for machines where SAC is off or
where you have admin rights.
---
## 4. Build → sign → verify workflow
```bat
:: 1. Build (produces dist\KiwySignagePlayer\KiwySignagePlayer.exe)
cd windows
venv\Scripts\python.exe -m PyInstaller build.spec --clean --noconfirm
:: 2. Sign (auto if kiwy_signing.pfx present, else manual)
.\sign_exe.ps1 -CertPath "C:\certs\mycodesign.pfx" -CertPassword "..."
:: 3. Verify
Get-AuthenticodeSignature "dist\KiwySignagePlayer\KiwySignagePlayer.exe"
```
`build_win.bat` now does step 1 + step 2 automatically when a pfx is present.
---
## 5. Important caveats
- **Timestamping is mandatory.** The sign script timestamps by default
(`http://timestamp.digicert.com`). Without a timestamp, the signature becomes
invalid once the certificate expires and SAC will block the app.
- **SAC reputation takes time.** Even a validly signed exe from a brand-new
certificate may be blocked until the CA's reputation builds. EV certificates
and well-known CAs (DigiCert, Sectigo, SSL.com) pass immediately.
- **Re-sign after every build.** PyInstaller creates a new exe each time; the
old signature is lost. The auto-sign step in `build_win.bat` handles this.
- **Do not use UPX** on the signed exe — it invalidates the signature and can
trigger false positives. (`upx=True` in the spec currently does nothing
because UPX is not installed; if you ever install UPX, set it to False.)