Sentry previously initialized unconditionally in release builds. Now it
only starts when the user enables Diagnostic Mode, and shuts down when
they disable it. Also drops the stdout log target in release builds —
console output is invisible to users running the packaged app anyway.
tauri-runtime-wry's undecorated_resizing::attach_resize_handler was
attached unconditionally for every webview on Linux (unlike Windows,
which already checks !window.is_decorated()), and its hit-test
handler assumes the GTK widget tree is always exactly
webview -> parent -> window. With transparent windows the tree has
an extra GtkBox layer, so the downcast .unwrap() panics and aborts
the whole process on resize.
Since this app always runs with decorations: true and never toggles
them, port the same !window.is_decorated() gate Windows already uses
into a fresh local patch against tauri-runtime-wry 2.11.4, which lets
the app move to tauri 2.11.5. The crash no longer reproduces
(verified against vanilla 2.11.4 first).
report_swap() flipped first_frame_presented as soon as any frame
rendered, even the pre-seek frame 0 mpv holds while paused waiting
for the resume position to land. The frontend uses that flag to
dismiss the loading overlay, so resuming from continue watching
flashed the video's first frame before jumping to the saved position.
Gate it the same way restore_audio_after_first_presented_frame()
already does.
Move Tauri command handlers for core IPC, OAuth callback queuing, and
torrent streaming/transport out of lib.rs into dedicated modules, and
fold plugin_runtime into a single plugin_executor module now that it's
just the scraper execution path.
Separate the SQLite-backed progress/history/collections tables and the
generic key-value store from storage.rs, which had grown to cover
encryption, migrations, and every table's CRUD in one file.
Break the Tauri player commands into lifecycle, controls, presentation,
and state-tracking modules, following the same pattern already applied
to mpv_render and linux_vulkan.
Report mpv's `seeking` property in PlayerStatus for both mpv and libvlc
engines, and lower cache-secs/demuxer-max-bytes/readahead so playback
starts and reacts to seeks faster.
install()'s glib::timeout_add_local callback was a single ~300-line
closure because GTK's timer API only takes one FnMut, so all per-tick
mutable state (pending_load, latch_grace_ticks, chapters_native_loaded,
transparency_reassert_ticks, screenshot_countdown_ticks/seq, vulkan_state,
visible) got closure-captured as locals instead of living somewhere named,
and five unrelated concerns — draining SurfaceCommands, warming up the
render context, retrying a deferred load, and per-frame housekeeping —
ended up inline in one body.
Extracted a SurfaceTick struct owning that state, with one method per
concern (drain_commands, warm_up_render_context, retry_pending_load,
run_frame_tasks) and on_tick() as a thin dispatcher. install()'s closure
is now `move || tick.on_tick()`; the function itself drops from ~410 to
~140 lines.
This is a real restructuring, not a pure relocation (closure captures
became struct fields, one nested closure needed explicit reference
locals to satisfy the borrow checker) — verified with cargo check, but
unlike the mpv_render.rs/linux_vulkan.rs splits this touches the actual
render-loop control flow (GTK tick timing, Vulkan warmup handshake,
WebView transparency reassertion) which cargo check cannot behaviorally
verify. Needs a real playback smoke test (OpenGL and Vulkan backends,
X11 and Wayland) before trusting it in production.
linux_vulkan.rs's single impl VulkanContext spanned ~840 lines. Split
into three sibling submodules under linux_vulkan/, same file+dir mod
pattern used for mpv_render/: init.rs (new() bootstrap + Drop teardown),
swapchain.rs (semaphore/command-buffer/swapchain creation + resize),
render.rs (frame acquire/submit/present loop + accessors).
create_semaphores, create_command_buffer, create_swapchain needed
pub(super) since new() (in init.rs) and recover_after_render_error()
(in render.rs) call them across the new file boundary.
No logic changed — verified with cargo check.
Several internal mpv commands were sent via command_string/command, which
call mpv_command_string/mpv_command — synchronous mpv API calls that
block the caller until mpv's core acknowledges them. These particular
call sites already discarded the result (`let _ = ...`) and run inside
render/event loops (poll_events, the surface command loops in
*_player_surface.rs) or the Windows mute-restore path, so there was
nothing gained from waiting: mute-restore, seek-restart, pause-resume,
stop, and the debug screenshot-to-file path now go through command_args
(mpv_command_async), which enqueues and returns immediately; the reply
arrives later via poll_events' MPV_EVENT_COMMAND_REPLY handling, which
was already dropped.
Left load(), seek_to(), and user_command() on the synchronous path —
those propagate a Result the frontend actually uses for error feedback
(failed loadfile, failed seek, failed user-triggered command), so
switching them to fire-and-forget would silently swallow real errors.
Verified with cargo check on Linux. macos_player_surface.rs and
windows_player_surface.rs changes are the same one-line substitution
but unverified — this machine only compiles the Linux target.
mpv_render.rs's single impl MpvRenderer spanned ~1250 lines. Split into
five sibling submodules under mpv_render/, following the same file+dir
mod pattern already used for api.rs and platform_gl.rs (and for player.rs
elsewhere in this codebase): lifecycle.rs (construction/loading/Drop),
commands.rs (low-level mpv command dispatch), context.rs (render-context
creation across sw/GL/Vulkan/D3D11 backends), frame.rs (per-frame
rendering + swap/audio-restore), status.rs (event polling, status
snapshot, track enumeration, property getters).
A few methods called across the new file boundaries needed pub(super)
instead of private visibility (create_software_context,
create_opengl_context, ensure_buffer, get_string_property,
track_list_status, restore_audio_only) since Rust's default privacy is
visible to the defining module and its descendants, not to siblings.
mpv_render.rs itself now only holds the FFI type/struct/const
definitions, PlayerStatus's small accessor impl, and the
library-loading helpers. No logic changed — verified with cargo check.
Trakt/SIMKL scrobble-close threshold, torrent sibling-subtitle matching,
and subtitle language dedup were duplicated logic living in the desktop
shell; route them through fluxa_core so mobile doesn't have to reimplement
the same policy.
Subtitle track grouping keyed on the raw mpv lang code, so "eng" (external
OpenSubtitles subs) and "en" (embedded ass tracks) showed as two separate
English entries instead of one. Group by display name instead.
player_set_anime4k_enabled could fail with "player renderer busy" when
hwaccel is degraded and the render loop holds the renderer lock longer
than the previous 300ms retry budget, causing the UI toggle to silently
revert. Raised the retry budget and logged failures for diagnosis.
- windows_d3d11.rs / windows_player_surface.rs: detect whether the
active display supports HDR and, when the "HDR output" preference is
on, create the D3D11 swap chain with an HDR-capable format; expose
hdr_display_supported() for the frontend to query.
- mpv_render.rs: drop the now-redundant format/width/height fields
from the D3D11 render target — mpv_d3d11_fbo only needs the texture,
which already carries its own format/dimensions.
- linux_player_surface.rs: track whether the current Vulkan context is
HDR-capable on a shared flag for parity with the Windows path.
- storage.rs: add read_pref_bool() to read boolean prefs (used for the
hdrEnabled toggle).
- lib.rs: register the player_hdr_supported command; tauri.conf.json
sets a transparent window background so HDR/SDR compositing doesn't
show a black flash.
- Frontend: add an "HDR output" toggle in Playback settings, auto-detect
and store display support on first run via player_hdr_supported.
- mpv_render.rs: enable "info"-level mpv logging and route messages to
the matching Rust log level with prefix/level context; use mpv's
native int64/flag property getters (get_i64_property/
get_flag_property) instead of parsing track-list strings, and log
each queried track (id/title/lang/codec/selected/external) to make
subtitle-track issues diagnosable from the app log.
- player.rs: wire up the previously-unused torrent_sibling_subtitles
scan as a new player_torrent_sibling_subtitles command — finds
subtitle files (.srt/.ass/.ssa/.vtt/.sub) bundled in the same torrent
as the playing video, matching by exact filename, by a shared
S01E02-style episode tag, or (for single-video torrents) by being
the only video in the release.
- lib.rs: register the new command, elevate "subtitles:" debug_log
lines to warn so they're visible without FLUXA_DEBUG_LOGS, and make
torrent_engine_request pub(crate) so player.rs can call it.
- usePlayer.ts: load resolved subtitles into mpv one at a time (was
all at once), which was triggering 429s from subtitle CDNs; merge in
the torrent-bundled subtitles found above.
- mpvPlayer.ts: add the player_torrent_sibling_subtitles wrapper.
- subtitles.ts: trace addon subtitle resolution via debug_log, and cap
addon results to 2 per language (OpenSubtitles returns dozens of
near-duplicates).
Downstream of the fluxa-core rquickjs "bindgen" feature removal —
prunes bindgen/cexpr/clang-sys and their transitive deps from the lock
file (avoids requiring libclang to build).
- libvlc_render.rs: locate/load libvlc more robustly (Windows DLL
search path, plugin path discovery), route libvlc log messages into
the app logger, lazily create the media player, and add audio
mute/track and subtitle-track control bindings.
- macos_player_surface.rs: attach the libVLC output to the native
NSView so it renders inside the app window (macOS), switching
between the Vulkan and libVLC render paths based on the active
engine.
- Update the player-engine description strings to reflect that libVLC
now renders embedded rather than in its own window.