jozamudi 2efb537edc home polish: continue-watching hero, progress bars, posters, time-ago
- showHome(): most recent entry renders as a hero card (poster,
  episode/lang/position, progress bar, Resume); the rest become
  mini poster cards with progress + relative timestamp
- history entries now persist dur (video.duration) and poster;
  /api/anime returns poster (parsed from the details page)
- old entries without dur render without a bar (backward compatible)
- browser test asserts the seeded entry, not a section header that
  only existed via a cross-test side effect
2026-08-19 17:01:04 +00:00
2026-08-19 15:54:50 +00:00

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.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. With ANIWEB_SECURE=1 (set by run.sh), the session cookie is Secure, so remote plain-HTTP access can't hold a session — only the HTTPS proxy works. A legacy credentials.json password, if any, is migrated to user admin on first start.

Account management (no self-signup by design):

.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

Security: passwords are PBKDF2-SHA256 (210k iters, per-user salts) in server/data/users.json (0600). Sessions are signed 30-day HttpOnly SameSite=Lax cookies; run.sh sets ANIWEB_SECURE=1 so cookies are Secure and only work through your HTTPS proxy. Accounts deleted via the app or CLI are rejected immediately, even with an old cookie.

Features

  • Per-user accounts (CLI-created, no self-signup): 30-day signed session cookie, 5 wrong attempts → 15-min lockout per user. PBKDF2-SHA256 hashes in server/data/users.json (0600).
  • Per-user watch history (server/data/history/<user>.json): position saved every ~10s, resume where you left off, top-5 "recently played" on the home page. Nothing stored in the browser — new devices pick up your watch state after login, isolated per account. In-header chip shows the user, with logout + delete-account.
  • 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

Layout

server/   FastAPI app (app.py), anidb.app fetchers (upstream.py),
          accounts (users.py). upstream.py is the ONLY Cloudflare-facing code
static/   front-end: index.html, login.html, app.js, style.css, hls.min.js (vendored)
tests/    test suite (run.py orchestrator, HTTP + browser tests)
run.sh    launch/restart the server (sets ANIWEB_SECURE=1)
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).
  • python tests/run.py runs the full test suite against a throwaway server (:8399, temp data dir — never touches the real data). Browser tests need playwright (pip install playwright && playwright install chromium); they're skipped if absent.
  • Episode titles are not available: anidb's episode API only returns {id, number, number2, filler}. Adding titles needs a free MyAnimeList API key.
S
Description
No description provided
Readme
8 MiB
0 Stars 1 Watchers 0 Forks
Languages
Python 65.2%
JavaScript 25.9%
CSS 5.9%
HTML 2.1%
Shell 0.9%