Skip to content

Releases: dennisadvani/metixel-photoframe

v1.1.0

Choose a tag to compare

@dennisadvani dennisadvani released this 10 Aug 10:13

Image Download Links Below

  • Instructions
  • IMG can be used for Raspberry Pi 3+
  • For Raspberry Pi 2, follow the manual installation instructions as Trixie 13 Lite 32-bit is required
[1.1.0]

Added

  • GPU memory introspectionDisplayBackend.gpu_memory_info() and
    flush_gpu() methods. Pi3dBackend reads vcgencmd get_mem and
    DRM bo_stats debugfs for V3D buffer object counts and heap usage.
    Periodic GPU memory logged every 30 s alongside CPU/memory stats.
    GPU state logged on texture allocation failure for diagnostics.
  • Guaranteed no‑black‑screen video architecture — the last‑frame texture
    is fully loaded, uploaded to the GPU, and verified BEFORE VLC is launched.
    See ARCHITECTURE.md → "Video Playback Architecture" for the 8‑step
    state machine diagram.
  • VLC RC TCP playback detection — VLC is launched with --extraintf rc --rc‑host localhost:<port> (LUA CLI). is_playing now queries VLC's
    TCP interface for a real "is rendering" signal instead of guessing with
    timers. Supports Pi 2's slow VLC startup without premature swap.
  • Centralised timeout configuration — new timeouts section in
    config.json with Config.timeout(key, fallback) helper. All
    critical timeouts (ffprobe, frame extraction, thumbnail generation,
    transcode, VLC start) now editable in one place.

Changed

  • GPU memory raised to 128 MB for Pi 2/3 — setup script now detects Pi
    model and sets gpu_mem=128 for Pi ≤3 (static GPU partition needs room
    for framebuffer ~8 MB + pi3d textures ~4 MB each). Pi ≥4 stay at
    gpu_mem=16 (CMA dynamic allocation).
  • Timeout increases across the board for CPU‑starved Pi 2/3 hardware:
    ffprobe metadata probe 30→120 s, cached‑video validation 15→60 s,
    thumbnail extraction 120→300 s, first‑frame extract 60→180 s, HW codec
    detection 10→30 s, VLC start 5→30 s.
  • Last‑frame swap timer now starts from VLC's confirmed playback time
    (via RC interface), not subprocess launch. Eliminates the swap‑before‑
    VLC‑appears race on slow hardware.

Fixed

  • Black last‑frame screen (root cause) — pi3d Texture(file_path) does
    NOT eagerly create the GL texture (opengl_loaded=False). You must
    call tex.load_opengl() followed by glFinish() (ctypes → libGLESv2)
    to drain the VideoCore IV DMA pipeline before pi3d's free_after_load
    releases the CPU buffer. Without the flush, DMA reads freed memory →
    black pixels.
  • _load_texture_for_slot unload‑before‑load — the old texture was
    destroyed before the new one was confirmed loaded. If the new load
    failed (GPU memory full), the slot went permanently black. Now loads
    first, only unloads old on success.
  • Cache hash mismatch_cleanup_cached_video used a path‑based
    hash to find frame files, but _extract_video_frames named them with
    a content‑based hash. Frame files were never cleaned up on re‑transcode.
    Both now use the content hash (file_hash).
  • Orphaned frame files never deleted — folder watcher's
    _cleanup_cached_for_deleted was missing the .jpg extension when
    looking for {hash}.1.frame files (should be {hash}.1.frame.jpg).
  • Thumbnails deleted on cache invalidation — folder watcher cleanup
    was deleting thumbnails alongside cached videos. Thumbnails now survive
    cleanup; they're only ~50 KB and regenerating them on every re‑transcode
    wastes CPU.
  • _validate_cached_video crash — still decorated @staticmethod
    after adding self._timeout() call, causing NameError on every
    invocation. All three precached videos silently failed validation and
    never reached the playlist.
  • free_after_load kwarg conflictload_texture() hardcoded
    free_after_load=True while the engine passed free_after_load=False
    via **kwargs, causing TypeError: multiple values. load_texture
    now pops the kwarg to let callers override the default.
  • gpu_mem=16 on Pi 3 — the setup script was applying 16 MB GPU
    memory to Pi 3 (which uses a static partition), leaving only 8 MB for
    textures after the framebuffer. Videos rendered as black because the
    GPU couldn't allocate texture memory.
  • Keyboard defaults — KEY_UP / KEY_DOWN removed (redundant), KEY_RIGHT
    corrected from prevnext, KEY_SPACE and KEY_POWER removed.
    Defaults now: KEY_LEFT → prev, KEY_RIGHT → next, KEY_ENTER → toggle.

