--- description: "Use when building, compiling, packaging or releasing the Kiwy Signage Player: PyInstaller build, .exe generation, build.spec, hiddenimports, code signing, Smart App Control, Windows development, Raspberry Pi deployment, or adding new modules under src/. Covers the exact build commands, environment constraints, bundling rules and verification steps." name: "Kiwy Build & Development" --- # Kiwy Signage Player — Build & Development Cross-platform Kivy digital signage player. - **Raspberry Pi / Linux** — `src/main.py` is the entry point (root `install.sh`, `start.sh`). - **Windows** — `windows/run_win.py` is the entry point; it sets Windows env vars, imports `main.py`, then monkey-patches platform differences. Packaged to `.exe` with PyInstaller. ## Ground Rules - **Keep `src/main.py` cross-platform.** Windows-specific behaviour belongs in `windows/run_win.py` (see `_patch_main()`), Pi-specific behaviour in `main.py` guarded by capability checks. Do not add Windows-only imports to `main.py`. - **Rebuild is mandatory.** The `.exe` bundles `src/`, so Python edits are invisible until you rebuild. There is no hot reload in the packaged app. - **`dist/` is a runtime folder, not just build output.** It holds `config/`, `media/`, `playlists/`, `logs/`, `.kiosk-profile/` and `.player_heartbeat`. Never wipe `dist/` blindly; a `--clean` rebuild preserves it, but a manual delete destroys player state. ## Environment | Item | Value | |------|-------| | Interpreter | **Python 3.12.9** (64-bit) — project venv at `windows\venv` | | Kivy | 2.3.1 | | PyInstaller | 6.21.0 | | Linux target | Python 3.13 (`repo/python-wheels/`, `repo/system-packages/`) | - **Python 3.13+/3.14 is NOT supported for the Windows build** — Kivy 2.3.1 has no wheels for them. - `cefpython3` is **not installed** in the project venv. The embedded-CEF web-link engine is therefore unavailable and web links fall back to the Chrome/Edge subprocess adapter. Installing `cefpython3` (uncomment it in `windows/requirements_win.txt`) restores the CEF engine. ## Build From `windows\`: ``` venv\Scripts\python.exe -m PyInstaller build.spec --clean --noconfirm ``` Or the full one-click path (creates venv, installs deps, builds, then signs): ``` build_win.bat ``` Output (folder mode, via `COLLECT`): ``` windows\dist\KiwySignagePlayer\KiwySignagePlayer.exe <- the real build output ``` **Deployment hazard:** `windows\dist\KiwySignagePlayer.exe` (one level up) is a stale leftover from an older single-file build of the same name. It is not refreshed by the current spec, so deploying it ships old code. Always take the executable from the `KiwySignagePlayer\` subfolder, and delete the stray copy if it reappears. ### `build.spec` facts - Entry point `run_win.py`; `pathex=[BUILD_DIR, SRC_DIR]` so `src/` modules resolve. - `runtime_hooks=[pyi_runtime_hook.py]` — sets per-monitor DPI awareness **before** SDL/Kivy initialise. Keep DPI work here; it must run before any window is created. - `datas += Tree(src)` bundles all of `src/`. `console=True` so startup errors and the Kivy log remain visible — do not flip this to `False` without a reason. - `hiddenimports` must list modules that PyInstaller's static analysis **cannot** see: lazily imported ones (`cef_browser` is imported inside a function) or dynamic imports (`getattr`, `importlib`). Top-level imports such as `weblink_session` are found automatically via `pathex`, but listing them is harmless insurance. **When you add a module under `src/` that is imported lazily or by string, add it to `hiddenimports` or the packaged exe will fail at runtime with `ModuleNotFoundError`.** - `excluded_imports` drops Linux-only packages (`evdev`, `gi`, GStreamer) and the non-matching `cefpython3` `.pyd` variants. ## Code Signing (production constraint) Production PCs run with **Smart App Control enforced** (`VerifiedAndReputablePolicyState=1`, UMCI enforced) with **no "Run anyway" bypass** — an unsigned exe is blocked at kernel level. - A **self-signed certificate does NOT satisfy Smart App Control.** A cert from a reputable public CA is required. `create_self_signed_cert.ps1` is dev-only. - Provide a cert as `KIWY_SIGN_PFX` (+ optional `KIWY_SIGN_PFX_PASSWORD`) or drop `kiwy_signing.pfx` in `windows\`; `build_win.bat` then signs via `sign_exe.ps1` (signtool with RFC3161 timestamp when available). - See `documentation/CODE_SIGNING_SMART_APP_CONTROL.md`. ## Verify Before Committing Fast syntax gate (no build, seconds): ``` python -m py_compile src/main.py src/weblink_session.py windows/run_win.py ``` **Close the running player before rebuilding.** A running `dist\KiwySignagePlayer\KiwySignagePlayer.exe` locks the output and the build fails with "Access is denied". Chrome/Edge kiosk processes left over from web-link playback can also hold locks; if the build fails, check for stray `msedge.exe` / `chrome.exe` before retrying. Because `.exe` is a **reserved Windows device name** as well as a real file, use `Test-Path -LiteralPath` when probing for the executable, otherwise the path silently resolves to the console device. Development run — **use this on a SAC-locked host**, since the unsigned exe cannot launch: ``` cd windows venv\Scripts\activate python run_win.py ``` Diagnose playback transitions via the trace log (`logs\playback_trace.log`), written by `src/playback_trace.py`. ## Release Checklist 1. Bump `PLAYER_VERSION` in `src/main.py` **and** `filevers`/`prodvers`/`FileVersion`/ `ProductVersion` in `windows\version_info.txt`; keep them in sync. 2. Rebuild (`build_win.bat`). 3. Confirm the exe is signed (`Get-AuthenticodeSignature`); unsigned builds are blocked on production hosts. 4. Smoke-test a mixed playlist: image → video → weblink → image, verifying durations, audio (`audio: off` / `muted`), and a clean exit/restart. ## Commit Hygiene - Never commit `windows\build\` or `windows\dist\` (git-ignored). Tracked build output such as `windows\archive_list.txt` and `windows\build_last.txt` is the exception, not the rule. - **Do not commit `player_auth.json` or `src/player_auth.json`** — they contain live credentials (`auth_code`, `player_id`, `server_url`). These are currently tracked; treat any future change to them as a deliberate, reviewed decision. - Do not commit `config\app_config.json` values that are host-specific without checking whether they belong in the repo default.