Initial reyna-cli implementation
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import httpx
|
||||
|
||||
from reyna_cli.env import load_hermes_env
|
||||
|
||||
|
||||
class ImmichClient:
|
||||
def __init__(self, base_url: str | None = None, api_key: str | None = None, timeout: float = 20.0):
|
||||
load_hermes_env()
|
||||
self.base_url = (base_url or os.getenv("IMMICH_BASE_URL") or os.getenv("IMMICH_URL") or "").rstrip("/")
|
||||
self.api_key = api_key or os.getenv("IMMICH_API_KEY") or os.getenv("IMMICH_KEY") or ""
|
||||
self.timeout = timeout
|
||||
if not self.base_url:
|
||||
raise RuntimeError("IMMICH_BASE_URL is not configured")
|
||||
if not self.api_key:
|
||||
raise RuntimeError("IMMICH_API_KEY is not configured")
|
||||
self.client = httpx.Client(timeout=timeout, headers={"x-api-key": self.api_key, "Accept": "application/json"})
|
||||
|
||||
def _url(self, path: str) -> str:
|
||||
if not path.startswith("/"):
|
||||
path = "/" + path
|
||||
return self.base_url + path
|
||||
|
||||
def _request(self, method: str, candidates: List[str], **kwargs: Any) -> Any:
|
||||
errors: list[str] = []
|
||||
for path in candidates:
|
||||
try:
|
||||
response = self.client.request(method, self._url(path), **kwargs)
|
||||
if response.status_code == 404:
|
||||
errors.append(f"{path}: 404")
|
||||
continue
|
||||
response.raise_for_status()
|
||||
if not response.content:
|
||||
return {}
|
||||
return response.json()
|
||||
except Exception as exc:
|
||||
errors.append(f"{path}: {exc}")
|
||||
raise RuntimeError("; ".join(errors))
|
||||
|
||||
def server_version(self) -> Dict[str, Any]:
|
||||
return self._request("GET", ["/api/server/version", "/server/version"])
|
||||
|
||||
def statistics(self) -> Dict[str, Any]:
|
||||
return self._request("GET", ["/api/server/statistics", "/server/statistics", "/api/server/stats", "/server/stats"])
|
||||
|
||||
def albums(self) -> Any:
|
||||
return self._request("GET", ["/api/albums", "/albums"])
|
||||
|
||||
def search(self, query: str, limit: int = 10) -> Any:
|
||||
payloads = [
|
||||
{"query": query, "size": limit},
|
||||
{"query": query, "page": 1, "size": limit},
|
||||
{"smartSearch": query, "size": limit},
|
||||
]
|
||||
errors: list[str] = []
|
||||
for path in ["/api/search/smart", "/search/smart", "/api/search/metadata", "/search/metadata"]:
|
||||
for payload in payloads:
|
||||
try:
|
||||
response = self.client.post(self._url(path), json=payload)
|
||||
if response.status_code == 404:
|
||||
break
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception as exc:
|
||||
errors.append(f"{path}: {exc}")
|
||||
raise RuntimeError("; ".join(errors))
|
||||
Reference in New Issue
Block a user