Three independent fixes accumulated since the v0.2.2 viewport
compute swap. Bundled because they all touch preview.py and
app.py and the staging surface doesn't split cleanly.
1. Suppress dl_progress flash when popout is open (gui/app.py)
The QProgressBar at the bottom of the right splitter was
unconditionally show()'d on every post click via _on_post_activated
and _on_download_progress, including when the popout was open.
With the popout open, the right splitter is set to [0, 0, 1000]
and the user typically has the main splitter dragged to give the
grid full width — the show() call then forces a layout pass on
the right splitter that briefly compresses the main grid before
the download finishes (often near-instant for cached files) and
hide() fires. Visible flash on every grid click, including
clicks on the same post that's already loaded, because
download_image still runs against the cache and the show/hide
cycle still fires.
Three callsites now skip the dl_progress widget entirely when
the popout is visible. The status bar message ("Loading #X...")
still updates so the user has feedback in the main window. With
the popout closed, behavior is unchanged.
2. Cache hyprctl_get_window across one fit call (gui/preview.py)
_fit_to_content was calling _hyprctl_get_window three times per
fit:
- At the top, to determine the floating state
- Inside _derive_viewport_for_fit, to read at/size for the
viewport derivation
- Inside _hyprctl_resize_and_move, to look up the window
address for the dispatch
Each call is a ~3ms subprocess.run that blocks the Qt event
loop. ~9ms of UI freeze per navigation, perceptible as
"slow/glitchy" especially on rapid clicking.
Added optional `win=None` parameter to _derive_viewport_for_fit
and _hyprctl_resize_and_move. _fit_to_content now fetches `win`
once at the top and threads it down. Per-fit subprocess count
drops from 3 to 1 (~6ms saved per navigation).
3. Discord screen-share audio capture works (gui/preview.py)
mpv defaults to ao=pipewire on Linux, which is the native
PipeWire audio output. Discord's screen-share-with-audio
capture on Linux only enumerates clients connected via the
libpulse API; native PipeWire clients are invisible to it.
The visible symptom: video plays locally fine but audio is
silently dropped from any Discord screen share. Firefox works
because Firefox uses libpulse to talk to PipeWire's pulseaudio
compat layer.
Verified by inspection: with ao=pipewire, mpv's sink-input had
`module-stream-restore.id = "sink-input-by-application-id:..."`
(the native-pipewire form). With ao=pulse, the same client
shows `"sink-input-by-application-name:..."` (the pulseaudio
protocol form, identical to Firefox's entry). wireplumber
literally renames the restore key to indicate the protocol.
Fix is one mpv option. Set `ao="pulse,wasapi,"` in the MPV
constructor: comma-separated priority list, mpv tries each in
order. `pulse` works on Linux via the pipewire pulseaudio compat
layer; `wasapi` is the Windows audio API; trailing empty falls
through to the compiled-in default. No platform branch needed
in the constructor — mpv silently skips audio outputs that
aren't available on the current platform.
Also added `audio_client_name="booru-viewer"` so the client
shows up in pulseaudio/pipewire introspection tools as
booru-viewer rather than the default "mpv Media Player". Sets
application.name, application.id, application.icon_name,
node.name, and device.description to "booru-viewer". Cosmetic
on its own but groups mpv's audio under the same identity as
the Qt application.
References for the Discord audio bug:
https://github.com/mpv-player/mpv/issues/11100https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linuxhttps://bbs.archlinux.org/viewtopic.php?id=307698