Entfernt den Windows-only-Guard in App.__init__, damit der Dialog mit indeterminatem ttk.Progressbar auch unter Linux erscheint, wenn das Laden länger als 500 ms dauert. Ersetzt das literale \u2026 im Label durch das Zeichen … und passt Spec/Plan an den tatsächlichen Umsetzungsstand an (Timeout-basierter Wartebalken statt tqdm-Monkey-Patch, da die Xet-Engine Python-tqdm bypasst). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.0 KiB
Design: Modell-Lade-Wartebalken
Datum: 2026-04-12 Zuletzt geändert: 2026-04-16 Status: Umgesetzt
Problemstellung
Beim ersten Start lädt faster_whisper das Whisper-Modell von HuggingFace herunter (z.B. small ≈ 460 MB). Dieser Vorgang blockiert App.__init__() ohne jede Rückmeldung. Der Nutzer sieht einen eingefrorenen Start ohne Statusanzeige.
Ziel
Ein kleiner tkinter-Dialog erscheint automatisch, wenn das Laden des Modells länger als 500 ms dauert, und zeigt einen animierten Wartebalken. Ist das Modell bereits vollständig gecacht und wird in unter 500 ms bereit, erscheint kein Dialog.
Verworfener Ansatz: tqdm-Monkey-Patch
Die ursprüngliche Idee war, tqdm.tqdm während des Downloads mit einer eigenen Klasse (TkProgressTqdm) zu ersetzen und echten Byte-Fortschritt anzuzeigen. Dieser Ansatz funktioniert nicht mehr, weil huggingface_hub für große Dateien (model.bin) die Xet-Engine (Rust) nutzt, die Python-tqdm komplett bypasst. Der Dialog blieb dadurch leer, bis der Download fertig war.
Die Klasse TkProgressTqdm existiert noch im Code (mit Unit-Tests), wird aber von der Lade-Funktion nicht mehr aktiviert — sie bleibt als möglicher Fallback erhalten, falls zukünftige faster_whisper-Versionen wieder durch Python-tqdm laufen.
Aktueller Ansatz: Timeout-basierter Wartebalken
- Worker-Thread startet
WhisperModel(...). - Hauptthread wartet via
threading.Event.wait(timeout=0.5). - Ist der Thread innerhalb von 500 ms fertig (vollständiger Cache) → kein Dialog, direkter Rückweg.
- Sonst → tkinter-Dialog mit indeterminatem
ttk.Progressbaröffnen, bis der Worker signalisiert.
Architektur & Ablauf
main()
└─ App.__init__()
├─ load_model_with_progress(model_name, compute_type, download_root)
│ ├─ [Daemon-Thread] WhisperModel(...)
│ └─ [Hauptthread] done_event.wait(0.5)
│ ├─ sofort fertig → Modell zurückgeben (kein Dialog)
│ └─ Timeout → tkinter-Fenster + Indeterminate-Progressbar
│ └─ root.after(100, poll) bis done_event.is_set()
└─ Transcriber(model=fertig_geladenes_modell)
Dialog läuft plattformübergreifend (Linux + Windows) — tkinter gehört zur Standardbibliothek.
Komponenten
Datei: whisper_local/tray/_download_progress.py
TkProgressTqdm — ungenutzter tqdm.tqdm-Ersatz (historischer Fallback):
- Erbt von
tqdm.tqdm - Akkumuliert Fortschritt und schreibt Messages in
TkProgressTqdm._queue, falls gesetzt - Wird derzeit nicht aktiviert (Xet bypasst tqdm)
load_model_with_progress(model_name, compute_type, download_root) -> WhisperModel — öffentliche Funktion:
- Startet Daemon-Thread mit
WhisperModel(...) - Wartet 500 ms via
done_event.wait(timeout=0.5) - Kehrt bei schnellem Abschluss direkt zurück
- Öffnet sonst tkinter-Fenster mit indeterminatem
ttk.Progressbar - Pollt
done_eventalle 100 ms viaroot.afterund beendetmainloop()bei Signal - Bei Exception im Worker:
messagebox.showerror+sys.exit(1)
Datei: whisper_local/__main__.py
App.__init__() ruft load_model_with_progress() auf (plattformunabhängig) und übergibt das fertige WhisperModel-Objekt an Transcriber.
Datei: whisper_local/transcriber.py
Transcriber.__init__() akzeptiert optional ein bereits geladenes WhisperModel-Objekt via Parameter model: WhisperModel | None = None.
Dialog-Darstellung
- Titel:
"whisper-local – Modell wird geladen" - Label oben:
"Lade Whisper-Modell '<model_name>'..." - Label darunter:
"Bitte warten…"(grau) ttk.Progressbarimindeterminate-Modus (Länge 320 px, viapb.start(10)animiert)- Kein Abbrechen-Button
apply_system_theme(root)für konsistentes Aussehenroot.resizable(False, False)
Threading-Modell
| Thread | Aufgabe |
|---|---|
| Hauptthread | done_event.wait(0.5), tkinter mainloop(), Polling via root.after |
| Daemon-Thread | WhisperModel(...) |
Kommunikation über threading.Event (kein Monkey-Patch, keine Queue in der Lade-Funktion).
Fehlerbehandlung
- Kein Internet / Download-Fehler: Exception im Thread → nach
done_event.set()im Hauptthread geprüft →messagebox.showerror("Fehler beim Modell-Laden", str(exc))→sys.exit(1) - Modell bereits gecacht und schnell bereit:
done_event.wait(0.5)returntTrue→ kein Dialog erscheint
Plattform
Kein Plattform-Guard — load_model_with_progress() läuft auf Linux und Windows identisch. tkinter ist Teil der Python-Standardbibliothek und auf beiden Systemen verfügbar.
Dateien
| Datei | Status |
|---|---|
whisper_local/tray/_download_progress.py |
Umgesetzt (Timeout-basierter Wartebalken) |
whisper_local/__main__.py |
Umgesetzt (Preload plattformübergreifend) |
whisper_local/transcriber.py |
Umgesetzt (optionaler model-Parameter) |
tests/test_download_progress.py |
Vorhanden (deckt ungenutzte TkProgressTqdm ab) |
tests/test_transcriber.py |
Vorhanden (deckt model-Parameter ab) |