jkj
This commit is contained in:
@@ -244,6 +244,8 @@ class HTTPClient:
|
||||
self,
|
||||
method: str,
|
||||
url: str,
|
||||
raise_for_status: bool = True,
|
||||
log_http_errors: bool = True,
|
||||
**kwargs
|
||||
) -> httpx.Response:
|
||||
"""
|
||||
@@ -273,7 +275,8 @@ class HTTPClient:
|
||||
for attempt in range(self.retries):
|
||||
try:
|
||||
response = self._client.request(method, url, **kwargs)
|
||||
response.raise_for_status()
|
||||
if raise_for_status:
|
||||
response.raise_for_status()
|
||||
return response
|
||||
except httpx.TimeoutException as e:
|
||||
last_exception = e
|
||||
@@ -287,7 +290,8 @@ class HTTPClient:
|
||||
response_text = e.response.text[:500]
|
||||
except:
|
||||
response_text = "<unable to read response>"
|
||||
logger.error(f"HTTP {e.response.status_code} from {url}: {response_text}")
|
||||
if log_http_errors:
|
||||
logger.error(f"HTTP {e.response.status_code} from {url}: {response_text}")
|
||||
raise
|
||||
last_exception = e
|
||||
try:
|
||||
|
||||
@@ -71,6 +71,7 @@ class HydrusNetwork:
|
||||
url: str
|
||||
access_key: str = ""
|
||||
timeout: float = 60.0
|
||||
instance_name: str = "" # Optional store name (e.g., 'home') for namespaced logs
|
||||
|
||||
scheme: str = field(init=False)
|
||||
hostname: str = field(init=False)
|
||||
@@ -90,6 +91,12 @@ class HydrusNetwork:
|
||||
self.port = parsed.port or (443 if self.scheme == "https" else 80)
|
||||
self.base_path = parsed.path.rstrip("/")
|
||||
self.access_key = self.access_key or ""
|
||||
self.instance_name = str(self.instance_name or "").strip()
|
||||
|
||||
def _log_prefix(self) -> str:
|
||||
if self.instance_name:
|
||||
return f"[hydrusnetwork:{self.instance_name}]"
|
||||
return f"[hydrusnetwork:{self.hostname}:{self.port}]"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# low-level helpers
|
||||
@@ -120,7 +127,7 @@ class HydrusNetwork:
|
||||
url = f"{self.scheme}://{self.hostname}:{self.port}{path}"
|
||||
|
||||
# Log request details
|
||||
logger.debug(f"[Hydrus] {spec.method} {spec.endpoint} (auth: {'session_key' if self._session_key else 'access_key' if self.access_key else 'none'})")
|
||||
logger.debug(f"{self._log_prefix()} {spec.method} {spec.endpoint} (auth: {'session_key' if self._session_key else 'access_key' if self.access_key else 'none'})")
|
||||
|
||||
status = 0
|
||||
reason = ""
|
||||
@@ -135,14 +142,14 @@ class HydrusNetwork:
|
||||
file_path = Path(spec.file_path)
|
||||
if not file_path.is_file():
|
||||
error_msg = f"Upload file not found: {file_path}"
|
||||
logger.error(f"[Hydrus] {error_msg}")
|
||||
logger.error(f"{self._log_prefix()} {error_msg}")
|
||||
raise FileNotFoundError(error_msg)
|
||||
|
||||
file_size = file_path.stat().st_size
|
||||
headers["Content-Type"] = spec.content_type or "application/octet-stream"
|
||||
headers["Content-Length"] = str(file_size)
|
||||
|
||||
logger.debug(f"[Hydrus] Uploading file {file_path.name} ({file_size} bytes)")
|
||||
logger.debug(f"{self._log_prefix()} Uploading file {file_path.name} ({file_size} bytes)")
|
||||
|
||||
def file_gen():
|
||||
with file_path.open("rb") as handle:
|
||||
@@ -153,7 +160,9 @@ class HydrusNetwork:
|
||||
spec.method,
|
||||
url,
|
||||
content=file_gen(),
|
||||
headers=headers
|
||||
headers=headers,
|
||||
raise_for_status=False,
|
||||
log_http_errors=False,
|
||||
)
|
||||
else:
|
||||
content = None
|
||||
@@ -163,14 +172,16 @@ class HydrusNetwork:
|
||||
content = spec.data
|
||||
else:
|
||||
json_data = spec.data
|
||||
logger.debug(f"[Hydrus] Request body size: {len(content) if content else 'json'}")
|
||||
logger.debug(f"{self._log_prefix()} Request body size: {len(content) if content else 'json'}")
|
||||
|
||||
response = client.request(
|
||||
spec.method,
|
||||
url,
|
||||
content=content,
|
||||
json=json_data,
|
||||
headers=headers
|
||||
headers=headers,
|
||||
raise_for_status=False,
|
||||
log_http_errors=False,
|
||||
)
|
||||
|
||||
status = response.status_code
|
||||
@@ -178,20 +189,14 @@ class HydrusNetwork:
|
||||
body = response.content
|
||||
content_type = response.headers.get("Content-Type", "") or ""
|
||||
|
||||
logger.debug(f"[Hydrus] Response {status} {reason} ({len(body)} bytes)")
|
||||
logger.debug(f"{self._log_prefix()} Response {status} {reason} ({len(body)} bytes)")
|
||||
|
||||
except (httpx.ConnectError, httpx.TimeoutException, httpx.NetworkError) as exc:
|
||||
msg = f"Hydrus unavailable: {exc}"
|
||||
logger.warning(f"[Hydrus] {msg}")
|
||||
logger.warning(f"{self._log_prefix()} {msg}")
|
||||
raise HydrusConnectionError(msg) from exc
|
||||
except httpx.HTTPStatusError as exc:
|
||||
response = exc.response
|
||||
status = response.status_code
|
||||
reason = response.reason_phrase
|
||||
body = response.content
|
||||
content_type = response.headers.get("Content-Type", "") or ""
|
||||
except Exception as exc:
|
||||
logger.error(f"[Hydrus] Connection error: {exc}", exc_info=True)
|
||||
logger.error(f"{self._log_prefix()} Connection error: {exc}", exc_info=True)
|
||||
raise
|
||||
|
||||
payload: Any
|
||||
@@ -219,19 +224,23 @@ class HydrusNetwork:
|
||||
message = payload
|
||||
else:
|
||||
message = reason or "HTTP error"
|
||||
|
||||
logger.error(f"[Hydrus] HTTP {status}: {message}")
|
||||
|
||||
# Some endpoints are naturally "missing" sometimes and should not spam logs.
|
||||
if status == 404 and spec.endpoint.rstrip("/") == "/get_files/file_path":
|
||||
return {}
|
||||
|
||||
logger.error(f"{self._log_prefix()} HTTP {status}: {message}")
|
||||
|
||||
# Handle expired session key (419) by clearing cache and retrying once
|
||||
if status == 419 and self._session_key and "session" in message.lower():
|
||||
logger.warning(f"[Hydrus] Session key expired, acquiring new one and retrying...")
|
||||
logger.warning(f"{self._log_prefix()} Session key expired, acquiring new one and retrying...")
|
||||
self._session_key = "" # Clear expired session key
|
||||
try:
|
||||
self._acquire_session_key()
|
||||
# Retry the request with new session key
|
||||
return self._perform_request(spec)
|
||||
except Exception as retry_error:
|
||||
logger.error(f"[Hydrus] Retry failed: {retry_error}", exc_info=True)
|
||||
logger.error(f"{self._log_prefix()} Retry failed: {retry_error}", exc_info=True)
|
||||
# If retry fails, raise the original error
|
||||
raise HydrusRequestError(status, message, payload) from retry_error
|
||||
|
||||
@@ -316,6 +325,16 @@ class HydrusNetwork:
|
||||
def add_file(self, file_path: Path) -> dict[str, Any]:
|
||||
return self._post("/add_files/add_file", file_path=file_path)
|
||||
|
||||
def undelete_files(self, hashes: Union[str, Iterable[str]]) -> dict[str, Any]:
|
||||
"""Restore files from Hydrus trash back into 'my files'.
|
||||
|
||||
Hydrus Client API: POST /add_files/undelete_files
|
||||
Required JSON args: {"hashes": [<sha256 hex>, ...]}
|
||||
"""
|
||||
hash_list = self._ensure_hashes(hashes)
|
||||
body = {"hashes": hash_list}
|
||||
return self._post("/add_files/undelete_files", data=body)
|
||||
|
||||
def add_tag(self, hash: Union[str, Iterable[str]], tags: Iterable[str], service_name: str) -> dict[str, Any]:
|
||||
hash = self._ensure_hashes(hash)
|
||||
body = {"hashes": hash, "service_names_to_tags": {service_name: list(tags)}}
|
||||
|
||||
Reference in New Issue
Block a user