The device-authorization poll for debrid providers (TorBox/Premiumize)
treated any thrown redeem request as a fatal error and stopped polling,
showing "Could not start sign-in". The only thrown error it tolerated was
iOS request cancellation (isCancelledHttpRequest matched NSURLErrorDomain
/ code=-999); on Android a thrown request fell through to Failed(null)
and terminated the loop.
On aggressive ROMs (OxygenOS, MIUI, ...) the OS severs a backgrounded
app's sockets and fails its DNS while the user is in the browser approving
the code, so the in-flight poll throws UnknownHostException/IOException.
That terminated the login even though approval succeeded — reproduced on
OnePlus 13 / Android 16 (issue #1409): browser shows "logged in", app shows
"Could not start sign-in".
Treat a thrown redeem as Pending and keep polling instead of failing. To
avoid polling forever when the failure is persistent (airplane mode,
captive portal) rather than a brief background blip, a monotonic-clock
watchdog (DEVICE_AUTH_MAX_POLL_DURATION) bounds the throwing path and
surfaces failure once it elapses; a response-bearing expiry still stops
the loop earlier via the Expired branch. This also removes the now-unused,
iOS-only isCancelledHttpRequest helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SkipIntroRepository returned the result of the first provider that had
any data, so a provider with only a partial result (e.g. AniSkip with
just the intro) shadowed another provider that had the complete set
(e.g. IntroDB with intro + outro), silently dropping the outro.
Query IntroDB, Anime-Skip and AniSkip and merge their results by segment
category (opening / ending / recap), filling each category from the
highest-priority provider that has it (IntroDB, then Anime-Skip, then
AniSkip). The most complete set is always used and no provider shadows
another in either direction. The skipIntroEnabled gate and caching are
unchanged.
The skip lookup ran on the caller's composition-bound coroutine scope,
so navigating into the external player cancelled the in-flight network
request before it returned (and the cancelled lookup cached an empty
result, poisoning retries). Run resolution on a dedicated app scope so
it completes regardless of UI lifecycle.
Adds an opt-in External Player setting that hands resolved intro and
outro skip timestamps to external players via a skip_segments intent
extra, so a supporting player can auto-skip the opening and ending the
same way the internal player already does.
Resolution reuses the existing SkipIntroRepository (IntroDB, ARM,
AniSkip, Anime-Skip), so there is no new API, key, provider, or
dependency. The toggle is off by default and sits under Forward
Subtitles in the External Player settings. Resolution is bounded by a
timeout and only runs when the setting is on; players that do not read
the extra ignore it.
The LifecycleEventObserver was only capturing playWhenReady at the
time the effect ran. When ON_START fired later, it would use the old
value and resume playback even if the user had paused before leaving
the app.
Use rememberUpdatedState so we always read the live value.
Fixes#1080
When a metadata addon (e.g. aiometadata) supplies a genuine IMDb rating,
the TMDB enrichment on the details page was overwriting it with TMDB's
vote_average because the copy() preferred enrichment.rating over the
existing value. This made the IMDb placeholder display the TMDB score on
mobile, even though the addon provided IMDb ratings (the TV version was
already correct).
Invert the precedence so the addon-provided imdbRating is kept and TMDB's
vote_average is only used as a fallback when no rating was supplied.
Fixes#1377