]> jfr.im git - yt-dlp.git/blame - youtube_dlc/update.py
Improve build/updater
[yt-dlp.git] / youtube_dlc / update.py
CommitLineData
15938ab6
PH
1from __future__ import unicode_literals
2
d2790370 3import io
d5ed35b6
FV
4import json
5import traceback
6import hashlib
ce02ed60 7import os
e5813e53 8import platform
d2790370 9import subprocess
46353f67 10import sys
d5ed35b6
FV
11from zipimport import zipimporter
12
bfe2b8cf 13from .compat import compat_realpath
c0384f22 14from .utils import encode_compat_str
d3d3e2e3 15
d5ed35b6
FV
16from .version import __version__
17
5f6a1245 18
3dd264bf 19''' # Not signed
d5ed35b6 20def rsa_verify(message, signature, key):
d5ed35b6 21 from hashlib import sha256
15938ab6 22 assert isinstance(message, bytes)
4d318be1
FV
23 byte_size = (len(bin(key[0])) - 2 + 8 - 1) // 8
24 signature = ('%x' % pow(int(signature, 16), key[1], key[0])).encode()
25 signature = (byte_size * 2 - len(signature)) * b'0' + signature
26 asn1 = b'3031300d060960864801650304020105000420'
27 asn1 += sha256(message).hexdigest().encode()
28 if byte_size < len(asn1) // 2 + 11:
5f6a1245 29 return False
4d318be1
FV
30 expected = b'0001' + (byte_size - len(asn1) // 2 - 3) * b'ff' + b'00' + asn1
31 return expected == signature
3dd264bf 32'''
d5ed35b6 33
d7386f62 34
0c3e5f49 35def update_self(to_screen, verbose, opener):
e5813e53 36 """
37 Update the program file with the latest version from the repository
38 Returns whether the program should terminate
39 """
d5ed35b6 40
3dd264bf 41 JSON_URL = 'https://api.github.com/repos/pukkandan/yt-dlp/releases/latest'
d5ed35b6 42
fa57af1e
U
43 def sha256sum():
44 h = hashlib.sha256()
45 b = bytearray(128 * 1024)
46 mv = memoryview(b)
47 with open(os.path.realpath(sys.executable), 'rb', buffering=0) as f:
48 for n in iter(lambda: f.readinto(mv), 0):
49 h.update(mv[:n])
50 return h.hexdigest()
51
52 to_screen('Current Build Hash %s' % sha256sum())
53
611c1dd9 54 if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, 'frozen'):
e5813e53 55 to_screen('It looks like you installed yt-dlp with a package manager, pip, setup.py or a tarball. Please use that to update.')
d5ed35b6
FV
56 return
57
d5ed35b6
FV
58 # Download and check versions info
59 try:
3dd264bf 60 version_info = opener.open(JSON_URL).read().decode('utf-8')
61 version_info = json.loads(version_info)
70a1165b 62 except Exception:
5f6a1245 63 if verbose:
c0384f22 64 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 65 to_screen('ERROR: can\'t obtain versions info. Please try again later.')
06869367 66 to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest')
d5ed35b6
FV
67 return
68
d7386f62
PH
69 def version_tuple(version_str):
70 return tuple(map(int, version_str.split('.')))
3dd264bf 71
e5813e53 72 version_id = version_info['tag_name']
2e767313 73 if version_tuple(__version__) >= version_tuple(version_id):
e5813e53 74 to_screen('yt-dlp is up to date (%s)' % __version__)
d7386f62
PH
75 return
76
15938ab6 77 to_screen('Updating to version ' + version_id + ' ...')
3bf79c75 78
e5813e53 79 def get_bin_info(bin_or_exe, version):
80 labels = {
81 'zip_3': '',
82 'zip_2': '',
83 # 'zip_2': '_py2',
84 'exe_64': '.exe',
85 'exe_32': '_x86.exe',
86 }
87 label = labels['%s_%s' % (bin_or_exe, version)]
88 return next(
89 i for i in version_info['assets']
90 if i['name'] in ('yt-dlp%s' % label, 'youtube-dlc%s' % label))
d5ed35b6 91
e9297256 92 # sys.executable is set to the full pathname of the exe-file for py2exe
bfe2b8cf
S
93 # though symlinks are not followed so that we need to do this manually
94 # with help of realpath
95 filename = compat_realpath(sys.executable if hasattr(sys, 'frozen') else sys.argv[0])
46353f67 96
d5ed35b6 97 if not os.access(filename, os.W_OK):
15938ab6 98 to_screen('ERROR: no write permissions on %s' % filename)
d5ed35b6
FV
99 return
100
3dd264bf 101 # PyInstaller
611c1dd9 102 if hasattr(sys, 'frozen'):
e9297256 103 exe = filename
d5ed35b6
FV
104 directory = os.path.dirname(exe)
105 if not os.access(directory, os.W_OK):
15938ab6 106 to_screen('ERROR: no write permissions on %s' % directory)
d5ed35b6
FV
107 return
108
109 try:
e5813e53 110 arch = platform.architecture()[0][:2]
111 urlh = opener.open(get_bin_info('exe', arch)['browser_download_url'])
d5ed35b6
FV
112 newcontent = urlh.read()
113 urlh.close()
e5813e53 114 except (IOError, OSError, StopIteration):
5f6a1245 115 if verbose:
c0384f22 116 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 117 to_screen('ERROR: unable to download latest version')
06869367 118 to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest')
d5ed35b6
FV
119 return
120
121 try:
122 with open(exe + '.new', 'wb') as outf:
123 outf.write(newcontent)
0b63aed8 124 except (IOError, OSError):
5f6a1245 125 if verbose:
c0384f22 126 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 127 to_screen('ERROR: unable to write the new version')
d5ed35b6
FV
128 return
129
130 try:
3dd264bf 131 bat = os.path.join(directory, 'yt-dlp-updater.cmd')
d2790370 132 with io.open(bat, 'w') as batfile:
15938ab6 133 batfile.write('''
3dd264bf 134@(
135 echo.Waiting for file handle to be closed ...
136 ping 127.0.0.1 -n 5 -w 1000 > NUL
137 move /Y "%s.new" "%s" > NUL
e5813e53 138 echo.Updated yt-dlp to version %s.
3dd264bf 139)
140@start /b "" cmd /c del "%%~f0"&exit /b
141 ''' % (exe, exe, version_id))
d5ed35b6 142
d2790370
PH
143 subprocess.Popen([bat]) # Continues to run in the background
144 return # Do not show premature success messages
0b63aed8 145 except (IOError, OSError):
5f6a1245 146 if verbose:
c0384f22 147 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 148 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
149 return
150
151 # Zip unix package
152 elif isinstance(globals().get('__loader__'), zipimporter):
153 try:
e5813e53 154 py_ver = platform.python_version()[0]
155 urlh = opener.open(get_bin_info('zip', py_ver)['browser_download_url'])
d5ed35b6
FV
156 newcontent = urlh.read()
157 urlh.close()
e5813e53 158 except (IOError, OSError, StopIteration):
5f6a1245 159 if verbose:
c0384f22 160 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 161 to_screen('ERROR: unable to download latest version')
06869367 162 to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest')
d5ed35b6
FV
163 return
164
165 try:
166 with open(filename, 'wb') as outf:
167 outf.write(newcontent)
0b63aed8 168 except (IOError, OSError):
5f6a1245 169 if verbose:
c0384f22 170 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 171 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
172 return
173
e5813e53 174 to_screen('Updated yt-dlp. Restart youtube-dlc to use the new version.')
3bf79c75 175
5f6a1245 176
3dd264bf 177''' # UNUSED
46a127ee 178def get_notes(versions, fromVersion):
3bf79c75 179 notes = []
5f6a1245 180 for v, vdata in sorted(versions.items()):
3bf79c75
PH
181 if v > fromVersion:
182 notes.extend(vdata.get('notes', []))
46a127ee
PH
183 return notes
184
5f6a1245 185
46a127ee
PH
186def print_notes(to_screen, versions, fromVersion=__version__):
187 notes = get_notes(versions, fromVersion)
3bf79c75 188 if notes:
15938ab6 189 to_screen('PLEASE NOTE:')
3bf79c75
PH
190 for note in notes:
191 to_screen(note)
3dd264bf 192'''