Initial commit
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import logging
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
import config
|
||||
import db
|
||||
import extractor
|
||||
import sftp as sftp_module
|
||||
from uploader import CalibreClient
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_lock = threading.Lock()
|
||||
_running = False
|
||||
|
||||
|
||||
def is_running() -> bool:
|
||||
return _running
|
||||
|
||||
|
||||
def run_sync() -> None:
|
||||
global _running
|
||||
if not _lock.acquire(blocking=False):
|
||||
log.warning("Sync already running, skipping")
|
||||
return
|
||||
|
||||
_running = True
|
||||
run_id = db.start_sync_run()
|
||||
counters = dict(zips_found=0, zips_new=0, books_uploaded=0, books_skipped=0, books_errored=0)
|
||||
|
||||
try:
|
||||
cfg = config.load()
|
||||
_validate_config(cfg)
|
||||
|
||||
work_dir = Path(cfg.local_work_dir)
|
||||
work_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
log.info("Listing remote zips at %s@%s:%s", cfg.sftp.user, cfg.sftp.host, cfg.sftp.remote_path)
|
||||
new_zips = sftp_module.list_new_zips(cfg.sftp)
|
||||
counters["zips_found"] = len(new_zips)
|
||||
counters["zips_new"] = len(new_zips)
|
||||
|
||||
client = CalibreClient(cfg.calibre)
|
||||
|
||||
for remote_zip in new_zips:
|
||||
zip_status = "success"
|
||||
zip_error = None
|
||||
local_zip = None
|
||||
try:
|
||||
local_zip = sftp_module.download(cfg.sftp, remote_zip, str(work_dir / "downloads"))
|
||||
books = extractor.extract(local_zip, work_dir / "extracted")
|
||||
|
||||
for book in books:
|
||||
status = client.upload(book, zip_source=remote_zip.remote_path)
|
||||
if status == "uploaded":
|
||||
counters["books_uploaded"] += 1
|
||||
elif status == "skipped_duplicate":
|
||||
counters["books_skipped"] += 1
|
||||
else:
|
||||
counters["books_errored"] += 1
|
||||
|
||||
extractor.cleanup(work_dir / "extracted" / local_zip.stem)
|
||||
except Exception as e:
|
||||
log.error("Error processing %s: %s", remote_zip.remote_path, e)
|
||||
zip_status = "error"
|
||||
zip_error = str(e)
|
||||
finally:
|
||||
if local_zip and local_zip.exists():
|
||||
extractor.cleanup(local_zip)
|
||||
db.mark_zip_processed(remote_zip.remote_path, remote_zip.file_size, zip_status, zip_error)
|
||||
|
||||
db.finish_sync_run(run_id, status="success", **counters)
|
||||
log.info(
|
||||
"Sync done. Zips: %d, Uploaded: %d, Skipped: %d, Errors: %d",
|
||||
counters["zips_new"], counters["books_uploaded"],
|
||||
counters["books_skipped"], counters["books_errored"],
|
||||
)
|
||||
except Exception as e:
|
||||
log.exception("Sync run failed: %s", e)
|
||||
db.finish_sync_run(run_id, status="error", error_msg=str(e), **counters)
|
||||
finally:
|
||||
_running = False
|
||||
_lock.release()
|
||||
|
||||
|
||||
def _validate_config(cfg) -> None:
|
||||
missing = []
|
||||
if not cfg.sftp.host:
|
||||
missing.append("SFTP host")
|
||||
if not cfg.sftp.user:
|
||||
missing.append("SFTP user")
|
||||
if not cfg.sftp.remote_path:
|
||||
missing.append("SFTP remote path")
|
||||
if cfg.sftp.auth_method == "key" and not cfg.sftp.key:
|
||||
missing.append("SSH private key")
|
||||
if cfg.sftp.auth_method == "password" and not cfg.sftp.password:
|
||||
missing.append("SSH password")
|
||||
if not cfg.calibre.url:
|
||||
missing.append("Calibre-Web URL")
|
||||
if not cfg.calibre.user:
|
||||
missing.append("Calibre-Web username")
|
||||
if missing:
|
||||
raise ValueError(f"Missing configuration: {', '.join(missing)}")
|
||||
Reference in New Issue
Block a user