Commit graph

32 commits

Author SHA1 Message Date
KhooLy
ae3e135b6f Retire FluxaCoreNative's hand-written JNI in favor of core_invoke
Every stateless method on FluxaCoreNative now calls the corresponding
core_invoke route via FluxaCoreUniFfi instead of a hand-written
external fun; only the handle-lifecycle functions (headless engine,
app core state) keep their dedicated JNI bindings. NuvioCoreBridge and
ExternalLibraryClient's generic coreInvoke calls move onto the same
UniFFI path. Public method signatures are unchanged, so no other call
site needed to change.
2026-07-18 15:01:38 +03:00
KhooLy
c5df51f68a Relocate UniFFI codegen and bindings from app module to data module
FluxaCoreUniFfi.kt and the UniFFI-generated com.fluxa.core.uniffi
bindings only existed in the app Gradle module, but FluxaCoreNative.kt
(data module) needs to call into them and data cannot depend on app.
Moves the generateFluxaCoreUniFfiBindings task and the Kotlin facade
into data, which app already depends on.
2026-07-18 15:01:27 +03:00
KhooLy
e8dced3b20 Move player overlays, sidebars, and gesture input into shared CMP
Relocates skip-segment/transient overlays, the sidebar shell + track
list primitives, mark-segment/settings/track sidebars, and the
playerInputControls gesture modifier from app-module Android files
into shared/commonMain. Promotes coil3/coil3-compose to commonMain
(genuinely multiplatform; only coil3-network-okhttp stays
Android-only) so the overlay cards using AsyncImage can move too, and
promotes TorrentStreamStatus to player/commonMain as a plain data
holder.

Where a composable used to reach into UserProfile's JNI-backed
`safeXxx` properties (holdSpeed, holdToSpeedEnabled, language) or an
Android-only Locale API, it now takes the resolved primitive as a
parameter instead, with the app-side caller supplying it - keeping
the commonMain/androidMain boundary honest rather than papering over
it.

EpisodeSidebar (Hilt ViewModel-bound) and TrackSelectionState (direct
FluxaCoreNative JNI calls) stay Android-only, along with the actual
video surface, playback effects, and setup effects - those are the
next sub-steps in the player-extraction migration.
2026-07-17 00:06:49 +03:00
KhooLy
fc6bc620b3 Move data/player/domain logic into commonMain KMP source sets
Sweeps addon repositories, watchlist/profile storage, Trakt/Nuvio
sync, discovery models, and player policy classes out of Android-only
packages into data/commonMain and player/commonMain, with thin
androidMain wrappers where platform APIs are still required.
2026-07-16 23:00:28 +03:00
KhooLy
bd780396d1 Complete shared KMP and CMP migration 2026-07-16 15:50:35 +03:00
KhooLy
658b3ce3e3 KMP/CMP migration: purge legacy Android screens, route TV through shared FluxaApp
Continue the shared CMP/KMP migration: remove the legacy app/ui/catalog
Screen.kt files now superseded by shared/commonMain equivalents (Detail,
Search, Settings, Login, Profile, Watchlist, AddonStore, and the full
legacy Tv* screen set), and thread a DeviceType through FluxaApp/
FluxaAppHost so Android TV routes Home/Search/Discover/Calendar/Library
through shared destinations instead of falling into the removed legacy
composables (which previously hit an unreachable error() case).

Adds TvCatalogHomeScreen wiring plus new TvSearchScreen/TvDiscoverScreen/
TvCalendarScreen/TvLibraryScreen as thin TV-safe wrappers around the
existing shared screens. Removes the orphaned shared player/PlayerScreen.kt,
which had no call sites since the player stays platform-native by design.
2026-07-16 01:17:27 +03:00
KhooLy
4780549655 Move HomeBillboardRanking/StateHolder to shared KMP now that Meta is shared
Both were blocked solely on Meta living in the Android-only app module,
which the previous commit fixed. Split ContentIdentity's two pure functions
(billboardKey, normalizedBillboardTitle) into a new shared
ContentIdentityTitle.kt, since the rest of ContentIdentity still calls
FluxaCoreNative (JNI-only, not yet exposed to shared code).
2026-07-16 00:41:46 +03:00
KhooLy
e5581918cd Migrate Meta/MetaDetail models from Gson to kotlinx.serialization
Gson is JVM-only and can't live in shared commonMain, which was the real
reason Meta/MetaDetail/Video/CastMember/DetailTrailer were stuck in the
Android-only app module. Ports the existing JsonElement-tree-based custom
parsing (flexible alternate-key extraction, free-form cast/trailer JSON
tolerance) to kotlinx.serialization's equivalent JsonObject/JsonElement API,
using @JsonNames for simple alternate keys and custom KSerializers for
CastMember/DetailTrailer/MetaDetail where the existing Gson deserializers did
manual extraction.

