Commit graph

71 commits

Author SHA1 Message Date
KhooLy
5fefaf367a refactor: resolve discover catalog/genre selection via shared core plan
AndroidDiscoverDataSource duplicated catalog+genre resolution logic
in Kotlin instead of calling discoverSelectionPlan, the same shared
Rust function fluxa-desktop already relies on for this. Route Android
through it too: one implementation for both platforms, and it's more
robust than the Kotlin version since it treats whichever extra a
catalog lists first as the filterable dimension generically, not just
one literally named "genre".

Add DiscoverCatalogOption.extras (carrying each raw catalog extra:
name/options/default/isRequired) and CatalogExtra.default, both
needed for the plan call and previously missing from the addon
manifest models entirely -- the actual bug hunted down across this
and the two preceding core commits was that "default" was dropped at
manifest-parse time in Rust, so no amount of Kotlin plumbing could
have surfaced it.
2026-07-28 04:08:32 +03:00
KhooLy
eb53587a61 fix: auto-select a catalog's default genre when one is required
Catalogs with requiresGenre now pre-select defaultGenre (falling back
to the first available option) instead of leaving the dropdown
looking unselected while discover() silently ran without a genre --
results were already coming back filtered by the addon's own
server-side fallback, but the UI gave no indication why.
2026-07-28 03:40:04 +03:00
KhooLy
a17144a416 feat: add profile picker avatar packs and background image (Android)
Let users customize the "who's watching" screen: add GitHub-hosted
avatar packs to pick profile pictures from, and set a background
image behind the profile grid. The Rust side (GitHub URL/tree/manifest
parsing and validation) already existed in fluxa_core but was never
wired to Kotlin; this adds the FFI wrappers, an OkHttp-driven GitHub
crawl, a small global picker-settings store, and the new/updated
Compose screens. Android only for now — iOS/desktop keep compiling via
default no-op ProfilePersistence methods.
2026-07-28 01:16:12 +03:00
KhooLy
613a6899d0 feat: use core-computed calendar artwork priority
Prefers resolvedArtworkUrl from fluxa-core over the local
episodePoster/poster/meta-field chain so artwork selection matches
fluxa-desktop's calendar.
2026-07-28 00:38:47 +03:00
KhooLy
9cb5f4b811 fix: improve episode release notification text and artwork priority
Notification title now shows the actual episode/show title with the
release kind as subtext, and season/episode text is generated per case
(episode with title, episode without, season-only) instead of one
generic template. Calendar artwork now prefers the episode-specific
poster before falling back to show-level artwork.
2026-07-28 00:26:17 +03:00
KhooLy
78569c8f15 feat: show per-item progress during Nuvio account import
Surfaces which item is currently being imported (index/total/title)
under each import step on both mobile and TV auth screens, with a
checkmark for completed steps.
2026-07-28 00:26:07 +03:00
KhooLy
c030fd66b2 perf: fix Discover screen load and catalog switch latency
- AddonRepository: cache meta detail lookups with in-flight de-dup, and
  race addons instead of fanning out to all of them unconditionally,
  cutting redundant requests to slow/duplicate addon instances.
- HomeCatalogFeedCoordinator: publish each background catalog row as it
  arrives instead of waiting for a whole batch, and support cancelling
  in-flight remaining-catalog loads.
- HomeBillboardRuntime: add a pause for rotation/prefetch/trailer work.
- HomeViewModel: pause Home's background billboard/catalog work when
  Discover becomes active so it stops competing for the same addons.
- AndroidDiscoverDataSource: skip redundant addon-list recomputation on
  catalog-only filter changes, and move card mapping off the main thread.
- FluxaHeadlessEffectRunner/FluxaHeadlessAppRuntime: narrow the runtime
  mutex to only guard Rust engine calls, not the network effects they
  trigger, so one screen's dispatch can no longer block another's for
  the duration of a slow effect chain.
- NetworkModule: raise the shared OkHttp dispatcher's per-host/total
  request ceilings.
