Commit graph

142 commits

Author SHA1 Message Date
KhooLy
b1c87299a2 Format core sources
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-26 18:52:49 +03:00
KhooLy
110267076b Fix SIMKL watched episode payload 2026-07-26 18:46:38 +03:00
KhooLy
51cb6a06f4 Add Trakt comments request plan 2026-07-26 18:32:15 +03:00
KhooLy
b1fc4c0b8f Improve external continue watching sync 2026-07-26 18:01:01 +03:00
KhooLy
fc0d5d56ba Support provider sync and calendar data plans 2026-07-26 17:15:18 +03:00
KhooLy
f2568a46c3 chore: format core sources 2026-07-26 15:22:31 +03:00
KhooLy
4ce094c702 fix calendar episode mapping and visibility
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-26 04:40:10 +03:00
KhooLy
669f36f259 Refine library history and recommendation requests
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-26 03:14:04 +03:00
KhooLy
0179c0871d Handle wrapped SIMKL library responses 2026-07-26 02:53:55 +03:00
KhooLy
36c10734ae Add integration settings contract 2026-07-26 02:24:33 +03:00
KhooLy
427a86c1a3 fix: refresh OAuth and resolve Nuvio avatars 2026-07-26 01:09:40 +03:00
KhooLy
1fab5d27c2 chore: format core sources
Some checks failed
CI / test (push) Has been cancelled
CI / clippy (push) Has been cancelled
CI / bench-check (push) Has been cancelled
CI / wasm-check (push) Has been cancelled
CI / fmt (push) Has been cancelled
2026-07-22 20:36:55 +03:00
KhooLy
4e787268b8 refactor: split core and streaming engine modules 2026-07-22 18:46:54 +03:00
KhooLy
fffbe628c7 Add shared cross-platform domain policies 2026-07-22 16:56:28 +03:00
KhooLy
5435cb9025 feat: improve calendar episode metadata 2026-07-22 02:10:07 +03:00
KhooLy
fe817cb41d feat: extend core integrations 2026-07-22 01:07:50 +03:00
KhooLy
cd2973d9bb Enable rquickjs bindgen against the iOS SDK sysroot in the xcframework script
Some checks failed
CI / test (push) Has been cancelled
CI / clippy (push) Has been cancelled
CI / bench-check (push) Has been cancelled
CI / wasm-check (push) Has been cancelled
CI / fmt (push) Has been cancelled
Same class of fix as the Android NDK build: rquickjs-sys has no prebuilt
FFI bindings for Apple targets, so bindgen needs the iOS SDK sysroot and
clang target triple explicitly, or it falls back to the host's own
headers and fails. Unverified — this machine has no Xcode/macOS to
actually run the build; needs confirming on a real Mac.
2026-07-20 03:24:38 +03:00
KhooLy
945f8357f4 Add scraper settings-layout support to plugin runtime
Thread scraper_id and scraper_settings_json into execute_scraper,
injecting globalThis.SCRAPER_ID/SCRAPER_SETTINGS ahead of the
scraper's own code, matching Nuvio's JsBindings polyfill convention.
Add get_settings_layout to run a scraper's onSettings() export and
return its JSON field layout, with a matching UniFFI export.
2026-07-20 02:48:27 +03:00
KhooLy
f9000b4dbd Preserve plugin-sourced subtitles and scraper overrides across re-fetch
plugin_result_to_stream only populated subtitle_tracks, not the subtitles
field Kotlin's Stream model actually reads, silently dropping plugin
subtitles. Also add pluginScraperSettingsUpdated and make repository
re-fetch (on manual refresh or app-start replay) carry over a scraper's
existing enabled/settings instead of resetting to manifest defaults.
2026-07-20 02:40:12 +03:00
KhooLy
80657b75bf Enable rquickjs bindgen so plugin-js-engine cross-compiles for Android
rquickjs-sys ships no prebuilt FFI bindings for any Android target
triple, so cargo build --target aarch64-linux-android (and the other
Android ABIs) failed once plugin-js-engine pulled in rquickjs. Switch
the rquickjs dependency to its explicit non-macro feature set plus
bindgen, so bindings are generated at build time via libclang against
the NDK sysroot instead of relying on a missing prebuilt binding file.