StremioAddonResourceClient now parses addon catalog/meta responses via
kotlinx.serialization (stremioJson) instead of Gson for these types; Gson
stays in place for Stream/SubtitleData, which are out of scope here.
StremioService.getMetaDetail now returns the raw response body instead of
relying on Retrofit's automatic (Gson-based) conversion, since the models it
returns no longer carry Gson annotations.

Verified on-device: addon catalog list parsing (Home rows), billboard, and
meta detail (title/year/runtime/description) all render correctly against
real addon responses.
2026-07-16 00:36:08 +03:00
KhooLy
36f8671199 Preserve local Nuvio profile presentation 2026-07-14 22:50:34 +03:00
KhooLy
273fb1ef70 Sync addon changes to Nuvio 2026-07-14 22:42:19 +03:00
KhooLy
7c26c9d832 Refresh Nuvio after server recovery 2026-07-14 22:34:56 +03:00
KhooLy
83e2cbd98d Clear removed Nuvio addons on import 2026-07-14 22:32:54 +03:00
KhooLy
4a15336389 Replace Nuvio watchlist on import 2026-07-14 22:32:14 +03:00
KhooLy
d01b562e9d Preserve Nuvio source filter values 2026-07-14 22:31:03 +03:00
KhooLy
46baacd5df Preserve Nuvio folder metadata 2026-07-14 22:29:33 +03:00
KhooLy
cd6cac1685 Clear removed Nuvio collections on sync 2026-07-14 22:28:34 +03:00
KhooLy
68d9e1c19e Load every Nuvio folder source 2026-07-14 22:26:13 +03:00
KhooLy
b330eb475c Add Nuvio remote collection sources 2026-07-14 22:24:25 +03:00
KhooLy
8a75354e7b Sync Nuvio collections live 2026-07-14 22:09:18 +03:00
KhooLy
5fb04a5a6f Improve Nuvio import parity 2026-07-14 22:07:43 +03:00
KhooLy
20476761a9 Migrate shared UI and Apple playback to KMP 2026-07-14 18:14:18 +03:00
KhooLy
1bc683acbf Serialize concurrent Stremio addon requests 2026-07-14 16:58:21 +03:00
KhooLy
b0a68d6cee Preserve profile names during Nuvio import 2026-07-14 16:57:50 +03:00
KhooLy
cda0c17b0a Replace TorrServer namings with Native torrent engine 2026-07-14 16:57:20 +03:00
KhooLy
713cd42ae3 Cache catalog fetch results (memory, 10min TTL), same pattern as the addon list
getAddonCatalogResult had no caching at all, unlike getUserAddons which
already uses RepositoryMemoryCache. Every Discover filter change (type/
catalog/genre) or revisit re-fetched from the addon over the network
with zero speedup, even for a combination already fetched moments
earlier. Reuses the same RepositoryMemoryCache (10min TTL, already
injected into this class) keyed by transportUrl+type+id+skip+genre+
search. Doesn't fix a slow addon's first-load latency, but repeat
visits to the same catalog+genre combo are now instant.
2026-07-13 22:55:55 +03:00
KhooLy
7b8a5c61bf Fix missing Discover catalogs and add dynamic content types (movie/series/anime)
Diagnosed against NuvioMobile's open-source discover implementation
(same addons, more catalogs visible there). Two real gaps:

1. buildDiscoverCatalogOptions() excluded any catalog requiring an
   extra parameter beyond genre. Nuvio's equivalent (supportsDiscover())
   explicitly whitelists "skip" (pagination) as never blocking a
   catalog even when required — Fluxa's filter didn't, silently
   dropping every catalog that declares skip as required, which is a
   common Stremio addon SDK convention. Added "skip" to the allowed set.

2. The Type filter (movie/series) was hardcoded in the shared Discover
   UI instead of derived from what addons actually expose. Nuvio builds
   its type list from distinct catalog types across installed addons,
   which is why "anime" shows up there. Added a new
   buildDiscoverContentTypes() helper (same extras filter, minus the
   "all"/type restriction), threaded contentTypes through the
   readDiscoverCatalogFilters effect, Rust's DiscoverState (new field,
   needed since the effect completion flows back through the engine's
   own state, not directly to Kotlin), HomeViewModel, and the shared
   DiscoverUiState/DiscoverScreen, with a movie/series fallback so the
   dropdown isn't empty before the first fetch completes.
