fdf
This commit is contained in:
@@ -5,17 +5,22 @@ import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import httpx
|
||||
|
||||
from SYS.logger import debug, log
|
||||
from SYS.utils_constant import mime_maps
|
||||
|
||||
from Store._base import Store
|
||||
|
||||
|
||||
_HYDRUS_INIT_CHECK_CACHE: dict[tuple[str, str], tuple[bool, Optional[str]]] = {}
|
||||
|
||||
|
||||
class HydrusNetwork(Store):
|
||||
"""File storage backend for Hydrus client.
|
||||
|
||||
Each instance represents a specific Hydrus client connection.
|
||||
Maintains its own HydrusClient with session key.
|
||||
Maintains its own HydrusClient.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> "HydrusNetwork":
|
||||
@@ -64,22 +69,67 @@ class HydrusNetwork(Store):
|
||||
|
||||
self.NAME = instance_name
|
||||
self.API = api_key
|
||||
self.URL = url
|
||||
# Create persistent client with session key for this instance
|
||||
self._client = HydrusClient(url=url, access_key=api_key)
|
||||
self.URL = url.rstrip("/")
|
||||
|
||||
# Self health-check: acquire a session key immediately so broken configs
|
||||
# fail-fast and the registry can skip registering this backend.
|
||||
try:
|
||||
if self._client is not None:
|
||||
self._client.ensure_session_key()
|
||||
except Exception as exc:
|
||||
# Best-effort cleanup so partially constructed objects don't linger.
|
||||
# Total count (best-effort, used for startup diagnostics)
|
||||
self.total_count: Optional[int] = None
|
||||
|
||||
# Self health-check: validate the URL is reachable and the access key is accepted.
|
||||
# This MUST NOT attempt to acquire a session key.
|
||||
cache_key = (self.URL, self.API)
|
||||
cached = _HYDRUS_INIT_CHECK_CACHE.get(cache_key)
|
||||
if cached is not None:
|
||||
ok, err = cached
|
||||
if not ok:
|
||||
raise RuntimeError(f"Hydrus '{self.NAME}' unavailable: {err or 'Unavailable'}")
|
||||
else:
|
||||
api_version_url = f"{self.URL}/api_version"
|
||||
verify_key_url = f"{self.URL}/verify_access_key"
|
||||
try:
|
||||
self._client = None
|
||||
except Exception:
|
||||
pass
|
||||
raise RuntimeError(f"Hydrus '{self.NAME}' unavailable: {exc}") from exc
|
||||
with httpx.Client(timeout=5.0, verify=False, follow_redirects=True) as client:
|
||||
version_resp = client.get(api_version_url)
|
||||
version_resp.raise_for_status()
|
||||
version_payload = version_resp.json()
|
||||
if not isinstance(version_payload, dict):
|
||||
raise RuntimeError("Hydrus /api_version returned an unexpected response")
|
||||
|
||||
verify_resp = client.get(
|
||||
verify_key_url,
|
||||
headers={"Hydrus-Client-API-Access-Key": self.API},
|
||||
)
|
||||
verify_resp.raise_for_status()
|
||||
verify_payload = verify_resp.json()
|
||||
if not isinstance(verify_payload, dict):
|
||||
raise RuntimeError("Hydrus /verify_access_key returned an unexpected response")
|
||||
|
||||
_HYDRUS_INIT_CHECK_CACHE[cache_key] = (True, None)
|
||||
except Exception as exc:
|
||||
err = str(exc)
|
||||
_HYDRUS_INIT_CHECK_CACHE[cache_key] = (False, err)
|
||||
raise RuntimeError(f"Hydrus '{self.NAME}' unavailable: {err}") from exc
|
||||
|
||||
# Create a persistent client for this instance (auth via access key by default).
|
||||
self._client = HydrusClient(url=self.URL, access_key=self.API)
|
||||
|
||||
# Best-effort total count (fast on Hydrus side; does not fetch IDs/hashes).
|
||||
try:
|
||||
payload = self._client.search_files(
|
||||
tags=["system:everything"],
|
||||
return_hashes=False,
|
||||
return_file_ids=False,
|
||||
return_file_count=True,
|
||||
)
|
||||
count_val = None
|
||||
if isinstance(payload, dict):
|
||||
count_val = payload.get("file_count")
|
||||
if count_val is None:
|
||||
count_val = payload.get("file_count_inclusive")
|
||||
if count_val is None:
|
||||
count_val = payload.get("num_files")
|
||||
if isinstance(count_val, int):
|
||||
self.total_count = count_val
|
||||
except Exception as exc:
|
||||
debug(f"Hydrus total count unavailable for '{self.NAME}': {exc}", file=sys.stderr)
|
||||
|
||||
def name(self) -> str:
|
||||
return self.NAME
|
||||
|
||||
Reference in New Issue
Block a user