2026-04-06 20:22:25 +02:00
|
|
|
"""Konfiguration aus TOML-Datei mit sinnvollen Defaults."""
|
|
|
|
|
|
2026-04-08 10:26:17 +02:00
|
|
|
import os
|
|
|
|
|
import sys
|
2026-04-06 20:22:25 +02:00
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import tomllib
|
|
|
|
|
|
|
|
|
|
|
2026-04-08 10:26:17 +02:00
|
|
|
def _default_config_path() -> Path:
|
|
|
|
|
if sys.platform == "win32":
|
|
|
|
|
return Path(os.environ.get("APPDATA", "")) / "whisper-local" / "config.toml"
|
|
|
|
|
return Path.home() / ".config" / "whisper-local" / "config.toml"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_CONFIG_PATH = _default_config_path()
|
2026-04-06 20:22:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class Config:
|
|
|
|
|
hotkey: str = "KEY_F12"
|
|
|
|
|
whisper_model: str = "small"
|
|
|
|
|
language: str = "de"
|
|
|
|
|
compute_type: str = "int8"
|
|
|
|
|
sample_rate: int = 16000
|
|
|
|
|
channels: int = 1
|
|
|
|
|
min_duration: float = 0.5
|
2026-04-10 21:01:08 +02:00
|
|
|
microphone: str = ""
|
2026-04-06 20:22:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config:
|
|
|
|
|
"""Lädt Config aus TOML-Datei. Gibt Defaults zurück wenn Datei fehlt."""
|
|
|
|
|
config = Config()
|
|
|
|
|
|
|
|
|
|
if not path.exists():
|
|
|
|
|
return config
|
|
|
|
|
|
|
|
|
|
with open(path, "rb") as f:
|
|
|
|
|
data = tomllib.load(f)
|
|
|
|
|
|
|
|
|
|
hotkey_section = data.get("hotkey", {})
|
|
|
|
|
if "key" in hotkey_section:
|
|
|
|
|
config.hotkey = hotkey_section["key"]
|
|
|
|
|
|
|
|
|
|
whisper_section = data.get("whisper", {})
|
|
|
|
|
if "model" in whisper_section:
|
|
|
|
|
config.whisper_model = whisper_section["model"]
|
|
|
|
|
if "language" in whisper_section:
|
|
|
|
|
config.language = whisper_section["language"]
|
|
|
|
|
if "compute_type" in whisper_section:
|
|
|
|
|
config.compute_type = whisper_section["compute_type"]
|
|
|
|
|
|
|
|
|
|
audio_section = data.get("audio", {})
|
|
|
|
|
if "sample_rate" in audio_section:
|
|
|
|
|
config.sample_rate = audio_section["sample_rate"]
|
|
|
|
|
if "channels" in audio_section:
|
|
|
|
|
config.channels = audio_section["channels"]
|
|
|
|
|
if "min_duration" in audio_section:
|
|
|
|
|
config.min_duration = audio_section["min_duration"]
|
2026-04-10 21:01:08 +02:00
|
|
|
if "device" in audio_section:
|
|
|
|
|
config.microphone = audio_section["device"]
|
2026-04-06 20:22:25 +02:00
|
|
|
|
|
|
|
|
return config
|
2026-04-10 21:01:08 +02:00
|
|
|
|
|
|
|
|
|
2026-04-10 21:02:36 +02:00
|
|
|
def _toml_str(value: str) -> str:
|
|
|
|
|
"""Escaped einen String-Wert für TOML (verhindert ungültiges TOML bei Sonderzeichen)."""
|
|
|
|
|
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
|
|
|
|
|
|
|
|
|
2026-04-10 21:01:08 +02:00
|
|
|
def save_config(config: Config, path: Path = DEFAULT_CONFIG_PATH) -> None:
|
|
|
|
|
"""Schreibt Config als TOML-Datei. Erstellt Verzeichnisse bei Bedarf."""
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
content = (
|
2026-04-10 21:02:36 +02:00
|
|
|
f'[hotkey]\nkey = "{_toml_str(config.hotkey)}"\n\n'
|
|
|
|
|
f'[whisper]\nmodel = "{_toml_str(config.whisper_model)}"\n'
|
|
|
|
|
f'language = "{_toml_str(config.language)}"\n'
|
|
|
|
|
f'compute_type = "{_toml_str(config.compute_type)}"\n\n'
|
2026-04-10 21:01:08 +02:00
|
|
|
f'[audio]\nsample_rate = {config.sample_rate}\n'
|
|
|
|
|
f'channels = {config.channels}\n'
|
|
|
|
|
f'min_duration = {config.min_duration}\n'
|
2026-04-10 21:02:36 +02:00
|
|
|
f'device = "{_toml_str(config.microphone)}"\n'
|
2026-04-10 21:01:08 +02:00
|
|
|
)
|
|
|
|
|
path.write_text(content, encoding="utf-8")
|