Release ani-cli-web 0.26.0
This commit is contained in:
@@ -2,14 +2,21 @@
|
||||
|
||||
A local web UI for a system-wide `ani-cli` install.
|
||||
|
||||
Current version: `0.12.0`
|
||||
Current version: `0.26.0`
|
||||
|
||||
## Project Layout
|
||||
|
||||
```text
|
||||
ani-cli-web/
|
||||
app.py Web server, runtime wiring, request handlers, and CLI entrypoint.
|
||||
web_templates.py Shared Search, Config, and Watchlist page markup plus page-shell helpers.
|
||||
app.py Public entrypoint, runtime wiring, watchlist domain logic, and CLI startup.
|
||||
app_support.py Shared runtime/state/config helpers plus queue and library file utilities.
|
||||
queue_jobs.py Download queue and background watchlist-refresh workers.
|
||||
http_handler.py HTTP route handling, request parsing, and remote-access guard.
|
||||
web_templates.py Shared Search, Config, and Watchlist page markup aggregation.
|
||||
search_page.py Search page template.
|
||||
config_page.py Config page template.
|
||||
watchlist_page.py Watchlist page template.
|
||||
template_helpers.py Shared page-shell navigation and sidebar brand helpers.
|
||||
ani-cli-web Launcher script.
|
||||
test_app.py Regression tests with isolated temp-state setup.
|
||||
VERSION Project version.
|
||||
@@ -53,11 +60,26 @@ The app currently includes:
|
||||
- A dedicated Config page for saved default folder, mode, and quality settings.
|
||||
- A paged SQLite-backed download queue with retry, cancel, remove, retry-failed, and clear-finished actions.
|
||||
- A paged SQLite-backed watchlist with summary cards, per-show refresh, bulk refresh, source links, and thumbnail tools.
|
||||
- Background `Refresh All` execution with status polling instead of a long blocking HTTP request.
|
||||
- Watchlist category tabs for `Watching`, `Planned`, `Finished`, and `Dropped`, with per-anime category editing.
|
||||
- AnimeSchedule-backed watchlist completion tracking that compares live sub/dub availability with the expected total episode count when known.
|
||||
- Automatic watchlist sync after successful downloads, including downloaded-vs-manual finished markers.
|
||||
- Per-show background watchlist refresh queueing so adding a show or syncing a finished download does not block the request on upstream episode, schedule, and thumbnail lookups.
|
||||
- Queued per-show watchlist refresh work now survives app restarts and resumes from rows still marked `queued`.
|
||||
- Per-show watchlist refresh queueing now also deduplicates in-flight refreshes, so the same show is not fetched twice back-to-back while one refresh is already running.
|
||||
- The watchlist page now silently refreshes itself while queued per-show refresh work is still being processed, so queued entries settle into their updated state without a manual reload.
|
||||
- The watchlist page now also avoids re-triggering the same unresolved thumbnail warm-up request on every silent poll cycle while a show's thumbnail state is unchanged.
|
||||
- Background `Refresh All` execution with status polling, parallel per-show refresh work, and no inline thumbnail downloads during the bulk job.
|
||||
- Search and Watchlist polling now use a shared serial browser polling helper so slow responses do not cause overlapping poll requests to pile up in the background.
|
||||
- Controlled shutdown and runtime reset now cancel active download workers when waiting for teardown, reducing the chance of orphaned background downloads outliving the web process.
|
||||
- Persistent watchlist refresh history that survives restarts and marks interrupted bulk refresh jobs clearly.
|
||||
- Local thumbnail caching plus manual thumbnail upload when automatic cover lookup fails.
|
||||
- A refreshed glassy dark UI inspired by the Tsuki frontend design language.
|
||||
- Shared page templates moved out of `app.py` plus broader regression tests for higher-risk queue, watchlist, and handler flows.
|
||||
- Shared page templates and backend services split into dedicated modules while keeping `app.py` and `web_templates.py` as stable public aggregation points.
|
||||
- Broader regression coverage for higher-risk queue, watchlist, remote-access, and handler flows.
|
||||
- Search and watchlist requests guard against stale out-of-order async responses during rapid repeated actions.
|
||||
- Search-page queue polling also guards against stale out-of-order refresh responses now.
|
||||
- Queue log writes are batched during active downloads to reduce SQLite churn on noisy jobs.
|
||||
- Queue persistence now stores core job metadata and logs in structured SQLite columns instead of treating the whole job as one large JSON blob.
|
||||
- Project-local runtime state under `.ani-cli-web/`.
|
||||
|
||||
## Search Page
|
||||
@@ -97,14 +119,24 @@ If you intentionally want LAN or remote access, set:
|
||||
|
||||
```sh
|
||||
ANI_CLI_WEB_ALLOW_REMOTE=1
|
||||
ANI_CLI_WEB_REMOTE_TOKEN=choose-a-long-random-token
|
||||
```
|
||||
|
||||
This keeps the queue, config, and watchlist mutation APIs safer by default when the app is rebound away from localhost.
|
||||
Non-local clients must then send either:
|
||||
|
||||
- `Authorization: Bearer <token>`
|
||||
- `X-AniCli-Web-Token: <token>`
|
||||
|
||||
When the app is placed behind a loopback reverse proxy, forwarded external client IP headers such as `X-Forwarded-For` are also treated as non-local, so proxied internet or LAN traffic still needs the token.
|
||||
|
||||
This keeps the queue, config, and watchlist APIs safer when the app is intentionally exposed beyond localhost.
|
||||
|
||||
## Download Queue
|
||||
|
||||
The queue is stored in SQLite and shown 10 jobs at a time.
|
||||
|
||||
Core queue fields and logs are now stored in structured SQLite columns, while JSON payload storage is kept only for extra fields that do not have dedicated columns yet.
|
||||
|
||||
Queue actions currently available:
|
||||
|
||||
- `Retry` for finished or failed jobs.
|
||||
@@ -121,22 +153,43 @@ ANI_CLI_DOWNLOAD_DIR/anime_name/Season 01/anime_name - S01E01.mp4
|
||||
|
||||
## Watchlist
|
||||
|
||||
The watchlist stores one row per show in SQLite and keeps separate `sub` and `dub` episode counts.
|
||||
The watchlist stores one row per show in SQLite, keeps separate `sub` and `dub` episode counts, and assigns each show to one of four categories:
|
||||
|
||||
- `Watching`
|
||||
- `Planned`
|
||||
- `Finished`
|
||||
- `Dropped`
|
||||
|
||||
You can add entries in two ways:
|
||||
|
||||
1. From the Search page with `Add to watchlist`.
|
||||
2. Manually on `/watchlist` with a title and AllAnime `show_id`.
|
||||
1. From the Search page with `Add to watchlist`, choosing the category first.
|
||||
2. Manually on `/watchlist` with a title, AllAnime `show_id`, and category.
|
||||
|
||||
Watchlist features:
|
||||
|
||||
1. `Refresh` updates one tracked show.
|
||||
2. `Refresh All` runs in the background, rejects duplicate queueing while a refresh is already pending or running, and preserves interrupted job status across restarts.
|
||||
2. `Refresh All` runs in the background, rejects duplicate queueing while a refresh is already pending or running, preserves interrupted job status across restarts, and refreshes shows in parallel without doing inline thumbnail downloads for every entry.
|
||||
3. `Open` jumps back to the Search page with the title pre-filled.
|
||||
4. Clicking the anime title opens the AllManga source page at `bangumi/<show_id>` in a new tab.
|
||||
5. `Remove` deletes the watchlist entry and clears its cached poster file.
|
||||
6. Poster cards use a compact multi-column grid with pagination at 30 cards per page.
|
||||
7. Summary cards show tracked shows plus total `sub` and `dub` episode counts.
|
||||
8. Episode pills show `current / total` progress when AnimeSchedule exposes the planned episode count.
|
||||
9. `Sub` and `Dub` pills turn green and gain `Sub ready` or `Dub ready` tags when that release track appears fully finished and ready for download.
|
||||
10. Each category has its own tab on the watchlist page.
|
||||
11. Every card includes an inline category selector so you can move tracked anime between tabs at any time.
|
||||
12. Finished shows display a small check badge: green means the anime was downloaded through the app, gray means it was marked finished manually.
|
||||
|
||||
### Download Sync
|
||||
|
||||
Successful downloads now feed back into the watchlist automatically.
|
||||
|
||||
Current behavior:
|
||||
|
||||
- If the anime is already on the watchlist, a successful download marks it as downloaded and keeps its current category.
|
||||
- If the anime is not yet on the watchlist, `ani-cli-web` creates a new watchlist entry in `Finished` after the download succeeds.
|
||||
- Those add and download-sync paths now queue a background refresh for episode counts, AnimeSchedule metadata, and thumbnails instead of waiting for all upstream fetches before returning.
|
||||
- Existing watchlist entries from older versions are migrated into the `Watching` category automatically.
|
||||
|
||||
### Thumbnail Behavior
|
||||
|
||||
@@ -147,10 +200,13 @@ Current thumbnail behavior:
|
||||
- Cached thumbnails render from the local project cache when available.
|
||||
- The page adds a fresh nonce to thumbnail URLs to avoid stale browser-cached misses.
|
||||
- Missing visible covers are warmed up in small batches instead of one request per card all at once.
|
||||
- The page only retries an unresolved cover warm-up once per thumbnail state change during the current browser session, so silent watchlist polling does not keep hammering the same missing poster.
|
||||
- If a show disappears while a thumbnail warm-up batch is running, that entry is skipped cleanly instead of failing the whole batch request.
|
||||
- Thumbnail image tags only hit the local route for already-cached files, so opening the watchlist no longer triggers a per-card remote lookup burst.
|
||||
- AnimeSchedule is tried first for cover lookup.
|
||||
- If a season-labeled title fails as-is, lookup retries with simplified base-title queries.
|
||||
- AniDB is used as a fallback when AnimeSchedule does not resolve a cover.
|
||||
- AniDB fallback reuses the cached parsed title list directly to keep repeated lookups lighter.
|
||||
- Failed fetch attempts no longer refresh the retry window, so a bad lookup does not get stuck in an immediate repeat-404 state.
|
||||
|
||||
Per-card thumbnail tools:
|
||||
@@ -161,6 +217,22 @@ Per-card thumbnail tools:
|
||||
|
||||
Manual uploads are saved into the same local thumbnail cache used by automatic cover downloads.
|
||||
|
||||
### Completion Detection
|
||||
|
||||
Watchlist refresh still uses AllAnime, the same upstream source that `ani-cli` uses, to count which `sub` and `dub` episodes are actually available right now.
|
||||
|
||||
To decide whether a release track looks complete, `ani-cli-web` also checks AnimeSchedule for:
|
||||
|
||||
- the anime's expected total episode count
|
||||
- the upstream airing status such as `Finished` or `Ongoing`
|
||||
|
||||
When AnimeSchedule reports a known total and `Finished`, the watchlist compares that total with the live AllAnime `sub` and `dub` counts:
|
||||
|
||||
- `Sub` becomes ready when available sub episodes reach the expected total.
|
||||
- `Dub` becomes ready when available dub episodes reach the expected total.
|
||||
|
||||
If AnimeSchedule does not know the total yet, the watchlist keeps showing the plain available-count value without a completion highlight.
|
||||
|
||||
## Runtime State
|
||||
|
||||
All runtime state is stored under:
|
||||
@@ -193,6 +265,7 @@ Environment variables:
|
||||
- `ANI_CLI_WEB_HOST`: server host, default `127.0.0.1`.
|
||||
- `ANI_CLI_WEB_PORT`: server port, default `8421`.
|
||||
- `ANI_CLI_WEB_ALLOW_REMOTE`: allow non-loopback clients when set to `1`, `true`, `yes`, or `on`.
|
||||
- `ANI_CLI_WEB_REMOTE_TOKEN`: required shared secret for non-loopback clients when remote access is enabled.
|
||||
- `ANI_CLI_WEB_DEBUG`: enable debug logging when set to `1`, `true`, `yes`, or `on`.
|
||||
- `ANI_CLI_WEB_STATE_ROOT`: override the project-local state directory path; useful for isolated test runs or custom storage locations.
|
||||
|
||||
@@ -201,6 +274,9 @@ Saved defaults from the UI are written into the project-local `config.json`.
|
||||
## Notes
|
||||
|
||||
- Importing `app.py` no longer boots the runtime worker threads automatically; runtime setup is deferred until startup or first use.
|
||||
- Importing `app_support.py` no longer migrates or creates runtime state on its own; migrations now happen during runtime bootstrap.
|
||||
- Cheap routes such as the page shells, `/api/version`, `/api/dependencies`, and `/api/config` do not need to boot the full runtime before responding.
|
||||
- The CLI startup path itself also stays lazy now; runtime bootstrap happens when the first runtime-backed request arrives.
|
||||
- Watchlist and queue data are stored in the same project-local SQLite database file, `state.sqlite3`.
|
||||
- Runtime folders such as `.ani-cli-web/`, `downloads/`, and Python bytecode cache directories at any depth are ignored by git.
|
||||
- Focused regression tests live in `test_app.py`, run against an isolated temporary state root, and can be started with `python3 -m unittest test_app.py`.
|
||||
|
||||
Reference in New Issue
Block a user