Skip to content

feat: playlist UI improvements, smooth transitions, and freezing fixes#253

Open
rhafaelcm wants to merge 17 commits intojeffshee:masterfrom
rhafaelcm:master
Open

feat: playlist UI improvements, smooth transitions, and freezing fixes#253
rhafaelcm wants to merge 17 commits intojeffshee:masterfrom
rhafaelcm:master

Conversation

@rhafaelcm
Copy link

@rhafaelcm rhafaelcm commented Mar 20, 2026

Summary

  • Playlist mode: Added playlist with configurable repeat count for all monitors
  • Thumbnails & duration: Show video thumbnails and duration in both Local Video and Playlist tabs
  • Add All to Playlist: Button to add all local videos to the playlist at once
  • Clear playlist: Button to clear all playlist items
  • Unsaved changes warning: Label shown when playlist has pending changes
  • Error handling: Graceful handling of thumbnail generation failures
  • VA-API: Enable hardware acceleration in Flatpak build
  • Open GUI on active tab: GUI now opens on the tab matching the current playback mode
  • Deadlock fix: Found that VLC's internal decoder stop() (triggered by set_media() on an active player) blocks waiting for decoder threads, which in turn need the GTK main thread for X11 video output — causing a deadlock. To solve this:
    • Removed fade transition effect between playlist videos (simplifies transition logic)
    • Replaced in-place set_media() with full VLCWidget recreation on each playlist video change — the old widget is cleaned up synchronously, completely avoiding the GTK main thread deadlock
  • GPU memory leak fix: Synchronized VLC widget cleanup to release GPU decoder buffers before allocating a new instance, preventing unbounded GPU memory growth during playlist playback
  • Centercrop timing fix: video_set_crop_geometry() was being called before VLC created its video output, causing intermittent failure to fill the screen (especially on 2K monitors). Now uses the MediaPlayerVout event with a timed retry fallback to apply crop geometry only after the video output is ready. Also fixed _on_size_changed() calling non-existent methods on Gdk.Monitor.

Commits

  • 6903541 feature: add playlist mode with repeat count for all monitors
  • 4c82608 fix: resolve TypeError in config load and improve playlist UX
  • c7aa0e1 feat: enable VA-API hardware acceleration in Flatpak
  • 692ca64 feat: smooth playlist transitions, thumbnails/duration in UI, add-all button
  • 5b46ad3 fix: resolve freezing during playlist transitions
  • 60740b2 fix: handle thumbnail generation errors gracefully
  • 6b8f46b feat: add clear playlist button and unsaved changes warning
  • 186e572 fix: resolve playlist freezing caused by Fade timer race condition and resource leaks
  • 7398aba fix: resolve playlist freeze caused by blocking ffprobe and config race
  • 7962ffb fix: use VLC input-repeat to prevent VA-API decoder leak on playlist repeats
  • 9d33b14 fix: stop players before loading new media and add stuck-video watchdog
  • 8462add fix: remove explicit stop() that caused GTK main thread deadlock
  • 134880a refactor: remove fade transition effect from playlist video switching
  • 2eff7a0 fix: recreate VLCWidget per playlist transition to prevent deadlock
  • 43b56f8 feat: open GUI on the tab matching the active playback mode
  • 350e46e fix: prevent GPU memory leak during playlist transitions
  • 10ef8ff fix: apply centercrop after VLC vout is ready to prevent intermittent scaling on 2K monitors

Test plan

  • Test playlist creation, reordering, removal, and clearing
  • Verify instant transitions between playlist videos (no freeze)
  • Confirm thumbnails and duration display in Local Video and Playlist tabs
  • Verify "Add All to Playlist" works correctly
  • Check unsaved changes label appears/disappears correctly
  • Confirm no freezing during extended playlist playback across multiple monitors
  • Verify GUI opens on the correct tab for each playback mode
  • Verify GPU memory stays stable during extended playlist playback (no unbounded growth)
  • Verify video fills entire screen on both 2K and Full HD monitors during playlist transitions

https://github.com/rhafaelcm/hidamari/releases/tag/fork-v0.1.0-playlist

Allows users to create a video playlist that rotates across all monitors.
Each video plays to completion N times (configurable repeat count) before
advancing to the next video in a circular loop. Uses VLC's
MediaPlayerEndReached event for precise end-of-video detection.

Made-with: Cursor
- Fix _checkDefaultSource crashing with None path for non-video modes
- Add "Add to Playlist" option to right-click context menu in Local Video tab
- Remove separate Add button from Playlist tab, update description
- Add PKG_CONFIG_PATH and runtime env vars for lib64 in Flatpak manifest

Made-with: Cursor
Add LIBVA_DRIVERS_PATH with Intel, Mesa, and Mesa default driver
paths so libva can auto-detect the correct VA-API driver for any GPU.

