Files
ani-cli-web/README.md
T
2026-05-22 23:17:00 +02:00

353 lines
9.1 KiB
Markdown

# ani-cli web
Small local web UI for a system-wide `ani-cli` install.
Current version: `0.35.0`
## Project Layout
```text
ani-cli-web/
app.py Main entrypoint and runtime wiring.
app_support.py Shared config, state, and helper utilities.
Dockerfile Container image build.
docker-compose.yaml Example Compose service.
queue_jobs.py Download queue and watchlist refresh workers.
watchlist_identity.py Download-to-watchlist identity matching.
title_matching.py Shared title normalization and matching.
http_handler.py HTTP routing and access control.
web_templates.py Shared page aggregation.
search_page.py Search page template.
queue_page.py Queue page template.
config_page.py Config page template.
watchlist_page.py Watchlist page template.
docker-entrypoint.sh Container startup wrapper.
ani-cli-web Launcher script.
test_app.py Regression tests.
VERSION Project version.
CHANGELOG.md Change history.
README.md Project documentation.
```
## Quick Start
Run the app:
```sh
./ani-cli-web
```
Run with debug logging:
```sh
./ani-cli-web --debug
```
Open:
```text
http://127.0.0.1:8421/
```
Default bind address is `127.0.0.1:8421`.
## Docker
The image installs `ani-cli` system-wide, downloads the latest upstream `yt-dlp` release binary from GitHub, and clones `ani-cli-web` into `/app` during build.
Default build args:
```text
ANI_CLI=https://github.com/pystardust/ani-cli.git
ANI_CLI_BRANCH=master
ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git
```
Build:
```sh
docker build -t ani-cli-web .
```
Build with a custom `ani-cli` branch:
```sh
docker build \
--build-arg ANI_CLI_BRANCH=my-branch \
--build-arg ANI_CLI=https://github.com/pystardust/ani-cli.git \
--build-arg ANI_CLI_WEB=https://gitea.coreplay.eu/Dymas/ani-cli-web.git \
-t ani-cli-web .
```
Run:
```sh
docker run --rm \
-p 8421:8421 \
-e UPDATE_ON_START=true \
-e USER_UID="$(id -u)" \
-e USER_GID="$(id -g)" \
-v "$(pwd)/downloads:/downloads" \
-v "$(pwd)/.ani-cli-web:/app/.ani-cli-web" \
ani-cli-web
```
Compose:
```sh
docker compose up --build -d
```
Use host ownership for mounted downloads:
```sh
USER_UID="$(id -u)" USER_GID="$(id -g)" docker compose up --build -d
```
Container notes:
- `ani-cli` is installed at `/usr/local/bin/ani-cli`.
- `yt-dlp` is installed at `/usr/local/bin/yt-dlp` from the latest GitHub release during build.
- `ani-cli-web` runs from `/app`.
- Downloads go to `/downloads`.
- App state lives in `/app/.ani-cli-web`.
- The container binds `0.0.0.0:8421`.
- `UPDATE_ON_START=true` runs `sudo ani-cli --update` before startup.
- `USER_UID` and `USER_GID` keep mounted files from being owned by `root`.
## What It Does
- Search AllAnime in `sub` or `dub`.
- Queue downloads with editable folder, quality, season, and episode range.
- Save defaults on `/config`.
- Track shows on `/watchlist`.
- Refresh watchlist episode counts in the background.
- Schedule periodic watchlist refresh jobs.
- Send Discord webhook notifications for refresh discoveries and failures.
- Send Discord webhook notifications for completed downloads.
- Cache watchlist thumbnails locally.
- Sync successful downloads back into the watchlist.
- Keep queue and watchlist data in SQLite under `.ani-cli-web/`.
## Pages
### Search Page
Path: `/`
Use it to:
1. Search for an anime.
2. Load episodes.
3. Adjust folder, quality, season, and episode range.
4. Add the show to the queue or watchlist.
Highlights:
- Search stays focused on finding shows, loading episodes, and queueing new downloads.
- Added-to-queue notices now point to the dedicated Queue page for progress tracking.
- Search request guards still avoid stale responses overwriting newer results.
### Queue Page
Path: `/queue`
Use it to:
1. Monitor active, pending, failed, and finished download jobs.
2. Inspect recent per-job logs with newest lines first.
3. Retry failed jobs.
4. Remove finished jobs.
5. Cancel running or pending jobs.
Highlights:
- Automatic queue polling every 2.5 seconds.
- Dedicated queue pagination and cleanup controls.
- Search and queue workflows are now separated into distinct pages.
### Config Page
Path: `/config`
Use it to set:
1. Default download folder.
2. Default `sub` or `dub` mode.
3. Default quality.
4. Scheduled watchlist refresh enable or disable.
5. Scheduled refresh period in minutes.
6. Delay between shows during `Refresh All`.
7. Discord webhook URL.
8. Discord notification events plus a test action.
Saved settings are written to `.ani-cli-web/config.json`.
Discord notification events:
- Download completed.
- Refresh finished and found new episodes.
- Refresh failed or finished with per-show refresh errors.
- Refresh interrupted by restart or shutdown.
- Unexpected runtime errors.
### Watchlist
Path: `/watchlist`
Categories:
- `Watching`
- `Planned`
- `Finished`
- `Dropped`
Ways to add shows:
1. From the Search page.
2. Manually on `/watchlist`.
Main behavior:
1. `Refresh` updates one show.
2. `Refresh All` runs in the background.
3. `Open` jumps back to Search.
4. Clicking the title opens the source page.
5. `Remove` deletes the entry and cached poster.
6. Each card has an inline category selector.
7. Summary cards show total tracked shows plus `sub` and `dub` counts.
8. Pagination shows 30 cards per page.
Status behavior:
- `Sub` and `Dub` pills turn green when that track looks complete.
- Cards with both `sub` and `dub` fully ready get a thin green border.
- Finished cards show a small check badge.
- Green check means downloaded through the app.
- Gray check means finished manually.
Download sync:
- Existing watchlist entries move to `Finished` when a download succeeds.
- Missing entries are added to `Finished`.
- If ani-cli resolves a different `show_id`, the app can reconcile a unique title match instead of creating a duplicate finished row.
Thumbnail behavior:
- Thumbnails are served from the local `/api/watchlist/thumb/...` route.
- Cached thumbnails are reused.
- Missing covers are warmed in small batches.
- Failed lookups back off before retrying.
- You can force `Reload` or use `Upload` when no thumbnail exists.
Refresh behavior:
- Per-show refreshes are queued in the background.
- Queued refresh state survives restarts.
- Duplicate refresh queueing is avoided.
- Scheduled refresh runs log start, stop, success, and failure to stdout.
- `Refresh All` works one show at a time with a configurable delay.
- When a Discord webhook is configured, new-episode refresh results include one notification card per anime with sub and dub counts and a cached thumbnail attachment when available.
## Access Guard
By default the app only serves loopback clients such as `127.0.0.1` and `::1`.
To allow LAN or remote access:
```sh
ANI_CLI_WEB_ALLOW_REMOTE=1
ANI_CLI_WEB_REMOTE_TOKEN=choose-a-long-random-token
```
Non-local clients must send one of:
- `Authorization: Bearer <token>`
- `X-AniCli-Web-Token: <token>`
Browser bootstrap also works:
```text
http://YOUR-HOST:8421/?token=your-token-here
```
The app stores the token in an HTTP-only cookie and redirects to the clean URL.
## Download Queue
Path: `/queue`
The queue is stored in SQLite, shown 10 jobs at a time, and managed from its dedicated page.
Available actions:
- `Retry`
- `Cancel`
- `Remove`
- `Retry failed`
- `Remove finished`
Downloads are staged first, then moved into a media-friendly layout:
```text
ANI_CLI_DOWNLOAD_DIR/anime_name/Season 01/anime_name - S01E01.mp4
```
## Runtime State
All local state is stored under:
```text
ani-cli-web/.ani-cli-web/
```
Main contents:
- `config.json`
- `state.sqlite3`
- `thumbnails/`
- AniDB title cache data
- staging files for active downloads
Legacy state from older locations is migrated on startup.
## Configuration
Environment variables:
- `ANI_CLI_BIN`: path or command name for `ani-cli`
- `ANI_CLI_DOWNLOAD_DIR`: initial default download folder
- `ANI_CLI_MODE`: initial mode, `sub` or `dub`
- `ANI_CLI_QUALITY`: initial quality such as `best`, `1080`, or `720`
- `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
- `ANI_CLI_WEB_REMOTE_TOKEN`: shared secret for remote access
- `ANI_CLI_WEB_DEBUG`: enable debug logging
- `ANI_CLI_WEB_STATE_ROOT`: override the project-local state directory
- `UPDATE_ON_START`: run `sudo ani-cli --update` in the container before startup
- `USER_UID` and `USER_GID`: run the container as a specific host user/group
Saved config fields include:
- `discord_webhook_url`
- `discord_webhook_events`
- `watchlist_auto_refresh_enabled`
- `watchlist_auto_refresh_minutes`
- `watchlist_refresh_delay_seconds`
## Testing
Run the regression suite with:
```sh
python3 -m unittest test_app.py
```
## Notes
- Runtime boot is lazy. Importing `app.py` does not immediately start worker threads.
- Queue and watchlist data share the same SQLite database: `.ani-cli-web/state.sqlite3`.
- Runtime folders such as `.ani-cli-web/` and `downloads/` are ignored by git.
- The Docker image clones both upstream repositories during build.