Changed (2026-08-10)

  • gpu_mem=128 for all Pi models — setup script simplified: Pi 4/5
    ignore gpu_mem via CMA dynamic allocation, so a single value avoids
    model‑detection complexity and keeps the base image portable across all
    Pi generations.
  • requirements-system.txt now complete — lists all 24 apt packages
    from the setup script (grouped by purpose) so the OTA updater can
    install any missing system dependencies after an update.

Added

  • Per‑profile CRF fieldcrf is now a first‑class profile setting
    (Pi 2/3: 28 for software decode, Pi 4/5: 23 for hardware decode).
    Exposed in the API, Web UI profile fields (locked for built‑in profiles,
    editable in Custom mode), and config as transcode_crf.
  • Diagnostic logging in needs_optimisation() — every check that
    triggers a transcode now logs exactly which limit was exceeded (codec,
    width, height, fps, bitrate, color depth, HDR, H.264 level) at INFO
    level for easy troubleshooting.
  • Workstation precache scriptscripts/precache_videos.py
    transcodes videos on a fast desktop using the exact same profile,
    hash, and encoding logic as the Pi, then pushes results via SSH.
    Supports --host to pull media, --push to deploy.
  • _VIDEO_WAITING state in the video state machine — the 50 %
    last‑frame swap timer now starts after VLC confirms it has begun
    rendering (MediaPlayerPlaying event), not at launch. Prevents
    black frames when VLC startup is delayed by CPU contention.

Changed

  • CRF replaces bitrate‑targeted encoding — Pi 2/3 profiles now use
    -crf 28 (was -b:v 8M ABR). CRF distributes bits intelligently
    across simple and complex scenes, producing more decode‑friendly output
    than constant‑bitrate ABR.
  • Pi 2/3 max bitrate lowered from 8 → 7 Mbps for more headroom
    below the ~8 Mbps ARM software decode ceiling.
  • FPS always explicit-r is now always set to
    min(source_fps, max_fps), preventing ffmpeg from silently
    upscaling 23.98 fps → 29.97 fps.
  • B‑frames restored — removed -bf 0 from transcode command.
    B‑frames break the P‑frame dependency chain and are actually easier
    to decode in software than a chain of pure P‑frames.
  • Max bitrate capped to source quality-maxrate now uses
    min(source_bitrate, profile_max) so a 5.5 Mbps source never
    gets upscaled to the 7 Mbps profile cap.
  • Frame extraction downscaled — thumbnails, first frames, and last
    frames are now downscaled to the display resolution (same as image
    optimisation) instead of being extracted at the source's native 4K
    resolution, saving ~20 MB GPU memory per texture on low‑RAM Pis.
  • Frame file extension.1.frame / .2.frame.1.frame.jpg /
    .2.frame.jpg for consistency with JPEG content.
  • Last‑frame swap at 50 % of video duration (was 20 %, then 80 %)
    — balances VLC startup time with completing before VLC exits.
  • Web UI quality slider removed — replaced by the per‑profile CRF
    numeric field in the profile settings section.

Fixed

  • H.264 level comparison — ffprobe returns level as integer (40
    for Level 4.0) but the profile stored it as string "4.0", causing
    float(40) > float("4.0") to always be true. Probe now normalises
    to float (40 → 4.0).
  • Folder watcher silently dropping videosffprobe timeout was
    10 s, too short for a CPU‑starved Pi 2; raised to 120 s.
    Failures now log at WARNING level instead of DEBUG.
  • Thumbnail cache deleted on re‑transcode_cleanup_cached_video
    was deleting thumbnails alongside corrupt cached videos; thumbnails
    now survive cache invalidation.
  • Frame extraction throttling reverted — single‑frame extraction
    (thumbnails, first/last frames) now uses nice only (no
    cpulimit) to avoid 120 s timeouts on slow hardware.
  • Last‑frame texture load race — the old GPU texture is now kept
    until the new one is confirmed loaded, preventing a black screen if
    the upload fails.
  • ffmpeg 8.x compatibility-vframes ordering (after -i),
    -update 1 as muxer option (after -f image2), -f mjpeg
    for single‑frame output.
  • Media route filter.frame.jpg extension recognised for
    exclusion from media listings.