2026-07-28 00:17:59 +03:00
KhooLy
b29cd3ebf4 refactor: split Android UI and core boundaries 2026-07-22 18:46:31 +03:00
KhooLy
e36a43bae5 Move shared app policies into fluxa core 2026-07-22 16:56:20 +03:00
KhooLy
441427d2d1 feat: improve Nuvio import and app experience 2026-07-22 01:07:52 +03:00
KhooLy
8e0969e67e Add a top navigation bar option for tablet and TV
Add a topNavigationBar appearance toggle that, on TV and non-compact tablet
widths, moves the navigation destinations into a horizontal bar across the top
instead of the TV sidebar, tablet rail, or bottom bar. Thread the flag through
the settings contract, the Android and Apple settings data sources, the user
profile, and both language files, and render the shared FluxaTopNavBar with
focus restoration on TV.
2026-07-21 15:03:40 +03:00
KhooLy
d5f3819ebe Fix shared UI boundary violations from the CMP player overlay migration
checkSharedUiBoundary was failing on master because the July 17 move of
player overlays into shared/commonMain kept imports of data/player domain
types (Meta, Stream, Video, IntroTimestamps, UserProfile, Chapter,
MediaTrack, TorrentStreamStatus, TrailerCue). Relocate the plain value
types into the shared-safe com.fluxa.app.shared.feature.player package,
and replace the remaining domain-object usages with small UI models
(StreamSourceUiModel, SkipSegmentUiModel, NextEpisodePreviewUiModel)
mapped at the Android call sites. Also drops the dead, UserProfile-only
QuickSettingsSidebar/TrackSidebar composables that had no callers.
2026-07-20 15:37:30 +03:00
KhooLy
2e95f6b48d Expose addon display metadata from AppleStremioBridge manifest parsing
Adds name/description/logo/version/configurable to
AppleAddonManifestSnapshot so the iOS Addon Store can show real addon
metadata instead of just catalog capability flags.
2026-07-20 15:05:23 +03:00
KhooLy
e0f211e5e1 Fix two pre-existing bugs blocking shared module iOS/tvOS compilation
@JvmSuppressWildcards (TraktModels.kt) and java.util.Locale (PlayerOverlayCards.kt)
are JVM-only and don't resolve under Kotlin/Native. Neither was needed:
the wildcards annotation has no runtime effect Gson relies on, and
uppercase(Locale.ROOT) has a locale-agnostic uppercase() equivalent in
the common stdlib. Verified :shared:compileKotlinIosSimulatorArm64 now
succeeds (previously failed on master too, unrelated to plugin work).
2026-07-20 03:48:16 +03:00
KhooLy
1e776bbb8a Add per-scraper settings UI backed by onSettings() JS export
Wire executePluginScraper's updated scraper_id/scraper_settings_json
params through FluxaCoreUniFfi and PluginRepositoryManager, add
getSettingsLayout() to fetch and parse a scraper's onSettings() field
layout into small UI models, and add a gear-icon-triggered bottom
sheet (PluginScraperSettingsSheet) rendering header/info/text/select/
toggle fields, matching Nuvio's real settings schema.
2026-07-20 02:54:53 +03:00
KhooLy
102d6755f8 Add headers/quality/size/provider/seeders/peers to Stream model
fluxa_core already emitted these for plugin-sourced streams, but Gson
silently dropped them since Stream had no matching properties. Renamed
the existing getHeaders() to resolveHeaders() to avoid a JVM signature
clash with the new headers property, and merged it into the raw
behaviorHints-derived headers.
2026-07-20 02:42:52 +03:00
KhooLy
ec634a13ec Wire Nuvio-compatible plugin scraper runtime into Android app
Implements PluginHttpClient over OkHttp with SSRF guarding (PluginNetGuard),
wires the FetchPluginManifest effect, adds PluginRepositoryManager for
repository/scraper state, folds plugin-scraper streams into
StreamDiscoveryUseCase, and adds a Plugins settings screen.
2026-07-20 02:28:45 +03:00
KhooLy
fc005ba1df Consolidate raw OkHttp request/response glue into a shared HttpEffectExecutor
StremioAddonManifestClient, StremioAddonResourceClient, and the trailer
effect in FluxaAndroidHeadlessEnvironment each hand-rolled the same
Request.Builder -> execute -> read status/body -> catch pattern. One
shared executor now backs all three call sites.
2026-07-20 02:03:00 +03:00
KhooLy
f0cf10d757 Add an Upcoming home row split from Continue Watching (off by default)
New setting under Settings > Appearance > Home Screen: when enabled,
Continue Watching items whose next episode/season hasn't released yet
move into their own Upcoming row instead of sitting in Continue
Watching with nothing to resume.

