mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-11-04 08:35:12 +00:00 
			
		
		
		
	[cleanup] Refactor updater
The updater now uses `.update.run_update` and not `.update.update_self`. Although I don't expect anyone to be using the updater via API, a wrapper `update_self` is provided for compatibility just in case
This commit is contained in:
		@@ -734,6 +734,7 @@ class YoutubeDL(object):
 | 
			
		||||
                else:
 | 
			
		||||
                    tb_data = traceback.format_list(traceback.extract_stack())
 | 
			
		||||
                    tb = ''.join(tb_data)
 | 
			
		||||
            if tb:
 | 
			
		||||
                self.to_stderr(tb)
 | 
			
		||||
        if not self.params.get('ignoreerrors', False):
 | 
			
		||||
            if sys.exc_info()[0] and hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ from .utils import (
 | 
			
		||||
    std_headers,
 | 
			
		||||
    write_string,
 | 
			
		||||
)
 | 
			
		||||
from .update import update_self
 | 
			
		||||
from .update import run_update
 | 
			
		||||
from .downloader import (
 | 
			
		||||
    FileDownloader,
 | 
			
		||||
)
 | 
			
		||||
@@ -663,7 +663,7 @@ def _real_main(argv=None):
 | 
			
		||||
        # Update version
 | 
			
		||||
        if opts.update_self:
 | 
			
		||||
            # If updater returns True, exit. Required for windows
 | 
			
		||||
            if update_self(ydl.to_screen, opts.verbose, ydl._opener):
 | 
			
		||||
            if run_update(ydl):
 | 
			
		||||
                if actual_use:
 | 
			
		||||
                    sys.exit('ERROR: The program must exit for the update to complete')
 | 
			
		||||
                sys.exit()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										138
									
								
								yt_dlp/update.py
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								yt_dlp/update.py
									
									
									
									
									
								
							@@ -1,13 +1,13 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import hashlib
 | 
			
		||||
import io
 | 
			
		||||
import json
 | 
			
		||||
import traceback
 | 
			
		||||
import hashlib
 | 
			
		||||
import os
 | 
			
		||||
import platform
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
from zipimport import zipimporter
 | 
			
		||||
 | 
			
		||||
from .compat import compat_realpath
 | 
			
		||||
