diff --git a/ytsage/core/ytsage_ffmpeg.py b/ytsage/core/ytsage_ffmpeg.py index a604cd2..0bb87f9 100644 --- a/ytsage/core/ytsage_ffmpeg.py +++ b/ytsage/core/ytsage_ffmpeg.py @@ -406,9 +406,14 @@ def install_ffmpeg_macos() -> bool: timeout=5, ) except (subprocess.SubprocessError, FileNotFoundError): - logger.info("Installing Homebrew...") - brew_install_cmd = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' - subprocess.run(brew_install_cmd, shell=True, check=True, timeout=300) + # Never curl|bash a remote script without the user's say-so. + # Homebrew installation is the user's decision to make in a + # terminal, where the script can also prompt for sudo properly. + logger.error( + "Homebrew is not installed. Install it from https://brew.sh and retry, " + "or install ffmpeg another way." + ) + return False # Install FFmpeg logger.info("Installing FFmpeg...") diff --git a/ytsage/gui/ytsage_gui_main.py b/ytsage/gui/ytsage_gui_main.py index 4958f50..1da5f0b 100644 --- a/ytsage/gui/ytsage_gui_main.py +++ b/ytsage/gui/ytsage_gui_main.py @@ -1176,12 +1176,16 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin): # Clean up the thread reference and ensure it's properly finished if hasattr(self, "auto_update_thread"): + thread = self.auto_update_thread # Disconnect all signals to prevent further callbacks - self.auto_update_thread.update_finished.disconnect() - # Make sure thread is finished - if self.auto_update_thread.isRunning(): - self.auto_update_thread.quit() - self.auto_update_thread.wait(1000) # Wait up to 1 second + thread.update_finished.disconnect() + # Dropping the Python reference while run() is still unwinding + # can destroy a live QThread; let Qt delete it once finished. + if thread.isRunning(): + thread.finished.connect(thread.deleteLater) + thread.quit() + else: + thread.deleteLater() # Remove the reference delattr(self, "auto_update_thread") diff --git a/ytsage/main.py b/ytsage/main.py index 6427578..f77e2db 100644 --- a/ytsage/main.py +++ b/ytsage/main.py @@ -7,6 +7,12 @@ from .gui.ytsage_gui_main import YTSageApp # Import the main application class def show_error_dialog(message): + # A QMessageBox needs a live QApplication; if startup failed before (or + # while) creating one, constructing the dialog would abort the process + # and swallow the real error. + if QApplication.instance() is None: + print(f"Application Error: {message}", file=sys.stderr) + return error_dialog = QMessageBox() error_dialog.setIcon(QMessageBox.Icon.Critical) error_dialog.setText("Application Error")