#!/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 import os # CRITICAL: Must be called before anything else with PyInstaller # This prevents the infinite spawning loop when the frozen executable runs # Required on all platforms (Windows, Linux, macOS) when using multiprocessing multiprocessing.freeze_support() # Set multiprocessing start method to 'spawn' for consistency across platforms # This prevents issues with PyInstaller frozen executables if __name__ == "__main__": try: multiprocessing.set_start_method('spawn', force=True) except RuntimeError: pass # Already set, ignore # Fix for Windows PyInstaller builds with console=False # When console is hidden, subprocess/multiprocessing can't write to stdout/stderr # This causes RealtimeSTT and other libraries to fail silently if getattr(sys, 'frozen', False) and sys.platform == 'win32': # Redirect stdout/stderr to null device on Windows when frozen # Only if they're not already valid (console=False builds) try: sys.stdout.flush() sys.stderr.flush() except (AttributeError, OSError): # stdout/stderr are not available, redirect to null import io sys.stdout = io.StringIO() sys.stderr = io.StringIO() # 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 # In PyInstaller frozen executables, use _MEIPASS for bundled files if getattr(sys, 'frozen', False): # Running in PyInstaller bundle icon_path = Path(sys._MEIPASS) / "LocalTranscription.png" else: # Running in normal Python 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()