Files
whisper-local/whisper_local.spec
T
info 05ff5765bf feat: Windows-Packaging mit PyInstaller (ZIP ohne Python-Installation)
Fügt Build-Infrastruktur hinzu, mit der whisper-local als
selbständiges Windows-ZIP-Paket ohne Python-Installation
bereitgestellt werden kann.

- whisper_local.spec: PyInstaller onedir-Konfiguration für Windows 64-bit
  mit allen nativen DLLs (ctranslate2/CUDA, pywin32, PortAudio,
  onnxruntime, av/FFmpeg) und Hidden Imports für platform-bedingte Backends
- build.ps1: Build-Skript das versioniertes ZIP erstellt (.\build.ps1 -Clean)
- transcriber.py: portabler Modell-Cache neben der EXE im gebündelten Modus
- pyproject.toml: pyinstaller>=6.0 als [build]-Abhängigkeitsgruppe, v1.0.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 12:01:02 +02:00

171 lines
5.6 KiB
RPMSpec

# whisper_local.spec
# PyInstaller-Build-Konfiguration für whisper-local (Windows 64-bit, onedir)
#
# Ausführen: uv run pyinstaller whisper_local.spec --noconfirm
# Oder via: .\build.ps1
import sys
from pathlib import Path
from PyInstaller.utils.hooks import collect_data_files
block_cipher = None
SP = Path(".venv/Lib/site-packages")
# ── Binaries ──────────────────────────────────────────────────────────────────
# ctranslate2: Haupt-DLL, CUDA (cudnn) und Intel OpenMP
# DLLs landen in ctranslate2/, damit ctranslate2's os.add_dll_directory() greift
binaries_list = [
(str(SP / "ctranslate2/ctranslate2.dll"), "ctranslate2"),
(str(SP / "ctranslate2/cudnn64_9.dll"), "ctranslate2"),
(str(SP / "ctranslate2/libiomp5md.dll"), "ctranslate2"),
# pywin32: COM- und WinTypes-DLLs müssen neben der EXE liegen
(str(SP / "pywin32_system32/pythoncom313.dll"), "."),
(str(SP / "pywin32_system32/pywintypes313.dll"), "."),
# sounddevice / PortAudio
(str(SP / "_sounddevice_data/portaudio-binaries/libportaudio64bit.dll"),
"_sounddevice_data/portaudio-binaries"),
(str(SP / "_sounddevice_data/portaudio-binaries/libportaudio64bit-asio.dll"),
"_sounddevice_data/portaudio-binaries"),
# onnxruntime (für Silero VAD in faster-whisper)
(str(SP / "onnxruntime/capi/onnxruntime.dll"),
"onnxruntime/capi"),
(str(SP / "onnxruntime/capi/onnxruntime_providers_shared.dll"),
"onnxruntime/capi"),
]
# av/FFmpeg: DLL-Namen enthalten Hashes — per Glob gesammelt
av_libs_dir = SP / "av.libs"
binaries_list += [(str(dll), "av.libs") for dll in av_libs_dir.glob("*.dll")]
# ── Datas ─────────────────────────────────────────────────────────────────────
datas_list = [
# Silero VAD ONNX-Modell (benötigt von faster-whisper)
(str(SP / "faster_whisper/assets/silero_vad_v6.onnx"),
"faster_whisper/assets"),
# sv_ttk Theme (TCL-Skripte + PNG-Spritesheets)
(str(SP / "sv_ttk/sv.tcl"), "sv_ttk"),
(str(SP / "sv_ttk/theme"), "sv_ttk/theme"),
# Beispiel-Konfiguration als Referenz
("config.example.toml", "."),
]
# certifi CA-Bundle für HTTPS (huggingface_hub Modell-Download)
datas_list += collect_data_files("certifi")
# ── Hidden Imports ────────────────────────────────────────────────────────────
# Alle Module hinter sys.platform-Guards oder lazy imports (importlib, __import__)
hidden_imports_list = [
# Eigene Windows-Backends (hinter sys.platform == "win32" Guards)
"whisper_local.hotkey._pynput",
"whisper_local.inserter._win32",
"whisper_local.tray._tray",
"whisper_local.tray._icon",
"whisper_local.tray._settings",
"whisper_local.tray._theme",
# pynput: Backend wird per importlib dynamisch gewählt
"pynput.keyboard._win32",
"pynput.mouse._win32",
"pynput._util",
"pynput._util.win32",
"pynput._util.win32_vks",
# pywin32
"win32api",
"win32con",
"win32gui",
"win32clipboard",
"pywintypes",
# pystray Windows-Backend + Utility-Unterpaket (relative imports)
"pystray._win32",
"pystray._util",
"pystray._util.win32",
# tkinter: lazy import in tray/_settings.py
"tkinter",
"tkinter.ttk",
# sv_ttk und darkdetect
"sv_ttk",
"darkdetect",
# onnxruntime Inference Collection
"onnxruntime.capi.onnxruntime_inference_collection",
# huggingface_hub für Modell-Download zur Laufzeit
"huggingface_hub",
"huggingface_hub.file_download",
"huggingface_hub.utils",
]
# ── Excludes ──────────────────────────────────────────────────────────────────
excludes_list = [
# Linux-only (nicht installiert auf Windows)
"evdev",
"whisper_local.hotkey._evdev",
"whisper_local.inserter._wayland",
# Build- und Test-Infrastruktur
"pytest",
"pytest_asyncio",
"hatchling",
# Nicht benötigt im gebündelten Binary
"unittest",
"doctest",
"pdb",
]
# ── Analysis ──────────────────────────────────────────────────────────────────
a = Analysis(
["whisper_local/__main__.py"],
pathex=["."],
binaries=binaries_list,
datas=datas_list,
hiddenimports=hidden_imports_list,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes_list,
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True, # onedir-Modus: DLLs bleiben separat
name="whisper-local",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # UPX mit CUDA-DLLs deaktiviert
console=False, # kein Konsolenfenster (Tray-App)
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
upx_exclude=[],
name="whisper-local",
)