Playback itself now works on Linux, but the React controls overlay
doesn't appear. nativePlayerActive only flips on the native-player-show
Tauri event, so log both sides: whether Rust actually emits it, and
whether the App.tsx listener is registered in time to receive it.
Found via the new debug_log checkpoints: the bundled Linux libmpv was
built without lua, so mpv_set_option_string("osc", "no") returned
"option not found" and player_init bailed out entirely before ever
creating a renderer. We only set this to turn OSC off, which is moot
if OSC doesn't exist in the first place, so treat it as a warning
instead of a fatal error.
player_load already logs but player_init never did, and handlePlay had
no top-level error handling, so a failure anywhere before the
playInEmbeddedMpv try/catch silently dropped the player back to the
previous screen with nothing in any log. Add debug_log checkpoints
through handlePlay and log player_init's outcome, to find where the
AppImage build actually fails since it isn't where we assumed.
find_libmpv_path() only checked <exe_dir>/lib, but Tauri's Linux
packaging (deb/rpm/AppImage) puts bundled resources at
<exe_dir>/../lib/<ProductName>/lib per the FHS convention, since /usr/bin
can't hold arbitrary app files. The executable sits at /usr/bin/fluxa-desktop
while libmpv was actually at /usr/lib/Fluxa Desktop/lib/. That mismatch
made every AppImage build silently fall back to a system libmpv.so.2 that
doesn't exist on most distros, so the player never loaded. Scan /usr/lib's
subdirectories for the bundled lib instead of assuming a fixed layout.
Bumping the crate's own version touched Cargo.lock on every release,
busting Swatinem/rust-cache's key (which hashes Cargo.lock) and forcing
a full from-scratch dependency rebuild on every single release across
all three platforms. Tauri natively supports reading the app version
from package.json, so pin Cargo.toml's version and let that carry it.
tauri.conf.json no longer carries its own version -- it now inherits
from Cargo.toml, so a release bump only touches package.json and
Cargo.toml (scripts/bump-version.sh handles both plus Cargo.lock).
Previously every thumbnail request paid a fixed 200ms (and 500ms on
first load) regardless of how fast the actual seek was. Polling exits
as soon as it's actually ready, capped at the same ceilings as before.
vo=libmpv requires a render context to exist before any loadfile/seek
command runs (unlike vo=null, which needs nothing). The context was
previously created lazily inside render_thumbnail(), called after
load/seek already failed with 'error running command'.
vo=null discards all decoded video frames before they can reach any
render API, so render_thumbnail() always got a black/empty buffer
despite the render call itself reporting success.
video-codec reflects the container's track list, available right
after the file opens, independent of decode/buffering progress.
Replaces the previous fixed-timeout heuristic, which was firing
early for legitimately slow torrent buffering and revealing a black
player view before the real frame was ready.
load() now stays paused; the frontend unpauses once vo-configured
(or a 4s safety timeout for video-less streams) is observed, so
audio no longer plays ahead of the picture during the loading screen.
User commands like pause must actually take effect; try_lock() was
silently failing with 'player renderer busy' whenever it collided
with the render thread's per-frame lock, requiring multiple clicks.
child_hwnd is created on the render thread via CreateWindowExW, but
nothing ever ran a message loop for it. Windows expects the thread
that creates a window to process its message queue; without that,
compositor/system messages sent to the window go unanswered, which
plausibly explains both the freeze after WS_CLIPSIBLINGS removal made
this window actively participate in compositing, and the crash when
moving the parent window (synchronous WM_WINDOWPOSCHANGING messages
propagate through the window hierarchy including this child).
WS_CLIPSIBLINGS clips a window's drawing to exclude any area overlapped
by a sibling window. WebView2's hosting surface is effectively a
sibling covering the whole client area, so this was clipping away all
of our rendered output regardless of what was drawn -- matching every
prior test (mpv frames, solid color clears) showing nothing on screen
despite rendering succeeding (confirmed via mpv's own screenshot).
A timed-out install() call left the original thread running and let a
later call spawn a second one, racing both over the same mpv render
context and segfaulting. install() is now single-flight per process.
Also gate release publishing on all three platform builds succeeding,
not just the first one to finish.
libloading's Display for this error is hardcoded to "LoadLibraryExW
failed" and never includes the actual OS error, even though it's
captured internally via source(). Walks the error chain so a future
failure (if any) gives an actual diagnosable reason instead of a dead end.
Was 0x200 (actually LOAD_LIBRARY_SEARCH_APPLICATION_DIR's value, already
implied by DEFAULT_DIRS), not 0x100. The previous fix never actually told
Windows to search lib/ for mpv-2.dll's dependencies -- it was a no-op.
LoadLibraryW on Windows doesn't search a DLL's own directory for its
dependencies unless told to -- only the exe dir, System32, and PATH.
mpv-2.dll's ~110 dependency DLLs (ffmpeg, libplacebo, etc.) live next to
it in lib/, so every load failed despite all the files being present.
Confirmed via the new logging: "LoadLibraryExW failed" right before the
player surface install times out, which is what silently kicked users
back to the previous screen on every playback attempt.
Release builds use windows_subsystem="windows" (no console) and there
was no logger backend registered at all -- every log::warn!/error! call
in the player code was silently dropped. Adds tauri-plugin-log writing
to the app's log dir, and logs every step of native player surface
setup, load attempts, and mpv end-file events so a real failure reason
is visible instead of just "it went back to the previous screen".
Polling eof-reached right after loadfile can read stale, and never
distinguished a real end-of-file from the stream simply failing to
open -- both looked like "silently kick back to the previous screen".
Drains mpv's actual event queue (MPV_EVENT_END_FILE) instead, and
surfaces the real error plus recent warn-level log lines when the
stream genuinely fails to open.