v1.0.12-beta.5

v1.0.12-beta.5 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 09 Aug 11:29

[1.0.13-beta.6]

Added

  • GPU memory introspectionDisplayBackend.gpu_memory_info() and
    flush_gpu() methods. Pi3dBackend reads vcgencmd get_mem and
    DRM bo_stats debugfs for V3D buffer object counts and heap usage.
    Periodic GPU memory logged every 30 s alongside CPU/memory stats.
    GPU state logged on texture allocation failure for diagnostics.
  • Guaranteed no‑black‑screen video architecture — the last‑frame texture
    is fully loaded, uploaded to the GPU, and verified BEFORE VLC is launched.
    See ARCHITECTURE.md → "Video Playback Architecture" for the 8‑step
    state machine diagram.
  • VLC RC TCP playback detection — VLC is launched with --extraintf rc --rc‑host localhost:<port> (LUA CLI). is_playing now queries VLC's
    TCP interface for a real "is rendering" signal instead of guessing with
    timers. Supports Pi 2's slow VLC startup without premature swap.
  • Centralised timeout configuration — new timeouts section in
    config.json with Config.timeout(key, fallback) helper. All
    critical timeouts (ffprobe, frame extraction, thumbnail generation,
    transcode, VLC start) now editable in one place.

Changed

  • GPU memory raised to 128 MB for Pi 2/3 — setup script now detects Pi
    model and sets gpu_mem=128 for Pi ≤3 (static GPU partition needs room
    for framebuffer ~8 MB + pi3d textures ~4 MB each). Pi ≥4 stay at
    gpu_mem=16 (CMA dynamic allocation).
  • Timeout increases across the board for CPU‑starved Pi 2/3 hardware:
    ffprobe metadata probe 30→120 s, cached‑video validation 15→60 s,
    thumbnail extraction 120→300 s, first‑frame extract 60→180 s, HW codec
    detection 10→30 s, VLC start 5→30 s.
  • Last‑frame swap timer now starts from VLC's confirmed playback time
    (via RC interface), not subprocess launch. Eliminates the swap‑before‑
    VLC‑appears race on slow hardware.

Fixed

  • Black last‑frame screen (root cause) — pi3d Texture(file_path) does
    NOT eagerly create the GL texture (opengl_loaded=False). You must
    call tex.load_opengl() followed by glFinish() (ctypes → libGLESv2)
    to drain the VideoCore IV DMA pipeline before pi3d's free_after_load
    releases the CPU buffer. Without the flush, DMA reads freed memory →
    black pixels.
  • _load_texture_for_slot unload‑before‑load — the old texture was
    destroyed before the new one was confirmed loaded. If the new load
    failed (GPU memory full), the slot went permanently black. Now loads
    first, only unloads old on success.
  • Cache hash mismatch_cleanup_cached_video used a path‑based
    hash to find frame files, but _extract_video_frames named them with
    a content‑based hash. Frame files were never cleaned up on re‑transcode.
    Both now use the content hash (file_hash).
  • Orphaned frame files never deleted — folder watcher's
    _cleanup_cached_for_deleted was missing the .jpg extension when
    looking for {hash}.1.frame files (should be {hash}.1.frame.jpg).
  • Thumbnails deleted on cache invalidation — folder watcher cleanup
    was deleting thumbnails alongside cached videos. Thumbnails now survive
    cleanup; they're only ~50 KB and regenerating them on every re‑transcode
    wastes CPU.
  • _validate_cached_video crash — still decorated @staticmethod
    after adding self._timeout() call, causing NameError on every
    invocation. All three precached videos silently failed validation and
    never reached the playlist.
  • free_after_load kwarg conflictload_texture() hardcoded
    free_after_load=True while the engine passed free_after_load=False
    via **kwargs, causing TypeError: multiple values. load_texture
    now pops the kwarg to let callers override the default.
  • gpu_mem=16 on Pi 3 — the setup script was applying 16 MB GPU
    memory to Pi 3 (which uses a static partition), leaving only 8 MB for
    textures after the framebuffer. Videos rendered as black because the
    GPU couldn't allocate texture memory.