@@ -33,6 +33,40 @@ def rsa_verify(message, signature, key):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_self(to_screen, verbose, opener):
 | 
			
		||||
    ''' Exists for backward compatibility. Use run_update(ydl) instead '''
 | 
			
		||||
 | 
			
		||||
    printfn = to_screen
 | 
			
		||||
 | 
			
		||||
    class FakeYDL():
 | 
			
		||||
        _opener = opener
 | 
			
		||||
        to_screen = printfn
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def report_warning(msg, *args, **kwargs):
 | 
			
		||||
            return printfn('WARNING: %s' % msg, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def report_error(msg, tb=None):
 | 
			
		||||
            printfn('ERROR: %s' % msg)
 | 
			
		||||
            if not verbose:
 | 
			
		||||
                return
 | 
			
		||||
            if tb is None:
 | 
			
		||||
                # Copied from YoutubeDl.trouble
 | 
			
		||||
                if sys.exc_info()[0]:
 | 
			
		||||
                    tb = ''
 | 
			
		||||
                    if hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
 | 
			
		||||
                        tb += ''.join(traceback.format_exception(*sys.exc_info()[1].exc_info))
 | 
			
		||||
                    tb += encode_compat_str(traceback.format_exc())
 | 
			
		||||
                else:
 | 
			
		||||
                    tb_data = traceback.format_list(traceback.extract_stack())
 | 
			
		||||
                    tb = ''.join(tb_data)
 | 
			
		||||
            if tb:
 | 
			
		||||
                printfn(tb)
 | 
			
		||||
 | 
			
		||||
    return run_update(FakeYDL())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_update(ydl):
 | 
			
		||||
    """
 | 
			
		||||
    Update the program file with the latest version from the repository
 | 
			
		||||
    Returns whether the program should terminate
 | 
			
		||||
@@ -40,6 +74,11 @@ def update_self(to_screen, verbose, opener):
 | 
			
		||||
 | 
			
		||||
    JSON_URL = 'https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest'
 | 
			
		||||
 | 
			
		||||
    def report_error(msg, network=False, expected=False, delim=';'):
 | 
			
		||||
        if network:
 | 
			
		||||
            msg += '%s Visit  https://github.com/yt-dlp/yt-dlp/releases/latest' % delim
 | 
			
		||||
        ydl.report_error(msg, tb='' if network or expected else None)
 | 
			
		||||
 | 
			
		||||
    def calc_sha256sum(path):
 | 
			
		||||
        h = hashlib.sha256()
 | 
			
		||||
        b = bytearray(128 * 1024)
 | 
			
		||||
@@ -50,112 +89,91 @@ def update_self(to_screen, verbose, opener):
 | 
			
		||||
        return h.hexdigest()
 | 
			
		||||
 | 
			
		||||
    if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, 'frozen'):
 | 
			
		||||
        to_screen('It looks like you installed yt-dlp with a package manager, pip, setup.py or a tarball. Please use that to update')
 | 
			
		||||
        return
 | 
			
		||||
        return report_error(
 | 
			
		||||
            'It looks like you installed yt-dlp with a package manager, pip, setup.py or a tarball. '
 | 
			
		||||
            'Please use that to update', expected=True)
 | 
			
		||||
 | 
			
		||||
    # sys.executable is set to the full pathname of the exe-file for py2exe
 | 
			
		||||
    # though symlinks are not followed so that we need to do this manually
 | 
			
		||||
    # with help of realpath
 | 
			
		||||
    filename = compat_realpath(sys.executable if hasattr(sys, 'frozen') else sys.argv[0])
 | 
			
		||||
    to_screen('Current Build Hash %s' % calc_sha256sum(filename))
 | 
			
		||||
    ydl.to_screen('Current Build Hash %s' % calc_sha256sum(filename))
 | 
			
		||||
 | 
			
		||||
    # Download and check versions info
 | 
			
		||||
    try:
 | 
			
		||||
        version_info = opener.open(JSON_URL).read().decode('utf-8')
 | 
			
		||||
        version_info = ydl._opener.open(JSON_URL).read().decode('utf-8')
 | 
			
		||||
        version_info = json.loads(version_info)
 | 
			
		||||
    except Exception:
 | 
			
		||||
        if verbose:
 | 
			
		||||
            to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
        to_screen('ERROR: can\'t obtain versions info. Please try again later')
 | 
			
		||||
        to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
        return
 | 
			
		||||
        return report_error('can\'t obtain versions info. Please try again later ', True, delim='or')
 | 
			
		||||
 | 
			
		||||
    def version_tuple(version_str):
 | 
			
		||||
        return tuple(map(int, version_str.split('.')))
 | 
			
		||||
 | 
			
		||||
    version_id = version_info['tag_name']
 | 
			
		||||
    if version_tuple(__version__) >= version_tuple(version_id):
 | 
			
		||||
        to_screen('yt-dlp is up to date (%s)' % __version__)
 | 
			
		||||
        ydl.to_screen('yt-dlp is up to date (%s)' % __version__)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    to_screen('Updating to version ' + version_id + ' ...')
 | 
			
		||||
    ydl.to_screen('Updating to version ' + version_id + ' ...')
 | 
			
		||||
 | 
			
		||||
    version_labels = {
 | 
			
		||||
        'zip_3': '',
 | 
			
		||||
        'zip_2': '',
 | 
			
		||||
        # 'zip_2': '_py2',
 | 
			
		||||
        'exe_64': '.exe',
 | 
			
		||||
        'exe_32': '_x86.exe',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def get_bin_info(bin_or_exe, version):
 | 
			
		||||
        label = version_labels['%s_%s' % (bin_or_exe, version)]
 | 
			
		||||
        return next(
 | 
			
		||||
            (i for i in version_info['assets'] if i['name'] == 'yt-dlp%s' % label),
 | 
			
		||||
            {})
 | 
			
		||||
        return next((i for i in version_info['assets'] if i['name'] == 'yt-dlp%s' % label), {})
 | 
			
		||||
 | 
			
		||||
    def get_sha256sum(bin_or_exe, version):
 | 
			
		||||
        label = version_labels['%s_%s' % (bin_or_exe, version)]
 | 
			
		||||
        urlh = next(
 | 
			
		||||
            (i for i in version_info['assets']
 | 
			
		||||
                if i['name'] in ('SHA2-256SUMS')), {}).get('browser_download_url')
 | 
			
		||||
            (i for i in version_info['assets'] if i['name'] in ('SHA2-256SUMS')),
 | 
			
		||||
            {}).get('browser_download_url')
 | 
			
		||||
        if not urlh:
 | 
			
		||||
            return None
 | 
			
		||||
        hash_data = opener.open(urlh).read().decode('utf-8')
 | 
			
		||||
        hash_data = ydl._opener.open(urlh).read().decode('utf-8')
 | 
			
		||||
        hashes = list(map(lambda x: x.split(':'), hash_data.splitlines()))
 | 
			
		||||
        return next(
 | 
			
		||||
            (i[1] for i in hashes if i[0] == 'yt-dlp%s' % label),
 | 
			
		||||
            None)
 | 
			
		||||
        return next((i[1] for i in hashes if i[0] == 'yt-dlp%s' % label), None)
 | 
			
		||||
 | 
			
		||||
    if not os.access(filename, os.W_OK):
 | 
			
		||||
        to_screen('ERROR: no write permissions on %s' % filename)
 | 
			
		||||
        return
 | 
			
		||||
        return report_error('no write permissions on %s' % filename, expected=True)
 | 
			
		||||
 | 
			
		||||
    # PyInstaller
 | 
			
		||||
    if hasattr(sys, 'frozen'):
 | 
			
		||||
        exe = filename
 | 
			
		||||
        directory = os.path.dirname(exe)
 | 
			
		||||
        if not os.access(directory, os.W_OK):
 | 
			
		||||
            to_screen('ERROR: no write permissions on %s' % directory)
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('no write permissions on %s' % directory, expected=True)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            arch = platform.architecture()[0][:2]
 | 
			
		||||
            url = get_bin_info('exe', arch).get('browser_download_url')
 | 
			
		||||
            if not url:
 | 
			
		||||
                to_screen('ERROR: unable to fetch updates')
 | 
			
		||||
                to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
                return
 | 
			
		||||
            urlh = opener.open(url)
 | 
			
		||||
                return report_error('unable to fetch updates', True)
 | 
			
		||||
            urlh = ydl._opener.open(url)
 | 
			
		||||
            newcontent = urlh.read()
 | 
			
		||||
            urlh.close()
 | 
			
		||||
        except (IOError, OSError, StopIteration):
 | 
			
		||||
            if verbose:
 | 
			
		||||
                to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen('ERROR: unable to download latest version')
 | 
			
		||||
            to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('unable to download latest version', True)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(exe + '.new', 'wb') as outf:
 | 
			
		||||
                outf.write(newcontent)
 | 
			
		||||
        except (IOError, OSError):
 | 
			
		||||
            if verbose:
 | 
			
		||||
                to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen('ERROR: unable to write the new version')
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('unable to write the new version')
 | 
			
		||||
 | 
			
		||||
        expected_sum = get_sha256sum('exe', arch)
 | 
			
		||||
        if not expected_sum:
 | 
			
		||||
            to_screen('WARNING: no hash information found for the release')
 | 
			
		||||
            ydl.report_warning('no hash information found for the release')
 | 
			
		||||
        elif calc_sha256sum(exe + '.new') != expected_sum:
 | 
			
		||||
            to_screen('ERROR: unable to verify the new executable')
 | 
			
		||||
            to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
            report_error('unable to verify the new executable', True)
 | 
			
		||||
            try:
 | 
			
		||||
                os.remove(exe + '.new')
 | 
			
		||||
            except OSError:
 | 
			
		||||
                to_screen('ERROR: unable to remove corrupt download')
 | 
			
		||||
            return
 | 
			
		||||
                return report_error('unable to remove corrupt download')
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            bat = os.path.join(directory, 'yt-dlp-updater.cmd')
 | 
			
		||||
@@ -171,12 +189,9 @@ def update_self(to_screen, verbose, opener):
 | 
			
		||||
                ''' % (exe, exe, version_id))
 | 
			
		||||
 | 
			
		||||
            subprocess.Popen([bat])  # Continues to run in the background
 | 
			
		||||
            return True  # Exit app
 | 
			
		||||
        except (IOError, OSError):
 | 
			
		||||
            if verbose:
 | 
			
		||||
                to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen('ERROR: unable to overwrite current version')
 | 
			
		||||
            return
 | 
			
		||||
            report_error('unable to overwrite current version')
 | 
			
		||||
        return True  # Exit app
 | 
			
		||||
 | 
			
		||||
    # Zip unix package
 | 
			
		||||
    elif isinstance(globals().get('__loader__'), zipimporter):
 | 
			
		||||
@@ -184,35 +199,24 @@ def update_self(to_screen, verbose, opener):
 | 
			
		||||
            py_ver = platform.python_version()[0]
 | 
			
		||||
            url = get_bin_info('zip', py_ver).get('browser_download_url')
 | 
			
		||||
            if not url:
 | 
			
		||||
                to_screen('ERROR: unable to fetch updates')
 | 
			
		||||
                to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
                return
 | 
			
		||||
            urlh = opener.open(url)
 | 
			
		||||
                return report_error('unable to fetch updates', True)
 | 
			
		||||
            urlh = ydl._opener.open(url)
 | 
			
		||||
            newcontent = urlh.read()
 | 
			
		||||
            urlh.close()
 | 
			
		||||
        except (IOError, OSError, StopIteration):
 | 
			
		||||
            if verbose:
 | 
			
		||||
                to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen('ERROR: unable to download latest version')
 | 
			
		||||
            to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('unable to download latest version', True)
 | 
			
		||||
 | 
			
		||||
        expected_sum = get_sha256sum('zip', py_ver)
 | 
			
		||||
        if expected_sum and hashlib.sha256(newcontent).hexdigest() != expected_sum:
 | 
			
		||||
            to_screen('ERROR: unable to verify the new zip')
 | 
			
		||||
            to_screen('Visit https://github.com/yt-dlp/yt-dlp/releases/latest')
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('unable to verify the new zip', True)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(filename, 'wb') as outf:
 | 
			
		||||
                outf.write(newcontent)
 | 
			
		||||
        except (IOError, OSError):
 | 
			
		||||
            if verbose:
 | 
			
		||||
                to_screen(encode_compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen('ERROR: unable to overwrite current version')
 | 
			
		||||
            return
 | 
			
		||||
            return report_error('unable to overwrite current version')
 | 
			
		||||
 | 
			
		||||
    to_screen('Updated yt-dlp. Restart yt-dlp to use the new version')
 | 
			
		||||
    ydl.to_screen('Updated yt-dlp to version %s; Restart yt-dlp to use the new version' % version_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
'''  # UNUSED
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user