]> jfr.im git - yt-dlp.git/blob - yt_dlp/compat.py
[MainStreaming] Add extractor (#2180)
[yt-dlp.git] / yt_dlp / compat.py
1 # coding: utf-8
2
3 import asyncio
4 import base64
5 import ctypes
6 import getpass
7 import html
8 import html.parser
9 import http
10 import http.client
11 import http.cookiejar
12 import http.cookies
13 import http.server
14 import itertools
15 import optparse
16 import os
17 import re
18 import shlex
19 import shutil
20 import socket
21 import struct
22 import subprocess
23 import sys
24 import tokenize
25 import urllib
26 import xml.etree.ElementTree as etree
27 from subprocess import DEVNULL
28
29
30 # HTMLParseError has been deprecated in Python 3.3 and removed in
31 # Python 3.5. Introducing dummy exception for Python >3.5 for compatible
32 # and uniform cross-version exception handling
33 class compat_HTMLParseError(Exception):
34 pass
35
36
37 # compat_ctypes_WINFUNCTYPE = ctypes.WINFUNCTYPE
38 # will not work since ctypes.WINFUNCTYPE does not exist in UNIX machines
39 def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
40 return ctypes.WINFUNCTYPE(*args, **kwargs)
41
42
43 class _TreeBuilder(etree.TreeBuilder):
44 def doctype(self, name, pubid, system):
45 pass
46
47
48 def compat_etree_fromstring(text):
49 return etree.XML(text, parser=etree.XMLParser(target=_TreeBuilder()))
50
51
52 compat_os_name = os._name if os.name == 'java' else os.name
53
54
55 if compat_os_name == 'nt':
56 def compat_shlex_quote(s):
57 return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
58 else:
59 from shlex import quote as compat_shlex_quote
60
61
62 def compat_ord(c):
63 if type(c) is int:
64 return c
65 else:
66 return ord(c)
67
68
69 def compat_setenv(key, value, env=os.environ):
70 env[key] = value
71
72
73 if compat_os_name == 'nt' and sys.version_info < (3, 8):
74 # os.path.realpath on Windows does not follow symbolic links
75 # prior to Python 3.8 (see https://bugs.python.org/issue9949)
76 def compat_realpath(path):
77 while os.path.islink(path):
78 path = os.path.abspath(os.readlink(path))
79 return path
80 else:
81 compat_realpath = os.path.realpath
82
83
84 def compat_print(s):
85 assert isinstance(s, compat_str)
86 print(s)
87
88
89 # Fix https://github.com/ytdl-org/youtube-dl/issues/4223
90 # See http://bugs.python.org/issue9161 for what is broken
91 def workaround_optparse_bug9161():
92 op = optparse.OptionParser()
93 og = optparse.OptionGroup(op, 'foo')
94 try:
95 og.add_option('-t')
96 except TypeError:
97 real_add_option = optparse.OptionGroup.add_option
98
99 def _compat_add_option(self, *args, **kwargs):
100 enc = lambda v: (
101 v.encode('ascii', 'replace') if isinstance(v, compat_str)
102 else v)
103 bargs = [enc(a) for a in args]
104 bkwargs = dict(
105 (k, enc(v)) for k, v in kwargs.items())
106 return real_add_option(self, *bargs, **bkwargs)
107 optparse.OptionGroup.add_option = _compat_add_option
108
109
110 try:
111 compat_Pattern = re.Pattern
112 except AttributeError:
113 compat_Pattern = type(re.compile(''))
114
115
116 try:
117 compat_Match = re.Match
118 except AttributeError:
119 compat_Match = type(re.compile('').match(''))
120
121
122 try:
123 compat_asyncio_run = asyncio.run # >= 3.7
124 except AttributeError:
125 def compat_asyncio_run(coro):
126 try:
127 loop = asyncio.get_event_loop()
128 except RuntimeError:
129 loop = asyncio.new_event_loop()
130 asyncio.set_event_loop(loop)
131 loop.run_until_complete(coro)
132
133 asyncio.run = compat_asyncio_run
134
135
136 # Python 3.8+ does not honor %HOME% on windows, but this breaks compatibility with youtube-dl
137 # See https://github.com/yt-dlp/yt-dlp/issues/792
138 # https://docs.python.org/3/library/os.path.html#os.path.expanduser
139 if compat_os_name in ('nt', 'ce') and 'HOME' in os.environ:
140 _userhome = os.environ['HOME']
141
142 def compat_expanduser(path):
143 if not path.startswith('~'):
144 return path
145 i = path.replace('\\', '/', 1).find('/') # ~user
146 if i < 0:
147 i = len(path)
148 userhome = os.path.join(os.path.dirname(_userhome), path[1:i]) if i > 1 else _userhome
149 return userhome + path[i:]
150 else:
151 compat_expanduser = os.path.expanduser
152
153
154 try:
155 from Cryptodome.Cipher import AES as compat_pycrypto_AES
156 except ImportError:
157 try:
158 from Crypto.Cipher import AES as compat_pycrypto_AES
159 except ImportError:
160 compat_pycrypto_AES = None
161
162
163 WINDOWS_VT_MODE = False if compat_os_name == 'nt' else None
164
165
166 def windows_enable_vt_mode(): # TODO: Do this the proper way https://bugs.python.org/issue30075
167 if compat_os_name != 'nt':
168 return
169 global WINDOWS_VT_MODE
170 startupinfo = subprocess.STARTUPINFO()
171 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
172 try:
173 subprocess.Popen('', shell=True, startupinfo=startupinfo)
174 WINDOWS_VT_MODE = True
175 except Exception:
176 pass
177
178
179 # Deprecated
180
181 compat_basestring = str
182 compat_chr = chr
183 compat_input = input
184 compat_integer_types = (int, )
185 compat_kwargs = lambda kwargs: kwargs
186 compat_numeric_types = (int, float, complex)
187 compat_str = str
188 compat_xpath = lambda xpath: xpath
189 compat_zip = zip
190
191 compat_HTMLParser = html.parser.HTMLParser
192 compat_HTTPError = urllib.error.HTTPError
193 compat_Struct = struct.Struct
194 compat_b64decode = base64.b64decode
195 compat_cookiejar = http.cookiejar
196 compat_cookiejar_Cookie = compat_cookiejar.Cookie
197 compat_cookies = http.cookies
198 compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
199 compat_etree_Element = etree.Element
200 compat_etree_register_namespace = etree.register_namespace
201 compat_get_terminal_size = shutil.get_terminal_size
202 compat_getenv = os.getenv
203 compat_getpass = getpass.getpass
204 compat_html_entities = html.entities
205 compat_html_entities_html5 = compat_html_entities.html5
206 compat_http_client = http.client
207 compat_http_server = http.server
208 compat_itertools_count = itertools.count
209 compat_parse_qs = urllib.parse.parse_qs
210 compat_shlex_split = shlex.split
211 compat_socket_create_connection = socket.create_connection
212 compat_struct_pack = struct.pack
213 compat_struct_unpack = struct.unpack
214 compat_subprocess_get_DEVNULL = lambda: DEVNULL
215 compat_tokenize_tokenize = tokenize.tokenize
216 compat_urllib_error = urllib.error
217 compat_urllib_parse = urllib.parse
218 compat_urllib_parse_quote = urllib.parse.quote
219 compat_urllib_parse_quote_plus = urllib.parse.quote_plus
220 compat_urllib_parse_unquote = urllib.parse.unquote
221 compat_urllib_parse_unquote_plus = urllib.parse.unquote_plus
222 compat_urllib_parse_unquote_to_bytes = urllib.parse.unquote_to_bytes
223 compat_urllib_parse_urlencode = urllib.parse.urlencode
224 compat_urllib_parse_urlparse = urllib.parse.urlparse
225 compat_urllib_parse_urlunparse = urllib.parse.urlunparse
226 compat_urllib_request = urllib.request
227 compat_urllib_request_DataHandler = urllib.request.DataHandler
228 compat_urllib_response = urllib.response
229 compat_urlparse = urllib.parse
230 compat_urlretrieve = urllib.request.urlretrieve
231 compat_xml_parse_error = etree.ParseError
232
233
234 # Set public objects
235
236 __all__ = [
237 'WINDOWS_VT_MODE',
238 'compat_HTMLParseError',
239 'compat_HTMLParser',
240 'compat_HTTPError',
241 'compat_Match',
242 'compat_Pattern',
243 'compat_Struct',
244 'compat_asyncio_run',
245 'compat_b64decode',
246 'compat_basestring',
247 'compat_chr',
248 'compat_cookiejar',
249 'compat_cookiejar_Cookie',
250 'compat_cookies',
251 'compat_cookies_SimpleCookie',
252 'compat_ctypes_WINFUNCTYPE',
253 'compat_etree_Element',
254 'compat_etree_fromstring',
255 'compat_etree_register_namespace',
256 'compat_expanduser',
257 'compat_get_terminal_size',
258 'compat_getenv',
259 'compat_getpass',
260 'compat_html_entities',
261 'compat_html_entities_html5',
262 'compat_http_client',
263 'compat_http_server',
264 'compat_input',
265 'compat_integer_types',
266 'compat_itertools_count',
267 'compat_kwargs',
268 'compat_numeric_types',
269 'compat_ord',
270 'compat_os_name',
271 'compat_parse_qs',
272 'compat_print',
273 'compat_pycrypto_AES',
274 'compat_realpath',
275 'compat_setenv',
276 'compat_shlex_quote',
277 'compat_shlex_split',
278 'compat_socket_create_connection',
279 'compat_str',
280 'compat_struct_pack',
281 'compat_struct_unpack',
282 'compat_subprocess_get_DEVNULL',
283 'compat_tokenize_tokenize',
284 'compat_urllib_error',
285 'compat_urllib_parse',
286 'compat_urllib_parse_quote',
287 'compat_urllib_parse_quote_plus',
288 'compat_urllib_parse_unquote',
289 'compat_urllib_parse_unquote_plus',
290 'compat_urllib_parse_unquote_to_bytes',
291 'compat_urllib_parse_urlencode',
292 'compat_urllib_parse_urlparse',
293 'compat_urllib_parse_urlunparse',
294 'compat_urllib_request',
295 'compat_urllib_request_DataHandler',
296 'compat_urllib_response',
297 'compat_urlparse',
298 'compat_urlretrieve',
299 'compat_xml_parse_error',
300 'compat_xpath',
301 'compat_zip',
302 'windows_enable_vt_mode',
303 'workaround_optparse_bug9161',
304 ]