Skip to content

[FEATURE] Collapse viewer to a single Qt 6/C++ binary; eliminate Qt 5 toolchain; rename AnthiasWebview → AnthiasViewer #2906

@vpetersson

Description

@vpetersson

Goal

Collapse src/anthias_viewer/ (~1.8k lines of Python) and src/anthias_webview/ (~700 lines of C++ at HEAD; ~1.4k after #2905 lands) into a single Qt 6/C++ binary called AnthiasViewer. Eliminate the Python interpreter, pydbus, D-Bus session, dbus-run-session wrapping, the Qt 5 cross-compile toolchain, and the parent/child process boundary in the viewer image.

Why

Dependencies

What the Python viewer currently does (must be absorbed)

File Responsibility Port difficulty
__init__.py Lifecycle, D-Bus glue, rotation (linuxfb env + wlr-randr + eglfs-rotate on Pi 4), watchdog, recheck POST, splash Low once D-Bus goes away. wlr-randr stays a QProcess call; the Pi-4 eglfs rotation primitive (added by #2905) goes via QT_QPA_EGLFS_ROTATION / KMS transform.
scheduling.py Playlist build + windowed deadline + shuffle. Calls Asset.objects + Asset.is_active() + Asset.has_window_filter() High — couples to Django ORM and model methods. Mitigated by REST-shim (Phase 1 below).
messaging.py Redis pub/sub on anthias.viewer, retry+backoff, readiness key Medium — Qt has no first-class Redis. Use hiredis + QSocketNotifier.
media_player.py Thin D-Bus shim post-#2905 ({audio-device, video-rotate} only) + ALSA HDMI-A-1/A-2 autodetect on Pi 5 Low — most of this lives in C++ already post-#2905. ALSA autodetect ports verbatim.
playback.py threading.Event skip, scheduler reverse/extra-asset Trivial — QWaitCondition.
utils.py wait_for_server, watchdog file touch Trivial.

External contracts to preserve:

  • Redis channel anthias.viewer (commands: next, previous, asset&<id>, reload, stop, play, current_asset_id&<corr>)
  • Redis reply lists anthias.reply.<corr> for the current_asset_id request/reply path
  • Redis key viewer:display_resolution (60s cadence, 180s TTL) — read by the System Info card
  • /data/.local/share/AnthiasWebview/ + /data/.cache/AnthiasWebview/ (QtWebEngine state) — need symlink migration to the renamed paths

Build matrix consolidation

Today: Qt 6 (pi4-64, pi5, x86, arm64) + Qt 5 cross-compiled (pi2, pi3). Two Dockerfiles, two toolchains.

After:

Board Today After
Pi 4-64 / Pi 5 / x86 / arm64 Qt 6 arm64/amd64 Same
Pi 3 Qt 5 armhf Qt 6 arm64 (reflash; folds into pi4-64-style image)
Pi 2 Qt 5 armhf Qt 6 armhf (Trixie apt path; same Dockerfile branch as the rest) — or deprecated, see Risks

Deletions:

  • docker/Dockerfile.qt5-webview-builder.j2
  • bin/rebuild_qt5_toolchain.sh
  • qt5_toolchain_url in tools/image_builder/utils.py
  • The WebView-v2026.04.1 GH release pin (toolchain artifact stays on the release page for archival; just unreferenced)
  • The qt_version = '6.4.2' if is_qt6 else '5.15.14' branch — single Qt 6.8 from Debian apt
  • The {% if is_qt6 %} {% else %} {% include 'Dockerfile.qt5-webview-builder.j2' %} {% endif %} block in Dockerfile.viewer.j2

Rename: AnthiasWebview → AnthiasViewer

  • Binary: /usr/local/bin/AnthiasWebview/usr/local/bin/AnthiasViewer
  • Source tree: src/anthias_webview/src/anthias_viewer/ (freed once the Python package is deleted)
  • AnthiasWebview.proAnthiasViewer.pro
  • Runtime state paths in bin/start_viewer.sh (/data/.local/share/AnthiasWebview, /data/.cache/AnthiasWebview) — one-shot symlink migration at container startup so upgraded devices don't lose QtWebEngine cookies/local-storage
  • D-Bus service name anthias.webviewdeleted entirely, no IPC in the new world

Phasing (each will be tracked as a sub-issue)

  1. REST-shim (server-side, no viewer changes). Add /api/v2/viewer/playlist (active assets + next deadline) and /api/v2/viewer/settings. Move Asset.is_active() evaluation, shuffle, and deadline computation server-side. Python viewer keeps working unchanged. Largest single de-risking step.
  2. Build-matrix flip (no viewer code changes). is_qt6 = True for all boards; Pi 2 builds against Trixie's qt6-*-dev apt packages; Pi 3 switches target_platform to linux/arm64/v8 and folds into the existing arm64 image. Real-hardware QA on Pi 2 during this phase.
  3. C++ port (scheduling, messaging, lifecycle, ALSA, rotation) into the single Qt 6 binary. New code is Qt 6-only — no Qt 5 macros needed.
  4. Rename + path migration.
  5. Delete src/anthias_viewer/ Python package, the viewer uv group, all pydbus/gi stubs, dbus-run-session from start_viewer.sh.

Risks

  • Pi 2 perf unknown. Cortex-A7 @ 900MHz + 1GB RAM + Qt 6.8 Chromium-based WebEngine has no proven baseline in this project. Pi 4 1GB SKU runs the stack today so the memory envelope is plausible; the CPU envelope on the A7 isn't. Mitigation: real-hardware QA on Pi 2 during Phase 2. If unacceptable → deprecate Pi 2 specifically; the Qt 5 toolchain still gets deleted because Pi 3 has moved to arm64.
  • Pi 3 reflash requires operator action. Existing Pi 3 deployments are on armhf images and won't auto-upgrade to arm64. Needs a release-notes call-out and probably a migration guide.
  • Schema coupling avoided by REST-shim (Phase 1). Without that, viewer reads SQLite directly and every model migration becomes a viewer release.
  • Test coverage gap. tests/test_viewer.py, test_scheduler.py, test_messaging.py, test_media_player.py (all pytest) need Qt Test equivalents in src/anthias_webview/tests/ (suite created by feat(viewer,webview): embed QtMultimedia in AnthiasWebview, eliminate two-process DRM contention + Pi 4 drops #2905), or the REST endpoints absorb most of what they assert.
  • Internal auth token (anthias_common.internal_auth.internal_auth_token) — needs a C++ equivalent. HMAC-style derivation from a shared secret read from anthias.conf is straightforward but new code.
  • Rebase pain if Phase 1 starts before feat(viewer,webview): embed QtMultimedia in AnthiasWebview, eliminate two-process DRM contention + Pi 4 drops #2905 lands.

Out of scope

  • Display resolution detection logic (Pi 5 vc4hdmi probing) — straight port.
  • Screen rotation matrix — port verbatim from _handle_reload.
  • The Qt 5 release artifact itself — left on the GH release page for archival; just unreferenced from build.

Sub-issues

To be opened once this issue is filed:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions