"""Store registry. Concrete store implementations live in the `Store/` package. This module is the single source of truth for store discovery. Config schema (canonical): { "store": { "folder": { "default": {"path": "C:/Media"}, "test": {"path": "C:/Temp"} }, "hydrusnetwork": { "home": {"Hydrus-Client-API-Access-Key": "...", "url": "http://..."} } } } """ from __future__ import annotations from pathlib import Path from typing import Any, Dict, Optional from SYS.logger import debug from Store._base import StoreBackend from Store.Folder import Folder from Store.HydrusNetwork import HydrusNetwork class Store: def __init__(self, config: Optional[Dict[str, Any]] = None, suppress_debug: bool = False) -> None: self._config = config or {} self._suppress_debug = suppress_debug self._backends: Dict[str, StoreBackend] = {} self._load_backends() def _load_backends(self) -> None: store_cfg = self._config.get("store") if not isinstance(store_cfg, dict): store_cfg = {} folder_cfg = store_cfg.get("folder") if isinstance(folder_cfg, dict): for name, value in folder_cfg.items(): path_val: Optional[str] if isinstance(value, dict): path_val = value.get("path") elif isinstance(value, (str, bytes)): path_val = str(value) else: path_val = None if not path_val: continue location = str(Path(str(path_val)).expanduser()) self._backends[str(name)] = Folder(location=location, name=str(name)) hydrus_cfg = store_cfg.get("hydrusnetwork") if isinstance(hydrus_cfg, dict): for instance_name, instance_config in hydrus_cfg.items(): if not isinstance(instance_config, dict): continue api_key = instance_config.get("Hydrus-Client-API-Access-Key") url = instance_config.get("url") if not api_key or not url: continue try: self._backends[str(instance_name)] = HydrusNetwork( instance_name=str(instance_name), api_key=str(api_key), url=str(url), ) except Exception as exc: if not self._suppress_debug: debug(f"[Store] Failed to register Hydrus instance '{instance_name}': {exc}") def list_backends(self) -> list[str]: return sorted(self._backends.keys()) def list_searchable_backends(self) -> list[str]: searchable: list[str] = [] for name, backend in self._backends.items(): if type(backend).search_store is not StoreBackend.search_store: searchable.append(name) return sorted(searchable) def __getitem__(self, backend_name: str) -> StoreBackend: if backend_name not in self._backends: raise KeyError(f"Unknown store backend: {backend_name}. Available: {list(self._backends.keys())}") return self._backends[backend_name] def is_available(self, backend_name: str) -> bool: return backend_name in self._backends