Store library settings in the pool index and drop legacy sidecars
This commit is contained in:
+18
-58
@@ -1,10 +1,15 @@
|
||||
"""
|
||||
Configuration management for Curator
|
||||
Configuration 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)
|
||||
Two stores, by responsibility:
|
||||
|
||||
1. Global config (``.Curator.!gtag`` next to Curator.py) — **application data
|
||||
only**: window state, MRU folders, and the pointers to where the pool and the
|
||||
Filmotéka output live (``pool_dir`` / ``filmoteka_dir``). Those pointers must
|
||||
stay here because the pool index is found *through* them.
|
||||
2. Pool index (``<pool>/.Curator.!index``, see ``pool_index.py``) — everything
|
||||
describing a particular library: per-movie metadata **and** library-level
|
||||
settings (``tag_schema``, ``copyasis_folders``). It travels with the pool.
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
@@ -12,12 +17,9 @@ 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
|
||||
# TAG SCHEMA - default library settings (stored per-pool in the index)
|
||||
# =============================================================================
|
||||
|
||||
# Tag schema: the single source of truth for which tag categories exist, how
|
||||
@@ -35,6 +37,8 @@ FOLDER_CONFIG_NAME = ".Curator.!ftag"
|
||||
# keep the pool filename. Pool files are never renamed by this.
|
||||
# Grouping folders are prefixed with "- " so DLNA/TV browsers sort the special
|
||||
# folders (Dle …, Tipy dne, Nově přidané) before the genre folders at the root.
|
||||
# This lives in the code as the default; the effective schema is stored per-pool
|
||||
# in the index (``PoolIndex.settings["tag_schema"]``).
|
||||
DEFAULT_TAG_SCHEMA = [
|
||||
{"category": "Žánr", "csfd_field": "genres", "transform": None, "filmoteka_root": ""},
|
||||
{"category": "Rok", "csfd_field": "year", "transform": None, "filmoteka_root": "- Dle roku"},
|
||||
@@ -44,6 +48,11 @@ DEFAULT_TAG_SCHEMA = [
|
||||
"filmoteka_root": "- Dle hodnocení"},
|
||||
]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# GLOBAL CONFIG - Application settings only
|
||||
# =============================================================================
|
||||
|
||||
DEFAULT_GLOBAL_CONFIG = {
|
||||
"window_geometry": "1200x800",
|
||||
"window_maximized": False,
|
||||
@@ -52,8 +61,6 @@ DEFAULT_GLOBAL_CONFIG = {
|
||||
"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)
|
||||
"tag_schema": DEFAULT_TAG_SCHEMA, # tag categories + ČSFD/Filmotéka rules
|
||||
}
|
||||
|
||||
|
||||
@@ -79,53 +86,6 @@ def save_global_config(cfg: dict):
|
||||
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
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user