@ -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