From 6a6cdcd1824a14e3b336332c8f31f65497b8c4b8 Mon Sep 17 00:00:00 2001 From: sepro <4618135+seproDev@users.noreply.github.com> Date: Sat, 24 Feb 2024 12:58:03 +0100 Subject: [PATCH] [core] Warn user when not launching through shell on Windows (#9250) Authored by: seproDev, Grub4K Co-authored-by: Simon Sawicki --- yt_dlp/__init__.py | 25 +++++++++++++++++++++++-- yt_dlp/options.py | 7 +++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 57a48715752..4380b888d04 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -14,7 +14,7 @@ import re import traceback -from .compat import compat_shlex_quote +from .compat import compat_os_name, compat_shlex_quote from .cookies import SUPPORTED_BROWSERS, SUPPORTED_KEYRINGS from .downloader.external import get_external_downloader from .extractor import list_extractor_classes @@ -984,7 +984,28 @@ def _real_main(argv=None): if pre_process: return ydl._download_retcode - ydl.warn_if_short_id(sys.argv[1:] if argv is None else argv) + args = sys.argv[1:] if argv is None else argv + ydl.warn_if_short_id(args) + + # Show a useful error message and wait for keypress if not launched from shell on Windows + if not args and compat_os_name == 'nt' and getattr(sys, 'frozen', False): + import ctypes.wintypes + import msvcrt + + kernel32 = ctypes.WinDLL('Kernel32') + + buffer = (1 * ctypes.wintypes.DWORD)() + attached_processes = kernel32.GetConsoleProcessList(buffer, 1) + # If we only have a single process attached, then the executable was double clicked + # When using `pyinstaller` with `--onefile`, two processes get attached + is_onefile = hasattr(sys, '_MEIPASS') and os.path.basename(sys._MEIPASS).startswith('_MEI') + if attached_processes == 1 or is_onefile and attached_processes == 2: + print(parser._generate_error_message( + 'Do not double-click the executable, instead call it from a command line.\n' + 'Please read the README for further information on how to use yt-dlp: ' + 'https://github.com/yt-dlp/yt-dlp#readme')) + msvcrt.getch() + _exit(2) parser.error( 'You must provide at least one URL.\n' 'Type yt-dlp --help to see a list of all options.') diff --git a/yt_dlp/options.py b/yt_dlp/options.py index ab4986515ba..14b030cfb12 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -196,9 +196,12 @@ def parse_known_args(self, args=None, values=None, strict=True): raise return self.check_values(self.values, self.largs) - def error(self, msg): + def _generate_error_message(self, msg): msg = f'{self.get_prog_name()}: error: {str(msg).strip()}\n' - raise optparse.OptParseError(f'{self.get_usage()}\n{msg}' if self.usage else msg) + return f'{self.get_usage()}\n{msg}' if self.usage else msg + + def error(self, msg): + raise optparse.OptParseError(self._generate_error_message(msg)) def _get_args(self, args): return sys.argv[1:] if args is None else list(args)