From d075aa11f92a575c36787bc225ae122f6e14ec9f Mon Sep 17 00:00:00 2001 From: Dymas Date: Tue, 26 May 2026 20:12:57 +0200 Subject: [PATCH] Trim README to user-facing deployment guide --- README.md | 423 +++++++++++------------------------------------------- 1 file changed, 83 insertions(+), 340 deletions(-) diff --git a/README.md b/README.md index 40d18b8..91e4850 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,65 @@ # ani-cli web -Small local web UI for a system-wide `ani-cli` install. +Local web UI for a system-wide `ani-cli` install. Current version: `0.45.14` -## Project Layout +## What it does -```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. -``` +- Search anime in `sub` or `dub` +- Queue downloads with folder, quality, media type, season, and episode controls +- Track shows in a watchlist with `Watching`, `Planned`, `Finished`, and `Dropped` categories +- Refresh watchlist episode counts manually or on a schedule +- Auto-download missing episodes for `Watching` entries after later refreshes +- Move fully completed libraries into Jellyfin TV or movie folders +- Send optional Discord webhook notifications -## Quick Start +## Pages -Run the app: +- `/`: Search and queue downloads +- `/queue`: Monitor jobs, inspect logs, retry failed jobs, remove finished jobs +- `/watchlist`: Track shows, refresh them, download all available episodes, manage auto-download settings +- `/config`: Save defaults, schedule refreshes, configure Jellyfin and Discord, view runtime info and the changelog + +## Quick start + +Requirements: + +- `ani-cli` installed and available in `PATH` +- Common runtime tools used by `ani-cli`, especially `curl`, `ffmpeg`, `fzf`, `grep`, `openssl`, `sed`, `aria2c`, and `yt-dlp` + +Run locally: ```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`. +Useful flags and env: + +- `./ani-cli-web --debug` +- `ANI_CLI_WEB_HOST=0.0.0.0` +- `ANI_CLI_WEB_PORT=8421` +- `ANI_CLI_BIN=/path/to/ani-cli` ## 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" \ @@ -94,227 +69,53 @@ docker run --rm \ 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: +Docker 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`. -- The runtime image removes repo-only files such as `README.md`, `Dockerfile`, `docker-compose.yaml`, and `test_app.py` after cloning, while keeping the app's `VERSION` and `CHANGELOG.md` files for Runtime info and the changelog viewer. -- 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`. +- The container serves on `0.0.0.0:8421` +- Downloads are written under `/downloads` +- App state is stored under `/app/.ani-cli-web` +- `USER_UID` and `USER_GID` help keep mounted files owned by your host user +- `UPDATE_ON_START=true` runs `ani-cli --update` before startup -## What It Does - -- Search AllAnime in `sub` or `dub`. -- Queue downloads with editable folder, quality, media type, season, and episode range. -- Save defaults on `/config`. -- Track shows on `/watchlist`. -- Refresh watchlist episode counts in the background. -- Schedule periodic watchlist refresh jobs. -- Auto-download newly discovered episodes for `Watching` entries after later refreshes, including backlog episodes already available on the first non-initial refresh. -- Send Discord webhook notifications for refresh discoveries and failures. -- Send Discord webhook notifications for completed downloads. -- Optionally move fully completed watchlist libraries into separate Jellyfin TV and movie folders. -- Cache watchlist thumbnails locally. -- Sync successful downloads back into the watchlist. -- Keep queue and watchlist data in SQLite under `.ani-cli-web/`. -- Browse host folders directly from Config page path fields when choosing library locations. -- Prefer title-based queueing and unique-match fallback identity checks so search and watchlist downloads do not depend on fragile cross-tool result ordering. - -## Pages - -### Search Page - -Path: `/` - -Use it to: - -1. Search for an anime. -2. Load episodes. -3. Adjust folder, quality, media type, 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. -- Search-page queueing now sends the selected title directly, which avoids relying on `ani-cli` search result numbers matching the web API ordering. -- Added-to-queue notices now point to the dedicated Queue page for progress tracking. -- Search request guards still avoid stale responses overwriting newer results. -- The shared sidebar menu now shows one navigation entry per line for cleaner balance across all pages. - -### 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 or canceled jobs. -4. Remove finished and canceled jobs. -5. Cancel running or pending jobs. - -Highlights: - -- The queue list loads once, then only visible pending or running jobs are refreshed every 2.5 seconds. -- Terminal jobs (`done`, `failed`, `canceled`) stop log polling automatically. -- Retry actions are only shown for `failed` and `canceled` jobs, so finished downloads cannot be re-queued accidentally from the UI. -- Dedicated queue pagination and cleanup controls. -- Search and queue workflows are now separated into distinct pages. -- Each queue job card can be collapsed, and finished jobs start collapsed by default to keep the page compact. - -### 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. Global auto-download enable or disable. -8. Default auto-download language and quality. -9. Jellyfin automatic handoff enable or disable. -10. Jellyfin TV and movie library paths plus a manual trigger action. -11. Discord webhook URL. -12. Discord notification events plus a test action. - -Layout: - -- `Dependencies` stays at the top as a full-width runtime status card. -- The remaining Config sections use two columns on larger screens and collapse to one column at `1080px` width and below. -- Runtime info shows both the `ani-cli-web` version and the installed `ani-cli` version. -- The `ani-cli-web` Runtime info version is read directly from the project `VERSION` file so the UI and startup logs stay aligned with release metadata. -- Runtime info now includes a full-width `Changelog` button that opens a floating scrollable viewer backed by the project's `CHANGELOG.md`. -- Path fields include `Browse` buttons that open a host-side folder picker modal in the browser. - -Saved settings are written to `.ani-cli-web/config.json`. - -Jellyfin handoff: - -- Automatic Jellyfin moves run after a watchlist refresh updates an entry and the show now looks complete. -- TV entries only move after the anime is marked `Finished` and at least one downloaded language covers the expected episode total. -- Movie entries move after they are downloaded and the local movie library folder exists. -- Existing Jellyfin targets are only treated as already moved when the destination library still contains the expected media files. -- The manual `Run now` action on `/config` scans the whole watchlist and moves anything already eligible. - -Watchlist queueing: - -- Manual `Download all` and automatic watchlist downloads now queue directly from the stored watchlist title and `show_id`, so they no longer fail just because a fresh provider search returns a shifted or incomplete result list. -- The first manual or scheduled refresh after adding a `Watching` show can now queue already-available missing episodes even if the seeded add-time refresh has not populated `last_checked` yet. -- Removing a watchlist entry now detaches its existing queue jobs from later watchlist sync, so finishing an old queued download cannot silently recreate that deleted show. -- Manual watchlist `Download all` now uses the anime's own configured auto-download quality when one is set. -- Completed movie downloads now keep their `movie` media type during watchlist sync and move movie-tagged watchlist entries into `Finished`, which keeps later Jellyfin handoff eligibility consistent. - -Discord notification events: - -- Download completed. -- Jellyfin move succeeded. -- Jellyfin move failed. -- Refresh finished and found new episodes. -- Refresh failed or finished with per-show refresh errors. -- Refresh interrupted by restart or shutdown. -- Unexpected runtime errors. - -Webhook details: - -- Download-completed notifications include the full saved file list, split across extra Discord embeds when needed. -- Jellyfin handoff notifications fire when a finished library is moved successfully and also on actionable move failures such as missing targets or move errors. -- Watchlist entries only move to `Finished` automatically when the downloaded mode itself is complete, and Jellyfin handoff for TV entries follows that same mode-aware completion rule. -- Queue jobs that exit successfully but produce no downloaded files are marked as failed with a clearer explanation instead of looking like a generic rename problem. -- Watchlist-driven downloads no longer force `ani-cli -S ` from API search ordering, which avoids bad result mismatches when `ani-cli`’s own search order differs from the web app’s result list. +## Key behavior ### Watchlist -Path: `/watchlist` +- Adding a show tracks it without downloading immediately +- Later manual refreshes or scheduled refreshes can queue missing episodes +- `Download all` queues every currently available episode for the chosen mode +- Successful downloads sync back into the watchlist +- TV entries move to `Finished` when the selected downloaded mode reaches the expected episode count +- Movie entries can be treated as complete after download and routed to movie storage -Categories: +### Auto-download -- `Watching` -- `Planned` -- `Finished` -- `Dropped` +- Runs only for entries in `Watching` +- Uses the per-show language, quality, season, library name, and optional episode offset settings +- Can queue backlog episodes that were already available before the show was first tracked -Ways to add shows: +### Jellyfin handoff -1. From the Search page. -2. Manually on `/watchlist`. +- Optional and configured from `/config` +- TV libraries move only after the show is finished and download-complete +- Movie libraries move after download completion +- Manual `Run now` scans the watchlist and moves anything already eligible -Main behavior: +## Runtime info -1. `Refresh` updates one show. -2. `Refresh All` runs in the background. -3. `Download all` expands an inline picker for `Sub` or `Dub` and then queues every currently available episode directly. -4. Clicking the title opens the source page. -5. `Remove` deletes the entry and cached poster. -6. Each card has inline category and media-type selectors. -7. Summary cards show total tracked shows plus `sub` and `dub` counts. -8. Pagination shows 30 cards per page. -9. `Alternative titles` opens a centered overlay with English alternate titles from AniDB when the watchlist refresh can match the show to an AniDB entry. -10. `Auto-download` opens a centered overlay with per-series settings for language, quality, series number, optional episode offset, and the sanitized library name used for automatically queued episodes. -Auto-download layout note: `Name source` and `Name` share a wider half-and-half row inside the modal for easier editing. -11. Adding a show from Search reuses the current `Season` field as the initial auto-download series value for that new watchlist entry. -12. Each watchlist entry can be tagged as `TV` or `Movie`, which changes where finished downloads are stored. -13. Card actions are split into two rows so `Refresh` and `Remove` sit on the first line, with `Download all` and `Auto-download` on the second. +The Config page runtime panel shows: -Episode offset: +- `ani-cli-web` version loaded from `VERSION` +- Installed `ani-cli` version +- A `Changelog` button that opens a scrollable viewer backed by `CHANGELOG.md` -- Leave it empty to keep normal episode numbering. -- Set it to a starting number such as `13` when a split season continues inside a new folder, so downloaded files are named like `Show Name - S02E13`, `Show Name - S02E14`, and so on. +## Remote access -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. -- Auto-download does not trigger when a show is first added to the watchlist. -- Auto-download only runs for entries in `Watching` after later manual refreshes or scheduled refreshes. -- Auto-download uses stored downloaded-episode history per language, so it can queue any still-missing episodes after a later refresh, including backlog episodes that existed before the show was first tracked. -- 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`. +By default the app only serves loopback clients. To allow LAN or remote access: @@ -324,59 +125,15 @@ ANI_CLI_WEB_AUTH_USERNAME=admin ANI_CLI_WEB_AUTH_PASSWORD=choose-a-long-random-password ``` -Non-local browser visits are redirected to a sign-in form automatically when no valid session cookie is present. - -Remote browser writes use same-origin `Origin` checks. If you run the app behind a reverse proxy, make sure the browser reaches it through the same host you expect the backend to validate. - -API clients can authenticate with: - -- `Authorization: Basic ` - -The browser login stores a persistent HTTP-only session token, so you usually only need to sign in once per browser. - -Remote path access can be limited to explicit server-approved roots with: +Optional remote path restriction: ```sh ANI_CLI_WEB_REMOTE_PATH_ROOTS=/downloads:/srv/jellyfin-tv:/srv/jellyfin-movies ``` -Those roots are added to the remote path picker alongside the configured download and Jellyfin library folders. +## State and storage -You can also set the same credentials directly in `.ani-cli-web/config.json`: - -```json -{ - "auth_username": "admin", - "auth_password": "choose-a-long-random-password" -} -``` - -Environment variables take precedence over values saved in `config.json`. - -## 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/tv/anime_name/Season 01/anime_name - S01E01.mp4 -ANI_CLI_DOWNLOAD_DIR/movie/anime_name/anime_name.mp4 -``` - -## Runtime State - -All local state is stored under: +Project state is stored in: ```text ani-cli-web/.ani-cli-web/ @@ -388,42 +145,35 @@ Main contents: - `remote-sessions.json` - `state.sqlite3` - `thumbnails/` -- AniDB title cache data -- staging files for active downloads -Legacy state from older locations is migrated on startup. +Downloaded files are organized like this: + +```text +ANI_CLI_DOWNLOAD_DIR/tv/Show Name/Season 01/Show Name - S01E01.mp4 +ANI_CLI_DOWNLOAD_DIR/movie/Movie Name/Movie Name.mp4 +``` ## Configuration -Environment variables: +Most users only need these 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_AUTH_USERNAME`: single remote-login username -- `ANI_CLI_WEB_AUTH_PASSWORD`: single remote-login password -- `ANI_CLI_WEB_REMOTE_PATH_ROOTS`: optional `:`-separated allowlist of remote-browse roots -- `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 +- `ANI_CLI_BIN` +- `ANI_CLI_WEB_HOST` +- `ANI_CLI_WEB_PORT` +- `ANI_CLI_WEB_ALLOW_REMOTE` +- `ANI_CLI_WEB_AUTH_USERNAME` +- `ANI_CLI_WEB_AUTH_PASSWORD` +- `ANI_CLI_WEB_REMOTE_PATH_ROOTS` +- `ANI_CLI_WEB_DEBUG` +- `ANI_CLI_WEB_STATE_ROOT` -Saved config fields include: +Container-specific variables: -- `auth_username` -- `auth_password` -- `jellyfin_sync_enabled` -- `jellyfin_tv_dir` -- `jellyfin_movie_dir` -- `discord_webhook_url` -- `discord_webhook_events` -- `watchlist_auto_refresh_enabled` -- `watchlist_auto_refresh_minutes` -- `watchlist_refresh_delay_seconds` +- `UPDATE_ON_START` +- `USER_UID` +- `USER_GID` + +Everything else can usually be configured from `/config`. ## Testing @@ -432,10 +182,3 @@ 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.