# ani-web Web front-end for anime streaming: search, browse, and stream anime in the browser (hianime.at primary; anidb.app and anikototv.to as fallbacks). Multi-user, with per-user watch history and HLS playback proxied through the backend. Tuned for phone as much as desktop. ![login](docs/img/login.png) ## Quick start ```sh uv venv .venv uv pip install --python .venv/bin/python -r requirements.txt .venv/bin/python -m server.users add # create your account (one-time) ./run.sh ``` Open it through your HTTPS proxy (or from the host itself — browsers treat localhost as trusted). `run.sh` restarts cleanly if the server is already running; logs go to `server.log`. Accounts are CLI-created only (no self-signup): ```sh .venv/bin/python -m server.users add # add user (prompted password) .venv/bin/python -m server.users rm # remove user + their history .venv/bin/python -m server.users list # list users ``` Passwords are PBKDF2-SHA256 in `server/data/aniweb.db` (SQLite, 0600); sessions are signed 30-day `HttpOnly` `SameSite=Lax` cookies, `Secure` via `run.sh`, so remote plain-HTTP can't hold a session — the HTTPS proxy is the way in. See [SPEC.md](SPEC.md) for the rest. ## Features - **History** — server-side and per-user: a new device picks up your exact watch state after login. Entries show poster, progress, and a per-entry delete. ![history](docs/img/history.png) - **Home** — continue-watching hero (poster, position, Resume) plus a recently-played grid with progress bars. ![home](docs/img/home.png) - **Search** — poster-grid results from the active source; your last 8 searches (per user) in a live-filtering dropdown. Results open the detail view (synopsis, season tabs, filler badges). ![search](docs/img/search.png) - **Player** — resume at the saved position, skip intro (MAL timings), dub/sub toggle, playback speed, hide fillers, auto-next with a 5-second countdown, ✓ on completed episodes. Playback goes through a backend HLS proxy (the CDN sends no CORS headers); segments are pulled across parallel byte-range lanes so a slow connection doesn't stall. ![detail](docs/img/detail.png) - **Status** — the header pill shows upstream/CDN health (dots + label); tap opens a panel with detail + the ~1h throughput trend. A failure overlay names the CDN when it's the likely culprit. - **Stats** — `?stats`: watch minutes, episodes, streaks, sub/dub split, weekday/per-day heatmaps, top-10 shows, top searches (30–365 day windows). ## Layout ``` server/ FastAPI app (app.py), source providers (providers/: hianime, anidb, anikoto) over shared HTTP + dispatch (upstream.py) static/ front-end: index.html, login.html, app.js, hls.min.js (vendored) tests/ pytest suite: hermetic default, live browser e2e opt-in docs/img/ README screenshots run.sh launch/restart the server (sets ANIWEB_SECURE=1) SPEC.md design, verified anidb.app data flow, security + runbook notes ``` ## Dev - `.venv/bin/pytest tests/ -q` — hermetic suite against a throwaway server (never touches real data). Live-upstream browser e2e is opt-in: `ANIWEB_LIVE_TESTS=1 .venv/bin/pytest tests/ -q` (needs playwright chromium). - `ANI_SOURCE=anidb .venv/bin/python -m server.upstream` — live self-check of the anidb fetchers (must be the `-m` form; plain script execution breaks the providers' relative imports). - Everything else (Cloudflare/impersonation, HLS proxy, CDN probe, auth hardening, cache-busting): [SPEC.md](SPEC.md).