import json import os import sys def _app_dir() -> str: """Root dir for config.json and vocabulary.json.""" if getattr(sys, "frozen", False): return os.path.dirname(sys.executable) # config.py lives at whisper_app/config.py → parent of parent = repo root return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATA_DIR = os.environ.get("WHISPER_DATA_DIR") or _app_dir() _env_local = os.environ.get("WHISPER_LOCAL_DIR") if _env_local: _local_dir = _env_local elif os.name == "nt": _local_dir = os.path.join(os.environ.get("LOCALAPPDATA", DATA_DIR), "WhisperDictation") else: _local_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "WhisperDictation") CONFIG_FILE = os.path.join(DATA_DIR, "config.json") CONFIG_LOCAL_FILE = os.path.join(_local_dir, "config_local.json") VOCAB_FILE = os.path.join(DATA_DIR, "vocabulary.json") DEFAULT_CONFIG = { "hotkey": "ctrl+shift+space", "model": "medium", "device": "cuda", "compute_type": "float16", "language": "de", "audio_device": None, "sample_rate": 16000, "vocab_path": "", "model_dir": "", "grammar_check": True, "paste_delay_ms": 300, "media_duck": True, "duck_percent": 20, } MODELS = ["tiny", "base", "small", "medium", "large-v2", "large-v3"] LANGUAGES = {"Deutsch": "de", "English": "en", "Français": "fr", "Español": "es", "Italiano": "it", "Auto": None} DEVICES = ["cuda", "cpu"] COMPUTE_TYPES = {"float16 (GPU)": "float16", "int8 (CPU/GPU)": "int8", "float32": "float32"} LOCAL_KEYS = {"audio_device", "device", "compute_type", "model"} config: dict = {} vocab: dict = {"words": [], "replacements": []} def _resolve_vocab_file() -> None: """Set VOCAB_FILE from config['vocab_path'], falling back to DATA_DIR.""" global VOCAB_FILE vp = config.get("vocab_path", "") if vp: VOCAB_FILE = vp if os.path.isabs(vp) else os.path.join(DATA_DIR, vp) else: VOCAB_FILE = os.path.join(DATA_DIR, "vocabulary.json") def load_config() -> None: global config os.makedirs(_local_dir, exist_ok=True) config = dict(DEFAULT_CONFIG) if os.path.exists(CONFIG_FILE): with open(CONFIG_FILE, encoding="utf-8") as f: try: config.update(json.load(f)) except json.JSONDecodeError: print(f"Warning: could not parse {CONFIG_FILE}; using defaults") config = dict(DEFAULT_CONFIG) if os.path.exists(CONFIG_LOCAL_FILE): with open(CONFIG_LOCAL_FILE, encoding="utf-8") as f: try: config.update(json.load(f)) except json.JSONDecodeError: print(f"Warning: could not parse {CONFIG_LOCAL_FILE}; ignoring") _resolve_vocab_file() def save_config() -> None: os.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True) os.makedirs(os.path.dirname(CONFIG_LOCAL_FILE), exist_ok=True) shared = {k: v for k, v in config.items() if k not in LOCAL_KEYS} local = {k: v for k, v in config.items() if k in LOCAL_KEYS} with open(CONFIG_FILE, "w", encoding="utf-8") as f: json.dump(shared, f, indent=2) with open(CONFIG_LOCAL_FILE, "w", encoding="utf-8") as f: json.dump(local, f, indent=2) _resolve_vocab_file() def load_vocab() -> None: global vocab if os.path.exists(VOCAB_FILE): with open(VOCAB_FILE, encoding="utf-8") as f: try: vocab = json.load(f) except json.JSONDecodeError: print(f"Warning: could not parse {VOCAB_FILE}; using empty vocab") vocab = {"words": [], "replacements": []} else: vocab = {"words": [], "replacements": []} def save_vocab() -> None: os.makedirs(os.path.dirname(VOCAB_FILE), exist_ok=True) with open(VOCAB_FILE, "w", encoding="utf-8") as f: json.dump(vocab, f, indent=2, ensure_ascii=False) def apply_vocab(text: str) -> str: for r in vocab.get("replacements", []): text = text.replace(r["from"], r["to"]) return text _STYLE_HINTS = { "de": "Hallo, wie geht es Ihnen? Ich arbeite an einem wichtigen Projekt. " "Die Ergebnisse der Analyse zeigen deutliche Verbesserungen.", "en": "Hello, how are you? I am working on an important project. " "The analysis results show clear improvements.", "fr": "Bonjour, comment allez-vous ? Je travaille sur un projet important. " "Les résultats de l'analyse montrent des améliorations nettes.", } def get_initial_prompt() -> str: parts = [] lang = config.get("language") hint = _STYLE_HINTS.get(lang) if hint: parts.append(hint) words = vocab.get("words", []) if words: parts.append(", ".join(words)) return " ".join(parts) if parts else ""