Mirror download job logs to stdout
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 0.49.4 - 2026-07-19
|
||||
|
||||
- Added optional download job stdout mirroring with `ANI_CLI_WEB_JOB_STDOUT`, enabled by default in Docker and Docker Compose so container log collectors can capture downloader output.
|
||||
|
||||
## 0.49.3 - 2026-07-19
|
||||
|
||||
- Changed Docker builds to copy the current local `ani-cli-web` checkout into the image instead of cloning the configured remote branch, so rebuilt images include local Docker/runtime fixes immediately.
|
||||
|
||||
@@ -14,6 +14,7 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
||||
ANI_CLI_DOWNLOAD_DIR=/downloads \
|
||||
ANI_CLI_WEB_HOST=0.0.0.0 \
|
||||
ANI_CLI_WEB_PORT=8421 \
|
||||
ANI_CLI_WEB_JOB_STDOUT=true \
|
||||
UPDATE_ON_START=false \
|
||||
USER_UID= \
|
||||
USER_GID=
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Local web UI for a system-wide `ani-cli` install.
|
||||
|
||||
Current version: `0.49.3`
|
||||
Current version: `0.49.4`
|
||||
|
||||
## What it does
|
||||
|
||||
@@ -49,6 +49,7 @@ Useful flags and env:
|
||||
- `./ani-cli-web --debug`
|
||||
- `ANI_CLI_WEB_HOST=0.0.0.0`
|
||||
- `ANI_CLI_WEB_PORT=8421`
|
||||
- `ANI_CLI_WEB_JOB_STDOUT=true`
|
||||
- `ANI_CLI_BIN=/path/to/ani-cli`
|
||||
- `ANIPY_CLI_BIN=/path/to/anipy-cli`
|
||||
- `ANIMDL_BIN=/path/to/animdl`
|
||||
@@ -88,6 +89,7 @@ Docker notes:
|
||||
- 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
|
||||
- `ANI_CLI_WEB_JOB_STDOUT=true` mirrors download job output to container stdout for log collectors; set it to `false` to keep job output only in the Queue page
|
||||
- `botan` is installed inside the container for providers or scripts that need Botan cryptography tooling
|
||||
- `anipy-cli` and `animdl` are installed systemwide inside the container at `/usr/local/bin`, linked from `/usr/bin`, and included in the runtime `PATH`; `animdl` is isolated in a Python 3.11 virtualenv so its pinned dependencies stay executable
|
||||
|
||||
|
||||
@@ -271,6 +271,10 @@ def background_workers_enabled():
|
||||
return not env_flag("ANI_CLI_WEB_DISABLE_WORKER")
|
||||
|
||||
|
||||
def job_stdout_enabled():
|
||||
return env_flag("ANI_CLI_WEB_JOB_STDOUT")
|
||||
|
||||
|
||||
def client_address_is_local(host):
|
||||
text = str(host or "").strip()
|
||||
if not text:
|
||||
|
||||
@@ -20,6 +20,7 @@ services:
|
||||
ANI_CLI_WEB_ALLOW_REMOTE: ${ANI_CLI_WEB_ALLOW_REMOTE:-false}
|
||||
ANI_CLI_WEB_AUTH_USERNAME: ${ANI_CLI_WEB_AUTH_USERNAME:-}
|
||||
ANI_CLI_WEB_AUTH_PASSWORD: ${ANI_CLI_WEB_AUTH_PASSWORD:-}
|
||||
ANI_CLI_WEB_JOB_STDOUT: ${ANI_CLI_WEB_JOB_STDOUT:-true}
|
||||
ANI_CLI_DOWNLOAD_DIR: /downloads
|
||||
volumes:
|
||||
- ./downloads:/downloads
|
||||
|
||||
@@ -29,6 +29,7 @@ from app_support import (
|
||||
finalize_library_files,
|
||||
job_output_dir,
|
||||
job_staging_dir,
|
||||
job_stdout_enabled,
|
||||
load_json,
|
||||
normalize_config,
|
||||
now_iso,
|
||||
@@ -1434,6 +1435,7 @@ class DownloadQueue:
|
||||
text = strip_control(line).strip()
|
||||
if not text:
|
||||
return
|
||||
self._stdout_log(job, text)
|
||||
with self.lock:
|
||||
job.setdefault("log", []).append(text)
|
||||
job["log"] = job["log"][-MAX_LOG_LINES:]
|
||||
@@ -1447,6 +1449,16 @@ class DownloadQueue:
|
||||
if should_flush:
|
||||
self._save_job_locked(job)
|
||||
|
||||
def _stdout_log(self, job, line):
|
||||
if not job_stdout_enabled():
|
||||
return
|
||||
text = strip_control(str(line)).strip()
|
||||
if not text:
|
||||
return
|
||||
job_id = str((job or {}).get("id") or "-")
|
||||
backend = str((job or {}).get("download_backend") or "download")
|
||||
print(f"[download:{job_id}:{backend}] {text}", flush=True)
|
||||
|
||||
def _cleanup_staging_dir(self, job):
|
||||
staging_dir = job_staging_dir(job)
|
||||
if not staging_dir.exists():
|
||||
|
||||
+15
@@ -381,6 +381,21 @@ class DownloadQueueCancelTests(unittest.TestCase):
|
||||
self.assertEqual(len(saved), 1)
|
||||
self.assertEqual(saved[0][-1], "line flush")
|
||||
|
||||
def test_append_log_can_mirror_download_output_to_stdout(self):
|
||||
queue = object.__new__(APP.DownloadQueue)
|
||||
queue.lock = threading.RLock()
|
||||
queue._save_job_locked = lambda _job: None
|
||||
job = {"id": "job-1", "download_backend": "animdl", "log": [], "updated_at": APP.now_iso()}
|
||||
|
||||
with mock.patch.dict(os.environ, {"ANI_CLI_WEB_JOB_STDOUT": "true"}, clear=False), mock.patch("builtins.print") as print_mock:
|
||||
APP.DownloadQueue._append_log(queue, job, "\x1b[32mDownloading episode 1\x1b[0m\n")
|
||||
|
||||
print_mock.assert_called_once_with(
|
||||
"[download:job-1:animdl] Downloading episode 1",
|
||||
flush=True,
|
||||
)
|
||||
self.assertEqual(job["log"], ["Downloading episode 1"])
|
||||
|
||||
|
||||
class QueueApiTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user