feat(updater): add KavitaVolumeCoverUpdater for back-filling null volumes
Build and Deploy / build (push) Successful in 22s
Build and Deploy / deploy (push) Successful in 36s

Introduce a new background service that periodically re-checks chapters
whose volume could not be resolved at move time.

- Add KavitaVolumeCoverUpdater.py to resolve null volumes via MangaDex,
  update ComicInfo.xml in-archive, and swap in MangaBaka volume covers
- Wire updater into main.py entry point with UPDATER_ENABLED env flag
- Add UPDATER_ENABLED env var to docker-compose.prod.yml
- Update CronSchedule.py to schedule updater runs
This commit is contained in:
2026-06-10 13:09:01 +02:00
parent 59ea1f8c8f
commit 4557137ad0
5 changed files with 746 additions and 5 deletions
+26
View File
@@ -27,6 +27,11 @@ Environment variables
MATCH_PATH default /config/matches.json
WEB_PORT default 8080 (Flask web UI for matches.json)
WEB_HOST default 0.0.0.0
UPDATER_ENABLED default true (volume/cover back-fill cron)
UPDATER_SCHEDULE cron expression for the updater scans,
default "0 19 * * 1,4" = 19:00 every Mon + Thu
(local time — set TZ inside the container!)
UPDATER_LOG default /config/volume_updater.log
"""
from __future__ import annotations
@@ -43,6 +48,7 @@ from src.SuwayomiMover import SuwayomiMover # noqa: E402
from src.SuwayomiFolderWatcher import SuwayomiFolderWatcher # noqa: E402
from src.MatchesCache import MatchesCache # noqa: E402
from src.MatchesWebApp import MatchesWebApp # noqa: E402
from src.KavitaVolumeCoverUpdater import KavitaVolumeCoverUpdater # noqa: E402
def _env_str(name: str, default: "str | None" = None,
@@ -85,6 +91,9 @@ def main() -> int:
match_path = _env_str("MATCH_PATH", "/config/matches.json")
web_host = _env_str("WEB_HOST", "0.0.0.0") or "0.0.0.0"
web_port = _env_int("WEB_PORT", 8080)
updater_enabled = _env_bool("UPDATER_ENABLED", True)
updater_schedule = _env_str("UPDATER_SCHEDULE", "0 19 * * 1,4")
updater_log = _env_str("UPDATER_LOG", "/config/volume_updater.log")
print(f"[main] suwayomi = {suwayomi_path}", flush=True)
print(f"[main] kavita = {kavita_path}", flush=True)
@@ -112,6 +121,23 @@ def main() -> int:
web_app = MatchesWebApp(matches_cache, mover=mover, host=web_host, port=web_port)
web_app.start()
if updater_enabled:
try:
updater = KavitaVolumeCoverUpdater(
kavita_path,
matches_cache=matches_cache,
language=language,
request_timeout=request_timeout,
log_path=updater_log,
schedule=updater_schedule,
)
updater.start()
except ValueError as exc:
# Invalid cron expression — keep the service up, just without
# the updater, and make the config error obvious in the logs.
print(f"[main] UPDATER_SCHEDULE invalid ({exc}); "
f"volume/cover updater DISABLED", flush=True)
# def shutdown(signum, _frame):
# print(f"[main] received signal {signum}", flush=True)
# watcher.stop()