v1.0.11-beta.4

v1.0.11-beta.4 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 08 Aug 14:07

[1.0.11-beta.4]

Changed

  • Software video decode strategy — Pi 2/3 now use CPU software decode
    (libVLC) instead of GPU hardware decode. gpu_mem returned to
    16 MB (1.0.10-beta.3 briefly raised it to 128) so more RAM is
    available to the ARM cores. Pi 4/5 are unaffected — they continue to
    use hardware decode via rpi-hevc-dec / drm_avcodec.
  • Pi 3 transcode bitratemax_bitrate lowered from 20
    8 Mbps, matching the ~8 Mbps software decode ceiling of the
    Cortex‑A53 cores (measured: 5.6 & 7.7 Mbps play smoothly, ≥10.9 Mbps
    drops frames).
  • Bitrate‑targeted encoding for Pi 2/3 — profiles with
    bitrate_target: true now use -b:v {max_bitrate}M (target
    average bitrate) instead of -crf. CRF with -maxrate only caps
    peaks — the average can still overshoot by 30–50 %, exceeding the
    software decode ceiling. -b:v produces predictable output at the
    target rate.
  • Setup scriptgpu_mem=16 for all Pi models, enforced on both
    fresh installs and existing configs regardless of prior value.

Fixed

  • Pi 3 choppy video at 10–22 Mbps — CRF‑encoded files for
    14947567 (10.9 Mbps) and 17815074 (21.5 Mbps) regularly
    exceeded the ARM software decode ceiling (~8 Mbps), causing frame
    drops. Bitrate‑targeted encoding reliably produces ≤8 Mbps output.
  • Inconsistent gpu_mem on upgraded installs — the 1.0.10-beta.3
    setup script would upgrade gpu_mem to 128 on existing installs;
    now reverted to 16.

v1.0.10-beta.3

v1.0.10-beta.3 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 08 Aug 12:03

[1.0.10-beta.3]

Fixed

  • Pi 3 hardware video decode broken by low GPU memory — the setup
    script was setting gpu_mem=16 MB which prevented VideoCore IV from
    loading the H.264 codec firmware, forcing VLC into 100 % CPU software
    decode even with correctly‑transcoded Level 4.0 files. Raised to
    gpu_mem=128 MB for Pi 2/3 (Pi 4/5 use kernel‑managed CMA and don't
    need this).
  • Infinite re‑transcode loop on Pi 5 — CRF encoding produced files
    ~3 Mbps above the profile bitrate limit, triggering a re‑transcode on
    every reboot that produced the same overshoot. needs_optimisation()
    now allows 10 % tolerance on bitrate checks.
  • Frame‑extraction ffmpeg processes not throttled — first‑frame,
    last‑frame, and thumbnail extraction ran under nice only, ignoring
    the CPU throttle setting. Now uses _wrap_with_throttle() so they
    also get cpulimit when CPU throttling is enabled.
  • Missing gpu_mem on Pi 4/5 Trixie images — some images ship
    without any gpu_mem line in config.txt; the setup script now
    adds gpu_mem=128 when the setting is absent.

Changed

  • Setup script GPU memorygpu_mem=128 (was 16) for new
    installs; existing installs are auto‑upgraded from <128 or have
    the setting added if missing entirely

v1.0.9-beta.2

v1.0.9-beta.2 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 08 Aug 10:12

[1.0.9-beta.2]

Added

  • USB keyboard / wireless remote input handler — evdev-based listener
    with learn mode for custom key mapping; supports next, prev,
    pause, resume, toggle_pause, screen_on, and
    screen_off commands; mappings persisted in config.json under
    input.keyboard_map
  • Keyboard/Remote Control card in Web UI Advanced page — per‑command
    learn and clear buttons with live key‑code display; learn mode polls
    for the next keypress and persists the mapping automatically
  • toggle_pause IPC command — single‑key pause/resume toggle for
    keyboard remotes and the Web UI control endpoint; frontend shows a
    brief "Paused" / "Resumed" feedback popup on the frame display
  • _show_feedback() helper in the frontend renderer — on‑screen
    popup messages for pause, resume, and toggle_pause actions using the
    existing message layer
  • OTA system package support — new requirements-system.txt lists
    required system packages (python3-evdev); UpdateManager
    installs any missing packages before the pip step during OTA updates
  • input config sectionkeyboard_enabled toggle and
    keyboard_map dictionary for storing learned key bindings

