Commit Graph

8 Commits

Author SHA1 Message Date
Arkadiusz Fal
fac297e4d6 Cache and prewarm Invidious proxy auto-detection
The proxy auto-detect path (when proxiesVideos is off) HEADed a
googlevideo URL with a 5 s timeout on every video. The verdict is a
property of the network, not the video, so the cost was paid for no
reason on videos 2..N. On a network where the CDN is blocked the full
5 s timeout was added to playback startup every single time.

Two changes:

1) ProxyDetectionCache (actor, per-instance, 10 min TTL). First miss
   pays the HEAD once and caches the verdict; subsequent videos hit
   the cache synchronously. Concurrent callers share one in-flight
   probe. The last-seen sample CDN URL is retained so future probes
   don't need a fresh URL from the current video.

2) PlayerService kicks off InvidiousAPI.prewarmProxyDetection() in
   parallel with the videoWith... API call. By the time streams come
   back, the verdict is usually already cached and proxyStreamsIfNeeded
   is a sync lookup. Cheap when there's nothing to prewarm.

Cache invalidation:
- on InstancesManager.update (URL change, proxy toggle flip)
- on InstancesManager.remove
- TTL covers the network-change case for now (no NWPathMonitor yet)
2026-05-04 08:04:55 +02:00
Arkadiusz Fal
93240b4314 Wire Yattee Server playback through /proxy/relay when proxiesVideos is on
The Sources -> Edit Source -> Proxy toggle now renders for Yattee
Server entries (supportsVideoProxying gains .yatteeServer). When the
toggle is on, playback fetches go through
videoWithProxyStreamsAndCaptionsAndStoryboards with mode .relay, so
the server returns signed /proxy/relay URLs (byte-relay, supports
HTTP Range, no on-disk caching). Downloads keep going through mode
.download so the server-side /proxy/fast/ flow continues to cache
files for repeat use.

InvidiousAPI.proxyStreamsIfNeeded early-returns for .yatteeServer
since proxying is now done at fetch time via ?proxy=true rather than
client-side host rewriting.
2026-05-04 08:01:51 +02:00
Arkadiusz Fal
b9a6d76ab3 Fix original audio detection so dubbed tracks don't play by default
parseAudioInfo() returned isOriginal=false for all streams when the
audioTrack object was present in the API response, preventing xtags
parsing from correctly identifying original tracks. This caused the
player to fall through to codec/bitrate sorting, often picking a
locale dub (e.g. Polish) instead of the original English audio.

Now determines isOriginal from both the audioTrack displayName
("original" keyword) and URL xtags (acont=original) for robustness.
Also adds isDefault to InvidiousAudioTrack for future use.
2026-04-18 20:38:01 +02:00
Arkadiusz Fal
eefd49f743 Fix three basic-auth regressions surfaced by end-to-end testing
- InstanceDetector: a single 401 from one probe was over-eagerly concluded
  as "credentials invalid" / "credentials required". On instances behind a
  reverse proxy where one probe path (e.g. Yattee Server's /info) hits a
  same-origin redirect, iOS URLSession strips the Authorization header on
  the redirect and the request 401s even with valid credentials. Track 401s
  across all probes and only conclude basicAuthRequired/basicAuthInvalid
  when no probe matched and at least one returned 401.

- InstanceLoginView: the Invidious/Piped login flow constructed an API
  client backed by the shared appEnvironment.httpClient, which has no
  per-instance basic-auth headers. For instances behind a reverse proxy,
  the login POST 401d before reaching the upstream login endpoint. Build a
  per-instance HTTPClient with the basic-auth Authorization header baked in
  via setDefaultHeaders, mirroring ContentService.httpClientWithBasicAuth.

- InvidiousAPI.login: the login function constructs its own URLSession (to
  capture Set-Cookie via a redirect-blocking delegate), so it never
  inherits headers from the injected httpClient. Add an optional
  extraHeaders parameter and have InstanceLoginView pass the basic-auth
  header through when present. PipedAPI.login uses httpClient.fetch and
  inherits defaultHeaders correctly, so no change is needed there.
2026-04-18 20:38:00 +02:00
Arkadiusz Fal
6298b38cba Add video proxy support with live toggle for Invidious/Piped instances
Adds a "Proxy videos" toggle in instance settings that routes video
streams through the instance instead of connecting directly to YouTube
CDN. Includes auto-detection of 403 blocks and live re-application of
proxy settings without requiring app restart or video reload.
2026-04-18 20:38:00 +02:00
Arkadiusz Fal
49a44da90e Fix Invidious login failing for passwords with special characters
Use URLComponents/URLQueryItem for standard form-URL encoding instead
of manual percent-encoding with CharacterSet.alphanumerics, which
included non-ASCII Unicode letters and had an unsafe raw-value fallback.
2026-04-18 20:37:25 +02:00
Arkadiusz Fal
ecaf553326 Fix incomplete playlist loading by paginating through all pages
Playlists only loaded the first page of videos. Add full pagination for
both Invidious and Piped playlist endpoints (public and authenticated).
Deduplicate Invidious results by playlist index to handle its overlapping
page windows. Also fix URL encoding in Invidious login to use strict
form-encoding charset.
2026-04-18 20:37:25 +02:00
Arkadiusz Fal
100df744d9 Yattee v2 rewrite 2026-04-18 20:37:24 +02:00