server/app.py DONE_RATIO is injected into index.html like {{VERSION}}, app.js
reads it as window.DONE_RATIO (fallback = same default). Change the threshold
in exactly one place now.
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.
Quick start
uv venv .venv
uv pip install --python .venv/bin/python -r requirements.txt
.venv/bin/python -m server.users add <you> # create your account (one-time)
./run.sh
Open it through your HTTPS proxy (or http://localhost:8321 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):
.venv/bin/python -m server.users add <name> # add user (prompted password)
.venv/bin/python -m server.users rm <name> # 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 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.
-
Home — continue-watching hero (poster, position, Resume) plus a recently-played grid with progress bars.
-
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).
-
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.
-
Status — the header pill shows one consolidated state — up / slow (KB/s) / down — as a single dot; tap opens a panel with the per-host breakdown, the ~1h CDN trend (dashed slow-threshold line), and origin up/down history. 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-mform; plain script execution breaks the providers' relative imports).- Everything else (Cloudflare/impersonation, HLS proxy, CDN probe, auth hardening, cache-busting): SPEC.md.