Changed

  • Web UI pollingdashboard.js switched from setInterval to
    setTimeout chains for dashboard refresh, sync status, log viewer,
    and processing progress; prevents request‑queue buildup when the
    browser tab is backgrounded on mobile
  • screen_on / screen_off renamed — display power commands
    renamed from legacy names across all input handlers (CEC, IR,
    keyboard, MQTT), IPC protocol, frontend renderer, and the Web UI
    control endpoint for consistent naming
  • Pi 3 H.264 Level loweredh264_level reduced from 4.2 to
    4.0 in the Pi 3 transcoding profile to stay within VideoCore IV
    hardware decode limits (max Level 4.1)
  • Keyboard handler thread — started by the backend daemon alongside
    other input handlers; uses selectors‑based blocking I/O instead of
    busy‑polling so CPU usage is near‑zero when no keys are pressed

Fixed

  • Pi 3 video stuttering / 100 % CPU — videos transcoded at H.264
    Level 4.2 exceeded VideoCore IV hardware decoder capabilities, causing
    VLC to fall back to software decode on all 4 cores; re‑transcoding at
    Level 4.0 enables hardware decode (~5–10 % CPU)
  • Keyboard learn clear button — clearing a key mapping via the Web UI
    now properly removes the defaults for that command instead of wiping
    all default key bindings; set_key_map() merges config overrides
    with defaults, treating an empty list as "clear this command"

v1.0.8-beta.1

v1.0.8-beta.1 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 08 Aug 03:59

[1.0.8-beta.8]

Added

  • Transcoding profiles — four Pi‑model‑specific profiles (Pi 2, Pi 3,
    Pi 4, Pi 5) with optimal codec, resolution, framerate, bitrate, H.264
    profile/level, colour depth, and HDR support limits. Profile is
    auto‑detected on first run from /proc/device-tree/model.
  • Custom profile mode — allows overriding every transcode parameter
    individually; all profile fields visible in the Web UI, editable only
    when Custom is selected
  • Keep Audio global setting — preserves the audio track when enabled
    (stripped by default)
  • Profile‑based cached‑video re‑validation — switching profiles
    re‑probes existing cached videos and re‑transcodes any that exceed the
    new limits
  • Extended video metadata extraction — ffprobe now captures framerate,
    bitrate, colour depth, H.264 profile/level, and HDR colour info
  • Profile‑based optimisation gatingneeds_optimisation() checks
    all profile limits (codec, resolution, fps, bitrate, colour depth, HDR,
    H.264 level) instead of only H.264 + resolution
  • Web UI Video Optimisation card — profile dropdown with auto‑detect,
    custom parameter fields, keep‑audio checkbox, and global quality/encoder/
    timeout/CPU‑limit controls

Changed

  • Transcode encodes to profile target codec — Pi 4/5 target H.265
    (HEVC) for hardware decode; Pi 2/3 target H.264
  • Transcode enforces H.264 level/profile — adds -level,
    -profile:v, -refs 2, -bf 0, -g 30 for smooth Pi
    playback
  • Transcode framerate cap-r only applied when source FPS
    exceeds the profile limit; never upscales 30 fps → 60 fps
  • Transcode colour depth cap — output depth is min(src, profile);
    never upscales 8‑bit → 10‑bit
  • Transcode HDR → SDR downgrade — forces BT.709 colour space on
    non‑HDR‑capable Pi models
  • Transcode max bitrate enforcement-maxrate + -bufsize
    applied from profile limits
  • libx265 RAM optimisation — uses ultrafast preset on ≤3 GB
    devices (Pi 4/5 with 2 GB) to avoid OOM; superfast on >3 GB
  • CPU throttle default — reduced from 200 % to 100 % (1 core)
  • Playlist hot‑reload refreshes metadata — frontend now updates
    width, height, first_frame_path, last_frame_path, and
    cached_path for existing items when the backend updates the
    playlist, fixing aspect‑ratio mismatches and black frame glitches after
    re‑transcode

Fixed

  • Next‑item video playback — pressing Next to a video now launches VLC
    immediately instead of sitting on the first frame until the slide timer
    expires
  • config.example.json video defaultsplayback_enabled
    corrected to true, max_duration_seconds to 0, CPU throttle
    to 100

