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