diff --git a/whisper_local/__main__.py b/whisper_local/__main__.py index ea259db..476cc0f 100644 --- a/whisper_local/__main__.py +++ b/whisper_local/__main__.py @@ -5,6 +5,7 @@ import logging import sys from whisper_local.config import Config, load_config +from whisper_local.microphone import create_monitor from whisper_local.hotkey import create_listener from whisper_local.inserter import create_inserter from whisper_local.media import create_media_controller @@ -52,6 +53,10 @@ class App: self.hotkey = create_listener(key_name=config.hotkey) self.hotkey.on_press = self.on_press self.hotkey.on_release = self.on_release + self.monitor = create_monitor(config.microphone or None) + self.monitor.on_device_added = self._on_microphone_added + self.monitor.on_device_removed = self._on_microphone_removed + self.monitor.on_configured_missing = self._on_configured_microphone_missing self.tray = create_tray(on_settings=self._open_settings, on_quit=self._quit) async def on_press(self) -> None: @@ -93,6 +98,44 @@ class App: from whisper_local.tray._settings import SettingsDialog SettingsDialog(config=self._config, on_save=self._on_config_reload).open() + async def _on_configured_microphone_missing(self) -> None: + """Konfiguriertes Mikrofon nicht gefunden — auf Standard wechseln.""" + from whisper_local.tray._notification import notify + device_name = self._config.microphone or "Mikrofon" + logger.warning("Konfiguriertes Mikrofon '%s' nicht gefunden, nutze Standard", device_name) + self.recorder = Recorder( + sample_rate=self._config.sample_rate, + channels=self._config.channels, + min_duration=self._config.min_duration, + device=None, + ) + notify( + "Mikrofon nicht gefunden", + f'„{device_name}“ ist nicht verfügbar. Standard-Mikrofon wird verwendet.', + ) + self.tray.set_warning("Mikrofon nicht gefunden") + + async def _on_microphone_added(self, device_name: str) -> None: + """Neues Mikrofon erkannt — konfiguriertes Gerät ggf. wiederherstellen.""" + if device_name != self._config.microphone: + return + from whisper_local.tray._notification import notify + logger.info("Konfiguriertes Mikrofon '%s' wieder verfügbar", device_name) + self.recorder = Recorder( + sample_rate=self._config.sample_rate, + channels=self._config.channels, + min_duration=self._config.min_duration, + device=self._config.microphone or None, + ) + notify("Mikrofon verbunden", f'„{device_name}" ist wieder verfügbar.') + self.tray.set_warning(None) + + async def _on_microphone_removed(self, device_name: str) -> None: + """Mikrofon entfernt — konfiguriertes Gerät → Fallback auslösen.""" + logger.info("Mikrofon entfernt: %s", device_name) + if device_name == self._config.microphone: + await self._on_configured_microphone_missing() + def _on_config_reload(self, new_config: Config) -> None: """Übernimmt neue Konfiguration ohne App-Neustart.""" self._config = new_config @@ -102,6 +145,14 @@ class App: min_duration=new_config.min_duration, device=new_config.microphone or None, ) + self.monitor.stop() + self.monitor = create_monitor(new_config.microphone or None) + self.monitor.on_device_added = self._on_microphone_added + self.monitor.on_device_removed = self._on_microphone_removed + self.monitor.on_configured_missing = self._on_configured_microphone_missing + if self._loop is not None: + asyncio.run_coroutine_threadsafe(self.monitor.start(), self._loop) + self.tray.set_warning(None) old_media = self.media self.media = create_media_controller( enabled=new_config.pause_media_during_recording @@ -128,6 +179,7 @@ class App: logger.info("whisper-local gestartet, warte auf Hotkey...") self.tray.start() self._hotkey_task = asyncio.create_task(self.hotkey.listen()) + asyncio.create_task(self.monitor.start()) await self._quit_event.wait()