diff --git a/tagstudio/src/qt/helpers/file_opener.py b/tagstudio/src/qt/helpers/file_opener.py index 51afd230a..8650be0be 100644 --- a/tagstudio/src/qt/helpers/file_opener.py +++ b/tagstudio/src/qt/helpers/file_opener.py @@ -11,6 +11,7 @@ import structlog from PySide6.QtCore import Qt from PySide6.QtWidgets import QLabel +from src.qt.helpers.silent_popen import silent_Popen logger = structlog.get_logger(__name__) @@ -38,7 +39,7 @@ def open_file(path: str | Path, file_manager: bool = False): # For some reason, if the args are passed in a list, this will error when the # path has spaces, even while surrounded in double quotes. - subprocess.Popen( + silent_Popen( command_name + command_arg, shell=True, close_fds=True, @@ -47,7 +48,7 @@ def open_file(path: str | Path, file_manager: bool = False): ) else: command = f'"{normpath}"' - subprocess.Popen( + silent_Popen( command, shell=True, close_fds=True, @@ -79,7 +80,7 @@ def open_file(path: str | Path, file_manager: bool = False): command_args = [str(path)] command = shutil.which(command_name) if command is not None: - subprocess.Popen([command] + command_args, close_fds=True) + silent_Popen([command] + command_args, close_fds=True) else: logger.info("Could not find command on system PATH", command=command_name) except Exception: diff --git a/tagstudio/src/qt/helpers/silent_popen.py b/tagstudio/src/qt/helpers/silent_popen.py index df426fe56..84beccde0 100644 --- a/tagstudio/src/qt/helpers/silent_popen.py +++ b/tagstudio/src/qt/helpers/silent_popen.py @@ -1,13 +1,15 @@ # Copyright (C) 2024 Travis Abendshien (CyanVoxel). # Licensed under the GPL-3.0 License. # Created for TagStudio: https://github.com/CyanVoxel/TagStudio +import os import subprocess import sys -"""Implementation of subprocess.Popen that does not spawn console windows or log output.""" +"""Implementation of subprocess.Popen that does not spawn console windows or log output +and sanitizes pyinstall environment variables.""" -def promptless_Popen( # noqa: N802 +def silent_Popen( # noqa: N802 args, bufsize=-1, executable=None, @@ -21,6 +23,7 @@ def promptless_Popen( # noqa: N802 env=None, universal_newlines=None, startupinfo=None, + creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), @@ -36,9 +39,20 @@ def promptless_Popen( # noqa: N802 process_group=None, ): """Call subprocess.Popen without creating a console window.""" - creation_flags = 0 if sys.platform == "win32": - creation_flags = subprocess.CREATE_NO_WINDOW + creationflags |= subprocess.CREATE_NO_WINDOW + import ctypes + + ctypes.windll.kernel32.SetDllDirectoryW(None) + elif ( + sys.platform == "linux" + or sys.platform.startswith("freebsd") + or sys.platform.startswith("openbsd") + ): + # pass clean environment to the subprocess + env = os.environ + original_env = env.get("LD_LIBRARY_PATH_ORIG") + env["LD_LIBRARY_PATH"] = original_env if original_env else "" return subprocess.Popen( args=args, @@ -54,7 +68,7 @@ def promptless_Popen( # noqa: N802 env=env, universal_newlines=universal_newlines, startupinfo=startupinfo, - creationflags=creation_flags, + creationflags=creationflags, restore_signals=restore_signals, start_new_session=start_new_session, pass_fds=pass_fds, diff --git a/tagstudio/src/qt/helpers/vendored/ffmpeg.py b/tagstudio/src/qt/helpers/vendored/ffmpeg.py index 05e1d2586..07c90346a 100644 --- a/tagstudio/src/qt/helpers/vendored/ffmpeg.py +++ b/tagstudio/src/qt/helpers/vendored/ffmpeg.py @@ -9,7 +9,7 @@ import ffmpeg import structlog -from src.qt.helpers.silent_popen import promptless_Popen +from src.qt.helpers.silent_popen import silent_Popen logger = structlog.get_logger(__name__) @@ -44,7 +44,7 @@ def _probe(filename, cmd=FFPROBE_CMD, timeout=None, **kwargs): args += [filename] # PATCHED - p = promptless_Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = silent_Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) communicate_kwargs = {} if timeout is not None: communicate_kwargs["timeout"] = timeout diff --git a/tagstudio/src/qt/helpers/vendored/pydub/audio_segment.py b/tagstudio/src/qt/helpers/vendored/pydub/audio_segment.py index a43ea5b3b..18bc6adf3 100644 --- a/tagstudio/src/qt/helpers/vendored/pydub/audio_segment.py +++ b/tagstudio/src/qt/helpers/vendored/pydub/audio_segment.py @@ -40,7 +40,7 @@ get_encoder_name, ratio_to_db, ) -from src.qt.helpers.silent_popen import promptless_Popen +from src.qt.helpers.silent_popen import silent_Popen from src.qt.helpers.vendored.pydub.utils import _mediainfo_json basestring = str @@ -606,7 +606,7 @@ def is_format(f): with open(os.devnull, "rb") as devnull: # PATCHED - p = promptless_Popen( + p = silent_Popen( conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) p_out, p_err = p.communicate() @@ -785,7 +785,7 @@ def is_format(f): log_conversion(conversion_command) # PATCHED - p = promptless_Popen( + p = silent_Popen( conversion_command, stdin=stdin_parameter, stdout=subprocess.PIPE, @@ -1012,7 +1012,7 @@ def export( # read stdin / write stdout with open(os.devnull, "rb") as devnull: # PATCHED - p = promptless_Popen( + p = silent_Popen( conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) p_out, p_err = p.communicate() diff --git a/tagstudio/src/qt/helpers/vendored/pydub/utils.py b/tagstudio/src/qt/helpers/vendored/pydub/utils.py index 5c9e3c4b4..7a6e3af6e 100644 --- a/tagstudio/src/qt/helpers/vendored/pydub/utils.py +++ b/tagstudio/src/qt/helpers/vendored/pydub/utils.py @@ -8,7 +8,7 @@ get_extra_info, get_prober_name, ) -from src.qt.helpers.silent_popen import promptless_Popen +from src.qt.helpers.silent_popen import silent_Popen def _mediainfo_json(filepath, read_ahead_limit=-1): @@ -38,7 +38,7 @@ def _mediainfo_json(filepath, read_ahead_limit=-1): command = [prober, "-of", "json"] + command_args # PATCHED - res = promptless_Popen( + res = silent_Popen( command, stdin=stdin_parameter, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) output, stderr = res.communicate(input=stdin_data)