docs: OpenWiki-Dokumentation und Auto-Update-Workflow hinzufügen
Fügt generierte OpenWiki-Docs (Architektur, Domain, Betrieb, Tests, Workflows) sowie den GitHub-Actions-Workflow zur automatischen Aktualisierung hinzu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Files
|
||||
|
||||
- [Plattform-Abstraktionen](platform-abstractions.md) - Die vier plattformabhängigen Module — Hotkey, Inserter, Media, Mikrofon — mit ihren jeweiligen Linux- und Windows-Backends. Protocol-Interfaces, Factory-Dispatch und implementierungsspezifische Details.
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
type: Domain
|
||||
title: Plattform-Abstraktionen
|
||||
description: Die vier plattformabhängigen Module — Hotkey, Inserter, Media, Mikrofon — mit ihren jeweiligen Linux- und Windows-Backends. Protocol-Interfaces, Factory-Dispatch und implementierungsspezifische Details.
|
||||
tags: [domain, platform-abstraction, hotkey, inserter, media, microphone, protocol, factory]
|
||||
---
|
||||
|
||||
# Plattform-Abstraktionen
|
||||
|
||||
whisper-local abstrahiert vier plattformabhängige Funktionsbereiche hinter `typing.Protocol`-Interfaces mit Factory-Dispatch. Die [Architektur-Übersicht](../architecture/overview.md) beschreibt das generelle Muster; hier werden die konkreten Backends dokumentiert.
|
||||
|
||||
## HotkeyListener
|
||||
|
||||
**Protocol**: `HotkeyListener` in [`hotkey/__init__.py`](../../whisper_local/hotkey/__init__.py)
|
||||
**Attribute**: `on_press: AsyncCallback | None`, `on_release: AsyncCallback | None`
|
||||
**Methoden**: `async listen()`, `stop()`
|
||||
|
||||
### EvdevHotkeyListener (Linux)
|
||||
|
||||
Datei: [`hotkey/_evdev.py`](../../whisper_local/hotkey/_evdev.py)
|
||||
|
||||
- `find_keyboard_devices(key_name)` durchsucht alle `/dev/input/`-Devices und findet jene, die den konfigurierten Key unterstützen. Wirft `RuntimeError` wenn kein passendes Device gefunden wird.
|
||||
- `listen()` startet pro gefundenem Device einen `asyncio.Task` mit `async_read_loop()`. Dadurch werden alle Tastaturen parallel überwacht — wichtig bei Multi-Device-Setups.
|
||||
- `stop()` cancelt alle Tasks und schließt Devices.
|
||||
|
||||
### PynputHotkeyListener (Windows/Fallback)
|
||||
|
||||
Datei: [`hotkey/_pynput.py`](../../whisper_local/hotkey/_pynput.py)
|
||||
|
||||
- `_evdev_to_pynput_key()` übersetzt evdev-Namen (`KEY_F12`) ins pynput-Format (`Key.f12`). Unterstützt F-Tasten per Regex und direkte Attribut-Zuordnung.
|
||||
- **Key-Repeat-Unterdrückung**: Das `_pressed`-Flag verhindert, dass OS-Key-Repeat mehrfache `on_press`-Callbacks auslöst. Erst beim echten `on_release` wird das Flag zurückgesetzt.
|
||||
- pynput betreibt einen Thread; Callbacks werden via `call_soon_threadsafe` + `asyncio.ensure_future` in den Loop gebridged.
|
||||
- `stop()` signalisiert über ein `asyncio.Event`; der Listener wird im `finally`-Block gestoppt.
|
||||
|
||||
## Inserter
|
||||
|
||||
**Protocol**: `Inserter` in [`inserter/__init__.py`](../../whisper_local/inserter/__init__.py)
|
||||
**Methode**: `async insert(text: str)`
|
||||
|
||||
Beide Backends sichern den alten Clipboard-Inhalt, setzen den Text, senden `Ctrl+V`, und restaurieren das Clipboard im `finally`-Block. `PASTE_DELAY = 0.2s` nach dem Paste-Befehl.
|
||||
|
||||
### WaylandInserter (Linux)
|
||||
|
||||
Datei: [`inserter/_wayland.py`](../../whisper_local/inserter/_wayland.py)
|
||||
|
||||
- Clipboard-Backup via `wl-paste --no-newline`.
|
||||
- Text setzen via `wl-copy -- <text>`.
|
||||
- `Ctrl+V` simulieren via `ydotool key 29:1 47:1 47:0 29:0` (evdev Keycodes für Ctrl down, V down, V up, Ctrl up).
|
||||
- Erfordert `ydotool`-Daemon und `input`-Gruppen-Mitgliedschaft.
|
||||
|
||||
### Win32Inserter (Windows)
|
||||
|
||||
Datei: [`inserter/_win32.py`](../../whisper_local/inserter/_win32.py)
|
||||
|
||||
- Clipboard-Operationen via `win32clipboard` (CF_UNICODETEXT).
|
||||
- `Ctrl+V` via `pynput.keyboard.Controller`.
|
||||
- Blocking-Aufrufe werden mit `asyncio.to_thread()` in Threads ausgelagert, um den Event-Loop nicht zu blockieren.
|
||||
|
||||
## MediaController
|
||||
|
||||
**Protocol**: `MediaController` in [`media/__init__.py`](../../whisper_local/media/__init__.py)
|
||||
**Methoden**: `async pause()`, `async resume()`
|
||||
|
||||
`create_media_controller(enabled)` gibt `NoopController` zurück wenn `enabled=False` oder die Plattform nicht unterstützt wird.
|
||||
|
||||
### MprisController (Linux)
|
||||
|
||||
Datei: [`media/_mpris.py`](../../whisper_local/media/_mpris.py)
|
||||
|
||||
- Verbindet sich via `dbus-next` (`MessageBus().connect()`) zur D-Bus-Session.
|
||||
- `pause()`: Listet alle `org.mpris.MediaPlayer2.*`-Services auf, pausiert nur Sessions im Status `Playing`, merkt sich pausierte Bus-Namen in `_paused`.
|
||||
- `resume()`: Setzt nur die Sessions fort, die selbst pausiert wurden (`_paused`-Liste).
|
||||
- **Circuit-Breaker**: `_bus_broken`-Flag deaktiviert den Controller dauerhaft bei D-Bus-Verbindungsfehler.
|
||||
|
||||
### SmtcController (Windows)
|
||||
|
||||
Datei: [`media/_smtc.py`](../../whisper_local/media/_smtc.py)
|
||||
|
||||
- Nutzt `winrt.windows.media.control.GlobalSystemMediaTransportControlsSessionManager`.
|
||||
- `pause()`: Iteriert über alle Sessions, pausiert nur `PLAYING`-Sessions via `try_pause_async()`, merkt sich AUMIDs in `_paused`.
|
||||
- `resume()`: Holt aktuelle Sessions, spielt nur die in `_paused` gelisteten via `try_play_async()` wieder ab. Überspringt Sessions, die nicht mehr existieren.
|
||||
- **Circuit-Breaker**: `_broken`-Flag bei SMTC-Initialisierungsfehler.
|
||||
|
||||
### NoopController
|
||||
|
||||
Datei: [`media/_noop.py`](../../whisper_local/media/_noop.py)
|
||||
|
||||
- `pause()` und `resume()` sind No-Ops. Wird verwendet wenn das Feature deaktiviert ist oder die Plattform nicht unterstützt wird.
|
||||
|
||||
## Mikrofon-Monitor
|
||||
|
||||
**Protocol**: `MicrophoneMonitor` in [`microphone/__init__.py`](../../whisper_local/microphone/__init__.py)
|
||||
**Callbacks**: `on_device_added`, `on_device_removed`, `on_configured_missing` (alle `Awaitable`)
|
||||
**Methoden**: `async start()`, `stop()`
|
||||
|
||||
### PollMonitor (Cross-Platform)
|
||||
|
||||
Datei: [`microphone/_poll.py`](../../whisper_local/microphone/_poll.py)
|
||||
|
||||
- Pollt `sounddevice.query_devices()` alle 2,5 s, vergleicht mit `_known_devices`.
|
||||
- `start()` prüft sofort ob das konfigurierte Gerät vorhanden ist und ruft `on_configured_missing()` auf wenn nicht.
|
||||
- Bei Fehler beim Abfragen der Geräte wird der bisherige Stand beibehalten (kein Crash).
|
||||
|
||||
### Win32Monitor (Windows)
|
||||
|
||||
Datei: [`microphone/_win32.py`](../../whisper_local/microphone/_win32.py)
|
||||
|
||||
- Definiert COM-Interfaces (`IMMNotificationClient`, `IMMDeviceEnumerator`) via `comtypes` zur Laufzeit.
|
||||
- Registriert einen `IMMNotificationClient`-Callback bei der Core Audio API. COM-Events werden via `call_soon_threadsafe` in den asyncio-Loop delegiert.
|
||||
- **Polling-Fallback**: Wenn COM-Initialisierung fehlschlägt, wird automatisch ein `PollMonitor` als Fallback gestartet.
|
||||
- `stop()` deregistriert den COM-Callback und ruft `CoUninitialize()` auf.
|
||||
|
||||
### Geräte-Erkennungslogik
|
||||
|
||||
Alle Monitors verwenden dieselbe Logik: `sd.query_devices()` filtern auf `max_input_channels > 0` (Eingabegeräte) und extrahieren Gerätenamen. Der Vergleich erfolgt über Set-Differenzen (`added = current - known`, `removed = known - current`).
|
||||
Reference in New Issue
Block a user