f
This commit is contained in:
@@ -285,6 +285,10 @@ class HydrusNetwork:
|
||||
|
||||
# Some endpoints are naturally "missing" sometimes and should not spam logs.
|
||||
if status == 404 and spec.endpoint.rstrip("/") == "/get_files/file_path":
|
||||
# Some Hydrus deployments do not expose local file system paths via
|
||||
# /get_files/file_path. Treat 404 as 'not supported' and let callers
|
||||
# fall back to HTTP download URLs instead of raising an error.
|
||||
logger.debug(f"{self._log_prefix()} /get_files/file_path returned 404 (not supported) - caller should fallback to HTTP")
|
||||
return {}
|
||||
|
||||
logger.error(f"{self._log_prefix()} HTTP {status}: {message}")
|
||||
@@ -1572,8 +1576,8 @@ def is_available(config: dict[str,
|
||||
"""Check if Hydrus is available and accessible.
|
||||
|
||||
Performs a lightweight probe to verify:
|
||||
- Hydrus URL is configured
|
||||
- Can connect to Hydrus URL/port
|
||||
- At least one configured Hydrus instance has URL + API configured
|
||||
- A TCP connection to that instance's host/port can be established
|
||||
|
||||
Results are cached per session unless use_cache=False.
|
||||
|
||||
@@ -1593,16 +1597,22 @@ def is_available(config: dict[str,
|
||||
# Use new config helpers first, fallback to old method
|
||||
from SYS.config import get_hydrus_url, get_hydrus_access_key
|
||||
|
||||
url = (get_hydrus_url(config, "home") or "").strip()
|
||||
if not url:
|
||||
reason = "Hydrus URL not configured (check config.conf store.hydrusnetwork.home.URL)"
|
||||
_HYDRUS_AVAILABLE = False
|
||||
_HYDRUS_UNAVAILABLE_REASON = reason
|
||||
return False, reason
|
||||
# Collect candidate instances (prioritize 'home')
|
||||
store_block = (config or {}).get("store") or {}
|
||||
hydrus_block = store_block.get("hydrusnetwork") if isinstance(store_block, dict) else {}
|
||||
|
||||
access_key = get_hydrus_access_key(config, "home") or ""
|
||||
if not access_key:
|
||||
reason = "Hydrus access key not configured"
|
||||
candidate_names: list[str] = []
|
||||
if isinstance(hydrus_block, dict) and hydrus_block:
|
||||
# Prefer 'home' first when present for backwards compatibility
|
||||
names = list(hydrus_block.keys())
|
||||
if "home" in names:
|
||||
candidate_names = ["home"] + [n for n in names if n != "home"]
|
||||
else:
|
||||
candidate_names = names
|
||||
|
||||
# If no configured instances, keep previous behavior for clearer message
|
||||
if not candidate_names:
|
||||
reason = "Hydrus URL not configured (check config.conf store.hydrusnetwork.home.URL)"
|
||||
_HYDRUS_AVAILABLE = False
|
||||
_HYDRUS_UNAVAILABLE_REASON = reason
|
||||
return False, reason
|
||||
@@ -1613,37 +1623,46 @@ def is_available(config: dict[str,
|
||||
except (TypeError, ValueError):
|
||||
timeout = 5.0
|
||||
|
||||
try:
|
||||
# Simple TCP connection test to URL/port
|
||||
import socket
|
||||
from urllib.parse import urlparse
|
||||
import socket
|
||||
from urllib.parse import urlparse
|
||||
|
||||
parsed = urlparse(url)
|
||||
hostname = parsed.hostname or "localhost"
|
||||
port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
||||
errors: list[str] = []
|
||||
|
||||
for name in candidate_names:
|
||||
url = (get_hydrus_url(config, name) or "").strip()
|
||||
access_key = get_hydrus_access_key(config, name) or ""
|
||||
if not url:
|
||||
errors.append(f"Hydrus URL not configured for instance '{name}'")
|
||||
continue
|
||||
if not access_key:
|
||||
errors.append(f"Hydrus access key not configured for instance '{name}'")
|
||||
continue
|
||||
|
||||
# Try to connect to the host/port
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(timeout)
|
||||
try:
|
||||
result = sock.connect_ex((hostname, port))
|
||||
if result == 0:
|
||||
_HYDRUS_AVAILABLE = True
|
||||
_HYDRUS_UNAVAILABLE_REASON = None
|
||||
return True, None
|
||||
else:
|
||||
reason = f"Cannot connect to {hostname}:{port}"
|
||||
_HYDRUS_AVAILABLE = False
|
||||
_HYDRUS_UNAVAILABLE_REASON = reason
|
||||
return False, reason
|
||||
finally:
|
||||
sock.close()
|
||||
parsed = urlparse(url)
|
||||
hostname = parsed.hostname or "localhost"
|
||||
port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
||||
|
||||
except Exception as exc:
|
||||
reason = str(exc)
|
||||
_HYDRUS_AVAILABLE = False
|
||||
_HYDRUS_UNAVAILABLE_REASON = reason
|
||||
return False, reason
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(timeout)
|
||||
try:
|
||||
result = sock.connect_ex((hostname, port))
|
||||
if result == 0:
|
||||
_HYDRUS_AVAILABLE = True
|
||||
_HYDRUS_UNAVAILABLE_REASON = None
|
||||
return True, None
|
||||
errors.append(f"Cannot connect to {hostname}:{port} (instance '{name}')")
|
||||
finally:
|
||||
sock.close()
|
||||
except Exception as exc:
|
||||
errors.append(str(exc))
|
||||
continue
|
||||
|
||||
# No candidate succeeded
|
||||
_HYDRUS_AVAILABLE = False
|
||||
reason = "; ".join(errors[:3]) if errors else "No Hydrus instances configured with URL and API"
|
||||
_HYDRUS_UNAVAILABLE_REASON = reason
|
||||
return False, reason
|
||||
|
||||
|
||||
def is_hydrus_available(config: dict[str, Any]) -> bool:
|
||||
@@ -1659,55 +1678,101 @@ def is_hydrus_available(config: dict[str, Any]) -> bool:
|
||||
return available
|
||||
|
||||
|
||||
def get_client(config: dict[str, Any]) -> HydrusNetwork:
|
||||
def get_client(config: dict[str, Any], instance_name: str | None = None) -> HydrusNetwork:
|
||||
"""Create and return a Hydrus client.
|
||||
|
||||
If `instance_name` is provided, return a client for that named instance.
|
||||
If omitted, prefer the 'home' instance when configured, otherwise fall back
|
||||
to the first configured Hydrus instance found in `config['store']['hydrusnetwork']`.
|
||||
|
||||
Uses access-key authentication by default (no session key acquisition).
|
||||
A session key may still be acquired explicitly by calling
|
||||
`HydrusNetwork.ensure_session_key()`.
|
||||
|
||||
Args:
|
||||
config: Configuration dict with Hydrus settings
|
||||
instance_name: Optional named Hydrus instance to use (e.g. 'rpi')
|
||||
|
||||
Returns:
|
||||
HydrusClient instance
|
||||
HydrusNetwork client instance
|
||||
|
||||
Raises:
|
||||
RuntimeError: If Hydrus is not configured or unavailable
|
||||
"""
|
||||
# Check availability first - if unavailable, raise immediately
|
||||
# Perform a lightweight availability check first - this checks any configured instance.
|
||||
available, reason = is_available(config)
|
||||
if not available:
|
||||
raise RuntimeError(f"Hydrus is unavailable: {reason}")
|
||||
# We will still attempt to instantiate a client for a configured instance even if the
|
||||
# availability probe reported unreachable (this keeps behavior resilient in mixed
|
||||
# network setups), but if no configured instance exists we'll raise below.
|
||||
|
||||
from SYS.config import get_hydrus_url, get_hydrus_access_key
|
||||
|
||||
# Use new config helpers
|
||||
hydrus_url = (get_hydrus_url(config, "home") or "").strip()
|
||||
if not hydrus_url:
|
||||
raise RuntimeError(
|
||||
"Hydrus URL is not configured (check config.conf store.hydrusnetwork.home.URL)"
|
||||
)
|
||||
chosen_instance: str | None = None
|
||||
|
||||
if instance_name:
|
||||
chosen_instance = str(instance_name).strip()
|
||||
# Validate existence of configuration
|
||||
url_candidate = (get_hydrus_url(config, chosen_instance) or "").strip()
|
||||
if not url_candidate:
|
||||
raise RuntimeError(f"Hydrus URL is not configured for instance '{chosen_instance}'")
|
||||
access_key_candidate = get_hydrus_access_key(config, chosen_instance) or ""
|
||||
if not access_key_candidate:
|
||||
raise RuntimeError(f"Hydrus access key is not configured for instance '{chosen_instance}'")
|
||||
else:
|
||||
# Determine candidate instances from config and prefer 'home' when present
|
||||
store_block = (config or {}).get("store") or {}
|
||||
hydrus_block = store_block.get("hydrusnetwork") if isinstance(store_block, dict) else {}
|
||||
candidate_names: list[str] = []
|
||||
if isinstance(hydrus_block, dict) and hydrus_block:
|
||||
names = list(hydrus_block.keys())
|
||||
if "home" in names:
|
||||
candidate_names = ["home"] + [n for n in names if n != "home"]
|
||||
else:
|
||||
candidate_names = names
|
||||
|
||||
# Try to pick the first instance with URL+API configured
|
||||
for name in candidate_names:
|
||||
url_candidate = (get_hydrus_url(config, name) or "").strip()
|
||||
access_key_candidate = get_hydrus_access_key(config, name) or ""
|
||||
if url_candidate and access_key_candidate:
|
||||
chosen_instance = name
|
||||
break
|
||||
|
||||
# If nothing suitable found in config, fall back to 'home' behavior (for backwards compatibility)
|
||||
if chosen_instance is None:
|
||||
hydrus_url = (get_hydrus_url(config, "home") or "").strip()
|
||||
if not hydrus_url:
|
||||
raise RuntimeError(
|
||||
"Hydrus URL is not configured (check config.conf store.hydrusnetwork.home.URL)"
|
||||
)
|
||||
chosen_instance = "home"
|
||||
|
||||
# Now we have a chosen instance name; resolve its URL and access key (these validations are defensive)
|
||||
hydrus_url = (get_hydrus_url(config, chosen_instance) or "").strip()
|
||||
access_key = get_hydrus_access_key(config, chosen_instance) or ""
|
||||
|
||||
if not hydrus_url:
|
||||
raise RuntimeError(f"Hydrus URL is not configured for instance '{chosen_instance}'")
|
||||
if not access_key:
|
||||
raise RuntimeError(f"Hydrus access key is not configured for instance '{chosen_instance}'")
|
||||
|
||||
access_key = get_hydrus_access_key(config, "home") or ""
|
||||
timeout_raw = config.get("HydrusNetwork_Request_Timeout")
|
||||
try:
|
||||
timeout = float(timeout_raw) if timeout_raw is not None else 60.0
|
||||
except (TypeError, ValueError):
|
||||
timeout = 60.0
|
||||
|
||||
# Create cache key from URL and access key
|
||||
cache_key = f"{hydrus_url}#{access_key}"
|
||||
# Create cache key from URL, access key, and instance name
|
||||
cache_key = f"{hydrus_url}#{access_key}#{chosen_instance}"
|
||||
|
||||
# Check if we have a cached client
|
||||
if cache_key in _hydrus_client_cache:
|
||||
return _hydrus_client_cache[cache_key]
|
||||
|
||||
# Create new client
|
||||
client = HydrusNetwork(hydrus_url, access_key, timeout)
|
||||
|
||||
# Cache the client
|
||||
# Create new client and cache it
|
||||
client = HydrusNetwork(hydrus_url, access_key, timeout, instance_name=chosen_instance)
|
||||
_hydrus_client_cache[cache_key] = client
|
||||
return client
|
||||
|
||||
return client
|
||||
|
||||
|
||||
Reference in New Issue
Block a user