diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 1cd14a44d..db77ee39b 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -26,7 +26,7 @@ from .postprocessor import ( MetadataFromFieldPP, MetadataParserPP, ) -from .update import run_update +from .update import Updater from .utils import ( NO_DEFAULT, POSTPROCESS_WHEN, @@ -879,17 +879,23 @@ def _real_main(argv=None): return with YoutubeDL(ydl_opts) as ydl: + pre_process = opts.update_self or opts.rm_cachedir actual_use = all_urls or opts.load_info_filename if opts.rm_cachedir: ydl.cache.remove() - if opts.update_self and run_update(ydl) and actual_use: - # If updater returns True, exit. Required for windows - return 100, 'ERROR: The program must exit for the update to complete' + updater = Updater(ydl) + if opts.update_self and updater.update() and actual_use: + if updater.cmd: + return updater.restart() + # This code is reachable only for zip variant in py < 3.10 + # It makes sense to exit here, but the old behavior is to continue + ydl.report_warning('Restart yt-dlp to use the updated version') + # return 100, 'ERROR: The program must exit for the update to complete' if not actual_use: - if opts.update_self or opts.rm_cachedir: + if pre_process: return ydl._download_retcode ydl.warn_if_short_id(sys.argv[1:] if argv is None else argv) diff --git a/yt_dlp/update.py b/yt_dlp/update.py index 68418ce98..f4ce4f958 100644 --- a/yt_dlp/update.py +++ b/yt_dlp/update.py @@ -1,3 +1,4 @@ +import atexit import hashlib import json import os @@ -8,7 +9,7 @@ from zipimport import zipimporter from .compat import functools # isort: split from .compat import compat_realpath -from .utils import Popen, traverse_obj, version_tuple +from .utils import Popen, shell_quote, traverse_obj, version_tuple from .version import __version__ REPOSITORY = 'yt-dlp/yt-dlp' @@ -206,17 +207,28 @@ class Updater: if detect_variant() not in ('win32_exe', 'py2exe'): if old_filename: os.remove(old_filename) - self.ydl.to_screen(f'Updated yt-dlp to version {self.new_version}; Restart yt-dlp to use the new version') - return + else: + atexit.register(Popen, f'ping 127.0.0.1 -n 5 -w 1000 & del /F "{old_filename}"', + shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - try: - # Continues to run in the background - Popen(f'ping 127.0.0.1 -n 5 -w 1000 & del /F "{old_filename}"', - shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - self.ydl.to_screen(f'Updated yt-dlp to version {self.new_version}') - return True # Exit app - except OSError: - self._report_unable('delete the old version') + self.ydl.to_screen(f'Updated yt-dlp to version {self.new_version}') + return True + + @functools.cached_property + def cmd(self): + """The command-line to run the executable, if known""" + # There is no sys.orig_argv in py < 3.10. Also, it can be [] when frozen + if getattr(sys, 'orig_argv', None): + return sys.orig_argv + elif hasattr(sys, 'frozen'): + return sys.argv + + def restart(self): + """Restart the executable""" + assert self.cmd, 'Must be frozen or Py >= 3.10' + self.ydl.write_debug(f'Restarting: {shell_quote(self.cmd)}') + _, _, returncode = Popen.run(self.cmd) + return returncode def run_update(ydl):