]> jfr.im git - yt-dlp.git/blame - youtube_dl/update.py
[vk] Remove debug assert statement (fixes #4672, fixes #4514)
[yt-dlp.git] / youtube_dl / 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
d2790370 8import subprocess
46353f67 9import sys
d5ed35b6
FV
10from zipimport import zipimporter
11
15938ab6 12from .compat import (
ce02ed60
PH
13 compat_str,
14 compat_urllib_request,
15)
d5ed35b6
FV
16from .version import __version__
17
5f6a1245 18
d5ed35b6
FV
19def rsa_verify(message, signature, key):
20 from struct import pack
21 from hashlib import sha256
5f6a1245 22
15938ab6 23 assert isinstance(message, bytes)
d5ed35b6
FV
24 block_size = 0
25 n = key[0]
26 while n:
27 block_size += 1
28 n >>= 8
29 signature = pow(int(signature, 16), key[1], key[0])
30 raw_bytes = []
31 while signature:
32 raw_bytes.insert(0, pack("B", signature & 0xFF))
33 signature >>= 8
15938ab6
PH
34 signature = (block_size - len(raw_bytes)) * b'\x00' + b''.join(raw_bytes)
35 if signature[0:2] != b'\x00\x01':
5f6a1245 36 return False
d5ed35b6 37 signature = signature[2:]
15938ab6 38 if b'\x00' not in signature:
5f6a1245 39 return False
15938ab6
PH
40 signature = signature[signature.index(b'\x00') + 1:]
41 if not signature.startswith(b'\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'):
5f6a1245 42 return False
d5ed35b6 43 signature = signature[19:]
5f6a1245
JW
44 if signature != sha256(message).digest():
45 return False
d5ed35b6
FV
46 return True
47
d7386f62 48
46353f67 49def update_self(to_screen, verbose):
d5ed35b6
FV
50 """Update the program file with the latest version from the repository"""
51
43ff1a34 52 UPDATE_URL = "http://rg3.github.io/youtube-dl/update/"
d5ed35b6
FV
53 VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
54 JSON_URL = UPDATE_URL + 'versions.json'
55 UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
56
d5ed35b6 57 if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"):
15938ab6 58 to_screen('It looks like you installed youtube-dl with a package manager, pip, setup.py or a tarball. Please use that to update.')
d5ed35b6
FV
59 return
60
61 # Check if there is a new version
62 try:
63 newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
64 except:
5f6a1245
JW
65 if verbose:
66 to_screen(compat_str(traceback.format_exc()))
15938ab6 67 to_screen('ERROR: can\'t find the current version. Please try again later.')
d5ed35b6
FV
68 return
69 if newversion == __version__:
15938ab6 70 to_screen('youtube-dl is up-to-date (' + __version__ + ')')
d5ed35b6
FV
71 return
72
73 # Download and check versions info
74 try:
75 versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
76 versions_info = json.loads(versions_info)
77 except:
5f6a1245
JW
78 if verbose:
79 to_screen(compat_str(traceback.format_exc()))
15938ab6 80 to_screen('ERROR: can\'t obtain versions info. Please try again later.')
d5ed35b6 81 return
83e865a3 82 if 'signature' not in versions_info:
15938ab6 83 to_screen('ERROR: the versions file is not signed or corrupted. Aborting.')
d5ed35b6
FV
84 return
85 signature = versions_info['signature']
86 del versions_info['signature']
87 if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY):
15938ab6 88 to_screen('ERROR: the versions file signature is invalid. Aborting.')
d5ed35b6
FV
89 return
90
d2790370 91 version_id = versions_info['latest']
d7386f62
PH
92
93 def version_tuple(version_str):
94 return tuple(map(int, version_str.split('.')))
2e767313 95 if version_tuple(__version__) >= version_tuple(version_id):
15938ab6 96 to_screen('youtube-dl is up to date (%s)' % __version__)
d7386f62
PH
97 return
98
15938ab6 99 to_screen('Updating to version ' + version_id + ' ...')
d2790370 100 version = versions_info['versions'][version_id]
3bf79c75 101
46a127ee 102 print_notes(to_screen, versions_info['versions'])
d5ed35b6 103
46353f67
PH
104 filename = sys.argv[0]
105 # Py2EXE: Filename could be different
106 if hasattr(sys, "frozen") and not os.path.isfile(filename):
15938ab6
PH
107 if os.path.isfile(filename + '.exe'):
108 filename += '.exe'
46353f67 109
d5ed35b6 110 if not os.access(filename, os.W_OK):
15938ab6 111 to_screen('ERROR: no write permissions on %s' % filename)
d5ed35b6
FV
112 return
113
114 # Py2EXE
115 if hasattr(sys, "frozen"):
116 exe = os.path.abspath(filename)
117 directory = os.path.dirname(exe)
118 if not os.access(directory, os.W_OK):
15938ab6 119 to_screen('ERROR: no write permissions on %s' % directory)
d5ed35b6
FV
120 return
121
122 try:
123 urlh = compat_urllib_request.urlopen(version['exe'][0])
124 newcontent = urlh.read()
125 urlh.close()
0b63aed8 126 except (IOError, OSError):
5f6a1245
JW
127 if verbose:
128 to_screen(compat_str(traceback.format_exc()))
15938ab6 129 to_screen('ERROR: unable to download latest version')
d5ed35b6
FV
130 return
131
132 newcontent_hash = hashlib.sha256(newcontent).hexdigest()
133 if newcontent_hash != version['exe'][1]:
15938ab6 134 to_screen('ERROR: the downloaded file hash does not match. Aborting.')
d5ed35b6
FV
135 return
136
137 try:
138 with open(exe + '.new', 'wb') as outf:
139 outf.write(newcontent)
0b63aed8 140 except (IOError, OSError):
5f6a1245
JW
141 if verbose:
142 to_screen(compat_str(traceback.format_exc()))
15938ab6 143 to_screen('ERROR: unable to write the new version')
d5ed35b6
FV
144 return
145
146 try:
147 bat = os.path.join(directory, 'youtube-dl-updater.bat')
d2790370 148 with io.open(bat, 'w') as batfile:
15938ab6 149 batfile.write('''
d2790370
PH
150@echo off
151echo Waiting for file handle to be closed ...
d5ed35b6 152ping 127.0.0.1 -n 5 -w 1000 > NUL
d2790370
PH
153move /Y "%s.new" "%s" > NUL
154echo Updated youtube-dl to version %s.
155start /b "" cmd /c del "%%~f0"&exit /b"
15938ab6 156 \n''' % (exe, exe, version_id))
d5ed35b6 157
d2790370
PH
158 subprocess.Popen([bat]) # Continues to run in the background
159 return # Do not show premature success messages
0b63aed8 160 except (IOError, OSError):
5f6a1245
JW
161 if verbose:
162 to_screen(compat_str(traceback.format_exc()))
15938ab6 163 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
164 return
165
166 # Zip unix package
167 elif isinstance(globals().get('__loader__'), zipimporter):
168 try:
169 urlh = compat_urllib_request.urlopen(version['bin'][0])
170 newcontent = urlh.read()
171 urlh.close()
0b63aed8 172 except (IOError, OSError):
5f6a1245
JW
173 if verbose:
174 to_screen(compat_str(traceback.format_exc()))
15938ab6 175 to_screen('ERROR: unable to download latest version')
d5ed35b6
FV
176 return
177
178 newcontent_hash = hashlib.sha256(newcontent).hexdigest()
179 if newcontent_hash != version['bin'][1]:
15938ab6 180 to_screen('ERROR: the downloaded file hash does not match. Aborting.')
d5ed35b6
FV
181 return
182
183 try:
184 with open(filename, 'wb') as outf:
185 outf.write(newcontent)
0b63aed8 186 except (IOError, OSError):
5f6a1245
JW
187 if verbose:
188 to_screen(compat_str(traceback.format_exc()))
15938ab6 189 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
190 return
191
15938ab6 192 to_screen('Updated youtube-dl. Restart youtube-dl to use the new version.')
3bf79c75 193
5f6a1245 194
46a127ee 195def get_notes(versions, fromVersion):
3bf79c75 196 notes = []
5f6a1245 197 for v, vdata in sorted(versions.items()):
3bf79c75
PH
198 if v > fromVersion:
199 notes.extend(vdata.get('notes', []))
46a127ee
PH
200 return notes
201
5f6a1245 202
46a127ee
PH
203def print_notes(to_screen, versions, fromVersion=__version__):
204 notes = get_notes(versions, fromVersion)
3bf79c75 205 if notes:
15938ab6 206 to_screen('PLEASE NOTE:')
3bf79c75
PH
207 for note in notes:
208 to_screen(note)