|
|
|
@ -45,6 +45,7 @@ from .compat import ( |
|
|
|
|
compat_casefold, |
|
|
|
|
compat_chr, |
|
|
|
|
compat_collections_abc, |
|
|
|
|
compat_contextlib_suppress, |
|
|
|
|
compat_cookiejar, |
|
|
|
|
compat_ctypes_WINFUNCTYPE, |
|
|
|
|
compat_datetime_timedelta_total_seconds, |
|
|
|
@ -1855,25 +1856,18 @@ def write_json_file(obj, fn): |
|
|
|
|
try: |
|
|
|
|
with tf: |
|
|
|
|
json.dump(obj, tf) |
|
|
|
|
if sys.platform == 'win32': |
|
|
|
|
# Need to remove existing file on Windows, else os.rename raises |
|
|
|
|
# WindowsError or FileExistsError. |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(OSError): |
|
|
|
|
if sys.platform == 'win32': |
|
|
|
|
# Need to remove existing file on Windows, else os.rename raises |
|
|
|
|
# WindowsError or FileExistsError. |
|
|
|
|
os.unlink(fn) |
|
|
|
|
except OSError: |
|
|
|
|
pass |
|
|
|
|
try: |
|
|
|
|
mask = os.umask(0) |
|
|
|
|
os.umask(mask) |
|
|
|
|
os.chmod(tf.name, 0o666 & ~mask) |
|
|
|
|
except OSError: |
|
|
|
|
pass |
|
|
|
|
os.rename(tf.name, fn) |
|
|
|
|
except Exception: |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(OSError): |
|
|
|
|
os.remove(tf.name) |
|
|
|
|
except OSError: |
|
|
|
|
pass |
|
|
|
|
raise |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -2033,14 +2027,13 @@ def extract_attributes(html_element): |
|
|
|
|
NB HTMLParser is stricter in Python 2.6 & 3.2 than in later versions, |
|
|
|
|
but the cases in the unit test will work for all of 2.6, 2.7, 3.2-3.5. |
|
|
|
|
""" |
|
|
|
|
parser = HTMLAttributeParser() |
|
|
|
|
try: |
|
|
|
|
parser.feed(html_element) |
|
|
|
|
parser.close() |
|
|
|
|
# Older Python may throw HTMLParseError in case of malformed HTML |
|
|
|
|
except compat_HTMLParseError: |
|
|
|
|
pass |
|
|
|
|
return parser.attrs |
|
|
|
|
ret = None |
|
|
|
|
# Older Python may throw HTMLParseError in case of malformed HTML (and on .close()!) |
|
|
|
|
with compat_contextlib_suppress(compat_HTMLParseError): |
|
|
|
|
with contextlib.closing(HTMLAttributeParser()) as parser: |
|
|
|
|
parser.feed(html_element) |
|
|
|
|
ret = parser.attrs |
|
|
|
|
return ret or {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def clean_html(html): |
|
|
|
@ -2241,7 +2234,8 @@ def _htmlentity_transform(entity_with_semicolon): |
|
|
|
|
numstr = '0%s' % numstr |
|
|
|
|
else: |
|
|
|
|
base = 10 |
|
|
|
|
# See https://github.com/ytdl-org/youtube-dl/issues/7518 |
|
|
|
|
# See https://github.com/ytdl-org/youtube-dl/issues/7518\ |
|
|
|
|
# Also, weirdly, compat_contextlib_suppress fails here in 2.6 |
|
|
|
|
try: |
|
|
|
|
return compat_chr(int(numstr, base)) |
|
|
|
|
except ValueError: |
|
|
|
@ -2348,11 +2342,9 @@ def make_HTTPS_handler(params, **kwargs): |
|
|
|
|
# Some servers may (wrongly) reject requests if ALPN extension is not sent. See: |
|
|
|
|
# https://github.com/python/cpython/issues/85140 |
|
|
|
|
# https://github.com/yt-dlp/yt-dlp/issues/3878 |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(AttributeError, NotImplementedError): |
|
|
|
|
# fails for Python < 2.7.10, not ssl.HAS_ALPN |
|
|
|
|
ctx.set_alpn_protocols(ALPN_PROTOCOLS) |
|
|
|
|
except (AttributeError, NotImplementedError): |
|
|
|
|
# Python < 2.7.10, not ssl.HAS_ALPN |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
opts_no_check_certificate = params.get('nocheckcertificate', False) |
|
|
|
|
if hasattr(ssl, 'create_default_context'): # Python >= 3.4 or 2.7.9 |
|
|
|
@ -2362,12 +2354,10 @@ def make_HTTPS_handler(params, **kwargs): |
|
|
|
|
context.check_hostname = False |
|
|
|
|
context.verify_mode = ssl.CERT_NONE |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(TypeError): |
|
|
|
|
# Fails with Python 2.7.8 (create_default_context present |
|
|
|
|
# but HTTPSHandler has no context=) |
|
|
|
|
return YoutubeDLHTTPSHandler(params, context=context, **kwargs) |
|
|
|
|
except TypeError: |
|
|
|
|
# Python 2.7.8 |
|
|
|
|
# (create_default_context present but HTTPSHandler has no context=) |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
if sys.version_info < (3, 2): |
|
|
|
|
return YoutubeDLHTTPSHandler(params, **kwargs) |
|
|
|
@ -3176,12 +3166,10 @@ def parse_iso8601(date_str, delimiter='T', timezone=None): |
|
|
|
|
if timezone is None: |
|
|
|
|
timezone, date_str = extract_timezone(date_str) |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(ValueError): |
|
|
|
|
date_format = '%Y-%m-%d{0}%H:%M:%S'.format(delimiter) |
|
|
|
|
dt = datetime.datetime.strptime(date_str, date_format) - timezone |
|
|
|
|
return calendar.timegm(dt.timetuple()) |
|
|
|
|
except ValueError: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def date_formats(day_first=True): |
|
|
|
@ -3201,17 +3189,13 @@ def unified_strdate(date_str, day_first=True): |
|
|
|
|
_, date_str = extract_timezone(date_str) |
|
|
|
|
|
|
|
|
|
for expression in date_formats(day_first): |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(ValueError): |
|
|
|
|
upload_date = datetime.datetime.strptime(date_str, expression).strftime('%Y%m%d') |
|
|
|
|
except ValueError: |
|
|
|
|
pass |
|
|
|
|
if upload_date is None: |
|
|
|
|
timetuple = email.utils.parsedate_tz(date_str) |
|
|
|
|
if timetuple: |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(ValueError): |
|
|
|
|
upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d') |
|
|
|
|
except ValueError: |
|
|
|
|
pass |
|
|
|
|
if upload_date is not None: |
|
|
|
|
return compat_str(upload_date) |
|
|
|
|
|
|
|
|
@ -3240,11 +3224,9 @@ def unified_timestamp(date_str, day_first=True): |
|
|
|
|
date_str = m.group(1) |
|
|
|
|
|
|
|
|
|
for expression in date_formats(day_first): |
|
|
|
|
try: |
|
|
|
|
with compat_contextlib_suppress(ValueError): |
|
|
|
|
dt = datetime.datetime.strptime(date_str, expression) - timezone + datetime.timedelta(hours=pm_delta) |
|
|
|
|
return calendar.timegm(dt.timetuple()) |
|
|
|
|
except ValueError: |
|
|
|
|
pass |
|
|
|
|
timetuple = email.utils.parsedate_tz(date_str) |
|
|
|
|
if timetuple: |
|
|
|
|
return calendar.timegm(timetuple) + pm_delta * 3600 - compat_datetime_timedelta_total_seconds(timezone) |
|
|
|
|