Made-with: Cursor
… button

- Add fade-out/fade-in opacity transition between playlist videos
- Show thumbnails and video duration in the Playlist tab (TreeView)
- Display video duration below each thumbnail in Local Video tab
- Add "Add All to Playlist" button in Local Video tab
- Add get_video_duration() and get_thumbnail_pixbuf() utility functions
- Fix thumbnail loading bug when generate_thumbnail returns True but path is None

Made-with: Cursor
- Static wallpaper only set once (first video) instead of every transition,
  and now runs in a background thread to avoid blocking the GTK main loop
- Add _is_transitioning flag to prevent race conditions from overlapping
  fade animations when MediaPlayerEndReached fires multiple times
- Move get_video_duration calls to background threads in playlist store
  to avoid blocking UI with synchronous ffprobe calls
- Use GLib.idle_add for thread-safe ListStore updates in IconView

Made-with: Cursor
Wrap generate_thumbnail and get_thumbnail in try/except to catch
GLib.GError when the external thumbnailer (totem-video-thumbnailer)
fails for unsupported or corrupted videos, preventing thread crashes.

Made-with: Cursor
- Add "Clear playlist" button with edit-clear-all-symbolic icon
- Show "Unsaved changes" label when playlist is edited (add, remove,
  move, clear) that disappears after clicking "Apply Playlist"

Made-with: Cursor
…d resource leaks

- Fix critical race condition in Fade class using cycle_id to prevent
  orphaned Timer thread accumulation across playlist transitions
- Release old VLC Media objects in set_media() to prevent memory leak
- Protect _is_transitioning with try/finally to prevent permanent deadlock
- Cache ffprobe video dimensions in _setup_playlist to avoid blocking
  GTK main thread on every transition
- Fix Image.open() file descriptor leak in set_static_wallpaper
- Add diagnostic logs: health check every 60s, transition tracking,
  media end counting, thread count monitoring

Made-with: Cursor
- Add logging.basicConfig() in player process so diagnostic logs appear
- Replace synchronous ffprobe pre-cache of all 43 videos (which blocked
  GTK main loop for 20-90s) with lazy background probing per video
- Fix config save race: save config inside _setup_player() after setting
  mode and Default, so the player process reads correct values from disk

Made-with: Cursor
…repeats

The stop()/play() cycle for repeating videos was reinitializing the
hardware decoder on every repeat, leaking VA-API contexts and memory
(549MB -> 817MB in 7 media ends). VLC eventually entered a dead state.

Delegate repeat handling to VLC via :input-repeat media option, which
loops internally without destroying the decoder pipeline.

Made-with: Cursor
Stop all VLC players explicitly before set_media() during transitions
to ensure VA-API decoder resources are fully released before creating
new ones. Previously, set_media() on a still-playing player caused
overlapping decoder contexts and memory growth.

Add a watchdog timer (30s) that detects stuck playback by monitoring
player position, forcing advance to next video if VLC enters a dead
state. Also switch health check memory metric from ru_maxrss (peak)
to VmRSS (current RSS) for accurate diagnostics.

Made-with: Cursor
VLC's player.stop() waits synchronously for decoder threads to finish,
but those threads need the GTK main thread for X11 video output rendering,
creating a deadlock. set_media() handles the swap internally without this
issue. The watchdog timer recovers from stuck VLC decoder states.

Made-with: Cursor
Transition between playlist videos is now instant instead of fading
opacity out/in, simplifying the transition logic.

Made-with: Cursor
Replace in-place set_media() with full VLCWidget recreation on each
playlist video change. The old widget is cleaned up in a background
thread, avoiding the GTK main thread deadlock caused by VLC's internal
decoder stop.

Made-with: Cursor
Synchronize VLC widget cleanup to release GPU decoder buffers before
allocating a new instance. Previously, cleanup ran in a daemon thread
that could fail to complete, causing GPU memory to grow unbounded.

- Make replace_vlc_widget() cleanup synchronous (stop, release media,
  release player/instance, gc.collect) before creating the new widget
- Detach MediaPlayerEndReached event from old player before replacement
- Guard GLib timers with _timers_active flag to prevent accumulation
- Release vlc.Media in VLCWidget.cleanup() before releasing the player
- Call gc.collect() after VLC cleanup to force GPU resource deallocation

Made-with: Cursor
… scaling on 2K monitors

video_set_crop_geometry() was called before start_playback(), so VLC
could silently ignore the crop on newly created widget instances.
Now schedule_centercrop() uses the MediaPlayerVout event plus a timed
retry fallback to ensure the geometry is applied once the video output
actually exists. Also fixes _on_size_changed() calling non-existent
methods on Gdk.Monitor instead of the player window.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant