feat: implement get_clean_env function to manage environment variables for subprocesses

This commit is contained in:
Benexl
2025-12-31 21:43:43 +03:00
parent ce6294a17b
commit 1ce2d2740d
8 changed files with 50 additions and 11 deletions

View File

@@ -9,6 +9,8 @@ import importlib.util
import click
import httpx
from viu_media.core.utils import detect
logger = logging.getLogger(__name__)
@@ -138,6 +140,7 @@ def render(url: str, capture: bool = False, size: str = "30x30") -> Optional[str
[icat_executable, "--align", "left", url],
capture_output=capture,
text=capture,
env=detect.get_clean_env(),
)
if process.returncode == 0:
return process.stdout if capture else None

View File

@@ -21,7 +21,7 @@ from rich.progress import (
)
from rich.prompt import Confirm
from ..utils.file import sanitize_filename
from ..utils.detect import get_clean_env
from ..exceptions import ViuError
from ..patterns import TORRENT_REGEX
from ..utils.networking import get_remote_filename
@@ -372,6 +372,7 @@ class DefaultDownloader(BaseDownloader):
capture_output=params.silent, # Only suppress ffmpeg output if silent
text=True,
check=True,
env=get_clean_env(),
)
final_output_path = video_path.parent / merged_filename

View File

@@ -11,7 +11,7 @@ from rich.prompt import Confirm
import yt_dlp
from yt_dlp.utils import sanitize_filename
from ..utils.detect import get_clean_env
from ..exceptions import ViuError
from ..patterns import TORRENT_REGEX
from ..utils.networking import get_remote_filename
@@ -224,7 +224,7 @@ class YtDLPDownloader(BaseDownloader):
# Run the ffmpeg command
try:
subprocess.run(args)
subprocess.run(args, env=get_clean_env())
final_output_path = video_path.parent / merged_filename
if final_output_path.exists():

View File

@@ -83,3 +83,21 @@ def get_python_executable() -> str:
return "python"
else:
return sys.executable
def get_clean_env() -> dict[str, str]:
"""
Returns a copy of the environment with LD_LIBRARY_PATH fixed for system subprocesses
when running as a PyInstaller frozen application.
This prevents system binaries (like mpv, ffmpeg) from loading incompatible
libraries from the PyInstaller bundle.
"""
env = os.environ.copy()
if is_frozen():
# PyInstaller saves the original LD_LIBRARY_PATH in LD_LIBRARY_PATH_ORIG
if "LD_LIBRARY_PATH_ORIG" in env:
env["LD_LIBRARY_PATH"] = env["LD_LIBRARY_PATH_ORIG"]
else:
# If orig didn't exist, LD_LIBRARY_PATH shouldn't exist for the subprocess
env.pop("LD_LIBRARY_PATH", None)
return env

View File

@@ -97,7 +97,7 @@ class MpvPlayer(BasePlayer):
"is.xyz.mpv/.MPVActivity",
]
subprocess.run(args)
subprocess.run(args,env=detect.get_clean_env())
return PlayerResult(params.episode)
@@ -146,6 +146,7 @@ class MpvPlayer(BasePlayer):
text=True,
encoding="utf-8",
check=False,
env=detect.get_clean_env(),
)
if proc.stdout:
for line in reversed(proc.stdout.split("\n")):
@@ -185,7 +186,7 @@ class MpvPlayer(BasePlayer):
logger.info(f"Starting MPV with IPC socket: {socket_path}")
process = subprocess.Popen(pre_args + mpv_args)
process = subprocess.Popen(pre_args + mpv_args,env=detect.get_clean_env())
return process
@@ -210,7 +211,7 @@ class MpvPlayer(BasePlayer):
args.append("--player-args")
args.extend(mpv_args)
subprocess.run(args)
subprocess.run(args,env=detect.get_clean_env())
return PlayerResult(params.episode)
def _stream_on_desktop_with_syncplay(self, params: PlayerParams) -> PlayerResult:
@@ -232,7 +233,7 @@ class MpvPlayer(BasePlayer):
if mpv_args := self._create_mpv_cli_options(params):
args.append("--")
args.extend(mpv_args)
subprocess.run(args)
subprocess.run(args,env=detect.get_clean_env())
return PlayerResult(params.episode)

View File

@@ -103,7 +103,7 @@ class VlcPlayer(BasePlayer):
params.title,
]
subprocess.run(args)
subprocess.run(args,env=detect.get_clean_env())
return PlayerResult(episode=params.episode)
@@ -134,7 +134,7 @@ class VlcPlayer(BasePlayer):
if self.config.args:
args.extend(self.config.args.split(","))
subprocess.run(args, encoding="utf-8")
subprocess.run(args, encoding="utf-8",env=detect.get_clean_env())
return PlayerResult(episode=params.episode)
def _stream_on_desktop_with_webtorrent_cli(
@@ -159,7 +159,7 @@ class VlcPlayer(BasePlayer):
args.append("--player-args")
args.extend(self.config.args.split(","))
subprocess.run(args)
subprocess.run(args,env=detect.get_clean_env())
return PlayerResult(episode=params.episode)

View File

@@ -5,6 +5,8 @@ import subprocess
from rich.prompt import Prompt
from viu_media.core.utils import detect
from ....core.config import FzfConfig
from ....core.exceptions import ViuError
from ..base import BaseSelector
@@ -49,6 +51,7 @@ class FzfSelector(BaseSelector):
stdout=subprocess.PIPE,
text=True,
encoding="utf-8",
env=detect.get_clean_env(),
)
if result.returncode != 0:
return None
@@ -76,6 +79,7 @@ class FzfSelector(BaseSelector):
stdout=subprocess.PIPE,
text=True,
encoding="utf-8",
env=detect.get_clean_env(),
)
if result.returncode != 0:
return []
@@ -117,7 +121,16 @@ class FzfSelector(BaseSelector):
lines = result.stdout.strip().splitlines()
return lines[-1] if lines else (default or "")
def search(self, prompt, search_command, *, preview=None, header=None, initial_query=None, initial_results=None):
def search(
self,
prompt,
search_command,
*,
preview=None,
header=None,
initial_query=None,
initial_results=None,
):
"""Enhanced search using fzf's --reload flag for dynamic search."""
# Build the header with optional custom header line
display_header = self.header
@@ -156,6 +169,7 @@ class FzfSelector(BaseSelector):
stdout=subprocess.PIPE,
text=True,
encoding="utf-8",
env=detect.get_clean_env(),
)
if result.returncode != 0:
return None

View File

@@ -43,6 +43,7 @@ class RofiSelector(BaseSelector):
input=rofi_input,
stdout=subprocess.PIPE,
text=True,
env=detect.get_clean_env()
)
if result.returncode == 0:
@@ -106,6 +107,7 @@ class RofiSelector(BaseSelector):
input=rofi_input,
stdout=subprocess.PIPE,
text=True,
env=detect.get_clean_env()
)
if result.returncode == 0: