From 05b4ce1c11fe122e794835a4060d8c5b23b361bd Mon Sep 17 00:00:00 2001 From: grymphen Date: Tue, 12 May 2026 11:27:52 +0200 Subject: [PATCH] check for doubles --- uploader.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/uploader.py b/uploader.py index ad6491d..e315f99 100644 --- a/uploader.py +++ b/uploader.py @@ -183,7 +183,15 @@ def fetch_all_books(cfg: CalibreConfig) -> list[dict]: if not rows or len(all_books) >= total: break start += len(rows) - return all_books + # Deduplicate by ID in case of page-boundary overlap in the API response + seen: set[int] = set() + unique: list[dict] = [] + for b in all_books: + bid = b.get("id") + if bid is None or bid not in seen: + seen.add(bid) + unique.append(b) + return unique def delete_book(cfg: CalibreConfig, book_id: int, client: "CalibreClient | None" = None) -> tuple[bool, str]: @@ -200,14 +208,24 @@ def delete_book(cfg: CalibreConfig, book_id: int, client: "CalibreClient | None" client._upload_csrf = csrf except Exception: pass - resp = client._session.post( - f"{cfg.url}/delete/{book_id}", - data={"csrf_token": csrf} if csrf else {}, - timeout=30, - ) - if resp.ok: - return True, "Deleted" - return False, f"HTTP {resp.status_code}" + for attempt in range(2): + resp = client._session.post( + f"{cfg.url}/delete/{book_id}", + data={"csrf_token": csrf} if csrf else {}, + timeout=30, + ) + if resp.ok: + return True, "Deleted" + if resp.status_code == 400 and attempt == 0: + # CSRF token likely expired; re-authenticate and retry once + log.info("delete_book: 400 on book %d — refreshing CSRF and retrying", book_id) + client._authenticated = False + client._upload_csrf = None + client._ensure_auth() + csrf = client._upload_csrf + continue + return False, f"HTTP {resp.status_code}" + return False, "HTTP 400 after re-auth retry" def find_duplicate_groups(books: list[dict]) -> list[list[dict]]: