df
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled

This commit is contained in:
2025-12-29 17:05:03 -08:00
parent 226de9316a
commit c019c00aed
104 changed files with 19669 additions and 12954 deletions

View File

@@ -1,6 +1,5 @@
""" """
"""
"""
from __future__ import annotations
import re
@@ -76,7 +75,9 @@ def _merge_dict_inplace(base: Dict[str, Any], patch: Dict[str, Any]) -> Dict[str
return base
def _apply_conf_block(config: Dict[str, Any], kind: str, subtype: str, block: Dict[str, Any]) -> None:
def _apply_conf_block(
config: Dict[str, Any], kind: str, subtype: str, block: Dict[str, Any]
) -> None:
kind_l = str(kind).strip().lower()
subtype_l = str(subtype).strip().lower()
@@ -290,23 +291,24 @@ def _serialize_conf(config: Dict[str, Any]) -> str:
def _make_cache_key(config_dir: Optional[Path], filename: str, actual_path: Optional[Path]) -> str:
if actual_path:
return str(actual_path.resolve())
base_dir = (config_dir or SCRIPT_DIR)
base_dir = config_dir or SCRIPT_DIR
return str((base_dir / filename).resolve())
def get_hydrus_instance(config: Dict[str, Any], instance_name: str = "home") -> Optional[Dict[str, Any]]:
def get_hydrus_instance(
config: Dict[str, Any], instance_name: str = "home"
) -> Optional[Dict[str, Any]]:
"""Get a specific Hydrus instance config by name.
Supports multiple formats:
- Current: config["store"]["hydrusnetwork"][instance_name]
- Legacy: config["storage"]["hydrus"][instance_name]
- Old: config["HydrusNetwork"][instance_name]
Args:
config: Configuration dict
instance_name: Name of the Hydrus instance (default: "home")
Returns:
Dict with access key and URL, or None if not found
"""
@@ -323,14 +325,14 @@ def get_hydrus_instance(config: Dict[str, Any], instance_name: str = "home") ->
def get_hydrus_access_key(config: Dict[str, Any], instance_name: str = "home") -> Optional[str]:
"""Get Hydrus access key for an instance.
Config format:
- config["store"]["hydrusnetwork"][name]["API"]
Args:
config: Configuration dict
instance_name: Name of the Hydrus instance (default: "home")
Returns:
Access key string, or None if not found
"""
@@ -344,14 +346,14 @@ def get_hydrus_access_key(config: Dict[str, Any], instance_name: str = "home") -
def get_hydrus_url(config: Dict[str, Any], instance_name: str = "home") -> Optional[str]:
"""Get Hydrus URL for an instance.
Config format:
- config["store"]["hydrusnetwork"][name]["URL"]
Args:
config: Configuration dict
instance_name: Name of the Hydrus instance (default: "home")
Returns:
URL string, or None if not found
"""
@@ -380,15 +382,14 @@ def get_soulseek_password(config: Dict[str, Any]) -> Optional[str]:
return str(val).strip() if val else None
def resolve_output_dir(config: Dict[str, Any]) -> Path:
"""Resolve output directory from config with single source of truth.
Priority:
1. config["temp"] - explicitly set temp/output directory
2. config["outfile"] - fallback to outfile setting
3. Home/Videos - safe user directory fallback
Returns:
Path to output directory
"""
@@ -402,7 +403,7 @@ def resolve_output_dir(config: Dict[str, Any]) -> Path:
return path
except Exception:
pass
# Then try outfile setting
outfile_value = config.get("outfile")
if outfile_value:
@@ -410,22 +411,22 @@ def resolve_output_dir(config: Dict[str, Any]) -> Path:
return Path(str(outfile_value)).expanduser()
except Exception:
pass
# Fallback to user's Videos directory
return Path.home() / "Videos"
def get_local_storage_path(config: Dict[str, Any]) -> Optional[Path]:
"""Get local storage path from config.
Supports multiple formats:
- New: config["store"]["folder"]["default"]["path"]
- Old: config["storage"]["local"]["path"]
- Old: config["Local"]["path"]
Args:
config: Configuration dict
Returns:
Path object if found, None otherwise
"""
@@ -439,7 +440,7 @@ def get_local_storage_path(config: Dict[str, Any]) -> Optional[Path]:
path_str = default_config.get("path")
if path_str:
return Path(str(path_str)).expanduser()
# Fall back to storage.local.path format
storage = config.get("storage", {})
if isinstance(storage, dict):
@@ -448,14 +449,14 @@ def get_local_storage_path(config: Dict[str, Any]) -> Optional[Path]:
path_str = local_config.get("path")
if path_str:
return Path(str(path_str)).expanduser()
# Fall back to old Local format
local_config = config.get("Local", {})
if isinstance(local_config, dict):
path_str = local_config.get("path")
if path_str:
return Path(str(path_str)).expanduser()
return None
@@ -465,11 +466,11 @@ def get_debrid_api_key(config: Dict[str, Any], service: str = "All-debrid") -> O
Config format:
- config["store"]["debrid"][<name>]["api_key"]
where <name> is the store name (e.g. "all-debrid")
Args:
config: Configuration dict
service: Service name (default: "All-debrid")
Returns:
API key string if found, None otherwise
"""
@@ -490,21 +491,21 @@ def get_debrid_api_key(config: Dict[str, Any], service: str = "All-debrid") -> O
if isinstance(entry, str):
return entry.strip() or None
return None
def get_provider_credentials(config: Dict[str, Any], provider: str) -> Optional[Dict[str, str]]:
"""Get provider credentials (email/password) from config.
Supports both formats:
- New: config["provider"][provider] = {"email": "...", "password": "..."}
- Old: config[provider.capitalize()] = {"email": "...", "password": "..."}
Args:
config: Configuration dict
provider: Provider name (e.g., "openlibrary", "soulseek")
Returns:
Dict with credentials if found, None otherwise
"""
@@ -514,7 +515,7 @@ def get_provider_credentials(config: Dict[str, Any], provider: str) -> Optional[
creds = provider_config.get(provider.lower(), {})
if isinstance(creds, dict) and creds:
return creds
# Fall back to old format (capitalized key)
old_key_map = {
"openlibrary": "OpenLibrary",
@@ -526,11 +527,13 @@ def get_provider_credentials(config: Dict[str, Any], provider: str) -> Optional[
creds = config.get(old_key, {})
if isinstance(creds, dict) and creds:
return creds
return None
def resolve_cookies_path(config: Dict[str, Any], script_dir: Optional[Path] = None) -> Optional[Path]:
def resolve_cookies_path(
config: Dict[str, Any], script_dir: Optional[Path] = None
) -> Optional[Path]:
# Support both legacy top-level `cookies=...` and the modular conf style:
# [tool=ytdlp]
# cookies="C:\\path\\cookies.txt"
@@ -573,6 +576,7 @@ def resolve_cookies_path(config: Dict[str, Any], script_dir: Optional[Path] = No
return default_path
return None
def resolve_debug_log(config: Dict[str, Any]) -> Optional[Path]:
value = config.get("download_debug_log")
if not value:
@@ -582,7 +586,10 @@ def resolve_debug_log(config: Dict[str, Any]) -> Optional[Path]:
path = Path.cwd() / path
return path
def load_config(config_dir: Optional[Path] = None, filename: str = DEFAULT_CONFIG_FILENAME) -> Dict[str, Any]:
def load_config(
config_dir: Optional[Path] = None, filename: str = DEFAULT_CONFIG_FILENAME
) -> Dict[str, Any]:
base_dir = config_dir or SCRIPT_DIR
config_path = base_dir / filename
cache_key = _make_cache_key(config_dir, filename, config_path)
@@ -608,7 +615,9 @@ def load_config(config_dir: Optional[Path] = None, filename: str = DEFAULT_CONFI
return data
def reload_config(config_dir: Optional[Path] = None, filename: str = DEFAULT_CONFIG_FILENAME) -> Dict[str, Any]:
def reload_config(
config_dir: Optional[Path] = None, filename: str = DEFAULT_CONFIG_FILENAME
) -> Dict[str, Any]:
cache_key = _make_cache_key(config_dir, filename, None)
_CONFIG_CACHE.pop(cache_key, None)
return load_config(config_dir=config_dir, filename=filename)
@@ -617,6 +626,7 @@ def reload_config(config_dir: Optional[Path] = None, filename: str = DEFAULT_CON
def clear_config_cache() -> None:
_CONFIG_CACHE.clear()
def save_config(
config: Dict[str, Any],
config_dir: Optional[Path] = None,
@@ -626,7 +636,9 @@ def save_config(
config_path = base_dir / filename
if config_path.suffix.lower() != ".conf":
raise RuntimeError(f"Unsupported config format: {config_path.name} (only .conf is supported)")
raise RuntimeError(
f"Unsupported config format: {config_path.name} (only .conf is supported)"
)
try:
config_path.write_text(_serialize_conf(config), encoding="utf-8")
@@ -636,10 +648,12 @@ def save_config(
cache_key = _make_cache_key(config_dir, filename, config_path)
_CONFIG_CACHE[cache_key] = config
def load() -> Dict[str, Any]:
"""Return the parsed downlow configuration."""
return load_config()
def save(config: Dict[str, Any]) -> None:
"""Persist *config* back to disk."""
save_config(config)