- pill: two host dots -> ONE consolidated state (down > slow > up) —
the at-a-glance question is 'can I watch?', which is one state;
per-host detail belongs in the tap panel
- panel: dashed slow-threshold line in the CDN sparkline (drawn at the
server's real threshold, not a client copy) + origin up/down step
sparkline so 'how long has the origin been down' is visible
- server: origin probe keeps a 20-sample hist like the CDN probe;
DEGRADED threshold is now ANIWEB_CDN_KBPS (default 40) and is
returned in /api/cdn so the UI draws the line at the live value
- tests: pill assertions switched to consolidated states; fixtures
carry origin hist + threshold; assert both sparklines + threshold line
- docs: README + SPEC synced; preview on :8322 restarted (pid 44382)
- /stream/{token}: tokens are plain base64; allowlist https://hls.anidb.app
so a session can't use the proxy for arbitrary URLs (new test)
- X-Aniweb:1 required on POST/DELETE except /login — SameSite=Lax is
still sent on cross-site form POSTs, which could hit /account/delete
(new test; SPA + tests send the header)
- tmp+os.replace writes for users/history/searches: a crash mid-write
could corrupt the JSON -> load() returned {} -> silent data loss
- resume positions now read from full history (?all=1), not top-5
- pagehide bound once; savePos reads currentEp (no stale closures)
- account delete also removes the searches file; episodes TTL 5m
- ani-web.service: supervised restart (not installed; see header)
- gitignore pi-session dumps + .pytest_cache
- POST/GET /api/history: JSON file in data/history.json (50 entries
kept, top 5 shown on home), upsert per (anime, episode)
- watch position saved every ~10s of playback + on pause/unload
- resume: opening an anime seeks to the saved position unless
>=95% through (then starts fresh); position shown on home items
- drops localStorage; server is the source of truth (verified:
fresh watch -> new session resumes at saved pos -> home lists it)