v1.0.7

Choose a tag to compare

@dennisadvani dennisadvani released this 05 Aug 10:59

Image Download Links Below

  • Instructions
  • IMG can be used for Raspberry Pi 3+
  • For Raspberry Pi 2, follow the manual installation instructions as Trixie 13 Lite 32-bit is required

[1.0.7]

Fixed

  • Welcome messages not auto-dismissing — the message layer timer was
    being reset every frame while a video played, preventing the 2‑minute
    auto-dismiss from ever firing; now tracks accumulated visible time with
    proper pause/resume during video playback
  • Web UI welcome dismiss not clearing on‑screen messages — dismissing
    the welcome banner in the dashboard now sends dismiss_all_messages
    IPC to the frontend so the frame display popups disappear immediately
  • config.example.json video defaultsplayback_enabled
    corrected from false to true and max_duration_seconds from
    120 to 0 (unlimited), matching the Python defaults

Added

  • User Guide — new docs/USER_GUIDE.md covering first‑time setup,
    getting online, adding media, the dashboard, customisation, updates, and
    troubleshooting

v1.0.6

Choose a tag to compare

@dennisadvani dennisadvani released this 04 Aug 08:44

Image Download Links Below

  • Instructions
  • IMG can be used for Raspberry Pi 3+
  • For Raspberry Pi 2, follow the manual installation instructions as Trixie 13 Lite 32-bit is required

Key Fixes

  • Fixed WiFi, Captive Portal and Access Point issues.

Change Log

[1.0.5-beta.5]

Added

  • Pi 2 / Ethernet-only supportis_wifi_hardware_present() check prevents the controller from attempting AP activation on devices without WiFi hardware; the controller stays in CLIENT_CONNECTED or CLIENT_DISCONNECTED based on Ethernet state and never retries AP
  • Stale AP cleanup on boot — the controller now kills any leftover hostapd instance on initialisation, preventing the captive portal from blocking the web dashboard after an unclean shutdown

Changed

  • AP startup delay eliminated — removed the daemon's forced ap_timeout_seconds wait on boot; the controller now owns all timing (immediate AP when no saved networks, 5‑minute grace period when saved WiFi exists)
  • Boot screen message timing — 10 s delay after slideshow start ensures the boot screen fade-out completes before welcome or PIN messages appear
  • Setup script prompts before install — channel and WiFi country questions are now asked before git is installed; answers flow through to Phase 1 via environment variables so the user is never re‑prompted
  • Beta channel pins to pre-release tags — the setup script now checks out the latest v*-beta.* tag on main for beta channel installs instead of tracking the dev branch, matching the OTA updater's behaviour

Fixed

  • Captive portal blocking dashboard after reboot — hostapd left running from a previous AP session is now stopped at controller init
[1.0.4-beta.4]

Added

  • WiFi connection failed popup — when the captive portal WiFi connection fails (wrong password, out of range, etc.), an error message now appears on the photo frame display with guidance to check the password or try a different network (30 s auto-dismiss)
  • WiFi connection profile fallback — if the one-shot nmcli device wifi connect fails with a "key-mgmt property is missing" error (common on routers with mixed WPA2/WPA3 or certain TP-Link/ASUS models), the code now creates an explicit connection profile with WPA2-PSK settings and retries automatically
  • Setup script channel prompt — asks for stable or beta channel before installing; stable pins to the latest non-prerelease v* tag, beta tracks the dev branch
  • Setup script WiFi country prompt — asks for the regulatory domain (e.g. AU, US, GB) upfront alongside the channel choice; both answers are written to config.json for the Web UI

Changed

  • Boot welcome delay eliminated — the network monitor no longer waits ap_timeout_seconds when a network is already connected at boot; the welcome message appears as soon as the slideshow is ready instead of 60+ seconds later
  • Captive portal error messaging — the WiFi failure popup on the frame display uses a single clean message instead of concatenating the raw nmcli error with boilerplate text

Fixed

  • Setup script CRLF line endings.gitattributes now enforces eol=lf for *.sh files; the setup script was renormalized so it runs correctly when downloaded from GitHub raw on a Pi
[1.0.3-beta.3]