2026-07-13 22:48:20 +03:00
KhooLy
35c0c405ac Align mobile trailer and account sync flows 2026-07-12 02:47:24 +03:00
KhooLy
b0d15dd8f5 Add gifAutoplayEnabled profile preference 2026-07-10 13:06:54 +03:00
KhooLy
a1e5173a7a Add chapter-based skip toggle, JP audio preference, autoplay countdown, and auto-retry-next-source
- Use Chapters for Skipping: gates the existing chapter-derived skip
  segment fallback (already computed by deriveSkipSegmentsFromChapters)
  behind a new toggle instead of applying it unconditionally. Also
  brought TV's Skip Segments section up to parity with mobile (IntroDB +
  API key, AniSkip, chapter toggle, auto-skip), since TV was missing all
  but auto-skip.
- Japanese audio for anime: overrides the resolved preferred audio
  language to "ja" for anime-genre content when enabled, at the single
  entry point both audio and subtitle track resolution already share.
- Autoplay countdown: the next-episode card now counts down and fires
  automatically, instead of only advancing on click.
- Auto-retry next source: extends the existing Cloudstream-only
  auto-fallback-on-error path to also apply generally when enabled.
2026-07-10 02:43:32 +03:00
KhooLy
42422ea8a5 Add AniList account sync (OAuth2 connect/disconnect)
Mirrors the existing Trakt/MyAnimeList/Simkl connection pattern: a
fluxa://oauth/anilist deep link, authorize-URL launch, token exchange
through a new ExternalOAuthClient.exchangeAnilistCode call, and profile
fields (anilistAccessToken/RefreshToken/TokenExpiresAt) merged via the
existing authExchangeRequested headless effect. Adds a matching
AuthProvider::Anilist arm in fluxa_core's token_merge_plan_json so the
shared Rust merge path recognizes the new provider too.

Connect/disconnect tiles added to both TV and mobile Settings, folded
into the existing multi-provider "Disconnect" action.

Note: app/build.gradle.kts also carries a small pre-existing uncommitted
fix (rustCrateDir path) unrelated to this change, included here only
because it was already sitting in the working tree on the same file.
2026-07-10 02:23:39 +03:00
KhooLy
b91c91f205 CloudStream per-catalog feeds and home billboard integration
- Add Cs3CatalogFeedDescriptor and per-catalog feed keys (cs3CatalogFeedKey) so
  each CS3 catalog gets its own home row instead of one per plugin
- Build CS3 metadata feed options from per-catalog descriptors in buildCs3MetadataFeedOptions
- HomeBillboardLoader now fetches CS3 feeds alongside stremio addon feeds for
  billboard pool population
- Stream.kt: send Cloudstream referer header lowercase ("referer") to match how
  Media3 passes custom request properties
- HomeContentFormatters: skip cs3: video IDs in parseShortVideoId to avoid
  incorrect episode line parsing
- HomeLayoutHelpers: include poster as fallback in mobileHeroArtworkCandidates
2026-07-03 12:53:00 +03:00
KhooLy
adf7700f7d Fix mpv/ExoPlayer audio passthrough, channel layout, AO, and DV track selection
mpv:
- Set ao=audiotrack explicitly so the AudioTrack output is guaranteed
  (avoids openSLES which lacks encoded passthrough)
- Add audio-channels=7.1,5.1,stereo so mpv negotiates multichannel
  instead of defaulting to auto-safe stereo downmix
- Build audio-spdif at init time by querying AudioManager output device
  encodings; only advertises AC3/E-AC3/DTS/DTS-HD/TrueHD formats the
  device actually reports

ExoPlayer:
- Guard setVideoEffects(emptyList()) behind a videoEffectsActive flag:
  only call it when IptPqc2ToneMapEffect was actually installed, never
  on a clean path — the empty-list call triggers the GL VideoFrameProcessor
  pipeline in media3 1.10.1 which tone-maps HDR even for an empty effect list
- detectPreferredVideoMimeType now accepts Context and checks display +
  decoder DV capability first; on fully DV-capable devices it returns
  VIDEO_DOLBY_VISION so DV variant tracks are preferred rather than
  deprioritized behind H.265/H.264
2026-07-03 03:51:49 +03:00