Files

97 lines
3.3 KiB
Python
Raw Permalink Normal View History

2026-04-08 10:56:45 +02:00
"""Configuration management — env vars, config file, and defaults."""
from __future__ import annotations
from pathlib import Path
from typing import Optional
import toml
from platformdirs import user_config_dir
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
CONFIG_DIR = Path(user_config_dir("freepik-cli"))
CONFIG_FILE = CONFIG_DIR / "config.toml"
class FreepikConfig(BaseSettings):
"""
Configuration with priority (highest to lowest):
1. CLI --api-key flag (handled in commands directly)
2. FREEPIK_* environment variables
3. ~/.config/freepik-cli/config.toml
4. Defaults below
"""
model_config = SettingsConfigDict(
env_prefix="FREEPIK_",
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
api_key: Optional[str] = None
base_url: str = "https://api.freepik.com"
default_output_dir: str = "."
default_image_model: str = "flux-2-pro"
default_video_model: str = "kling-o1-pro"
default_upscale_mode: str = "precision-v2"
poll_timeout: int = 600
poll_max_interval: int = 15
show_banner: bool = True
@field_validator("api_key", mode="before")
@classmethod
def strip_api_key(cls, v: Optional[str]) -> Optional[str]:
return v.strip() if isinstance(v, str) else v
@classmethod
def load(cls) -> "FreepikConfig":
"""Load from config file, then overlay environment variables."""
file_data: dict = {}
if CONFIG_FILE.exists():
try:
file_data = toml.load(CONFIG_FILE)
except Exception:
pass
return cls(**file_data)
def save(self, exclude_keys: set[str] | None = None) -> None:
"""Persist non-sensitive config to disk."""
exclude_keys = (exclude_keys or set()) | {"api_key"}
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
data = self.model_dump(exclude=exclude_keys, exclude_none=True)
# Stringify paths
for k, v in data.items():
if isinstance(v, Path):
data[k] = str(v)
with open(CONFIG_FILE, "w") as f:
toml.dump(data, f)
def to_display_dict(self) -> dict:
"""Return all settings as a displayable dict (keeps api_key for masking)."""
d = self.model_dump()
return {k: v for k, v in d.items()}
def set_value(self, key: str, value: str) -> None:
"""Update a single config key and save."""
allowed = {
"base_url", "default_output_dir", "default_image_model",
"default_video_model", "default_upscale_mode", "poll_timeout",
"poll_max_interval", "show_banner",
}
if key not in allowed:
raise ValueError(
f"Key '{key}' is not configurable via this command. "
f"Use the FREEPIK_API_KEY environment variable to set the API key."
)
current = self.model_dump()
if key in ("poll_timeout", "poll_max_interval"):
current[key] = int(value)
elif key == "show_banner":
current[key] = value.lower() in ("true", "1", "yes")
else:
current[key] = value
updated = FreepikConfig(**{k: v for k, v in current.items() if v is not None})
updated.save()