""" Configuration management for Curator Three levels of configuration: 1. Global config (.Curator.!gtag next to Curator.py) - app-wide settings 2. Folder config (.Curator.!ftag in project root) - folder-specific settings 3. File tags (.{filename}.!tag) - per-file metadata (handled in file.py) """ import json from pathlib import Path # Global config file (next to the main script) GLOBAL_CONFIG_FILE = Path(__file__).parent.parent.parent / ".Curator.!gtag" # Folder config filename FOLDER_CONFIG_NAME = ".Curator.!ftag" # ============================================================================= # GLOBAL CONFIG - Application settings # ============================================================================= DEFAULT_GLOBAL_CONFIG = { "window_geometry": "1200x800", "window_maximized": False, "last_folder": None, "sidebar_width": 250, "recent_folders": [], "pool_dir": None, # managed pool root (single source of truth) "filmoteka_dir": None, # generated Filmotéka output (hardlink tree) "copyasis_folders": ["Seriály"], # pool subfolders mirrored 1:1 (copy-as-is) } def load_global_config() -> dict: """Load global application config""" if GLOBAL_CONFIG_FILE.exists(): try: with open(GLOBAL_CONFIG_FILE, "r", encoding="utf-8") as f: config = json.load(f) # Merge with defaults for any missing keys for key, value in DEFAULT_GLOBAL_CONFIG.items(): if key not in config: config[key] = value return config except Exception: return DEFAULT_GLOBAL_CONFIG.copy() return DEFAULT_GLOBAL_CONFIG.copy() def save_global_config(cfg: dict): """Save global application config""" with open(GLOBAL_CONFIG_FILE, "w", encoding="utf-8") as f: json.dump(cfg, f, indent=2, ensure_ascii=False) # ============================================================================= # FOLDER CONFIG - Per-folder settings # ============================================================================= DEFAULT_FOLDER_CONFIG = { "ignore_patterns": [], "custom_tags": {}, # Additional tags specific to this folder "recursive": True, # Whether to scan subfolders "hardlink_output_dir": None, # Output directory for hardlink structure "hardlink_categories": None, # Categories to include in hardlink (None = all) } def get_folder_config_path(folder: Path) -> Path: """Get path to folder config file""" return folder / FOLDER_CONFIG_NAME def load_folder_config(folder: Path) -> dict: """Load folder-specific config""" config_path = get_folder_config_path(folder) if config_path.exists(): try: with open(config_path, "r", encoding="utf-8") as f: config = json.load(f) # Merge with defaults for any missing keys for key, value in DEFAULT_FOLDER_CONFIG.items(): if key not in config: config[key] = value return config except Exception: return DEFAULT_FOLDER_CONFIG.copy() return DEFAULT_FOLDER_CONFIG.copy() def save_folder_config(folder: Path, cfg: dict): """Save folder-specific config""" config_path = get_folder_config_path(folder) with open(config_path, "w", encoding="utf-8") as f: json.dump(cfg, f, indent=2, ensure_ascii=False) def folder_has_config(folder: Path) -> bool: """Check if folder has a tagger config""" return get_folder_config_path(folder).exists() # ============================================================================= # BACKWARDS COMPATIBILITY # ============================================================================= def load_config(): """Legacy function - returns global config""" return load_global_config() def save_config(cfg: dict): """Legacy function - saves global config""" save_global_config(cfg)