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