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.
Disabling an addon in Settings only excluded it from the home-page catalog
(readHomeBootstrap). Every other content-fetch path — stream resolution,
episode lists, calendar, notifications, watch progress — called the raw
addon list, so disabled addons kept being queried for streams.
Added loadEnabledAddons() in libraryOps.ts and switched all content-fetch
call sites to it, while addon-management code (SettingsScreen, manifest
refresh) keeps using the raw loadAddons() since it needs to see disabled
addons too.
IntroDB/SkipDB/TheIntroDB/AniSkip reads are now gated by a single
useSkipSegments preference instead of four toggles that were always
flipped together. fetchIntroSegments also now reports which providers
already returned a result per segment type (coverage), threaded through
usePlayer -> App -> ReactPlayerOverlay. The player's segment marker panel
uses that to show a dropdown for picking which single provider to submit
a marked segment to, with a checkmark on providers that already have a
result for the selected segment type.
The desktop shell no longer builds provider URLs/headers/bodies itself for
IntroDB/SkipDB/AniSkip/AnimeSkip - it asks fluxa-core for a request plan and
executes it generically (executeRequestPlan), then hands the raw response
back to core for parsing. Also wires up TheIntroDB.org as a 5th skip-segment
source (settings toggle + API key, merge pipeline, and segment marker panel
submission), and fixes content-id resolution to stop conflating a bare TMDB
numeric id with an IMDb id when only one of the two is available.
Fetches intro/recap/outro/preview segments from api.skipdb.tv alongside
IntroDB/AniSkip/AnimeSkip, merged into the existing dedup pipeline. Adds
a SkipDB API key setting for submitting marked segments from the player's
segment marker panel (which now also supports the 'preview' type), and a
useSkipDb toggle bundled with the existing intro/outro/recap switch.
Trakt and Simkl only mark an episode watched (or save resumable progress)
via an explicit scrobble stop call; auto-advancing to the next episode
was resetting scrobble state and firing a new start without ever
stopping the outgoing episode's session.
Adding a pack whose manifest URL already exists in the saved list
silently produced neither a success nor an error toast, since
discovery succeeded but zero new packs were appended. Also adds the
missing common.loading i18n key, which was rendering as the literal
key text on the add-pack button while a request was in flight.
Toast gains a 'success' variant (green check icon) alongside the
existing warning/error ones, and adding a repository/pack link now
shows it with a count of newly added packs.
Avatar pack discovery used to return an empty array on every failure
path (bad URL, repo not found, no packs, no valid images), so a failed
add looked identical to nothing happening. It now throws a typed
AvatarPackDiscoveryError and the settings screen shows it in a Toast.
Also wires up the new profileAvatarPackManifestPlan core route so a
direct pack.json link (GitHub or any other host) can be added without
needing the whole-repository discovery flow.
Addon installation already surfaced its error inline; it now also
shows a Toast popup with the failure reason.
The Windows runner's redirect target served a 200 response with a 29KB
error page instead of the real ~80MB archive. download.videolan.org is
VideoLan's own origin, not the geo-mirror load balancer.
The checksum comparison kept failing even with retries, most likely because
not every mirror in VideoLan's round-robin hosts the .sha256 sidecar file.
fetch-libmpv.sh already trusts HTTPS from its source without a checksum;
do the same here.
The hover overlay's check/X buttons duplicated the right-click menu
and covered part of the artwork. Remove sits in the context menu
already; add mark-as-watched there too and drop the hover buttons.
Simkl-sourced continue-watching items only cleared local state on
remove; now dropSimklPlaybackProgress mirrors the Trakt path by
matching the item against GET /sync/playback and deleting the session.
The remove confirmation also warns when the item will be cleared from
its external source (Trakt/Simkl), not just locally.
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).
persistLastPlaybackSource was only called from closePlayer(), the
in-app back-button path, so exiting any other way (closing the app,
window close) skipped it and left the content without a saved
source next time. Save it alongside progress in saveProgressTick,
which already runs every 30s and on app-quit flush.
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.
The dot indicator relied on a React key remount to restart the CSS
heroIndicatorFill keyframe on each slide change, which WebKitGTK
sometimes fails to pick up as a fresh animation start, leaving the
active dot frozen at 0% while the underlying slide timer kept
advancing normally. Drive the restart imperatively via a ref with a
forced reflow instead.
Extract the header, grid, day dialog, and artwork-fetch hook from
CalendarScreen.tsx into src/screens/calendar/, along with their shared
styles and utils.
ReactPlayerOverlay had grown to cover the whole in-player UI plus a
long tail of behavior hooks (gestures, telemetry, casting, media
session, Anime4K, keyboard shortcuts, track/subtitle controls, window
mode, etc). Break the JSX into PlayerBottomControls, PlayerFeedback,
PlayerMiniMode, PlayerOverlayDecorations/Styles, PlayerSkipPrompt,
PlayerStatsOverlay, PlayerStreamLinksMenu, PlayerSupplementalPanels,
PlayerTrackPanel, and SoftwareVideoCanvas, and move each behavior
concern into its own usePlayer*.ts hook.
The thumbnail container was only rendered once an image loaded, causing
the preview to jump size; keep the box mounted and swap the image in.
Also drop the redundant "Buffered" row from the torrent stats popover
and show downloaded progress with one decimal place.
Pull the embedded-mpv lifecycle, scrobbling, progress persistence,
retry, playback-start, and close-action logic out of usePlayer into
their own hooks, mirroring the existing usePlayerNativeEvents split.
Replace duplicated JS logic for clearing playback progress, merging
continue-watching lists, selecting plugin scrapers, and resolving TMDB
ids/urls with coreInvoke calls into fluxa-core, so the frontend just
executes plans the core computes. Drops tmdbShared.ts now that its
helpers live in fluxa-core.
engine.ts had grown into a mixed bag of engine lifecycle calls, generic
core-invoke plumbing, and content/library-specific core wrappers.
Split those concerns out and re-export them from engine.ts as a barrel
so existing imports keep working.
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.