@ -1,13 +1,13 @@
from __future__ import unicode_literals
from __future__ import unicode_literals
import hashlib
import io
import io
import json
import json
import traceback
import hashlib
import os
import os
import platform
import platform
import subprocess
import subprocess
import sys
import sys
import traceback
from zipimport import zipimporter
from zipimport import zipimporter
from . compat import compat_realpath
from . compat import compat_realpath
@ -33,6 +33,40 @@ def rsa_verify(message, signature, key):
def update_self ( to_screen , verbose , opener ) :
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
Update the program file with the latest version from the repository
Returns whether the program should terminate
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 '
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 ) :
def calc_sha256sum ( path ) :
h = hashlib . sha256 ( )
h = hashlib . sha256 ( )
b = bytearray ( 128 * 1024 )
b = bytearray ( 128 * 1024 )
@ -50,112 +89,91 @@ def update_self(to_screen, verbose, opener):
return h . hexdigest ( )
return h . hexdigest ( )
if not isinstance ( globals ( ) . get ( ' __loader__ ' ) , zipimporter ) and not hasattr ( sys , ' frozen ' ) :
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 report_error (
return
' 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
# 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
# though symlinks are not followed so that we need to do this manually
# with help of realpath
# with help of realpath
filename = compat_realpath ( sys . executable if hasattr ( sys , ' frozen ' ) else sys . argv [ 0 ] )
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
# Download and check versions info
try :
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 )
version_info = json . loads ( version_info )
except Exception :
except Exception :
if verbose :
return report_error ( ' can \' t obtain versions info. Please try again later ' , True , delim = ' or ' )
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
def version_tuple ( version_str ) :
def version_tuple ( version_str ) :
return tuple ( map ( int , version_str . split ( ' . ' ) ) )
return tuple ( map ( int , version_str . split ( ' . ' ) ) )
version_id = version_info [ ' tag_name ' ]
version_id = version_info [ ' tag_name ' ]
if version_tuple ( __version__ ) > = version_tuple ( version_id ) :
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
return
to_screen( ' Updating to version ' + version_id + ' ... ' )
ydl. to_screen( ' Updating to version ' + version_id + ' ... ' )
version_labels = {
version_labels = {
' zip_3 ' : ' ' ,
' zip_3 ' : ' ' ,
' zip_2 ' : ' ' ,
' zip_2 ' : ' ' ,
# 'zip_2': '_py2',
' exe_64 ' : ' .exe ' ,
' exe_64 ' : ' .exe ' ,
' exe_32 ' : ' _x86.exe ' ,
' exe_32 ' : ' _x86.exe ' ,
}
}
def get_bin_info ( bin_or_exe , version ) :
def get_bin_info ( bin_or_exe , version ) :
label = version_labels [ ' %s _ %s ' % ( bin_or_exe , version ) ]
label = version_labels [ ' %s _ %s ' % ( bin_or_exe , version ) ]
return next (
return next ( ( i for i in version_info [ ' assets ' ] if i [ ' name ' ] == ' yt-dlp %s ' % label ) , { } )
( i for i in version_info [ ' assets ' ] if i [ ' name ' ] == ' yt-dlp %s ' % label ) ,
{ } )
def get_sha256sum ( bin_or_exe , version ) :
def get_sha256sum ( bin_or_exe , version ) :
label = version_labels [ ' %s _ %s ' % ( bin_or_exe , version ) ]
label = version_labels [ ' %s _ %s ' % ( bin_or_exe , version ) ]
urlh = next (
urlh = next (
( i for i in version_info [ ' assets ' ]
( i for i in version_info [ ' assets ' ] if i [ ' name ' ] in ( ' SHA2-256SUMS ' ) ) ,
if i [ ' name ' ] in ( ' SHA2-256SUMS ' ) ) , { } ) . get ( ' browser_download_url ' )
{ } ) . get ( ' browser_download_url ' )
if not urlh :
if not urlh :
return None
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 ( ) ) )
hashes = list ( map ( lambda x : x . split ( ' : ' ) , hash_data . splitlines ( ) ) )
return next (
return next ( ( i [ 1 ] for i in hashes if i [ 0 ] == ' yt-dlp %s ' % label ) , None )
( i [ 1 ] for i in hashes if i [ 0 ] == ' yt-dlp %s ' % label ) ,
None )
if not os . access ( filename , os . W_OK ) :
if not os . access ( filename , os . W_OK ) :
to_screen ( ' ERROR: no write permissions on %s ' % filename )
return report_error ( ' no write permissions on %s ' % filename , expected = True )
return
# PyInstaller
# PyInstaller
if hasattr ( sys , ' frozen ' ) :
if hasattr ( sys , ' frozen ' ) :
exe = filename
exe = filename
directory = os . path . dirname ( exe )
directory = os . path . dirname ( exe )
if not os . access ( directory , os . W_OK ) :
if not os . access ( directory , os . W_OK ) :
to_screen ( ' ERROR: no write permissions on %s ' % directory )
return report_error ( ' no write permissions on %s ' % directory , expected = True )
return
try :
try :
arch = platform . architecture ( ) [ 0 ] [ : 2 ]
arch = platform . architecture ( ) [ 0 ] [ : 2 ]
url = get_bin_info ( ' exe ' , arch ) . get ( ' browser_download_url ' )
url = get_bin_info ( ' exe ' , arch ) . get ( ' browser_download_url ' )
if not url :
if not url :
to_screen ( ' ERROR: unable to fetch updates ' )
return report_error ( ' unable to fetch updates ' , True )
to_screen ( ' Visit https://github.com/yt-dlp/yt-dlp/releases/latest ' )
urlh = ydl . _opener . open ( url )
return
urlh = opener . open ( url )
newcontent = urlh . read ( )
newcontent = urlh . read ( )
urlh . close ( )
urlh . close ( )
except ( IOError , OSError , StopIteration ) :
except ( IOError , OSError , StopIteration ) :
if verbose :
return report_error ( ' unable to download latest version ' , True )
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
try :
try :
with open ( exe + ' .new ' , ' wb ' ) as outf :
with open ( exe + ' .new ' , ' wb ' ) as outf :
outf . write ( newcontent )
outf . write ( newcontent )
except ( IOError , OSError ) :
except ( IOError , OSError ) :
if verbose :
return report_error ( ' unable to write the new version ' )
to_screen ( encode_compat_str ( traceback . format_exc ( ) ) )
to_screen ( ' ERROR: unable to write the new version ' )
return
expected_sum = get_sha256sum ( ' exe ' , arch )
expected_sum = get_sha256sum ( ' exe ' , arch )
if not expected_sum :
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 :
elif calc_sha256sum ( exe + ' .new ' ) != expected_sum :
to_screen ( ' ERROR: unable to verify the new executable ' )
report_error ( ' unable to verify the new executable ' , True )
to_screen ( ' Visit https://github.com/yt-dlp/yt-dlp/releases/latest ' )
try :
try :
os . remove ( exe + ' .new ' )
os . remove ( exe + ' .new ' )
except OSError :
except OSError :
to_screen ( ' ERROR: unable to remove corrupt download ' )
return report_error ( ' unable to remove corrupt download ' )
return
try :
try :
bat = os . path . join ( directory , ' yt-dlp-updater.cmd ' )
bat = os . path . join ( directory , ' yt-dlp-updater.cmd ' )
@ -171,12 +189,9 @@ def update_self(to_screen, verbose, opener):
''' % (exe, exe, version_id))
''' % (exe, exe, version_id))
subprocess . Popen ( [ bat ] ) # Continues to run in the background
subprocess . Popen ( [ bat ] ) # Continues to run in the background
return True # Exit app
except ( IOError , OSError ) :
except ( IOError , OSError ) :
if verbose :
report_error ( ' unable to overwrite current version ' )
to_screen ( encode_compat_str ( traceback . format_exc ( ) ) )
return True # Exit app
to_screen ( ' ERROR: unable to overwrite current version ' )
return
# Zip unix package
# Zip unix package
elif isinstance ( globals ( ) . get ( ' __loader__ ' ) , zipimporter ) :
elif isinstance ( globals ( ) . get ( ' __loader__ ' ) , zipimporter ) :
@ -184,35 +199,24 @@ def update_self(to_screen, verbose, opener):
py_ver = platform . python_version ( ) [ 0 ]
py_ver = platform . python_version ( ) [ 0 ]
url = get_bin_info ( ' zip ' , py_ver ) . get ( ' browser_download_url ' )
url = get_bin_info ( ' zip ' , py_ver ) . get ( ' browser_download_url ' )
if not url :
if not url :
to_screen ( ' ERROR: unable to fetch updates ' )
return report_error ( ' unable to fetch updates ' , True )
to_screen ( ' Visit https://github.com/yt-dlp/yt-dlp/releases/latest ' )
urlh = ydl . _opener . open ( url )
return
urlh = opener . open ( url )
newcontent = urlh . read ( )
newcontent = urlh . read ( )
urlh . close ( )
urlh . close ( )
except ( IOError , OSError , StopIteration ) :
except ( IOError , OSError , StopIteration ) :
if verbose :
return report_error ( ' unable to download latest version ' , True )
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
expected_sum = get_sha256sum ( ' zip ' , py_ver )
expected_sum = get_sha256sum ( ' zip ' , py_ver )
if expected_sum and hashlib . sha256 ( newcontent ) . hexdigest ( ) != expected_sum :
if expected_sum and hashlib . sha256 ( newcontent ) . hexdigest ( ) != expected_sum :
to_screen ( ' ERROR: unable to verify the new zip ' )
return report_error ( ' unable to verify the new zip ' , True )
to_screen ( ' Visit https://github.com/yt-dlp/yt-dlp/releases/latest ' )
return
try :
try :
with open ( filename , ' wb ' ) as outf :
with open ( filename , ' wb ' ) as outf :
outf . write ( newcontent )
outf . write ( newcontent )
except ( IOError , OSError ) :
except ( IOError , OSError ) :
if verbose :
return report_error ( ' unable to overwrite current version ' )
to_screen ( encode_compat_str ( traceback . format_exc ( ) ) )
to_screen ( ' ERROR: unable to overwrite current version ' )
return
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
''' # UNUSED