Anti-bot: cookie freshness + anonymous-jar warning

Stale or anonymous cookies make YouTube's bot-detection WORSE than no
cookies — so warn the user before downloads start captcha-failing.

cookies_freshness() parses the Netscape jar and reports:
- expires_at / expired / days_left: earliest expiry among the auth
  cookies that actually gate login (SID, SAPISID, __Secure-*PSID,
  LOGIN_INFO, …). Non-auth cookies (PREF, consent) are ignored so their
  expiry doesn't trigger false warnings.
- no_auth_cookies: the jar has cookies but NONE of the login ones — i.e.
  it's an anonymous/visitor session, not signed in. This is the worst
  case for bot-detection and a common foot-gun (exporting cookies from a
  browser that wasn't logged in to YouTube).

Surfaced in GET /api/cookies and both Settings UIs (web + desktop) with
graded severity: anonymous (red) > expired (red) > expiring-soon
(yellow) > fine.

5 tests cover expired, fresh, session/non-auth ignored, and the
anonymous-jar case. 91 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-01 15:15:51 -07:00
parent 2f711604f0
commit d098e5f6c5
3 changed files with 168 additions and 3 deletions

View file

@ -1088,7 +1088,18 @@ impl App {
self.settings_cookies_input.clear();
let (exists, n) = crate::web::cookies_status();
self.settings_cookies_status = if exists {
format!("{n} cookie(s) loaded")
let fresh = crate::web::cookies_freshness();
if fresh.no_auth_cookies {
format!("{n} cookie(s) — ⚠ ANONYMOUS (no YouTube login session); export a fresh cookies.txt while signed in — anonymous requests get captcha'd most")
} else if fresh.expired {
let ago = fresh.days_left.map(|d| d.abs()).unwrap_or(0);
format!("{n} cookie(s) — ⚠ login cookies EXPIRED ({ago}d ago); refresh them (stale cookies worsen bot-detection)")
} else if fresh.days_left.is_some_and(|d| d <= 3) {
let d = fresh.days_left.unwrap_or(0);
format!("{n} cookie(s) — ⚠ login cookies expire in {d}d; refresh soon")
} else {
format!("{n} cookie(s) loaded")
}
} else {
"no cookies.txt".to_string()
};