Harden yt-dlp binary resolution and analysis subprocess handling
- get_yt_dlp_path() no longer implicitly executes a bare "yt-dlp" from PATH (on Windows that lookup includes the CWD, so a planted binary in a writable directory could be run). A system yt-dlp is used only behind the explicit advanced.allow_system_ytdlp config opt-in, and then always as a which()-resolved absolute path. Analysis and download refuse to exec the not-installed sentinel. - Analysis subprocesses now run in their own session and the whole process group is killed on timeout, so deno grandchildren no longer leak; partial stderr is preserved and logged, and output decoding is pinned to utf-8 with replacement (Windows locale codecs crashed on non-UTF8 titles). - Flat-playlist entries are filtered for None (private/deleted first video no longer breaks analysis). - update_yt_dlp() normalizes the sentinel to Path, unbreaking the pip fallback path that crashed on str.exists(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -612,19 +612,30 @@ def check_ytdlp_installed() -> bool:
|
||||
|
||||
def get_yt_dlp_path() -> Path:
|
||||
"""
|
||||
Get the yt-dlp path, either from the app's bin directory or system PATH.
|
||||
This replaces the function in ytsage_utils.py.
|
||||
Get the yt-dlp path. Prefers the app-managed, SHA256-verified binary;
|
||||
a system-installed yt-dlp is only used when the user explicitly opts in
|
||||
via the advanced.allow_system_ytdlp config key (resolved to an absolute
|
||||
path so Windows' implicit CWD lookup can never pick up a planted binary).
|
||||
Returns:
|
||||
str: Path to yt-dlp binary
|
||||
Path to yt-dlp binary, or the bare string "yt-dlp" sentinel meaning
|
||||
"not installed" (triggers the setup dialog).
|
||||
"""
|
||||
# First check if we have yt-dlp in our app's bin directory or system PATH
|
||||
ytdlp_path = check_ytdlp_binary()
|
||||
if ytdlp_path:
|
||||
logger.info(f"Using yt-dlp from: {ytdlp_path}")
|
||||
return ytdlp_path
|
||||
|
||||
# If not found anywhere, fall back to the command name as a last resort
|
||||
logger.info("yt-dlp not found in app directory or PATH, falling back to command name")
|
||||
from ..utils.ytsage_config_manager import ConfigManager
|
||||
|
||||
if ConfigManager.get("advanced.allow_system_ytdlp"):
|
||||
system_ytdlp = shutil.which("yt-dlp")
|
||||
if system_ytdlp:
|
||||
resolved = Path(system_ytdlp).resolve()
|
||||
logger.info(f"Using system yt-dlp (advanced.allow_system_ytdlp): {resolved}")
|
||||
return resolved
|
||||
|
||||
# Not installed - return the sentinel that triggers the setup dialog
|
||||
logger.info("yt-dlp not found in app directory, returning setup sentinel")
|
||||
return "yt-dlp" # type: ignore[return-value]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user