Changed

  • Network Controller rewrittenNetworkPhase flag-based state machine replaced with NetworkState enum (CLIENT_CONNECTED, CLIENT_DISCONNECTED, AP_ACTIVE, AP_EXHAUSTED); all transitions go through a single _transition_to() method under lock; monitor loop drains a pending-actions queue instead of comparing phase snapshots. Ethernet connectivity is checked independently from WiFi and is always safe (different radio) — nmcli is never queried for WiFi while the AP is active, preventing hostapd beacon disruption.
  • WiFi connection deferred to background thread — the captive portal's /api/network/connect endpoint now returns a response immediately (before the AP is torn down) and spawns a background thread for the actual AP-stop + scan + nmcli-connect sequence. The phone receives the HTTP response while still associated with the AP.
  • WiFi scan delay after AP stopconnect_to_network() now performs a nmcli device wifi rescan and waits 5 s after stopping the AP before attempting the connection. Without a fresh scan wlan0 has no visible SSID list and nmcli fails with "No network with SSID 'X' found."
  • Controller connection guard — new begin_connection() / end_connection() methods prevent the monitor thread from treating an intentionally-stopped AP as "unexpectedly dead" during the scan gap
  • AP_EXHAUSTED sudo reduction_stop_ap() now guarded by is_ap_mode_active() check; no longer runs unnecessary sudo commands on every monitor tick when the AP is already down

Fixed

  • WiFi connection failing after captive portal — the AP was torn down during the HTTP request, severing the client's TCP connection before the response arrived; the phone showed a network error instead of success
  • "WiFi Offline" popup flashing during WiFi connection — the monitor tick saw the AP was down during the scan delay and marked it as AP_EXHAUSTED, triggering an on-screen warning that was dismissed seconds later when the connection succeeded
  • Captive portal error messages removed — the password field no longer displays error text on failure; all feedback is shown on the photo frame display where it is always visible even after the phone disconnects from the AP

Removed

  • Legacy PIN state_pin_state module-level dict and all legacy PIN functions (generate_ap_pin, clear_ap_pin, validate_ap_pin, is_pin_required, get_active_pin) removed from network_manager.py; PIN management is now exclusively owned by NetworkController with proper thread safety
  • SCAN_CACHE_TTL constant and force_live parameter — scan cache is now served indefinitely while the AP is active; live scan toggle no longer exposed to the web API
  • Legacy PIN fallbacks in web routes — all is_pin_required() and validate_ap_pin() fallback paths removed; the controller is the sole source of truth
[1.0.2-beta.2]

Added

  • CPU temperature tile — sparkline graph on the System Status dashboard card using vcgencmd measure_temp; scaled 0–85°C in red
  • libopenblas0 added to setup script package list — resolves NumPy import failures on Pi 2 (32-bit) where the shared library was missing
  • Hardware documentation — separated Models table from RAM Requirements table in README; added 64‑bit vs 32‑bit image availability per model; Pi 4 promoted to Phase 1 (untested); Pi Zero 2 W marked as untested

Changed

  • Video transcode CPU limit default — reduced from 300 % to 200 %
  • Pause button — switched from Unicode characters (⏸/▶) to Material Symbols icons (pause / play_arrow) for consistent rendering
  • Progress bar colours — optimising images and transcoding bars now use var(--primary) (theme red), matching the scanning bar
  • WiFi Country Code — hint text repositioned below the input field using form-group--stack layout
  • Connected button — vertically centered in network list rows via display:inline-flex;align-items:center
  • Background processing bars — hidden when their respective feature is disabled (image optimisation off → hide optimising bar; video transcoding off → hide transcoding bar)
  • README — hardware section restructured with separate Supported Models and RAM Requirements tables; Pi 2 marked active (32-bit manual install); Pi 4 marked untested; Pi Zero 2 W marked untested

Fixed

  • Pipeline reset on sync changes — enabling/disabling local watch folders now triggers a full pipeline reset ("sync" re‑added to the config route trigger list)
  • Hardware docs consistencyCLAUDE.md, HARDWARE.md, ARCHITECTURE.md, GETTING_STARTED.md, INSTALLATION.md, and FEATURES.md updated to match README hardware tables (Pi 4 in Phase 1, Pi Zero 2 W untested, 32‑bit vs 64‑bit, manual install notes)
[1.0.1-beta.1]

