2026-04-12 12:28:13 +02:00
|
|
|
|
"""Download-Fortschrittsdialog für den ersten Whisper-Modell-Download (Windows)."""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import queue
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import threading
|
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
|
|
import tqdm as tqdm_module
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TkProgressTqdm(tqdm_module.tqdm):
|
|
|
|
|
|
"""tqdm-Ersatz, der Fortschritts-Updates thread-safe in eine Queue schreibt."""
|
|
|
|
|
|
|
|
|
|
|
|
_queue: queue.Queue | None = None
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
|
|
|
|
self._desc = kwargs.get("desc", "")
|
|
|
|
|
|
self._accumulated_n = 0
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
2026-04-12 12:29:34 +02:00
|
|
|
|
def update(self, n: int | float | None = 1) -> bool | None:
|
|
|
|
|
|
if n is None:
|
|
|
|
|
|
n = 0
|
2026-04-12 12:28:13 +02:00
|
|
|
|
self._accumulated_n += n
|
|
|
|
|
|
result = super().update(n)
|
|
|
|
|
|
if self._queue is not None:
|
|
|
|
|
|
self._queue.put({
|
|
|
|
|
|
"file": self._desc,
|
|
|
|
|
|
"n": self._accumulated_n,
|
|
|
|
|
|
"total": self.total or 0,
|
|
|
|
|
|
})
|
|
|
|
|
|
return result
|
2026-04-12 12:30:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_model_with_progress(
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
compute_type: str,
|
|
|
|
|
|
download_root: str | None,
|
|
|
|
|
|
) -> Any:
|
2026-04-12 12:49:31 +02:00
|
|
|
|
"""Lädt WhisperModel — zeigt bei längerem Laden einen Wartebalken.
|
2026-04-12 12:30:48 +02:00
|
|
|
|
|
2026-04-12 12:49:31 +02:00
|
|
|
|
Wenn das Modell in unter 500 ms bereit ist (vollständiger Cache), erscheint
|
|
|
|
|
|
kein Dialog. Bei Download oder langsamer Initialisierung wird ein indeterminater
|
|
|
|
|
|
Wartebalken angezeigt. Auf Fehler wird ein Fehlerdialog gezeigt und sys.exit(1)
|
2026-04-12 12:30:48 +02:00
|
|
|
|
aufgerufen.
|
|
|
|
|
|
"""
|
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
|
from tkinter import messagebox, ttk
|
|
|
|
|
|
|
|
|
|
|
|
from faster_whisper import WhisperModel
|
|
|
|
|
|
|
|
|
|
|
|
from whisper_local.tray._theme import apply_system_theme
|
|
|
|
|
|
|
2026-04-12 12:49:31 +02:00
|
|
|
|
result: list[Any] = [None]
|
2026-04-12 12:30:48 +02:00
|
|
|
|
error: list[BaseException | None] = [None]
|
2026-04-12 12:49:31 +02:00
|
|
|
|
done_event = threading.Event()
|
2026-04-12 12:30:48 +02:00
|
|
|
|
|
|
|
|
|
|
def worker() -> None:
|
|
|
|
|
|
try:
|
|
|
|
|
|
result[0] = WhisperModel(
|
|
|
|
|
|
model_name,
|
|
|
|
|
|
compute_type=compute_type,
|
|
|
|
|
|
download_root=download_root,
|
|
|
|
|
|
)
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
error[0] = exc
|
|
|
|
|
|
finally:
|
2026-04-12 12:49:31 +02:00
|
|
|
|
done_event.set()
|
2026-04-12 12:30:48 +02:00
|
|
|
|
|
|
|
|
|
|
thread = threading.Thread(target=worker, daemon=True)
|
|
|
|
|
|
thread.start()
|
|
|
|
|
|
|
2026-04-12 12:49:31 +02:00
|
|
|
|
# Kurz warten – wenn Modell sofort bereit (vollständiger Cache), kein Dialog
|
|
|
|
|
|
if done_event.wait(timeout=0.5):
|
|
|
|
|
|
if error[0] is not None:
|
|
|
|
|
|
root = tk.Tk()
|
|
|
|
|
|
root.withdraw()
|
|
|
|
|
|
messagebox.showerror("Fehler beim Modell-Laden", str(error[0]))
|
|
|
|
|
|
root.destroy()
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
return result[0]
|
|
|
|
|
|
|
|
|
|
|
|
# Modell braucht länger (Download oder Initialisierung) → Dialog anzeigen
|
2026-04-12 12:30:48 +02:00
|
|
|
|
root = tk.Tk()
|
|
|
|
|
|
root.title("whisper-local – Modell wird geladen")
|
|
|
|
|
|
root.resizable(False, False)
|
|
|
|
|
|
apply_system_theme(root)
|
|
|
|
|
|
|
|
|
|
|
|
frame = ttk.Frame(root, padding=16)
|
|
|
|
|
|
frame.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text=f"Lade Whisper-Modell '{model_name}'...").pack(anchor=tk.W)
|
2026-04-16 20:56:34 +02:00
|
|
|
|
ttk.Label(frame, text="Bitte warten…", foreground="gray").pack(
|
2026-04-12 12:49:31 +02:00
|
|
|
|
anchor=tk.W, pady=(4, 8)
|
|
|
|
|
|
)
|
2026-04-12 12:30:48 +02:00
|
|
|
|
|
2026-04-12 12:49:31 +02:00
|
|
|
|
pb = ttk.Progressbar(frame, length=320, mode="indeterminate")
|
|
|
|
|
|
pb.pack(fill=tk.X)
|
|
|
|
|
|
pb.start(10)
|
2026-04-12 12:36:18 +02:00
|
|
|
|
|
2026-04-12 12:30:48 +02:00
|
|
|
|
def poll() -> None:
|
2026-04-12 12:49:31 +02:00
|
|
|
|
if done_event.is_set():
|
|
|
|
|
|
root.quit()
|
|
|
|
|
|
return
|
|
|
|
|
|
root.after(100, poll)
|
|
|
|
|
|
|
|
|
|
|
|
root.after(100, poll)
|
2026-04-12 12:30:48 +02:00
|
|
|
|
root.mainloop()
|
|
|
|
|
|
|
|
|
|
|
|
if error[0] is not None:
|
2026-04-12 12:32:40 +02:00
|
|
|
|
root.withdraw()
|
2026-04-12 12:49:31 +02:00
|
|
|
|
messagebox.showerror("Fehler beim Modell-Laden", str(error[0]))
|
2026-04-12 12:32:40 +02:00
|
|
|
|
root.destroy()
|
2026-04-12 12:30:48 +02:00
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
2026-04-12 12:31:59 +02:00
|
|
|
|
root.destroy()
|
2026-04-12 12:30:48 +02:00
|
|
|
|
return result[0]
|