Commit graph

210 commits

Author SHA1 Message Date
KhooLy
c9901cbbe4 Harden torrent engine lifecycle 2026-08-01 15:08:42 +03:00
KhooLy
37b75b1358 perf: scope effect dedupe keys
Some checks are pending
CI / test (push) Waiting to run
CI / clippy (push) Waiting to run
CI / bench-check (push) Waiting to run
CI / wasm-check (push) Waiting to run
CI / fmt (push) Waiting to run
2026-07-31 22:07:41 +03:00
KhooLy
23b3a697df perf: add incremental engine dispatch metadata 2026-07-31 21:59:22 +03:00
KhooLy
ca9e321c40 feat: wire PublicMetaDB skip timestamps into the skip-segment source pipeline
Adds parse_publicmetadb_segments_json alongside the existing
IntroDB/SkipDB/AniSkip parsers, reusing the shared collect_segments
extractor. Extends that extractor with a "credits" key-prefix alias
(mapped to the canonical "outro" type) since PublicMetaDB names its
outro fields credits_start_ms/credits_end_ms rather than outro_*.
2026-07-31 15:39:03 +03:00
KhooLy
cd9e3cdba0 feat: add PublicMetaDB request-plan support alongside mdblist/trakt/simkl
Covers all 58 documented endpoints (resume, watch history, skip
timestamps, ratings, episode ratings, highlights, external ID
mappings, anime seasons, lists, picks, and voting) as stateless
method/url/body plan builders, wired into core_invoke the same way
mdblist_plan is.
2026-07-31 15:34:34 +03:00
KhooLy
bc42053405 refactor: collapse per-provider skip-segment read toggles into one useSkipSegments flag
Some checks are pending
CI / test (push) Waiting to run
CI / clippy (push) Waiting to run
CI / bench-check (push) Waiting to run
CI / wasm-check (push) Waiting to run
CI / fmt (push) Waiting to run
IntroDB, SkipDB, TheIntroDB, and AniSkip are all read-only, no-API-key
lookups, so there was no reason to keep four separate on/off prefs that
were always toggled together. AnimeSkip keeps its own flag since it needs
a client id to work at all.
2026-07-31 15:23:40 +03:00
KhooLy
932acddfc0 feat: move skip-segment provider network requests into fluxa-core as reusable request plans
Every skip-segment source (IntroDB, SkipDB, AniSkip, AnimeSkip) now has its
HTTP request (url/method/headers/body) built in fluxa-core and returned as
a plan; the desktop shell just executes it and hands the raw response back
for parsing. This keeps the provider logic reusable for a future Android
consumer instead of reimplementing each integration per platform.

Also adds TheIntroDB.org (v3) as a new skip-segment source (read + submit),
alongside the existing providers, with its own request-plan builders and a
tolerant response parser (credits -> outro, null-timestamp handling).
2026-07-31 15:12:47 +03:00
KhooLy
ecf8b806af feat: add SkipDB as a skip-segment source, with a new 'preview' segment type
Adds parsing for SkipDB's intro/recap/outro/preview API responses
(reusing the existing tolerant segment collector), a 'preview' skip
type alongside intro/outro/recap, and a useSkipDb preference threaded
through playback prefs and profile sanitization the same way useIntroDb is.
2026-07-31 14:55:21 +03:00
KhooLy
91bc0f5d57 feat: support avatar pack links pointing at any host, not just GitHub repos
Some checks are pending
CI / test (push) Waiting to run
CI / clippy (push) Waiting to run
CI / bench-check (push) Waiting to run
CI / wasm-check (push) Waiting to run
CI / fmt (push) Waiting to run
Adds profileAvatarPackManifestPlan: recognizes a URL that already points
directly at a pack.json/json.pack file (GitHub blob link or any other
HTTPS host) and fetches it as a single pack, instead of requiring a bare
GitHub repository URL and walking its whole tree.
2026-07-31 00:43:46 +03:00
KhooLy
897db9aa6b fix: allow avatar pack URLs pointing at a single pack, not just repo root
parse_repository_url rejected any GitHub URL with more than two path
segments, so a /blob/<ref>/<path>/pack.json link (pasted to add one
specific pack) failed to parse. The frontend then silently treated the
resulting None as "no packs found" with no error surfaced.