Classification runs async per up-next series item (no active playback
progress): HomeContinueWatchingCoordinator resolves the specific
lastVideoId episode via the existing getSeasonEpisodes fetch, checks
its release date through the new isEpisodeReleased FFI call, and
caches the result by id:lastVideoId. Once known, it triggers a
dynamic-rows refresh so the split applies without blocking the
initial Home paint.

HomeCatalogFeedCoordinator/HomeDynamicRowsCoordinator partition the
Continue Watching list into continue_watching/upcoming HomeCategory
rows via the new isUpcoming lambda, gated by
UserProfile.upcomingRowEnabled (resolved through the Rust safe-prefs
struct like every other appearance toggle). HomeCategoryPolicy's
isContinueWatchingCategory()-gated behavior (card layout, action-row
treatment, title passthrough) now also covers the upcoming row so it
renders with the same horizontal episode-card treatment.
2026-07-20 00:06:38 +03:00
KhooLy
0e3d57b7a5 Add Liquid Glass appearance mode for the bottom navigation bar
New opt-in Appearance toggle (default off), mirroring how amoledMode
is wired through UserProfile and both settings data sources. Enabling
it swaps the nav bar's flat fill for a translucent frosted surface
with a soft top-light gradient and a hairline glass-edge border.
2026-07-19 18:59:54 +03:00
KhooLy
ca0f8e28b2 Update IMDb parents-guide API base URL to tiffara.com
imdbapi.dev moved its base URL; also drops the now-dead
ImdbApiService.create() factory since HomeViewModel already gets its
instance through Hilt DI in NetworkModule.
2026-07-19 18:48:54 +03:00
KhooLy
944484323a Add settings toggle to disable Content Warnings overlay
Adds contentWarningsEnabled to the playback settings model, defaulting
to true, and gates both the parents-guide fetch and the overlay
display in PlayerScreen behind it.
2026-07-19 18:48:46 +03:00
KhooLy
e9ccd2e358 Push actual episode number to AniList instead of watched-episode count
When marking a show COMPLETED on AniList, progress now comes from
parsing the season/episode locator off each watched episode's ID and
taking the max episode number, falling back to episodes.size only if
that parsing yields nothing. Previously it always pushed episodes.size,
which undercounts progress for shows with specials or merged
multi-season watch history.
2026-07-19 17:12:45 +03:00
KhooLy
eaef1fb4eb Thread trailer URL from detail state into DetailUiModel
Adds trailerUrl to DetailUiState and DetailUiModel so the detail
screen's data source can pass the resolved trailer through, and
adds the missing HeroPageChanged branch to CatalogHomeStore's
no-op action list.
2026-07-19 17:12:37 +03:00
KhooLy
35f9c67092 Reorganize Home/Detail appearance settings into drill-down sections
Home Screen and Detail Screen settings now split into dedicated
sub-pages (Hero Banner, Continue Watching, Navigation for Home;
Hero Banner, Episodes for Detail) instead of one long mixed page,
so a specific setting is reachable by name instead of scrolling.

Also removes the dead "Trailer on Hero" toggle in Detail Screen
settings, which persisted a value but was never read by any
consuming code — it just duplicated the real "Trailer on Detail
Hero" setting and caused exactly this kind of navigation confusion.
2026-07-19 15:58:20 +03:00
KhooLy
a858cadfd6 Migrate Android trailer resolution to fluxa-core and fix hero playback UI
- Route YouTube trailer resolution through the shared headless engine
  (headless_engine/trailer.rs) instead of a hand-rolled Innertube client
  in TrailerResolver, mirroring what fluxa-desktop already does. Wire
  the two HTTP effect kinds (fetchYoutubeTrailerWatchConfig/Player) as a
  generic passthrough in FluxaAndroidHeadlessEnvironment.
- Wire HeroTrailerVideoSurface's ExoPlayer through
  TrailerResolver.mediaDataSourceFactory() so trailer playback actually
  gets the browser UA + Range header YouTube's CDN requires — this was
  the root cause of the black-screen hero trailer.
- Fix AndroidCatalogHomeDataSource pinning the active billboard movie to
  index 0 of heroItems on every index change, which desynced the pager's
  page index from item identity and made forward swipes visually snap
  back to the previous item.
