prioritize_stream_file() replaced the torrent's only_files set instead
of adding to it, so fetching any other file (e.g. a subtitle sibling
served through the same /stream/fname endpoint) deprioritized the
video that was already streaming. Track prioritized files as a set per
torrent and union new requests into it instead of overwriting.
The chapter reader assumed Chapters always sits before the first
Cluster within a small prefix, but releases with sizeable embedded
attachments (fonts, etc.) can push Chapters well past that — one
observed case had it at ~10MB in, past the first Cluster entirely, so
the existing scan (which stops at the first Cluster) found nothing.
MKV's SeekHead element is always compact and near the file start and
routinely carries an entry pointing straight at Chapters. Have
scan_segment also record that offset when Chapters isn't found
directly, expose it via parse_mkv_chapters_json's new seekOffset field,
and add parse_mkv_chapters_at_offset_json to parse a buffer fetched
starting exactly at that offset. This also means the initial scan no
longer needs a multi-megabyte prefix to work reliably.
resolve_next_episode_json's released_only flag was backwards
(released_only || is_episode_released instead of !released_only ||
...), so the "raw next episode" and "released-only next episode"
call sites got each other's behavior. Also add debug timing around
torrent server teardown/bootstrap to help diagnose slow episode
transitions.
The stat-polling "get" action registered a magnet without narrowing
rqbit's only_files to the target index, so progress/readiness could be
driven by unrelated files in the torrent. That let isPlayableEnough
report true before the actual file had any data, handing mpv a stream
URL that wasn't ready yet (Sentry RUST-4). Also re-scope on repeat
lookups of an already-known link so switching files on a shared magnet
(season packs) doesn't keep the first file's scope.
YouTube's muxed mp4 formats cap out around 720p. Prefer, in order:
the highest-resolution muxed-audio HLS variant from the master
playlist, then the best adaptive video paired with the best adaptive
audio track (returned as a separate audioUrl), then falling back to
muxed mp4 as before. try_all_clients now keeps the best adaptive/muxed
fallback seen across client contexts instead of returning on first
success, since an earlier client can return a worse stream than a
later one.
Pairs with the audio-track sync already added to the desktop hero
trailer player, which was waiting on a resolvedTrailer.audioUrl this
enables.
Companion to the desktop-side prewarm call: lets the frontend trigger
the one-time YouTube watch-page scrape in the background as soon as
Home mounts, instead of paying that cost inline on the first real
trailer resolution.
Without a visitor_data header, YouTube strips the url/cipher from every
adaptive format and only serves the lowest-quality progressive stream
(itag 18, ~360p). Fetch a real watch page once (cached, 3h TTL) to grab
INNERTUBE_API_KEY/VISITOR_DATA and send it as X-Goog-Visitor-Id, and add
the ANDROID_VR client ahead of ANDROID/IOS, matching the approach used by
other Stremio-ecosystem clients (Nuvio) to unlock full adaptive quality.
Extends resolve_youtube_trailer_json to also extract caption tracks and
distinguish geo-blocked vs. failed outcomes, then exposes it over JNI
(Java_..._FluxaStreamingNative_resolveYoutubeTrailerJsonNative) so the
Android app's TrailerResolver can delegate to this instead of keeping
its own separate innertube client in sync.
Adds a minimal innertube client (ANDROID primary, IOS fallback) that
POSTs to /youtubei/v1/player and extracts a direct progressive mp4 URL
from streamingData.formats, with a disk cache (6h TTL) keyed by video
id. Lets the desktop shell play a muted/looping trailer through a
plain <video> element instead of embedding YouTube's own web player.
Add warn-level unsafe_op_in_unsafe_fn / unwrap_used / expect_used /
indexing_slicing / panic clippy lints (test code exempted via cfg_attr),
per the robustness plan's ratchet-to-deny strategy. Add a CI workflow
covering workspace test/clippy/fmt plus a wasm-only cargo check. Run
`cargo fmt` to clear pre-existing formatting drift so the fmt gate starts
clean, and drop a now-unnecessary `mut` flagged by clippy.
start_torrent_server is a process-wide singleton that fully tears
down and recreates the engine on every play. A stop invoked for an
old session could race a fast replay's start and land after the new
session was already up, silently killing the just-started server and
leaving mpv with a dead socket (MPV_ERROR_LOADING_FAILED / "Loading
failed" on the same torrent). Each server generation is now tagged;
stop is a no-op unless it targets the currently active generation.
Bind the listener only after the torrent session is up (with an 18s init
timeout) instead of before, wrap add_lock/api_add_torrent in a 45s timeout
so a stuck peer/tracker can't hang requests forever, and stop stat-only
polls from triggering ensure_torrent so they can't contend with the real
stream request. Also track bytes actually streamed to the client
(CountingReader) so the preload/stat readiness check reflects playback
progress past a seek, not just bytes written to disk.
- Add fluxa-streaming-engine companion server (torrent start/stop,
ffmpeg transcode/probe, OAuth token exchange) for the web build
- Add SSRF guard restricting transcode/probe url param to http(s)
loopback hosts, blocking file:// and remote-host abuse via ffmpeg
- Add src/ffi.rs string-routed RPC dispatcher and wasm bindings
- Add headless engine profile module and several FFI surface
additions (scrobble plans, episode navigation, collections import/export)
- Remove dead runtime::msg/update scaffolding and addon_transport trait
- Remove fluxa_play.rs dev scratch binary (hardcoded scraper, not
part of the product)
- Reorder dv_rewrite.rs so implementation precedes its test module;
strip decorative section dividers
- Add docs/, fuzz/ targets, and a rewritten README
- Fix .gitignore to cover fluxa-streaming-engine/target
- Add headless engine with full action/effect dispatch (detail, player,
home, library, search, discover, calendar, offline, auth, sync)
- Add ffi.rs RPC dispatch table used by UniFFI (iOS) and WASM bindings
- Add wasm.rs binding surface (core_invoke + fluxa_core_version)
- Extend FluxaCore public API with tmdb, intro_segments, external_sync,
library_state, player_policy, watchlist, and search plan methods
- Fix torrent server race: replace AtomicBool + Mutex dual-state with
mutex-only source of truth for running status
- Document result_json drain contract in headless engine
- Remove decorative section dividers from core_api.rs (style)
- Remove unused normalize_skip_time function (dead code warning)
- Remove generated doc block from FluxaCore struct (style)
- Clarify app_state comment in ffi.rs (not legacy, used by Android JNI)