Commit graph

174 commits

Author SHA1 Message Date
KhooLy
bdeea190d1 feat(desktop): HDR display output support (Windows)
- 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.
2026-07-27 16:17:18 +03:00
KhooLy
0dbdafd066 feat(desktop): find and load torrent-bundled subtitle files, richer mpv track logging
- 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).
2026-07-27 16:16:29 +03:00
KhooLy
9741d0d44c chore(desktop): refresh Cargo.lock for dropped rquickjs bindgen feature
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).
2026-07-27 16:14:30 +03:00
KhooLy
6087dcdf97 feat(desktop): embed libVLC playback instead of a separate window
- 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.
2026-07-27 16:13:03 +03:00
KhooLy
6c1bc94a07 style: run rustfmt over backend Rust modules
Whitespace/line-wrap only (rustfmt default width) across the
libmpv/plugin-runtime backend files; no behavior changes.
2026-07-27 16:12:54 +03:00
KhooLy
52284c53f8 fix(windows): synchronize native player controls and status 2026-07-26 14:48:01 +03:00
KhooLy
f357af0d9e Add profile picker customization and recommendation sources 2026-07-26 03:13:56 +03:00
KhooLy
7ae9919546 Fix mpv first-frame audio handoff 2026-07-26 02:36:00 +03:00
KhooLy
3e7b8d062e feat: add provider library sources and sync fixes 2026-07-26 01:09:30 +03:00
KhooLy
b9e7e6f805 Fix desktop OAuth callback handoff 2026-07-26 00:29:40 +03:00
KhooLy
4f21ab1566 fix: expose platform GL resolvers 2026-07-22 22:53:50 +03:00
KhooLy
dd8ab61eae fix: compile platform GL bindings 2026-07-22 22:20:03 +03:00
KhooLy
4eb0f54b8c fix: restore Windows and macOS builds 2026-07-22 21:41:47 +03:00
KhooLy
19b5b461a9 fix: sync generated core method list 2026-07-22 20:29:06 +03:00
KhooLy
5ea0d04a13 refactor: split desktop player and engine modules 2026-07-22 18:46:44 +03:00
KhooLy
876c61bd73 feat: improve desktop calendar and plugins 2026-07-22 02:10:23 +03:00
KhooLy
f360c71be2 fix: stabilize Linux Vulkan playback 2026-07-22 01:07:53 +03:00
KhooLy
87f209d7fb use shared core business policies 2026-07-18 21:47:47 +03:00
KhooLy
e054fd06bc delegate reusable policy to fluxa core 2026-07-18 21:05:30 +03:00
KhooLy
75c1b3a9d4 delegate library and playback policy to core 2026-07-18 20:18:06 +03:00
KhooLy
d3a001d193 Move Linux Vulkan rendering off the GTK main thread
The Vulkan render+present ran inside the 16ms glib timeout on the GTK
main thread — the same thread WebKit uses for overlay input — and FIFO
presents plus vkAcquireNextImageKHR (100ms timeout) blocked it for a
large slice of every frame, which made the React player overlay laggy
compared to the OpenGL path (which just draws into GTK's own FBO).
Windows and macOS already render on a dedicated thread; Linux now does
too. The GTK loop only publishes the surface size through atomics and
keeps the GDK/Wayland surface handles.

Also:
- prefer VK_PRESENT_MODE_MAILBOX_KHR when available so presents don't
  block on vsync at all
- take the player_renderer lock only around the mpv render itself, not
  across acquire/present, so overlay IPC (seek, status polls) no longer
  queues behind vsync waits
- on X11, create the Vulkan surface on a private XOpenDisplay connection;
  GTK never calls XInitThreads, so presenting over GTK's Display from
  another thread would race
- a load issued before the render thread has wired mpv's Vulkan render
  context is now deferred and retried instead of failing
2026-07-16 19:12:36 +03:00
KhooLy
a76674ee48 Recover swapchain state after a Vulkan render error
Bailing out between vkAcquireNextImageKHR and present left the acquire
semaphore with a pending signal and the image unpresented; the next frame
reused the semaphore in that state. On any render_and_present failure,
wait for the device to idle, recreate the binary semaphores, and rebuild
the swapchain.
2026-07-16 18:05:58 +03:00
KhooLy
cb059a6fcb Pin guarded fetches to the addresses vetted by net_guard
ensure_public_host checked DNS and then let reqwest re-resolve, so a
rebinding DNS entry could swap in a private IP between the check and the
request. vetted_client resolves once, validates every address, and pins
the connection to that set via resolve_to_addrs. Redirects to IP-literal
private hosts are refused as well. The shared artwork client goes away
since pinning is per-host.
2026-07-16 18:05:58 +03:00
KhooLy
4411d78580 Create storage key file with 0600 from the start
fs::write followed by set_permissions left a window where the key was
world-readable.
2026-07-16 18:05:43 +03:00
KhooLy
3f8368dbae Generate cast proxy tokens from the OS CSPRNG
RandomState hashes are seeded per-process but aren't a CSPRNG; the proxy
listens on all interfaces so the token is the only access control.
2026-07-16 18:05:43 +03:00
KhooLy
b04b8b830f Reject nuvio_request paths that don't start with /
A path like @evil.com turned the Supabase host into userinfo and sent the
anon key to an attacker-chosen host.
2026-07-16 18:05:43 +03:00
KhooLy
5a07658981 Interrupt runaway plugins and cap QuickJS memory
The 60s tokio timeout only fires at await points, so a plugin stuck in a
synchronous loop blocked its spawn_blocking thread forever. An interrupt
handler with the same deadline preempts synchronous execution too, and a
256MB memory limit stops unbounded allocation.
2026-07-16 18:05:43 +03:00
KhooLy
736b99313e Add stream/magnet link copy and download-from-context-menu
Adds a context menu on the player overlay and source rows to copy a
stream's direct/magnet link or trigger an offline download, backed
by a new stream_magnet_link command and a torrent-download resolver
that reuses (or starts) the torrent server on demand. Also refactors
start_torrent_stream's health-check into ensure_healthy_torrent_base_url
for reuse, and keeps the torrent server alive across stream stops so
downloads can still be resolved, tearing it down only on app exit.
2026-07-15 19:36:03 +03:00
KhooLy
dfc1299215 Add libVLC as an alternate playback engine
Introduces a PlaybackEngine trait so playback can be routed through
either mpv or a new dlopen-based libVLC backend, selectable from
settings. libVLC currently plays in its own separate window rather
than embedded, since it doesn't support headless frame rendering.
2026-07-15 19:35:24 +03:00
KhooLy
21c674dc43 Add Vulkan and D3D11 GPU render backends for mpv playback
Adds selectable Vulkan (Linux/macOS/Windows) and D3D11 (Windows,
HDR-capable) rendering paths alongside the existing OpenGL mpv
surface, wired through mpv's native render API, plus a Wayland
subsurface helper for Linux compositing and a settings toggle to
pick the backend.
2026-07-15 19:34:12 +03:00
KhooLy
82560d788b Bundle GStreamer plugins in Linux builds for media framework support
AppImage now keeps libgst*/liborc* libs instead of stripping all .so files, and deb/rpm packages declare gstreamer1.0-plugins-base/good as dependencies.
2026-07-13 19:58:44 +03:00
KhooLy
6887d8c0b2 Add RSA/ECDSA sign/verify for full crypto.subtle parity with Nuvio
Closes the gap flagged in the previous commit. Adds RSASSA-PKCS1-v1_5
(SHA256/384/512, via the rsa crate) and ECDSA P-256 (via p256/ecdsa)
sign/verify, native-backed through __crypto_sign_hex/__crypto_verify_hex,
with crypto.subtle.importKey/exportKey now accepting pkcs8/spki key
formats. RSA-SHA1 signing is not supported (rsa crate doesn't re-export
sha1; SHA1 signatures are already deprecated).

Verified against independently-produced signatures rather than just
self-consistency: RSA against an OpenSSL-signed message, ECDSA against
a signature produced by Python's `cryptography` library (DER-to-raw
r||s converted, matching WebCrypto's signature format) — both cross
into the native Rust layer and through the real crypto.subtle JS path.
14 plugin_runtime tests + 6 crypto_bridge tests, all passing, zero
clippy warnings.
2026-07-13 18:02:18 +03:00
KhooLy
112dde688e Add crypto polyfill to the plugin runtime (CryptoJS + WebCrypto)
Adds the crypto surface Nuvio scrapers commonly need to decrypt
obfuscated stream URLs: native digest (MD5/SHA1/256/384/512), HMAC,
PBKDF2, and AES-CBC/ECB/GCM (via sha1/sha2/md-5/hmac/pbkdf2/aes/cbc/
ecb + the existing aes-gcm dependency), exposed through the same
__crypto_*_hex bridge shape Nuvio uses, plus the CryptoJS-compatible
and crypto.subtle JS shims ported from JsBindings.kt.

RSA/ECDSA sign/verify are deliberately not implemented — crypto.subtle
.sign/.verify throw clearly rather than silently failing.

Verified against independently-known-correct vectors, not just
"compiles": SHA-256("abc"), HMAC-SHA256 (cross-checked against
Python's hmac/hashlib), AES-CBC roundtrip, and crypto.subtle.digest
all pass through the real JS engine end to end.
2026-07-13 17:48:38 +03:00
KhooLy
18e0eed24c Add production plugin runtime: rquickjs sandbox behind run_plugin_scraper
Promotes the rquickjs spike into a real Tauri command. Executes a
Nuvio-compatible scraper's getStreams() in a sandboxed QuickJS VM with
fetch (routed through net_guard::ensure_public_host, same SSRF guard
as http_fetch_text/http_execute_text) and the full cheerio bridge.
Runs via spawn_blocking with its own current-thread tokio runtime
since the DOM state (scraper/tendril) isn't Send; a 60s wall-clock
timeout matches Nuvio's own plugin timeout.

DomBridge switched from Arc<Mutex<_>> to Rc<RefCell<_>> per clippy —
it's only ever touched from the single thread each execution owns.
2026-07-13 17:17:41 +03:00
KhooLy
c0b5ecb0b0 Add rquickjs plugin-execution spike (Nuvio-compatible scraper plugins)
Proves out running unmodified Nuvio Kotlin-plugin-format JS scrapers
(module.exports.getStreams) inside rquickjs, with native fetch and a
full cheerio bridge (load/select/find/text/attr/next/prev, chaining,
:contains()) ported from Nuvio's DomBridge.kt/JsBindings.kt.
2026-07-13 16:14:22 +03:00
KhooLy
18db03bb1f Delegate similarTitles/search-merge/fetch-policy/link-classification to fluxa-core
fluxa-desktop is meant to be a thin shell over fluxa-core, but several
pieces of business logic had crept into TypeScript: Trakt/Simkl similar-
titles response mapping, search/discover source merging, addon fetch
retry/timeout/concurrency/race policy, AnimeSkip episode matching,
continue-watching week partitioning, library merge diffing, and
cast/director link classification (duplicated across three components).
All of it now calls the corresponding new fluxa-core methods and just
executes/renders the result.
2026-07-13 04:29:58 +03:00
KhooLy
2c5b159605 fix(player): wait for rendered video before handoff 2026-07-13 02:24:24 +03:00
KhooLy
91a68c899e Fix YouTube trailer playback and fullscreen overlay rendering
Route trailer resolution through fluxa-core's engine instead of a
duplicated desktop-side implementation, and add a thin local HTTP
proxy so the WebView's <video>/<audio> elements can stream the
resolved googlevideo URLs (which reject requests without proper
Range/client handling) without hitting CORS or range-negotiation
issues directly.

Also fix fullscreen trailer playback: requestFullscreen() was being
called on the bare <video> element, so sibling overlays (subtitle
text, progress bar) weren't descendants of the fullscreened element
and disappeared. Both hero trailer views now wrap video + overlays in
a shared container and fullscreen that instead.
2026-07-13 02:00:14 +03:00
KhooLy
fde3f44f21 fix(player): apply stream request headers 2026-07-12 18:58:48 +03:00
KhooLy
391c7b9893 Pair adaptive video/audio streams for YouTube trailers without HLS
Falls back to the best matching avc1 video + audio/mp4 adaptive
formats (by resolution/bitrate) when no hlsManifestUrl is available,
returning audioUrl alongside streamUrl so the player can mux them.
2026-07-12 13:51:38 +03:00
KhooLy
5042300b9f Select high quality trailer variants 2026-07-12 12:59:08 +03:00
KhooLy
be598c90d5 Move trailer resolution out of streaming engine 2026-07-12 12:52:36 +03:00
KhooLy
d36d76d904 Load skip segments before playback 2026-07-11 20:34:13 +03:00
KhooLy
23eb472b12 Improve subtitle customization and capture sync 2026-07-11 20:18:45 +03:00
KhooLy
da7f9ed34f Remember window size and position across restarts
Adds the tauri-plugin-window-state plugin so the app reopens at the
same size/position instead of always resetting to defaults.
2026-07-11 13:25:38 +03:00
KhooLy
82f4848f13 Fix resume seek freezing video while audio keeps playing
Applying the resume position via loadfile's start= option seeked
before mpv had indexed the stream's keyframes, landing the video
decoder in a bad spot while audio (no keyframe dependency) kept
advancing. Load at 0 and issue a real runtime seek once playback
actually restarts instead, same as a manual seekbar click. Also
expose a "resuming" status flag so the loading overlay stays up
through the whole seek instead of dismissing at the wrong restart
event and exposing the mid-seek frame directly to the user.
2026-07-11 04:19:07 +03:00
KhooLy
0a229970c2 Fix Windows display refresh build 2026-07-11 01:11:29 +03:00
KhooLy
51ddec8495 Add Anime4K mode shortcuts 2026-07-10 23:48:12 +03:00
KhooLy
ff99430262 Add in-player subtitle styling and auto sync 2026-07-10 23:23:51 +03:00
KhooLy
47d5173f29 Fix Anime4K chain portability and restore upscaler WHEN gates
Restore the //!WHEN OUTPUT > 1.2x gates in Upscale_CNN_x2_S/M/L: with
full restore/upscale/thin chains the native-resolution effect comes
from Restore/Thin, and the gates let the upscale stages self-disable
at 1:1 instead of force-upscaling and downscaling back.

Join chain paths with ';' on Windows (mpv's path list separator there;
':' would also split drive letters). Replace cscale=ewa_lanczossoft
with ewa_lanczos — gpu-next has no libplacebo analog for lanczossoft
and was silently falling back.

Verified all three chains against the bundled libmpv: no shader or
scaler mapping errors; visible pixel diffs at 2x upscale and, via
Restore/Thin only, at 1:1 native resolution.
2026-07-10 22:47:36 +03:00