- Hide synopsis/metadata/play button and shrink the logo while a hero
  trailer plays, soften the bottom scrim so it doesn't black out most of
  the video, and add trailer subtitles (selection/normalize/parse) via
  the same fluxa-core functions fluxa-desktop uses, rendered as a
  position-synced overlay above the shrunk logo.
2026-07-19 02:30:59 +03:00
KhooLy
1039bfa5d3 Pull Stremio's library as a timestamped watchlist and reconcile it
LibraryItem never declared removed/_mtime so those fields were
silently dropped by Gson before Rust ever saw them, even though
library_state.rs already worked on raw JSON and could have used them.
Adds the fields, StremioRepository.getLibraryWatchlistWithTimestamps,
and wires it into the same mergeWatchlist reconciliation the other
providers use -- but pull-only (reconcileWatchlistPullOnly), not the
full push-capable path.

Deliberately not pushing local watchlist changes to Stremio: that
would need a "toggle membership" write that preserves whatever
progress/state fields already exist on the remote item (a
read-modify-write), which isn't built and isn't safe to guess at
without being able to verify Stremio's actual datastore PUT semantics
(full-replace vs partial-patch) against a live account.
2026-07-18 20:14:09 +03:00
KhooLy
fea466cf27 Support removing items from the AniList watchlist
pushWatchlist's AniList branch only ever called SaveMediaListEntry
(add/update) -- removing locally never propagated, since AniList's
DeleteMediaListEntry mutation needs the list-entry id, not the media
id, and nothing looked it up. Adds a lookup query (Media(id) {
mediaListEntry { id } }, which resolves against the authenticated
viewer implicitly, no separate viewer-id round trip needed) followed
by the delete mutation, and branches pushWatchlist's AniList call on
isInWatchlist same as the other providers.

Un-marking a single episode as watched is left out of this pass --
AniList tracks a show-level episode-progress counter, not discrete
per-episode watched flags, so "unwatch this one episode" doesn't map
cleanly onto its data model without risking corrupting progress.
2026-07-18 20:06:31 +03:00
KhooLy
edc69261e0 Pull AniList's watchlist (PLANNING status) and reconcile it by timestamp
The AniList GraphQL query only ever fetched status: CURRENT, so the
watchlist-add push added in the AniList phase had nothing to
reconcile against on the next sync -- pushed items would just get
re-pushed or silently diverge. Drops the CURRENT-only filter (Rust's
anilist_entries_to_sync already builds a timestamped watchlist array
from PLANNING entries, just needed the query to actually fetch them)
and wires it through the same mergeWatchlist reconciliation Trakt
uses. Generalizes reconcileTraktWatchlist -> reconcileWatchlist since
the logic is fully provider-agnostic.
2026-07-18 20:04:22 +03:00
KhooLy
32e7774056 Add Simkl watched-episode pull with timestamp reconciliation
Simkl mark-watched push existed but nothing was ever pulled back --
marking an episode watched on simkl.com never reached Fluxa. Simkl's
sync/all-items already returns per-episode watched_at when fetched
with extended=full (already the default), just unused until now.

Adds getSimklWatchedEpisodesWithTimestamps (watching+completed status,
shows+anime types) and generalizes the Trakt watched-episode
reconciliation (reconcileTraktWatched -> reconcileWatchedEpisodes)
since the merge logic and push path are provider-agnostic -- both
Trakt and Simkl now call the same function.
2026-07-18 20:00:25 +03:00
KhooLy
871bd2dcb7 Reconcile Trakt watched-episode state by timestamp, same as watchlist
Trakt's watched-state pull only ever wrote into the external mirror
table for display; local watched_episodes and Trakt's remote state
were never compared or reconciled, so un-marking an episode locally
never propagated and remote-only watched episodes never landed
locally. Trakt's watched-shows response already carries per-episode
last_watched_at, just unused until now.

Adds getWatchedEpisodesWithTimestamps (parses last_watched_at),
global (not per-series) local watched-episode/removal snapshot
queries, and reuses the mergeWatched core_invoke method (Phase 1) to
decide push vs. apply per composite episode id, mirroring the
watchlist reconciliation pattern.
2026-07-18 19:50:46 +03:00
KhooLy
0171ae2d1d Add AniList push: watchlist add and mark-watched now reach AniList
AniList integration was structurally pull-only — no mutation was ever
sent, so adding to the watchlist or marking something watched in
Fluxa never propagated back, even though the stored OAuth token
already has full read/write access (AniList's OAuth has no scope
parameter). Wires pushWatchlist/pushMarkWatched to call the new
anilistSaveMediaListEntryVariables core_invoke method and POST a
SaveMediaListEntry mutation through the existing generic
anilistGraphQl endpoint.

