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