Added

  • first_frame_path and last_frame_path fields on MediaItem — backend-generated video frame caches referenced directly by the presentation engine
  • VideoProcessor._extract_video_frames() — extracts first frame (t=0) and last frame (sseof) during Phase 2 OPTIMISE; cached as .1.frame / .2.frame JPEGs
  • Image optimisation worker subprocess (worker.py) — PIL operations (load, transpose, resize, save) run in an isolated process with cpulimit -l 50 (hard 50 % CPU cap) and nice -n 19 (lowest priority); OS reclaims all memory on exit
  • Adaptive CPU throttling — sleep between image optimisations scales with 1‑minute load average (load1 × 0.2, capped at 1.0 s); folder watcher also yields between thumbnail generations when the optimiser is busy
  • Boot screen progress bar — red fill bar below the spinner showing optimisation progress (1/6 → 6/6); uses draw_image with 1×1 pixel textures to avoid pi3d colour-space issues
  • Per‑phase progress bars in the Web UI Background Processing card — separate persistent bars for scanning, image optimisation, and video transcoding; each retains its last position when the active phase switches
  • Frontend→backend slideshow‑started signalPOST /api/slideshow-started defers the network monitor's AP‑fallback countdown until the first sl...
Read more

v1.0.5-beta.5

v1.0.5-beta.5 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 04 Aug 07:10

[1.0.5-beta.5]

Added

  • Pi 2 / Ethernet-only supportis_wifi_hardware_present() check
    prevents the controller from attempting AP activation on devices without
    WiFi hardware; the controller stays in CLIENT_CONNECTED or
    CLIENT_DISCONNECTED based on Ethernet state and never retries AP
  • Stale AP cleanup on boot — the controller now kills any leftover
    hostapd instance on initialisation, preventing the captive portal from
    blocking the web dashboard after an unclean shutdown

Changed

  • AP startup delay eliminated — removed the daemon's forced
    ap_timeout_seconds wait on boot; the controller now owns all timing
    (immediate AP when no saved networks, 5‑minute grace period when saved
    WiFi exists)
  • Boot screen message timing — 10 s delay after slideshow start ensures
    the boot screen fade-out completes before welcome or PIN messages appear
  • Setup script prompts before install — channel and WiFi country
    questions are now asked before git is installed; answers flow through
    to Phase 1 via environment variables so the user is never re‑prompted
  • Beta channel pins to pre-release tags — the setup script now checks
    out the latest v*-beta.* tag on main for beta channel installs
    instead of tracking the dev branch, matching the OTA updater's
    behaviour

Fixed

  • Captive portal blocking dashboard after reboot — hostapd left running
    from a previous AP session is now stopped at controller init

v1.0.4-beta.4

v1.0.4-beta.4 Pre-release
Pre-release

Choose a tag to compare

@dennisadvani dennisadvani released this 04 Aug 06:30

[1.0.4-beta.4]

Added

  • WiFi connection failed popup — when the captive portal WiFi connection
    fails (wrong password, out of range, etc.), an error message now appears on
    the photo frame display with guidance to check the password or try a
    different network (30 s auto-dismiss)
  • WiFi connection profile fallback — if the one-shot nmcli device wifi connect fails with a "key-mgmt property is missing" error (common on
    routers with mixed WPA2/WPA3 or certain TP-Link/ASUS models), the code
    now creates an explicit connection profile with WPA2-PSK settings and
    retries automatically
  • Setup script channel prompt — asks for stable or beta channel
    before installing; stable pins to the latest non-prerelease v* tag,
    beta tracks the dev branch
  • Setup script WiFi country prompt — asks for the regulatory domain
    (e.g. AU, US, GB) upfront alongside the channel choice; both
    answers are written to config.json for the Web UI

Changed

  • Boot welcome delay eliminated — the network monitor no longer waits
    ap_timeout_seconds when a network is already connected at boot; the
    welcome message appears as soon as the slideshow is ready instead of
    60+ seconds later
  • Captive portal error messaging — the WiFi failure popup on the frame
    display uses a single clean message instead of concatenating the raw
    nmcli error with boilerplate text

Fixed

  • Setup script CRLF line endings.gitattributes now enforces
    eol=lf for *.sh files; the setup script was renormalized so it
    runs correctly when downloaded from GitHub raw on a Pi