Watchlist removal isn't covered: AniList's delete mutation needs the
list-entry id (not the media id), which requires an extra lookup
query — left as follow-up rather than scope creep here. Same for
un-marking watched.
2026-07-18 19:28:39 +03:00
KhooLy
afddc0ec7c Support removing items from the Simkl watchlist
pushWatchlist's Simkl branch was guarded by `if (isInWatchlist && ...)`
with no else — removing an item locally never removed it on Simkl,
since only sync/add-to-list was ever called. Adds the mirroring
sync/remove-from-list endpoint and routes the push based on
isInWatchlist, matching how the Trakt branch already handles both
directions.

Durability (WorkManager-backed) for watchlist push in general —
Trakt's branch has the same gap — is left as follow-up; this only
fixes the missing Simkl remove call.
2026-07-18 19:23:46 +03:00
KhooLy
da733e5226 Make Stremio playback progress push durable via WorkManager
StremioRepository.savePlaybackProgress was fire-and-forget on the
calling coroutine at both call sites (HomePlaybackController and the
writePlaybackProgress headless effect) — an app kill mid-request
silently dropped the push with no retry, unlike Trakt/Simkl scrobble
and Nuvio's progress push which are all worker-backed.

Adds StremioPlaybackProgressPushWorker on the ProviderSyncPushWorker
base, keyed by profileId+contentId same as Nuvio's so rapid saves
coalesce via ExistingWorkPolicy.REPLACE. savePlaybackProgress now
returns Boolean instead of swallowing failure, since the worker needs
to know whether to retry.
2026-07-18 19:22:10 +03:00
KhooLy
eaa863adde Reconcile Trakt watchlist against the local watchlist by timestamp
Trakt's watchlist pull previously fed only a live, un-timestamped,
display-only union in HomeLibraryCoordinator/AndroidLibraryDataSource
(distinctBy id, recomputed on every emission, never persisted or
pushed back). This wires it into the Phase 0/1 groundwork instead:

- TraktSyncItem now parses listed_at, exposed via
  TraktSyncClient.getWatchlistWithListedAt / TraktRepository
- ExternalSyncMergeBridge wraps the mergeWatchlistTimestamped
  core_invoke call for Kotlin callers
- WatchlistManager gains a local membership snapshot (active entries +
  removal tombstones) and applyRemoteWatchlistAdd
- HomeLibraryCoordinator.load() now reconciles Trakt's remote
  watchlist against the local one after each fetch: items the merge
  says are locally newer get pushed via the existing
  ExternalSyncPushCoordinator.pushWatchlist, items remote is newer on
  get applied into the local watchlist_entries table

Simkl and Stremio are follow-up work — their fetch shapes differ
(Simkl has no true watchlist endpoint, Stremio's is a unified
datastore blob) and need separate integration passes.
2026-07-18 19:16:22 +03:00
KhooLy
89c8c90669 Add local mutation timestamps for watchlist and watched state
Lays the groundwork for timestamp-based conflict resolution against
Trakt/Simkl/Stremio (matching the pattern Nuvio already uses), by
giving watchlist entries and watched episodes an updatedAt column and
a soft-delete tombstone table so removals carry a comparable
timestamp instead of vanishing on hard delete. Progress already had
updatedAt; this brings watchlist/watched up to the same level.
2026-07-18 18:36:18 +03:00
KhooLy
62d20e23c4 Refine mobile navigation and poster actions 2026-07-18 17:56:43 +03:00
KhooLy
909425202a Improve calendar library and catalog experiences 2026-07-18 16:52:38 +03:00
KhooLy
9a75c888f5 Add continue watching source preference and Nuvio account sync improvements
Adds a per-profile continue-watching source preference (Fluxa/Stremio/
Nuvio/Trakt/Simkl/AniList), a background worker to push playback
progress to Nuvio instead of pushing inline, dedup of mark-watched
pushes against already-watched episodes, and general Nuvio account
import/merge fixes.
2026-07-18 15:02:19 +03:00
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