Dropped the "full-async" and "phf" umbrella features because they pull
in the rquickjs-macro proc-macro crate, which is compiled for the host
and would otherwise pick up the same BINDGEN_EXTRA_CLANG_ARGS meant for
the Android cross target, corrupting its own bindings. fluxa_core does
not use any rquickjs proc-macro attributes, so nothing is lost.
2026-07-20 02:18:05 +03:00
KhooLy
0d1acf1eb3 Port Nuvio-compatible scraper JS engine into fluxa_core
Adds a plugin_runtime module (rquickjs VM, cheerio-style DOM bridge via
scraper/ego-tree, CryptoJS + WebCrypto polyfill backed by native
digest/HMAC/PBKDF2/AES/RSA/ECDSA) ported from fluxa-desktop's
plugin_runtime, gated behind a new plugin-js-engine feature.

Unlike the desktop version, the in-script fetch() bridge never touches
reqwest directly: it calls a new PluginHttpClient UniFFI callback
interface (Box<dyn PluginHttpClient> -> Arc internally), so Kotlin/Tauri
own the actual HTTP execution and any SSRF protections, consistent with
fluxa_core never performing direct HTTP. execute_plugin_scraper is
exposed as a synchronous UniFFI function alongside the existing
core_invoke surface.
2026-07-20 01:30:05 +03:00
KhooLy
ca34306f86 Add isEpisodeReleased FFI method and upcomingRowEnabled safe pref
Supports the new Home 'Upcoming' row: isEpisodeReleased lets Kotlin
check whether a specific episode's release date is still in the
future, reusing the existing is_episode_released date logic already
used by calendar/library release detection. upcoming_row_enabled is
a new resolved profile pref, off by default, mirroring
continue_watching_enabled.
2026-07-20 00:06:23 +03:00
KhooLy
8772f9a8e8 batch the two initial discover-page prefetches into one round trip
Both skip=20 and skip=40 payloads are fully known as soon as
runDiscover completes (only local counters, no dependency on the
first page's contents), so there's no reason to wait for skip=20's
completeEffect before requesting skip=40. Emit both FetchDiscoverPage
effects together, tagged with the same DiscoverPaging generation.
2026-07-19 16:48:59 +03:00
KhooLy
76b05c0216 route playerFrameUrl through the external-open path
stremio-core never hands PlayerFrame streams to its native streaming
resolver either (get_streaming_url only matches Url/Torrent/YouTube) —
it's an embeddable frame URL, not a playable file, so it belongs with
externalUrl rather than stream_playable_url.
2026-07-19 16:32:55 +03:00
KhooLy
9a253b34ff stop treating externalUrl as a playable stream URL
Stremio addons use externalUrl to mean "open outside the player"
(purchase pages, addon config links, etc.), not a media file. It was
being fed straight to mpv as a playable candidate, causing
"unrecognized file format" crashes. Now exposed separately as
externalUrl so callers can open it via the system browser instead.
2026-07-19 16:23:26 +03:00
KhooLy
6c4744333d Follow SeekHead to locate MKV Chapters past the first Cluster
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.
2026-07-19 03:02:05 +03:00
KhooLy
ef82039197 Sort merged external continue-watching items by savedAt
replace_external_continue_watching_json deduped items via a HashMap and
returned them via into_values() with no final sort, so the order was
whatever HashMap's randomized per-process iteration produced instead of
most-recently-watched-first. Sort descending by saved_at_ms before
returning, matching the pattern already used by
trakt_playback_items_dedup_json.
2026-07-19 02:30:04 +03:00
KhooLy
93f2651556 fix extract_anilist_id_from_links to try later links after a malformed match
The marker search stopped at the first "anilist.co/anime/" occurrence,
so a malformed link with no trailing id (or a share URL missing the
numeric suffix) silently hid a valid AniList link listed after it.
2026-07-19 00:01:33 +03:00
KhooLy
e69c035e59 move AniList anime-tracking detection and match policy into core
Desktop's anime-tracking gate, link-based AniList ID extraction, and
search-result best-match selection were duplicated logic Android lacks
entirely (it only tracks progress when a meta id is already prefixed
anilist:). Porting the decision logic here — while keeping GraphQL I/O
and caching on the platform side — lets both platforms share it.
2026-07-18 23:52:28 +03:00
KhooLy
a92a25c662 add shared business policy for addon priority, home ranking, scrobble, and collection sources
Extends existing cross-platform domain modules (nuvio_sync, addon_store,
home_ranking, watchlist_plan, external_sync, player_policy, player_scrobble,
calendar_plan, search_plan) with logic previously duplicated or missing on
the desktop side, so it's implemented once and reused across platforms.
2026-07-18 23:37:45 +03:00
KhooLy
e1a3b2445a deduplicate shared business logic 2026-07-18 21:47:23 +03:00
KhooLy
491868fa22 move remaining desktop policy into core 2026-07-18 21:04:54 +03:00
KhooLy
d6c3bd72be centralize desktop domain policy in core 2026-07-18 20:17:51 +03:00
KhooLy
25b3ca73a0 Read removed/_mtime from Stremio library items; add watchlist extraction
Kotlin's LibraryItem DTO never declared removed/_mtime, so even though
this file already worked on raw JSON Values, those fields never
survived the trip from the Stremio datastore response. Adds
libraryWatchlistItems (id/name/type/poster/background/updatedAtMs,
excluding removed items) so the pull side can finally see Stremio
library membership as a timestamped list, matching the shape the
mergeWatchlistTimestamped bridge expects. Also fixes
libraryContinueWatchingItems, which never checked "removed" at all --
a removed item with leftover progress state could still surface as
continue-watching.
2026-07-18 20:11:15 +03:00
KhooLy
5f43a8c11a Carry a timestamp on AniList PLANNING (watchlist) entries
anilist_entries_to_sync built a "watchlist" array from PLANNING-status
entries but never attached a timestamp to it, so nothing downstream
could run a timestamp merge against it. Adds updatedAtMs (converted
from AniList's updatedAt seconds, falling back to now_ms when absent)
so the Kotlin side can feed these into the same timestamped-merge
path already used for Trakt's watchlist.
2026-07-18 20:02:07 +03:00
KhooLy
d7def2c615 Add core_invoke method to build AniList SaveMediaListEntry variables
AniList integration has been pull-only — no mutation is ever sent, so
watchlist/mark-watched changes made in Fluxa never reach AniList, even
though its OAuth token already has full read/write access (no scope
restriction). Adds a pure function that extracts the numeric media id
from the "anilist:<id>" content id scheme and builds the GraphQL
mutation variables, so the Kotlin side only needs to POST the
variables through the existing generic anilistGraphQl call.
2026-07-18 19:26:06 +03:00
KhooLy
e9e6debc40 Accept nested JSON for timestamped merge args instead of double-encoded strings
Matches the nuvioImportMergePlan convention (args_json fields carry
raw JSON, not JSON-encoded strings) so Kotlin callers don't need to
double-serialize the local/remote item arrays.
2026-07-18 18:44:18 +03:00
KhooLy
3f68a26cbf Add generic timestamp-based membership merge for watchlist/watched sync
Trakt, Simkl, and Stremio currently reconcile watchlist and watched
state as blind set-unions (merge_external_watchlist_json/merge_external_watched_json)
with no notion of "which side changed more recently." Nuvio already
resolves this kind of conflict with timestamp comparisons; this adds
the same capability as a provider-agnostic core_invoke method
(mergeWatchlistTimestamped/mergeWatchedTimestamped) so Trakt/Simkl/
Stremio can reuse it instead of duplicating Nuvio-specific logic.

Takes local items (id, updatedAt, active) and remote items (id,
updatedAt), and returns which ids to apply locally vs. push to the
remote, picking a winner per id by comparing timestamps.
2026-07-18 18:42:38 +03:00
KhooLy
e68dcc405c Add reusable calendar planning and profile-aware library commands 2026-07-18 16:52:14 +03:00
KhooLy
256a0aeaee Route fluxa_core JNI functions through core_invoke
Retire hand-written per-function JNI bindings in fluxa-core in favor of
the generic core_invoke(method, args_json) dispatcher already exposed
via UniFFI. Every stateless domain function (calendar, watchlist, addon
parsing, trakt/simkl sync, home ranking, player policy, etc.) is now
reachable by method name through core_invoke; jni.rs keeps only the
coreInvoke passthrough and the handle-lifecycle functions that manage
opaque stateful engine/state handles, which don't fit the stateless
JSON-in/JSON-out shape.
2026-07-18 15:00:58 +03:00
KhooLy
1b223ecbc3 Parse and normalize addon stream results with provider-tagged stream fields 2026-07-18 15:00:49 +03:00
KhooLy
d03b86fa1c Sort home continue-watching by lastWatchedAt; stop bumping savedAt from watch history 2026-07-18 15:00:41 +03:00
KhooLy
f9532a9224 Add continue watching source preference (stremio/nuvio/trakt/simkl/anilist) 2026-07-18 15:00:36 +03:00
KhooLy
8038a1c1da Treat near-zero progress with a placeholder duration as up-next
Progress rows synced before duration is known carry a bogus >24h
duration; require >=99.5% completion for real durations instead of also
matching <0.5% starts.
2026-07-16 18:56:54 +03:00
KhooLy
0602944f0f Filter adult-genre items out of curated home shelves 2026-07-16 18:56:54 +03:00
KhooLy
b87cb98d3d Pick the torrent file matching the requested episode, not the largest
An episode-shaped title (tt123:1:2) now resolves to the file whose name
contains s01e02/1x02 before falling back to the largest video, and
fallback candidates are ordered episode-matches-first. Fixes playing the
wrong file from season-pack torrents where a later episode is larger.
2026-07-16 18:56:54 +03:00
KhooLy
a6f90bb36a Add Nuvio sync merge planning and desktop policy helpers
fluxa-desktop's nuvioSync.ts held the whole Nuvio import policy in
TypeScript: watchlist mapping, progress-entry construction (up-next
thresholds), watched-history reconciliation, savedAt bumping, profile
and collection mapping. That logic moves here as nuvio_sync.rs so other
shells share one implementation; the desktop keeps only the pulls and
persistence.

Also exposes three policies the desktop had duplicated or hardcoded:
airDateRefreshCandidates (12h cooldown due-check), simklScrobbleAction
(stop/pause threshold, reusing SCROBBLE_STOP_PROGRESS_PERCENT), and
torrentReadyBudget (torrent readiness retry budget).
2026-07-16 18:53:50 +03:00
KhooLy
f0d77c8b61 Apply cargo fmt across accumulated unformatted files
Some checks failed
CI / test (push) Has been cancelled
CI / clippy (push) Has been cancelled
CI / bench-check (push) Has been cancelled
CI / wasm-check (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI's fmt check was failing on formatting debt that had built up across
several commits merged from agent/apple-streaming-engine.
2026-07-15 20:08:18 +03:00
KhooLy
2594af72da Add torrent/magnet-link support to streaming and offline downloads
Derive a playable magnet URI from a stream's infoHash/sources (or
detect an existing magnet/torrent URL) via stream_magnet_link, and
let the offline-download planner accept torrent-sourced streams
instead of rejecting them outright.
2026-07-15 19:32:18 +03:00
KhooLy
0228bc97e4 Carry remote catalog sources through paging 2026-07-14 22:24:34 +03:00