190 lines
5.9 KiB
Python
190 lines
5.9 KiB
Python
"""
|
||
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)
|