Temporarily enable console output to diagnose "failed to start recording" error in the PyInstaller build. This will show all print() statements and error messages that are currently being hidden. Change console=False to console=True in the spec file. Once the issue is identified and fixed, set back to console=False for a production build without the console window. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
188 lines
5.5 KiB
Python
188 lines
5.5 KiB
Python
# -*- mode: python ; coding: utf-8 -*-
|
|
"""PyInstaller spec file for Local Transcription app."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
import os
|
|
|
|
block_cipher = None
|
|
|
|
# Determine if we're on Windows
|
|
is_windows = sys.platform == 'win32'
|
|
|
|
# Import PyInstaller utilities
|
|
from PyInstaller.utils.hooks import collect_submodules, collect_data_files
|
|
|
|
# Find faster_whisper assets folder
|
|
import faster_whisper
|
|
faster_whisper_path = os.path.dirname(faster_whisper.__file__)
|
|
vad_assets_path = os.path.join(faster_whisper_path, 'assets')
|
|
|
|
# Find pvporcupine resources folder (needed even though we don't use wake words)
|
|
try:
|
|
import pvporcupine
|
|
pvporcupine_path = os.path.dirname(pvporcupine.__file__)
|
|
pvporcupine_resources = os.path.join(pvporcupine_path, 'resources')
|
|
pvporcupine_lib = os.path.join(pvporcupine_path, 'lib')
|
|
pvporcupine_data_files = []
|
|
if os.path.exists(pvporcupine_resources):
|
|
pvporcupine_data_files.append((pvporcupine_resources, 'pvporcupine/resources'))
|
|
if os.path.exists(pvporcupine_lib):
|
|
pvporcupine_data_files.append((pvporcupine_lib, 'pvporcupine/lib'))
|
|
except ImportError:
|
|
pvporcupine_data_files = []
|
|
|
|
# Base configuration
|
|
binaries = []
|
|
datas = [
|
|
('config/default_config.yaml', 'config'),
|
|
('LocalTranscription.png', '.'), # Include icon for runtime window icon
|
|
(vad_assets_path, 'faster_whisper/assets'), # Include VAD model
|
|
] + pvporcupine_data_files # Include pvporcupine resources
|
|
hiddenimports = [
|
|
'PySide6.QtCore',
|
|
'PySide6.QtWidgets',
|
|
'PySide6.QtGui',
|
|
'faster_whisper',
|
|
'faster_whisper.transcribe',
|
|
'faster_whisper.vad',
|
|
'ctranslate2',
|
|
'sounddevice',
|
|
'scipy',
|
|
'scipy.signal',
|
|
'numpy',
|
|
# RealtimeSTT and its dependencies
|
|
'RealtimeSTT',
|
|
'RealtimeSTT.audio_recorder',
|
|
'webrtcvad',
|
|
'webrtcvad_wheels',
|
|
'silero_vad',
|
|
'torch',
|
|
'torch.nn',
|
|
'torch.nn.functional',
|
|
'torchaudio',
|
|
'onnxruntime',
|
|
'onnxruntime.capi',
|
|
'onnxruntime.capi.onnxruntime_pybind11_state',
|
|
'pyaudio',
|
|
'halo', # RealtimeSTT progress indicator
|
|
'colorama', # Terminal colors (used by halo)
|
|
# FastAPI and dependencies
|
|
'fastapi',
|
|
'fastapi.routing',
|
|
'fastapi.responses',
|
|
'starlette',
|
|
'starlette.applications',
|
|
'starlette.routing',
|
|
'starlette.responses',
|
|
'starlette.websockets',
|
|
'starlette.middleware',
|
|
'starlette.middleware.cors',
|
|
'pydantic',
|
|
'pydantic.fields',
|
|
'pydantic.main',
|
|
'anyio',
|
|
'anyio._backends',
|
|
'anyio._backends._asyncio',
|
|
'sniffio',
|
|
# Uvicorn and dependencies
|
|
'uvicorn',
|
|
'uvicorn.logging',
|
|
'uvicorn.loops',
|
|
'uvicorn.loops.auto',
|
|
'uvicorn.protocols',
|
|
'uvicorn.protocols.http',
|
|
'uvicorn.protocols.http.auto',
|
|
'uvicorn.protocols.http.h11_impl',
|
|
'uvicorn.protocols.websockets',
|
|
'uvicorn.protocols.websockets.auto',
|
|
'uvicorn.protocols.websockets.wsproto_impl',
|
|
'uvicorn.lifespan',
|
|
'uvicorn.lifespan.on',
|
|
'h11',
|
|
'websockets',
|
|
'websockets.legacy',
|
|
'websockets.legacy.server',
|
|
# Requests (for server sync)
|
|
'requests',
|
|
'urllib3',
|
|
'certifi',
|
|
'charset_normalizer',
|
|
]
|
|
|
|
# Collect all submodules for FastAPI and related packages
|
|
# This approach is more reliable than collect_all() which has design flaws
|
|
# Particularly important for pydantic which uses compiled cpython extensions
|
|
print("Collecting submodules for FastAPI packages...")
|
|
for package in ['fastapi', 'starlette', 'pydantic', 'pydantic_core', 'anyio', 'uvicorn', 'websockets', 'h11', 'httptools', 'uvloop']:
|
|
try:
|
|
submodules = collect_submodules(package)
|
|
hiddenimports += submodules
|
|
print(f" ✓ Collected {len(submodules)} submodules from {package}")
|
|
except Exception as e:
|
|
print(f" ⚠ Warning: Could not collect {package}: {e}")
|
|
|
|
# Collect data files for packages that need them
|
|
for package in ['fastapi', 'starlette', 'pydantic', 'uvicorn', 'RealtimeSTT']:
|
|
try:
|
|
data_files = collect_data_files(package)
|
|
if data_files:
|
|
datas += data_files
|
|
print(f" ✓ Collected {len(data_files)} data files from {package}")
|
|
except Exception as e:
|
|
pass # Not all packages have data files
|
|
|
|
# Add critical pydantic dependencies that may be missed
|
|
hiddenimports += [
|
|
'colorsys', 'decimal', 'json', 'ipaddress', 'pathlib', 'uuid',
|
|
'email.message', 'typing_extensions',
|
|
]
|
|
|
|
a = Analysis(
|
|
['main.py'],
|
|
pathex=[],
|
|
binaries=binaries,
|
|
datas=datas,
|
|
hiddenimports=hiddenimports,
|
|
hookspath=['hooks'], # Add hooks directory for custom PyInstaller hooks
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
excludes=['enum34'], # Exclude enum34 - incompatible with PyInstaller and Python 3.4+
|
|
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,
|
|
name='LocalTranscription',
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip=False,
|
|
upx=True,
|
|
console=True, # Show console for debugging (set to False for production)
|
|
disable_windowed_traceback=False,
|
|
argv_emulation=False,
|
|
target_arch=None,
|
|
codesign_identity=None,
|
|
entitlements_file=None,
|
|
icon='LocalTranscription.ico' if is_windows else 'LocalTranscription.icns', # Platform-specific icon
|
|
)
|
|
|
|
coll = COLLECT(
|
|
exe,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
strip=False,
|
|
upx=True,
|
|
upx_exclude=[],
|
|
name='LocalTranscription',
|
|
)
|