docs: README feature walkthrough with screenshots; css: .active wins over .nextup

This commit is contained in:
jozamudi committed 2026-08-19 20:23:39 +00:00
1 parent 29fab7ff5f
commit d6bf7e149b
7 files changed
+71 -29

No files matched your search

+70 -28
View File
@@ -1,7 +1,11 @@
# ani-web
Web front-end for the [ani-cli](https://github.com/pystardust/ani-cli)
workflow: search, browse, and stream anime in the browser via anidb.app.
Web front-end for anime streaming: search, browse, and stream anime in
the browser via anidb.app. 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
@@ -36,25 +40,61 @@ 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, full history page with per-entry delete. 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 (hamburger menu).
- Search with poster grid, detail view with synopsis + season tabs
- HLS playback (vendored hls.js), browser ABR across 1080/720/360p;
loading/buffering overlay, and a failure overlay with Retry when a
stream can't be played
- Dub/sub toggle
- Fast repeat navigation: upstream data is TTL-cached and HLS
playlists/segments are LRU-cached, so re-opening a show or re-seeking is
near-instant (first load still pays the upstream round-trips)
- Autoplays the first episode (or resumes the last watched one); if the
browser blocks autoplay, a "click to play" overlay appears
### Accounts & watch history
Accounts are created from the CLI only — there is no signup screen,
because the app is meant to sit behind your proxy on the open
internet. Each user gets a fully isolated watch history
(`server/data/history/<user>.json`): nothing is stored in the browser,
so a new device picks up your exact watch state after login. Deleting
an account (hamburger menu → delete, or `server.users rm`) kills its
session cookies immediately and removes its history.
The history page shows every entry with poster, progress, and a
per-entry delete button:
![history](docs/img/history.png)
### Home: continue watching
The home screen leads with a **continue watching** hero — the most
recently watched episode with its poster, position, and a Resume
button — followed by a **recently played** grid with progress bars and
relative timestamps.
![home](docs/img/home.png)
### Search
The header search box queries anidb.app and shows results as a poster
grid. Your last 8 searches (per user) appear in a dropdown under the
box, filter live as you type, and click to re-run. Results open the
detail view: synopsis, stats, season tabs, and filler badges.
![search](docs/img/search.png)
### Player
Playback is HLS through a backend proxy (`/stream/{token}`) — the
video CDN sends no CORS headers, so the browser can't fetch it
directly. hls.js handles ABR across 1080/720/360p.
- **Resume** — autoplay on open, resumes at the saved position; the
position is saved every ~10s
- **Dub/sub toggle**, **playback speed** (0.5–2×, remembered), and a
**hide fillers** switch (fillers are badged and skipped by
prev/next/auto-next)
- **Auto-next** — when an episode ends, a 5-second countdown offers the
next episode; Skip jumps straight to it, ✕ cancels
- **Watched state** — completed episodes are dimmed with a ✓, the next
unwatched one is highlighted
- **Failure overlay** with a Retry button if a stream can't be played,
plus loading/buffering indicators
- **Fast repeat navigation** — upstream data is TTL-cached and HLS
playlists/segments are LRU-cached, so re-opening a show or re-seeking
is near-instant (the first load still pays the upstream round-trips)
![detail](docs/img/detail.png)
## Layout
@@ -62,7 +102,8 @@ the app or CLI are rejected immediately, even with an old cookie.
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)
tests/ pytest suite: conftest.py (throwaway server), HTTP + browser tests
docs/img/ README screenshots
run.sh launch/restart the server (sets ANIWEB_SECURE=1)
SPEC.md design + the verified anidb.app data flow
```
@@ -74,12 +115,13 @@ SPEC.md design + the verified anidb.app data flow
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.
- `.venv/bin/python server/upstream.py` runs a self-check of the
fetchers (search → details → episodes → dub stream for Chainsaw Man).
- `.venv/bin/pytest tests/ -q` runs the full test suite against a
throwaway server (:8399, temp data dir — never touches the real
data). The API tests use stdlib only; the browser tests need
playwright (`uv pip install playwright && playwright install
chromium`) and are 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.