Now the trailing /blob or /tree path is used to scope discovery to
just that pack instead of importing every pack in the repository.
2026-07-31 00:32:47 +03:00
KhooLy
ff7a7c0197 fix: gate youtube_cipher/rquickjs behind plugin-js-engine, fix cargo fmt
Some checks are pending
CI / test (push) Waiting to run
CI / clippy (push) Waiting to run
CI / bench-check (push) Waiting to run
CI / wasm-check (push) Waiting to run
CI / fmt (push) Waiting to run
2026-07-30 21:17:51 +03:00
KhooLy
64c79b9dc5 feat: add Simkl playback-progress deletion parity with Trakt
Simkl's DELETE /sync/playback/{id} removes a single saved playback
session, mirroring Trakt's remove-progress endpoint. Continue-watching
drop plans now flag dropSimkl alongside dropTrakt so the desktop shell
can clear the remote record when an item originated from Simkl.
2026-07-30 21:00:48 +03:00
KhooLy
76e37aabad test: fix stale stream_request_referer assertion
The vidmoly.me case was asserting None, but stream_request_referer
already returns a same-origin referer for http(s) URLs. Also covers the
magnet-link case, which correctly has no referer.
2026-07-30 18:37:03 +03:00
KhooLy
ef80d7d88e feat: filter plugin scrapers by supported content type
plugin_execution_plan_json now normalizes media_type ("series"/"show" ->
"tv") and excludes scrapers whose supportedTypes list doesn't include it.
2026-07-30 18:36:59 +03:00
KhooLy
8558812a4b style: run cargo fmt 2026-07-30 18:36:56 +03:00
KhooLy
26c2e4fcdb refactor: split headless_engine/player.rs and trailer.rs into submodules
Fixes visibility of items re-exported two module levels up (player/* and
trailer/* to headless_engine) — pub(super) inside the submodules only
reached their immediate parent, not headless_engine, so the crate failed
to build.
2026-07-30 18:34:06 +03:00
KhooLy
6cc82eae8e refactor: split provider_mappers.rs into per-concern submodules
provider_mappers.rs was 851 lines. Split into provider_mappers/{items,
continue_watching,mark_watched,related,sync_activity,delta_merge}.rs:

- items.rs: Simkl/Trakt watched/watchlist list -> item JSON conversions
- continue_watching.rs: cross-provider continue-watching replace/merge
  and Trakt playback item dedup
- mark_watched.rs: Trakt/Simkl mark-watched and watchlist request body
  builders, plus Simkl episode matching
- related.rs: Trakt related-title and Simkl recommendation mapping
- sync_activity.rs: Trakt/Simkl activity-diff and resource sync planning
- delta_merge.rs: Simkl delta-merge helpers

No behavior change — same 393 tests pass.
2026-07-30 17:18:03 +03:00
KhooLy
14b8b1f0d4 refactor: split plugin_runtime/mod.rs execution paths into submodules
mod.rs was 881 lines mixing three execution paths behind two thin
public entrypoints. Split into plugin_runtime/{settings_layout,
scraper_exec,host_functions}.rs:

- settings_layout.rs: run_settings_layout + NoopHttpClient (network
  disabled during settings-layout evaluation)
- scraper_exec.rs: run (the real scraper execution path with a live
  HTTP client)
- host_functions.rs: native_fetch/to_hex/from_hex + register_host_functions
  (the JS-bridge registration, plus the polyfill include_str! constants)

mod.rs keeps PluginHttpRequest/Response/Client, the two public
entrypoints (execute_scraper, get_settings_layout), and the test suite
— 330 lines, down from 881. No behavior change — same 393 tests pass.
2026-07-30 17:04:05 +03:00
KhooLy
08340b8df1 refactor: split external_sync.rs test suite into a tests/ directory
external_sync.rs was 855 lines, ~800 of them the #[cfg(test)] module.
Split into external_sync/tests/{calendar,merge,anilist,stremio,trakt,
simkl,mal,cross_provider,wire_fixtures}.rs, mirroring the per-provider
submodule split (and the same pattern used for headless_engine/tests/).
external_sync.rs is now 55 lines of pure module wiring. No behavior
change — same 393 tests pass.
2026-07-30 17:00:19 +03:00
KhooLy
70a2aa0068 refactor: split tmdb_plan.rs into per-concern submodules
tmdb_plan.rs was 879 lines. Split into tmdb_plan/{helpers,
meta_conversion,request_plans,genres_catalog}.rs:

- helpers.rs: shared content-type/language/image-url/id-hint/query
  encoding primitives used across the other three files
- meta_conversion.rs: TMDB meta/video -> internal meta/trailer JSON
  conversion (single + bulk + full-detail variants)
- request_plans.rs: builtin meta/detail/season/credits/people request
  plan builders
- genres_catalog.rs: genre tables, builtin manifest, builtin catalog URL

tmdb_plan.rs is now 126 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.
2026-07-30 16:53:53 +03:00
KhooLy
22928eff5b refactor: split watchlist_plan.rs into per-concern submodules
watchlist_plan.rs was 974 lines (plus the already-split collections.rs
submodule). Split the remaining hub content into watchlist_plan/
{remote_collection,plans,library_commands}.rs:

- remote_collection.rs: remote collection request/response plans
- plans.rs: watchlist toggle, external merge, collection import
  validation, offline grouping, playback progress merge plans
- library_commands.rs: mark-watched application, progress-meta merge,
  the library command plan, and playback progress write plan (kept
  together since they call into each other)

collections.rs switched from `use super::*` to explicit imports since
it's now a sibling rather than a child of the hub's import scope.
watchlist_plan.rs is now 177 lines: module wiring plus the existing
test suite. No behavior change — same 393 tests pass.
2026-07-30 16:49:29 +03:00
KhooLy
81be38e390 refactor: split mdblist_plan.rs into per-concern submodules
mdblist_plan.rs was 998 lines. Split into mdblist_plan/{helpers,
media_ratings,lists,watchlist_sync,scrobble,discussion,user}.rs,
mirroring the categories already named in the crate's module map
(media/ratings/catalog/lists/watchlist/sync/scrobble/discussion/user):

- helpers.rs: shared bearer/poll-outcome/query-encoding/URL-building
  primitives
- media_ratings.rs: media info/ratings/catalog/search/genres request
  plans
- lists.rs: list CRUD, item mutation, list discovery URLs
- watchlist_sync.rs: watchlist/sync/up-next request plans
- scrobble.rs: scrobble/checkin plans
- discussion.rs: discussion thread/comment/reply plans
- user.rs: user profile/stats/follow plans

mdblist_plan.rs is now 245 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.
2026-07-30 16:44:42 +03:00
KhooLy
11e36b7167 refactor: split platform_plan.rs into per-concern submodules
platform_plan.rs was 1062 lines mixing resource fetch/parse plans,
playback prepare, library/prefs plans, and collection/detail plans.
Split into platform_plan/{resource_fetch,resource_parse,
playback_prepare,library_and_prefs,collection_and_detail}.rs:

- resource_fetch.rs: resource fetch plan + execution policy, addon/
  catalog manifest helpers used only there
- resource_parse.rs: resource parse plan, addon-resource parse+plan
  pipeline
- playback_prepare.rs: playback prepare plan, title/artwork resolution
- library_and_prefs.rs: library local-state plan, preferences schema/
  update plans
- collection_and_detail.rs: addon collection mutation, detail episode/
  season-watched/mark-seasons plans

platform_plan.rs is now 176 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.
2026-07-30 16:40:06 +03:00
KhooLy
9a892301b6 refactor: split nuvio_sync.rs into per-concern submodules
nuvio_sync.rs was 1102 lines mixing addon priority, export/import
plans, reconciliation, profile import, progress sync, and collection
mapping. Split into nuvio_sync/{helpers,addon_priority,export_push,
reconciliation,profiles,progress_sync,collections}.rs:

- helpers.rs: shared parse()/str_field()/iso_from_ms() JSON accessors
- addon_priority.rs: addon priority sort/state plans
- export_push.rs: export push plan + per-resource request builders
- reconciliation.rs: addon reconciliation/library mutation plans
- profiles.rs: local profile import from remote Nuvio profiles
- progress_sync.rs: watchlist/progress-meta plans, import merge plan
- collections.rs: catalog/folder source mapping, collection mapping

nuvio_sync.rs is now 168 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.
2026-07-30 16:35:56 +03:00
KhooLy
1010529852 refactor: split library_state.rs into per-concern submodules
library_state.rs was 1399 lines mixing playback progress, library list
building, continue-watching/next-episode logic, and artwork/diff
helpers. Split into library_state/{helpers,playback_progress,
library_lists,continue_watching,artwork_diff}.rs:

- helpers.rs: shared text()/number() JSON field accessors
- playback_progress.rs: playback progress item plans
- library_lists.rs: continue-watching/watchlist list building, library
  document normalization
- continue_watching.rs: badge computation, next-episode resolution
  (kept together since they share several private helpers), plus the
  UP_NEXT_POSITION_SECONDS/UP_NEXT_DURATION_SECONDS constants
- artwork_diff.rs: continue-watching artwork selection, before/after
  diffing helpers

library_state.rs is now 228 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.
2026-07-30 16:31:17 +03:00
KhooLy
1f79a46543 refactor: split stream_policy.rs into per-concern submodules
stream_policy.rs was 1486 lines mixing stream metadata, magnet-link
building, torrent file matching, torrent runtime status, language
matching, and stream selection. Split into stream_policy/{meta,magnet,
torrent_files,torrent_runtime,language,selection}.rs:

- meta.rs: SourceSelectionMode, stream field accessors, playback-info
  and header-forwarding plans
- magnet.rs: magnet link normalization/building
- torrent_files.rs: torrent file index resolution, sibling subtitle
  matching
- torrent_runtime.rs: torrent stream URL building, runtime/status plans,
  ready-budget policy
- language.rs: audio/subtitle language normalization and matching
- selection.rs: player track state, stream selection/ordering

stream_policy.rs re-exports each submodule's full pub(crate) surface
(rather than a curated subset) since nearly everything is exercised
directly by the existing test suite. No behavior change — same 393
tests pass.

Note: includes the pending torrent_ready_budget_json addition that was
already in stream_policy.rs before this refactor (relocated into
torrent_runtime.rs) — preserved verbatim.
2026-07-30 16:27:05 +03:00
KhooLy
164d214f3e refactor: split player_policy.rs into per-concern submodules
player_policy.rs was 1493 lines mixing several distinct plan builders
alongside the existing dolby_vision.rs submodule. Split into
player_policy/{backend_selection,torrent_fallback,buffer_targets,
retry_and_ordering,playback_close,source_sidebar,next_episode}.rs:

- backend_selection.rs: ExoPlayer/MPV/external backend choice
- torrent_fallback.rs: torrent file-fallback policy
- buffer_targets.rs: buffer/cache size targets
- retry_and_ordering.rs: retry policy, next-retry-source selection,
  stream shell/ordering plans (kept together — they share
  retry_stream_key/behavior_text/stream_is_p2p helpers)
- playback_close.rs: playback-close scrobble/progress plan
- source_sidebar.rs: source sidebar option state
- next_episode.rs: next-episode prefetch/selection

dolby_vision.rs switched from `use super::*` to explicit imports since
it's now a sibling rather than a child of the hub's import scope.
player_policy.rs is now 642 lines: module wiring plus the existing test
suite. No behavior change — same 393 tests pass.

Note: includes whatever pending edits were already in player_policy.rs
and player_policy/dolby_vision.rs before this refactor — preserved
verbatim, just relocated.
2026-07-30 16:17:10 +03:00
KhooLy
f367298f83 refactor: split remaining ffi.rs route_* functions into ffi/*_routes.rs
ffi.rs had grown to 1999 lines with ~24 route_* functions still inline
alongside the 6 already-extracted route files (library_routes.rs,
mdblist_routes.rs, external_sync_routes.rs, engine_routes.rs,
addon_resource_routes.rs, addon_support_routes.rs). Extracted the rest
into 15 new ffi/*_routes.rs files following the same convention
(use super::*; pub(super) fn route_x), grouping several small routers
together where a single domain didn't warrant its own file:

- addon_protocol_routes.rs, resource_plan_routes.rs, stream_policy_routes.rs,
  search_plan_routes.rs, player_policy_routes.rs, content_identity_routes.rs,
  calendar_routes.rs, tmdb_routes.rs, player_scrobble_routes.rs: one router each
- watchlist_offline_routes.rs: watchlist + offline
- anime_nuvio_routes.rs: anime detection + nuvio sync
- intro_plugins_routes.rs: intro segments + plugins
- core_addon_store_routes.rs: core contract + addon store + profile avatar pack
- profile_routes.rs: profile contract + profile prefs
- plan_misc_routes.rs: headless adapter plan, discovery plan, data policy,
  dolby vision rpu, player flow

ffi.rs is now 605 lines: core_invoke, the ROUTERS dispatch table, and
the shared arg-parsing helpers/tests. No behavior change — same 393
tests pass, including the fixture test that every known core_invoke
method still routes.

Note: this includes whatever pending edits were already present in
ffi.rs, library_routes.rs, and mdblist_routes.rs before this refactor —
preserved verbatim, just relocated along with everything else.
2026-07-30 16:09:45 +03:00
KhooLy
e406b2d9a7 refactor: split calendar_plan.rs into per-concern submodules
calendar_plan.rs was 1029 lines with several tightly-coupled but
distinct plan builders. Split into calendar_plan/{helpers,plans,
release_rows,widget_notifications,meta_dates}.rs:

- helpers.rs: shared CalendarItemInput, item identity/detail-score,
  artwork resolution, date parsing (used across the other four files)
- plans.rs: visibility/content/desktop-read/candidate plans
- release_rows.rs: release row + season candidate plans
- widget_notifications.rs: widget rows, notification content, release
  detection
- meta_dates.rs: meta-to-calendar-items, unaired episode lookup,
  this-week partitioning, month matching

calendar_plan.rs is now just module wiring plus the existing test suite.
No behavior change — same 393 tests pass.
2026-07-30 15:48:42 +03:00
KhooLy
8eafbb2120 refactor: split addon_protocol.rs into per-concern submodules
addon_protocol.rs was 1186 lines mixing URL handling, asset resolution,
catalog parsing, manifest parsing, and meta-link classification. Split
into addon_protocol/{url,assets,catalogs,manifest_parse,meta_links}.rs:

- url.rs: manifest URL normalization, local/ipv4 detection, resource URL building
- assets.rs: asset URL resolution, manifest-asset/live-manifest merging
- catalogs.rs: catalog extraction, resource/extra support checks
- manifest_parse.rs: raw manifest -> normalized descriptor parsing, addon
  descriptor normalization
- meta_links.rs: cast/director meta-link classification

addon_protocol.rs is now just module wiring plus the existing test suite.
No behavior change — same 393 tests pass.
2026-07-30 15:42:41 +03:00
KhooLy
41e19ee76f refactor: split search_plan.rs into per-concern submodules
search_plan.rs was 1262 lines mixing search, discover, and addon-catalog
concerns. Split into search_plan/{search,discover,addon_catalog,
library_sort,detail_nav}.rs:

- search.rs: search suggestions/screen/result-grouping/merge plans
- discover.rs: discover page merging, selection, and sort plans
- addon_catalog.rs: addon manifest/catalog helpers, metadata feed options,
  transport URL resolution, feed genre resolution
- library_sort.rs: library sort plan
- detail_nav.rs: detail series lookup id, season load plan

search_plan.rs is now just module wiring plus the existing test suite.
No behavior change — same 393 tests pass.
2026-07-30 15:37:51 +03:00
KhooLy
e59cbcb2a6 refactor: split content_identity.rs into per-concern submodules
content_identity.rs was 1486 lines mixing several distinct concerns.
Split into content_identity/{helpers,id,playback_plan,text,
feed_selection,merge_keys,cache_keys,episode_matching,discover_filters}.rs:

- helpers.rs: shared imdb_regex/year_regex/meta_text/push_unique/TMDB_ID_PREFIX
- id.rs: episode locator parsing, content-id normalization, video-id/trakt-ids parsing
- playback_plan.rs: stream request id resolution, direct playback plan building
- text.rs: percent-decoding, form-decoding, feed-key text normalization
- feed_selection.rs: metadata feed toggle/ordering/group-enable plans
- merge_keys.rs: trakt/watched merge-key derivation, continue-watching dedup
- cache_keys.rs: stream discovery / discover-catalog cache key + extra-args parsing
- episode_matching.rs: episode-in-text matching (compact/spaced codes, filenames)
- discover_filters.rs: discover-result year/rating/region filtering

content_identity.rs is now just module wiring plus the existing test suite.
No behavior change — same 393 tests pass.
2026-07-30 15:30:18 +03:00
KhooLy
ef340cf9dd refactor: extract plugin_runtime JS polyfills into .js files
mod.rs was 1590 lines, ~700 of which were four JS polyfill strings
(base64, text encoder, crypto/CryptoJS shim, cheerio) embedded as Rust
raw-string constants. Moved them to plugin_runtime/polyfills/*.js,
loaded via include_str! (same pattern external_sync.rs already uses
for JSON test fixtures). mod.rs is now 882 lines of actual Rust logic;
the polyfill JS gets real syntax highlighting/tooling as a side benefit.
No behavior change — same 393 tests pass, including the ones that
execute the crypto/cheerio polyfills through the JS engine.
2026-07-30 14:59:59 +03:00
KhooLy
dcae1c98ef refactor: split home_ranking.rs into per-concern submodules
home_ranking.rs was 1685 lines mixing five fairly distinct concerns.
Split into home_ranking/{helpers,folders,bootstrap,ranking,billboard}.rs:

- helpers.rs: shared meta_text/meta_i64/meta_string_array accessors
- folders.rs: folder paging/source-plan/merge + collection shelf building
- bootstrap.rs: home hero plan + bootstrap preparation/completion plans
- ranking.rs: NativeHomeCategory + personalization/prioritization/optimize
- billboard.rs: candidate/visual scoring, pool building, catalog normalization

home_ranking.rs is now just module wiring plus the existing test suite.
No behavior change — same 393 tests pass.
2026-07-30 14:55:32 +03:00
KhooLy
fb9d725b6e refactor: split headless_engine test suite into a tests/ directory
tests.rs alone was 1424 lines. Broken into one file per feature area
(detail, player, home, library, discover, plugins, calendar,
cross_cutting, engine, wire_fixtures), mirroring the existing
per-feature submodule split in headless_engine itself. No behavior
change — same 393 tests pass.
2026-07-30 14:47:45 +03:00
KhooLy
68b64fd330 refactor: move headless_engine test suite into its own file
mod.rs was 2204 lines, ~65% of it the #[cfg(test)] module. Splitting
the ~1424 lines of tests into headless_engine/tests.rs leaves mod.rs
at 777 lines of actual engine logic (struct, lifecycle fns, dispatch/
complete_effect tables). No behavior change — same 393 tests pass.
2026-07-30 14:44:30 +03:00
KhooLy
3dd3d9025c refactor: split external_sync.rs into per-provider submodules
Breaks the 2403-line external_sync.rs into trakt.rs, simkl.rs, mal.rs,
stremio.rs, calendar.rs, merge.rs, and plan.rs, mirroring the existing
provider_mappers.rs/anilist.rs split. external_sync.rs is now just
module wiring plus the test suite. No behavior changes.
2026-07-30 14:40:04 +03:00
KhooLy
6e5ff0c260 feat: centralize import category/dry-run gating in fluxa-core
Adds import_apply_plan_json (Trakt/Simkl/Stremio's shared
watchlist+watched merge shape) and extends anilist_entries_to_sync /
nuvio_sync::import_merge_plan_json with categories+dryRun. Each now
decides, from already-fetched provider data and local before-state,
what to merge and what to report as counts — the host only passes the
user's category selection through and applies whatever non-null
fields come back. This was previously TypeScript-only control flow in
fluxa-desktop; centralizing it lets any host platform (e.g. the
Kotlin mobile app) reuse the same category/dry-run decision logic
through the existing core_invoke funnel instead of reimplementing it.
2026-07-29 19:58:33 +03:00
KhooLy
f89f724dbe feat: add push_plan_json for cross-provider import push
Given a destination provider, selected import categories, and the
local library snapshot (watchlist/completed/dropped/continueWatching),
decides which push operations are needed and in what shape per
provider (per-item watchlist/status adds, bulk watched-ids, Nuvio's
bulk item/history/progress payloads). Centralizes the
provider/category capability matrix that fluxa-desktop's
crossProviderPush.ts previously hand-wrote in TypeScript, so other
host platforms (mobile) can consume the same decision logic through
the existing core_invoke("pushPlan", ...) funnel instead of
re-deriving it.
2026-07-29 19:44:51 +03:00
KhooLy
23a5c3c828 Add cipher-aware trailer resolution 2026-07-29 17:55:21 +03:00
KhooLy
da2ad40e37 refactor: rename simklScrobbleAction to scrobbleCloseAction, move out of simkl router
The close-action threshold is shared by both Trakt and SIMKL scrobbling,
not simkl-specific — the name and route location were misleading.
2026-07-29 15:22:41 +03:00
KhooLy
52e76d719c core: centralize scrobble close action, torrent subtitle matching, subtitle dedup
These were being reimplemented in fluxa-desktop instead of shared here,
which meant mobile would have had to duplicate the same policy again.
2026-07-29 15:19:08 +03:00
KhooLy
1d7c561da8 feat: move Trakt/SIMKL sync-delta decision logic into core
The activity-timestamp diffing (which resource changed since last sync)
and SIMKL's delta-merge algorithm were living entirely in fluxa-desktop's
TS layer, duplicating pure JSON-in/JSON-out decision logic that a second
platform consumer would otherwise have to reimplement from scratch. Only
the actual HTTP fetch/retry/storage I/O stays in the platform layer.
2026-07-29 15:03:59 +03:00
KhooLy
c5816492e3 feat: pin meta detail to the addon the catalog item came from
metaDetail requests carry an optional source addon transportUrl now.
When present, resource_fetch_plan_json filters the meta race down to
just that addon instead of racing every configured meta-capable
addon by priority, so a poster clicked from Cinemeta's catalog shows
Cinemeta's own meta response instead of whichever addon wins the
priority race. Falls back to the full race when the source addon is
unknown (deep links, search, library) or doesn't implement the meta
resource itself (e.g. a torrent-indexing addon).

Also tags search request objects with their addon's transportUrl so
the desktop shell can stamp search results with their source addon
the same way catalog rows already can.
2026-07-29 05:03:09 +03:00
KhooLy
0fe219c503 fix: include system/app language in the auto-translation fallback
The previous commit only synthesized a YouTube tlang= translation when
preferred/secondary subtitle prefs had no native track — it skipped
the systemLanguage signal (the app's own UI language), which the
selector already uses as a real fallback source for picking a track.
Now all three (preferred, secondary, systemLanguage) are eligible
translation targets, not just the two explicit prefs.
2026-07-29 04:14:10 +03:00
KhooLy
b7f3b1893c feat: fall back to YouTube's auto-translated captions for trailers
trailer_subtitle_selection_plan_json only ever picked from the
literal caption tracks YouTube's player response lists (usually just
the original-language one). If the user's preferred/secondary
subtitle language had no native track, playback silently fell back to
whatever scored best with no way to get their language — even though
YouTube's timedtext endpoint supports server-side translation via a
tlang= query param on any existing track's URL.

Now, when no available track matches the user's explicit preferred/
secondary language, synthesize a translated variant from the
best-scoring native track by appending tlang=<target> to its URL.
2026-07-29 04:12:07 +03:00
KhooLy
db816cd052 Decode non-breaking spaces in subtitles 2026-07-29 03:25:20 +03:00
KhooLy
cd225d3936 fix(core): thread mdblistRatings through the detail effect-completion merge
DetailState (headless_engine/detail.rs) whitelists which fields from a
completed fetchDetailSecondary effect get copied into engine state —
mdblistRatings wasn't in that list, so the frontend's fetch always
succeeded but the value was silently dropped before ever reaching the
UI. Added alongside the existing omdb_ratings field it mirrors.
2026-07-29 03:08:20 +03:00
KhooLy
d5d144e550 fix(core): fall back to score when a rating source's value is null
MDBList returns {"value": null, "score": null} for unrated sources
(e.g. myanimelist on non-anime titles) rather than omitting the key.
The previous or_else only triggered when "value" was absent, not when
present-but-null, so a source with null value and a real score never
fell through.
2026-07-29 02:55:12 +03:00
KhooLy
cfea89163d fix(core): correct MDBList body shapes verified against a live account
Scrobble/checkin bodies nest under movie/show (Trakt-style), not the
flat {ids, type} shape originally guessed. Discussion bodies use
"content" not "comment". Sync/ratings needs a per-item "rating" field.
mdblistSyncMutatePlan's route now builds the grouped body from a flat
items array like the watchlist/list-items routes, instead of taking a
pre-built body.
2026-07-29 02:41:18 +03:00