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>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Baut whisper-local mit PyInstaller und erstellt ein versioniertes ZIP.
|
||||
.DESCRIPTION
|
||||
Liest die Version aus pyproject.toml, führt PyInstaller aus und packt
|
||||
das dist-Verzeichnis als whisper-local-v{version}-win64.zip.
|
||||
.EXAMPLE
|
||||
.\build.ps1
|
||||
.\build.ps1 -Clean # löscht dist/ und build/ vor dem Build
|
||||
.\build.ps1 -SkipBuild # nur ZIP aus bestehendem dist/ erstellen
|
||||
#>
|
||||
param(
|
||||
[switch]$Clean,
|
||||
[switch]$SkipBuild
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# ── Versionsnummer aus pyproject.toml lesen ───────────────────────────────────
|
||||
$pyproject = Get-Content "pyproject.toml" -Raw
|
||||
if ($pyproject -match 'version\s*=\s*"([^"]+)"') {
|
||||
$version = $Matches[1]
|
||||
} else {
|
||||
Write-Error "Konnte Version nicht aus pyproject.toml lesen."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$zipName = "whisper-local-v$version-win64.zip"
|
||||
$distDir = "dist\whisper-local"
|
||||
|
||||
Write-Host "=== whisper-local Build v$version ===" -ForegroundColor Cyan
|
||||
|
||||
# ── Optional: Bereinigen ──────────────────────────────────────────────────────
|
||||
if ($Clean) {
|
||||
Write-Host "Bereinige build/ und dist/ ..." -ForegroundColor Yellow
|
||||
if (Test-Path "build") { Remove-Item -Recurse -Force "build" }
|
||||
if (Test-Path "dist") { Remove-Item -Recurse -Force "dist" }
|
||||
if (Test-Path $zipName) { Remove-Item -Force $zipName }
|
||||
}
|
||||
|
||||
# ── PyInstaller ausführen ────────────────────────────────────────────────────
|
||||
if (-not $SkipBuild) {
|
||||
Write-Host "Synchronisiere Build-Abhängigkeiten ..." -ForegroundColor Yellow
|
||||
uv sync --group build
|
||||
|
||||
Write-Host "Starte PyInstaller ..." -ForegroundColor Yellow
|
||||
uv run --group build python -m PyInstaller whisper_local.spec --noconfirm
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "PyInstaller fehlgeschlagen (Exit-Code $LASTEXITCODE)"
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
}
|
||||
|
||||
# ── Prüfen ob dist-Ordner vorhanden ──────────────────────────────────────────
|
||||
if (-not (Test-Path $distDir)) {
|
||||
Write-Error "Ordner '$distDir' nicht gefunden — Build fehlgeschlagen?"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── ZIP erstellen ─────────────────────────────────────────────────────────────
|
||||
if (Test-Path $zipName) { Remove-Item -Force $zipName }
|
||||
|
||||
Write-Host "Erstelle $zipName ..." -ForegroundColor Yellow
|
||||
Compress-Archive -Path $distDir -DestinationPath $zipName -CompressionLevel Optimal
|
||||
|
||||
$sizeMB = [math]::Round((Get-Item $zipName).Length / 1MB, 1)
|
||||
Write-Host ""
|
||||
Write-Host "Fertig: $zipName ($sizeMB MB)" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Testen:" -ForegroundColor Cyan
|
||||
Write-Host " Expand-Archive $zipName -DestinationPath test-release"
|
||||
Write-Host " .\test-release\whisper-local\whisper-local.exe"
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "whisper-local"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"faster-whisper>=1.1.0",
|
||||
@@ -27,3 +27,6 @@ dev = [
|
||||
"pytest>=8.0.0",
|
||||
"pytest-asyncio>=0.24.0",
|
||||
]
|
||||
build = [
|
||||
"pyinstaller>=6.0",
|
||||
]
|
||||
|
||||
@@ -2,6 +2,15 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
version = "0.17.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-doc"
|
||||
version = "0.0.4"
|
||||
@@ -313,6 +322,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macholib"
|
||||
version = "1.16.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "4.0.0"
|
||||
@@ -430,6 +451,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pefile"
|
||||
version = "2024.8.26"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008, upload-time = "2024-08-26T20:58:38.155Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.2.0"
|
||||
@@ -498,6 +528,47 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller"
|
||||
version = "6.19.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
{ name = "macholib", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pefile", marker = "sys_platform == 'win32'" },
|
||||
{ name = "pyinstaller-hooks-contrib" },
|
||||
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c8/63/fd62472b6371d89dc138d40c36d87a50dc2de18a035803bbdc376b4ffac4/pyinstaller-6.19.0.tar.gz", hash = "sha256:ec73aeb8bd9b7f2f1240d328a4542e90b3c6e6fbc106014778431c616592a865", size = 4036072, upload-time = "2026-02-14T18:06:28.718Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/eb/23374721fecfa72677e79800921cb6aceefa6ba48574dc404f3f6c6c3be7/pyinstaller-6.19.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:4190e76b74f0c4b5c5f11ac360928cd2e36ec8e3194d437bf6b8648c7bc0c134", size = 1040563, upload-time = "2026-02-14T18:05:22.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/7e/dfd724b0b533f5aaec0ee5df406fe2319987ed6964480a706f85478b12ea/pyinstaller-6.19.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8bd68abd812d8a6ba33b9f1810e91fee0f325969733721b78151f0065319ca11", size = 735477, upload-time = "2026-02-14T18:05:27.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/c9/ee3a4101c31f26344e66896c73c1fd6ed8282bf871473365b7f8674af406/pyinstaller-6.19.0-py3-none-manylinux2014_i686.whl", hash = "sha256:1ec54ef967996ca61dacba676227e2b23219878ccce5ee9d6f3aada7b8ed8abf", size = 747143, upload-time = "2026-02-14T18:05:31.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/0a/fc77e9f861be8cf300ac37155f59cc92aff99b29f2ddd78546f563a5b5a6/pyinstaller-6.19.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4ab2bb52e58448e14ddf9450601bdedd66800465043501c1d8f1cab87b60b122", size = 744849, upload-time = "2026-02-14T18:05:35.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/e3/6872e020ee758afe0b821663858492c10745608b07150e5e2c824a5b3e1c/pyinstaller-6.19.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:da6d5c6391ccefe73554b9fa29b86001c8e378e0f20c2a4004f836ba537eff63", size = 741590, upload-time = "2026-02-14T18:05:39.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/60/b8db5f1a4b0fb228175f2ea0aa33f949adcc097fbe981cc524f9faf85777/pyinstaller-6.19.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a0fc5f6b3c55aa54353f0c74ffa59b1115433c1850c6f655d62b461a2ed6cbbe", size = 741448, upload-time = "2026-02-14T18:05:45.636Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/4d/63b0600f2694e9141b83129fbc1c488ec84d5a0770b1448ec154dcd0fee9/pyinstaller-6.19.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:e649ba6bd1b0b89b210ad92adb5fbdc8a42dd2c5ca4f72ef3a0bfec83a424b83", size = 740613, upload-time = "2026-02-14T18:05:49.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/d4/e812ad36178093a0e9fd4b8127577748dd85b0cb71de912229dca21fd741/pyinstaller-6.19.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:481a909c8e60c8692fc60fcb1344d984b44b943f8bc9682f2fcdae305ad297e6", size = 740350, upload-time = "2026-02-14T18:05:54.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/03/b2c2ee41fb8e10fd2a45d21f5ec2ef25852cfb978dbf762972eed59e3d63/pyinstaller-6.19.0-py3-none-win32.whl", hash = "sha256:3c5c251054fe4cfaa04c34a363dcfbf811545438cb7198304cd444756bc2edd2", size = 1324317, upload-time = "2026-02-14T18:06:00.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d3/6d5e62b8270e2b53a6065e281b3a7785079b00e9019c8019952828dd1669/pyinstaller-6.19.0-py3-none-win_amd64.whl", hash = "sha256:b5bb6536c6560330d364d91522250f254b107cf69129d9cbcd0e6727c570be33", size = 1384894, upload-time = "2026-02-14T18:06:06.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/65/458cd523308a101a22fd2742893405030cc24994cc74b1b767cecf137160/pyinstaller-6.19.0-py3-none-win_arm64.whl", hash = "sha256:c2d5a539b0bfe6159d5522c8c70e1c0e487f22c2badae0f97d45246223b798ea", size = 1325374, upload-time = "2026-02-14T18:06:12.804Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller-hooks-contrib"
|
||||
version = "2026.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/fe/9278c29394bf69169febc21f96b4252c3ee7c8ec22c2fc545004bed47e71/pyinstaller_hooks_contrib-2026.4.tar.gz", hash = "sha256:766c281acb1ecc32e21c8c667056d7ebf5da0aabd5e30c219f9c2a283620eeaa", size = 173050, upload-time = "2026-03-31T14:10:51.188Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f4/035fb8c06deff827f540a9a4ed9122c54e5376fca3e42eddf0c263730775/pyinstaller_hooks_contrib-2026.4-py3-none-any.whl", hash = "sha256:1de1a5e49a878122010b88c7e295502bc69776c157c4a4dc78741a4e6178b00f", size = 455496, upload-time = "2026-03-31T14:10:49.867Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pynput"
|
||||
version = "1.8.1"
|
||||
@@ -578,6 +649,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32-ctypes"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
@@ -755,7 +835,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "whisper-local"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "darkdetect", marker = "sys_platform == 'win32'" },
|
||||
@@ -771,6 +851,9 @@ dependencies = [
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
build = [
|
||||
{ name = "pyinstaller" },
|
||||
]
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
@@ -791,6 +874,7 @@ requires-dist = [
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
build = [{ name = "pyinstaller", specifier = ">=6.0" }]
|
||||
dev = [
|
||||
{ name = "pytest", specifier = ">=8.0.0" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.24.0" },
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
# 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",
|
||||
)
|
||||
@@ -1,6 +1,8 @@
|
||||
"""Whisper-Transkription via faster-whisper."""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from faster_whisper import WhisperModel
|
||||
@@ -8,11 +10,24 @@ from faster_whisper import WhisperModel
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _model_cache_dir() -> str | None:
|
||||
"""Im gebündelten Modus: Modell neben der EXE cachen (portable).
|
||||
Im Entwicklungsmodus: None → HuggingFace-Standard-Cache."""
|
||||
if getattr(sys, "frozen", False):
|
||||
cache = Path(sys.executable).parent / "models"
|
||||
try:
|
||||
cache.mkdir(exist_ok=True)
|
||||
return str(cache)
|
||||
except OSError:
|
||||
return None # Fallback auf HuggingFace-Standard-Cache
|
||||
return None
|
||||
|
||||
|
||||
class Transcriber:
|
||||
def __init__(self, model_name: str = "small", compute_type: str = "int8", language: str = "de"):
|
||||
self.language = language
|
||||
logger.info("Lade Whisper-Modell '%s' (compute_type=%s)...", model_name, compute_type)
|
||||
self.model = WhisperModel(model_name, compute_type=compute_type)
|
||||
self.model = WhisperModel(model_name, compute_type=compute_type, download_root=_model_cache_dir())
|
||||
logger.info("Modell geladen")
|
||||
|
||||
def transcribe(self, audio: np.ndarray) -> str:
|
||||
|
||||
Reference in New Issue
Block a user