""" fcco-Converter V2 – Konfigurationsmodul Lädt und speichert Anwendungseinstellungen plattformübergreifend. Verwendet pathlib für alle Pfadoperationen. """ import json import platform import sys from pathlib import Path # ---- Globale App-Informationen (aus config.json) ---- APP_INFO = { "app_name": "fcco Converter", "app_version": "2.0.0", "repo_owner": "public", "repo_name": "fcco-Converter", "gitea_api_base": "https://git.joelunger.de/api/v1" } def _load_app_info(): try: # Finde config.json relativ zum Skript oder im PyInstaller Bundle if getattr(sys, "frozen", False): if platform.system() == "Darwin": # Path(sys.executable) ist fcco-Converter.app/Contents/MacOS/executable base_dir = Path(sys.executable).parent.parent.parent.parent else: base_dir = Path(sys.executable).parent else: base_dir = Path(__file__).parent.parent config_path = base_dir / "config.json" if config_path.exists(): with open(config_path, "r", encoding="utf-8") as f: data = json.load(f) APP_INFO.update(data) except Exception: pass _load_app_info() # ========================================================================= # Farbschema # ========================================================================= T = { "bg": "#000000", "surface": "#111111", "elevated": "#1a1a1a", "card": "#222222", "input": "#000000", "accent": "#ffffff", "accent_h": "#e6e6e6", "success": "#ffffff", "success_h": "#e6e6e6", "warn": "#ffffff", "error": "#ffffff", "text": "#ffffff", "text2": "#b3b3b3", "muted": "#808080", "border": "#333333", "btn2": "#1a1a1a", "btn2_h": "#333333", } # Standardmäßige Export-Kategorien DEFAULT_EXPORT_PATHS = { "Gottesdienst": "gottesdienste", "Hochzeit": "hochzeiten", "Weissagung": "weissagungen", "Jugend": "jugend", } # Kurze deutsche Wochentagsnamen für Ordnerbenennung WEEKDAY_SHORT_DE = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] # ---- Audio-Einstellungen: Gültige Werte ---- BITRATE_OPTIONS = ["64", "96", "128", "160", "192", "224", "256", "320"] SAMPLERATE_OPTIONS = ["Original", "22050", "44100", "48000"] CHANNEL_OPTIONS = ["Original", "Mono", "Stereo"] ENCODING_MODE_OPTIONS = ["CBR", "VBR"] VBR_QUALITY_OPTIONS = [str(i) for i in range(10)] # 0 (beste) bis 9 (schlechteste) OUTPUT_FORMAT_OPTIONS = ["mp3", "flac", "ogg", "wav"] def _get_config_dir() -> Path: """Gibt das plattformabhängige Konfigurationsverzeichnis zurück.""" system = platform.system() if system == "Windows": base = Path.home() / "AppData" / "Roaming" elif system == "Darwin": base = Path.home() / "Library" / "Application Support" else: # Linux / andere base = Path.home() / ".config" config_dir = base / "fcco-Converter" config_dir.mkdir(parents=True, exist_ok=True) return config_dir CONFIG_DIR = _get_config_dir() SETTINGS_FILE = CONFIG_DIR / "settings.json" HISTORY_FILE = CONFIG_DIR / "history.json" # ---- Standard-Einstellungen ---- DEFAULT_SETTINGS = { # Allgemein "threads": 0, # 0 = auto (alle verfügbaren Kerne) "appearance_mode": "Dark", # Dark / Light / System "check_updates_on_start": True, # Audio & Konvertierung "audio": { "output_format": "mp3", "bitrate": "192", # kbit/s (nur CBR) "encoding_mode": "CBR", # CBR oder VBR "vbr_quality": "2", # 0 (beste) bis 9 (schlechteste) "sample_rate": "Original", # Original, 22050, 44100, 48000 "channels": "Original", # Original, Mono, Stereo "overwrite_existing": True, }, # Metadaten "metadata": { "artist": "", "cover": "", "auto_track_number": True, "auto_album_from_folder": True, }, # Export-Pfade "export_paths": DEFAULT_EXPORT_PATHS.copy(), } def _deep_merge(base: dict, override: dict) -> dict: """Merged override-Werte in base, ohne fehlende Schlüssel zu verlieren.""" result = base.copy() for key, value in override.items(): if key in result and isinstance(result[key], dict) and isinstance(value, dict): result[key] = _deep_merge(result[key], value) else: result[key] = value return result def load_settings() -> dict: """Lädt die Einstellungen aus der JSON-Datei. Fehlende Schlüssel werden mit Standardwerten aufgefüllt. """ settings = DEFAULT_SETTINGS.copy() # Deep-copy verschachtelter Dicts settings["audio"] = DEFAULT_SETTINGS["audio"].copy() settings["metadata"] = DEFAULT_SETTINGS["metadata"].copy() settings["export_paths"] = DEFAULT_SETTINGS["export_paths"].copy() if SETTINGS_FILE.exists(): try: with open(SETTINGS_FILE, "r", encoding="utf-8") as f: loaded = json.load(f) settings = _deep_merge(settings, loaded) except (json.JSONDecodeError, OSError): pass # Bei fehlerhafter Datei: Standardwerte nutzen return settings def save_settings(settings: dict) -> None: """Speichert die Einstellungen in die JSON-Datei.""" with open(SETTINGS_FILE, "w", encoding="utf-8") as f: json.dump(settings, f, indent=2, ensure_ascii=False) def save_export_history(entry: dict) -> None: """Fügt einen Export-Eintrag zur Historie hinzu.""" history = [] if HISTORY_FILE.exists(): try: with open(HISTORY_FILE, "r", encoding="utf-8") as f: history = json.load(f) except (json.JSONDecodeError, OSError): history = [] history.append(entry) with open(HISTORY_FILE, "w", encoding="utf-8") as f: json.dump(history, f, indent=2, ensure_ascii=False)