From 0831ffed42426978ee1926d5ded3124e6546f7b9 Mon Sep 17 00:00:00 2001 From: grymphen Date: Sun, 10 May 2026 16:08:21 +0200 Subject: [PATCH] Calibreweb login errors --- uploader.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/uploader.py b/uploader.py index 4ffebe3..ed01b49 100644 --- a/uploader.py +++ b/uploader.py @@ -1,6 +1,6 @@ import hashlib import logging -import mimetypes +import re from pathlib import Path import requests @@ -25,15 +25,25 @@ class CalibreClient: def _ensure_auth(self) -> None: if self._authenticated: return + # Fetch login page first to get the CSRF token (Flask-WTF requirement) + login_url = f"{self._cfg.url}/login" + page = self._session.get(login_url, timeout=30) + page.raise_for_status() + csrf = _extract_csrf(page.text) + + data = {"username": self._cfg.user, "password": self._cfg.password} + if csrf: + data["csrf_token"] = csrf + resp = self._session.post( - f"{self._cfg.url}/login", - data={"username": self._cfg.user, "password": self._cfg.password}, + login_url, + data=data, allow_redirects=True, timeout=30, ) resp.raise_for_status() - # Calibre-Web redirects to / on success; a 200 on /login means bad creds - if resp.url.endswith("/login"): + # Calibre-Web redirects to / on success; landing back on /login means bad creds + if resp.url.rstrip("/").endswith("/login"): raise RuntimeError("Calibre-Web authentication failed — check credentials") self._authenticated = True log.info("Authenticated to Calibre-Web at %s", self._cfg.url) @@ -75,6 +85,13 @@ def test_connection(cfg: CalibreConfig) -> tuple[bool, str]: return False, str(e) +def _extract_csrf(html: str) -> str | None: + m = re.search(r'name="csrf_token"\s+value="([^"]+)"', html) + if not m: + m = re.search(r'value="([^"]+)"\s+name="csrf_token"', html) + return m.group(1) if m else None + + def _sha256(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: