Files
ani-web/README.md
T
jozamudi 76c45a8f29 single-user login screen
- server/credentials.py: PBKDF2 password + session secret, 0600 json,
  one-time setup command (server refuses to start without it)
- app.py: signed 30-day cookie (itsdangerous), middleware 401s API /
  redirects pages to /login?next=, 5-fail/15-min lockout, /logout
- static/login.html: dark-themed form, inline error display
- app.js: api() 401 -> redirect to /login preserving current URL
- docs: README quick start + features, SPEC auth section
2026-08-19 15:54:50 +00:00

2.1 KiB

ani-web

Web front-end for the ani-cli workflow: search, browse, and stream anime in the browser via anidb.app.

Quick start

uv venv .venv
uv pip install --python .venv/bin/python -r requirements.txt
.venv/bin/python -m server.credentials   # set the login password (one-time)
./run.sh

Open http://localhost:8321. run.sh restarts cleanly if the server is already running; logs go to server.log. The server refuses to start without a password.

Features

  • Login: single shared password, 30-day session cookie, 5 wrong attempts → 15-min lockout. PBKDF2-SHA256 hash + session secret in server/data/credentials.json (0600).
  • Search with poster grid, detail view with synopsis + season tabs
  • HLS playback (vendored hls.js), browser ABR across 1080/720/360p
  • Dub/sub toggle
  • Autoplays the first episode (or resumes the last watched one); if the browser blocks autoplay, a "click to play" overlay appears
  • Server-persisted watch history (server/data/history.json): watch position saved every ~10s, resume where you left off, top-5 "recently played" on the home page — shared across browsers and devices

Layout

server/   FastAPI app (app.py) + anidb.app fetchers (upstream.py)
          upstream.py is the ONLY Cloudflare-facing code
static/   front-end: index.html, app.js, style.css, hls.min.js (vendored)
run.sh    launch/restart the server
SPEC.md   design + the verified anidb.app data flow

Dev notes

  • anidb.app is behind Cloudflare; all upstream requests use curl_cffi Chrome TLS impersonation. If that ever stops working, everything is isolated in server/upstream.py.
  • The video CDN sends no CORS headers, so playback goes through the backend HLS proxy (/stream/{token}), which rewrites playlist URIs.
  • python server/upstream.py runs a self-check of the fetchers (search → details → episodes → dub stream for Chainsaw Man).
  • Episode titles are not available: anidb's episode API only returns {id, number, number2, filler}. Adding titles needs a free MyAnimeList API key.