diff --git a/gui/main_window_qt.py b/gui/main_window_qt.py index a681a41..8946f99 100644 --- a/gui/main_window_qt.py +++ b/gui/main_window_qt.py @@ -32,12 +32,19 @@ class WebServerThread(Thread): super().__init__(daemon=True) self.web_server = web_server self.loop = None + self.error = None def run(self): """Run the web server in async event loop.""" - self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.loop) - self.loop.run_until_complete(self.web_server.start()) + try: + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + self.loop.run_until_complete(self.web_server.start()) + except Exception as e: + self.error = e + print(f"ERROR: Web server failed to start: {e}") + import traceback + traceback.print_exc() class ModelLoaderThread(QThread): @@ -261,20 +268,62 @@ class MainWindow(QMainWindow): def _start_web_server_if_enabled(self): """Start web server.""" - host = self.config.get('web_server.host', '127.0.0.1') - port = self.config.get('web_server.port', 8080) - show_timestamps = self.config.get('display.show_timestamps', True) - fade_after_seconds = self.config.get('display.fade_after_seconds', 10) + try: + host = self.config.get('web_server.host', '127.0.0.1') + port = self.config.get('web_server.port', 8080) + show_timestamps = self.config.get('display.show_timestamps', True) + fade_after_seconds = self.config.get('display.fade_after_seconds', 10) - print(f"Starting web server at http://{host}:{port}") - self.web_server = TranscriptionWebServer( - host=host, - port=port, - show_timestamps=show_timestamps, - fade_after_seconds=fade_after_seconds - ) - self.web_server_thread = WebServerThread(self.web_server) - self.web_server_thread.start() + # Try up to 5 ports if the default is in use + ports_to_try = [port] + [port + i for i in range(1, 5)] + server_started = False + + for try_port in ports_to_try: + print(f"Attempting to start web server at http://{host}:{try_port}") + self.web_server = TranscriptionWebServer( + host=host, + port=try_port, + show_timestamps=show_timestamps, + fade_after_seconds=fade_after_seconds + ) + self.web_server_thread = WebServerThread(self.web_server) + self.web_server_thread.start() + + # Give it a moment to start and check for errors + import time + time.sleep(0.5) + + if self.web_server_thread.error: + error_str = str(self.web_server_thread.error) + # Check if it's a port-in-use error + if "address already in use" in error_str.lower() or "errno 98" in error_str.lower(): + print(f"Port {try_port} is in use, trying next port...") + self.web_server = None + self.web_server_thread = None + continue + else: + # Different error, don't retry + print(f"Web server failed to start: {self.web_server_thread.error}") + self.web_server = None + self.web_server_thread = None + break + else: + # Success! + print(f"✓ Web server started successfully at http://{host}:{try_port}") + if try_port != port: + print(f" Note: Using port {try_port} instead of configured port {port}") + server_started = True + break + + if not server_started: + print(f"WARNING: Could not start web server on any port from {ports_to_try[0]} to {ports_to_try[-1]}") + + except Exception as e: + print(f"ERROR: Failed to initialize web server: {e}") + import traceback + traceback.print_exc() + self.web_server = None + self.web_server_thread = None def _toggle_transcription(self): """Start or stop transcription.""" @@ -503,48 +552,61 @@ class MainWindow(QMainWindow): def _reload_model(self): """Reload the transcription model with new settings.""" - # Stop transcription if running - was_transcribing = self.is_transcribing - if was_transcribing: - self._stop_transcription() + try: + # Stop transcription if running + was_transcribing = self.is_transcribing + if was_transcribing: + self._stop_transcription() - # Update status - self.status_label.setText("⚙ Reloading model...") - self.start_button.setEnabled(False) + # Update status + self.status_label.setText("⚙ Reloading model...") + self.start_button.setEnabled(False) - # Unload current model - if self.transcription_engine: - self.transcription_engine.unload_model() + # Unload current model + if self.transcription_engine: + try: + self.transcription_engine.unload_model() + except Exception as e: + print(f"Warning: Error unloading model: {e}") - # Set device based on config - device_config = self.config.get('transcription.device', 'auto') - self.device_manager.set_device(device_config) + # Set device based on config + device_config = self.config.get('transcription.device', 'auto') + self.device_manager.set_device(device_config) - # Re-initialize transcription engine - model_size = self.config.get('transcription.model', 'base') - language = self.config.get('transcription.language', 'en') - device = self.device_manager.get_device_for_whisper() - compute_type = self.device_manager.get_compute_type() + # Re-initialize transcription engine + model_size = self.config.get('transcription.model', 'base') + language = self.config.get('transcription.language', 'en') + device = self.device_manager.get_device_for_whisper() + compute_type = self.device_manager.get_compute_type() - # Update tracked settings - self.current_model_size = model_size - self.current_device_config = device_config + # Update tracked settings + self.current_model_size = model_size + self.current_device_config = device_config - self.transcription_engine = TranscriptionEngine( - model_size=model_size, - device=device, - compute_type=compute_type, - language=language, - min_confidence=self.config.get('processing.min_confidence', 0.5) - ) + self.transcription_engine = TranscriptionEngine( + model_size=model_size, + device=device, + compute_type=compute_type, + language=language, + min_confidence=self.config.get('processing.min_confidence', 0.5) + ) - # Load model in background thread - if self.model_loader_thread and self.model_loader_thread.isRunning(): - self.model_loader_thread.wait() + # Load model in background thread + if self.model_loader_thread and self.model_loader_thread.isRunning(): + self.model_loader_thread.wait() - self.model_loader_thread = ModelLoaderThread(self.transcription_engine) - self.model_loader_thread.finished.connect(self._on_model_reloaded) - self.model_loader_thread.start() + self.model_loader_thread = ModelLoaderThread(self.transcription_engine) + self.model_loader_thread.finished.connect(self._on_model_reloaded) + self.model_loader_thread.start() + + except Exception as e: + error_msg = f"Error during model reload: {e}" + print(error_msg) + import traceback + traceback.print_exc() + self.status_label.setText("❌ Model reload failed") + self.start_button.setEnabled(False) + QMessageBox.critical(self, "Error", error_msg) def _on_model_reloaded(self, success: bool, message: str): """Handle model reloading completion.""" @@ -602,9 +664,21 @@ class MainWindow(QMainWindow): if self.is_transcribing: self._stop_transcription() + # Stop web server + if self.web_server_thread and self.web_server_thread.is_alive(): + try: + print("Shutting down web server...") + if self.web_server_thread.loop: + self.web_server_thread.loop.call_soon_threadsafe(self.web_server_thread.loop.stop) + except Exception as e: + print(f"Warning: Error stopping web server: {e}") + # Unload model if self.transcription_engine: - self.transcription_engine.unload_model() + try: + self.transcription_engine.unload_model() + except Exception as e: + print(f"Warning: Error unloading model: {e}") # Wait for model loader thread if self.model_loader_thread and self.model_loader_thread.isRunning(): diff --git a/server/web_display.py b/server/web_display.py index a9b3742..26a369f 100644 --- a/server/web_display.py +++ b/server/web_display.py @@ -223,11 +223,21 @@ class TranscriptionWebServer: async def start(self): """Start the web server.""" import uvicorn + import logging + + # Configure uvicorn to work without console (for PyInstaller builds) + # Suppress uvicorn's default console logging + logging.getLogger("uvicorn").setLevel(logging.ERROR) + logging.getLogger("uvicorn.access").setLevel(logging.ERROR) + logging.getLogger("uvicorn.error").setLevel(logging.ERROR) + config = uvicorn.Config( self.app, host=self.host, port=self.port, - log_level="warning" + log_level="error", # Only log errors + access_log=False, # Disable access logging + log_config=None # Don't use default logging config ) server = uvicorn.Server(config) await server.serve()