switch to grimmory as book web app and a more modern dashboard

This commit is contained in:
2026-05-14 21:15:46 +02:00
parent 27fa22eee1
commit e029da895b
8 changed files with 785 additions and 100 deletions
+35 -14
View File
@@ -1,5 +1,4 @@
import logging
import shutil
import threading
import time
from pathlib import Path
@@ -7,6 +6,7 @@ from pathlib import Path
import config
import db
import extractor
import grimmory as grimmory_module
import sftp as sftp_module
log = logging.getLogger(__name__)
@@ -33,20 +33,17 @@ def run_sync(limit: int | None = None) -> None:
_running = True
run_id = db.start_sync_run()
counters = dict(zips_found=0, zips_new=0, books_imported=0, books_errored=0)
counters = dict(zips_found=0, zips_new=0, books_imported=0, books_skipped=0, books_errored=0)
try:
log.info("Sync started (limit=%s)", limit)
cfg = config.load()
_validate_config(cfg)
log.info("Config OK — work dir: %s, import dir: %s", cfg.work_dir, cfg.import_dir)
log.info("Config OK — work dir: %s, bookdrop: %s", cfg.work_dir, cfg.grimmory.bookdrop_path)
work_dir = Path(cfg.work_dir)
work_dir.mkdir(parents=True, exist_ok=True)
import_dir = Path(cfg.import_dir)
import_dir.mkdir(parents=True, exist_ok=True)
log.info("Connecting to SFTP %s@%s:%s ...", cfg.sftp.user, cfg.sftp.host, cfg.sftp.port)
new_zips = sftp_module.list_new_zips(cfg.sftp, max_results=limit)
counters["zips_found"] = len(new_zips)
@@ -71,6 +68,11 @@ def run_sync(limit: int | None = None) -> None:
zip_status = "success"
zip_error = None
local_zip = None
# Insert zip row early so we have a zip_id for per-book records
db.mark_zip_processed(remote_zip.remote_path, remote_zip.file_size, "running")
zip_id = db.get_zip_id_by_path(remote_zip.remote_path)
try:
t0 = time.monotonic()
local_zip = sftp_module.download(cfg.sftp, remote_zip, str(work_dir / "downloads"))
@@ -81,13 +83,26 @@ def run_sync(limit: int | None = None) -> None:
log.info("Extract done in %.1fs — %d book(s)", time.monotonic() - t1, len(books))
for book in books:
dest = import_dir / book.name
if dest.exists():
log.info("Skipping '%s' — already exists in import dir", book.name)
else:
shutil.move(str(book), str(dest))
log.info("Moved '%s'%s", book.name, import_dir)
sha256 = grimmory_module.compute_sha256(book)
if db.is_book_processed(sha256):
log.info("Skipping '%s' sha256 already processed", book.name)
counters["books_skipped"] += 1
db.record_book(zip_id, book.name, sha256, status="skipped")
continue
result = grimmory_module.place_book(
book,
cfg.grimmory.bookdrop_path,
cfg.grimmory.url,
cfg.grimmory.user,
cfg.grimmory.password,
sha256=sha256,
)
if result.status == "success":
counters["books_imported"] += 1
elif result.status == "skipped":
counters["books_skipped"] += 1
db.record_book(zip_id, book.name, result.sha256,
status=result.status, error_msg=result.error_msg)
extractor.cleanup(work_dir / "extracted" / local_zip.stem)
except Exception as e:
@@ -127,7 +142,13 @@ def _validate_config(cfg) -> None:
missing.append("SSH private key")
if cfg.sftp.auth_method == "password" and not cfg.sftp.password:
missing.append("SSH password")
if not cfg.import_dir:
missing.append("CWA import folder")
if not cfg.grimmory.url:
missing.append("Grimmory URL")
if not cfg.grimmory.user:
missing.append("Grimmory username")
if not cfg.grimmory.password:
missing.append("Grimmory password")
if not cfg.grimmory.bookdrop_path:
missing.append("Grimmory bookdrop path")
if missing:
raise ValueError(f"Missing configuration: {', '.join(missing)}")