Files
local-transcription/main.py
jknapp 371d5d9a28 Fix infinite spawn loop on Windows PyInstaller builds
CRITICAL FIX: Added multiprocessing.freeze_support() to prevent the
frozen executable from spawning infinite copies of itself on Windows.

The issue:
When PyInstaller bundles Python apps that use multiprocessing (which
PyTorch, faster-whisper, and RealtimeSTT all use), Windows treats each
spawn as a new process that re-executes the script. Without freeze_support(),
this creates an infinite loop of processes spawning until the system crashes.

The fix:
- Added multiprocessing.freeze_support() at the very top of main.py
- Called before any imports that might use multiprocessing
- Windows-only (wrapped in sys.platform check)
- Must be before QApplication or any Qt imports

This is a standard requirement for all PyInstaller apps that use
multiprocessing on Windows.

Resolves: App spawns infinite copies when running from PyInstaller build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-28 20:09:43 -08:00

121 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""
Local Transcription Application
A standalone desktop application for real-time speech-to-text transcription
using Whisper models. Supports CPU/GPU processing, noise suppression, and
optional multi-user server synchronization.
"""
import sys
import multiprocessing
from pathlib import Path
# CRITICAL: Must be called before anything else on Windows with PyInstaller
# This prevents the infinite spawning loop when the frozen executable runs
if sys.platform == 'win32':
multiprocessing.freeze_support()
# Add project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from PySide6.QtWidgets import QApplication, QSplashScreen
from PySide6.QtGui import QPixmap, QPainter, QColor, QFont
from PySide6.QtCore import Qt, QTimer
from gui.main_window_qt import MainWindow
def create_splash_pixmap(message="Loading..."):
"""Create a pixmap for the splash screen with a custom message."""
pixmap = QPixmap(500, 300)
pixmap.fill(QColor("#2b2b2b"))
# Draw on the pixmap
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.Antialiasing)
# Draw title
title_font = QFont("Arial", 28, QFont.Bold)
painter.setFont(title_font)
painter.setPen(QColor("#ffffff"))
painter.drawText(pixmap.rect(), Qt.AlignCenter, "Local Transcription")
# Draw subtitle
subtitle_font = QFont("Arial", 12)
painter.setFont(subtitle_font)
painter.setPen(QColor("#888888"))
subtitle_rect = pixmap.rect().adjusted(0, 60, 0, 0)
painter.drawText(subtitle_rect, Qt.AlignCenter, message)
# Draw version/status at bottom
status_font = QFont("Arial", 10)
painter.setFont(status_font)
painter.setPen(QColor("#666666"))
status_rect = pixmap.rect().adjusted(0, 0, 0, -20)
painter.drawText(status_rect, Qt.AlignHCenter | Qt.AlignBottom, "Please wait...")
painter.end()
return pixmap
def create_splash_screen():
"""Create a splash screen for startup."""
pixmap = create_splash_pixmap("Initializing...")
splash = QSplashScreen(pixmap)
splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.SplashScreen)
return splash
def main():
"""Main application entry point."""
try:
print("Starting Local Transcription Application...")
print("=" * 50)
# Create Qt application
app = QApplication(sys.argv)
# Set application info
app.setApplicationName("Local Transcription")
app.setOrganizationName("LocalTranscription")
# Set application icon
icon_path = project_root / "LocalTranscription.png"
if icon_path.exists():
from PySide6.QtGui import QIcon
app.setWindowIcon(QIcon(str(icon_path)))
# Create and show splash screen
splash = create_splash_screen()
splash.show()
app.processEvents() # Make sure splash is visible
# Update splash with progress
splash.showMessage("Loading configuration...", Qt.AlignBottom | Qt.AlignCenter, QColor("#888888"))
app.processEvents()
# Create main window (this takes time due to model loading)
# Pass splash to window so it can update the message
window = MainWindow(splash_screen=splash)
# Close splash and show main window
splash.finish(window)
window.show()
# Run application
sys.exit(app.exec())
except KeyboardInterrupt:
print("\nApplication interrupted by user")
sys.exit(0)
except Exception as e:
print(f"Fatal error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()