diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py index a8f312fde..ecd6ab3c9 100644 --- a/test/test_jsinterp.py +++ b/test/test_jsinterp.py @@ -33,6 +33,55 @@ class TestJSInterpreter(unittest.TestCase): jsi = JSInterpreter('function x4(a){return 2*a+1;}') self.assertEqual(jsi.call_function('x4', 3), 7) + def test_add(self): + jsi = JSInterpreter('function f(){return 42 + 7;}') + self.assertEqual(jsi.call_function('f'), 49) + jsi = JSInterpreter('function f(){return 42 + undefined;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + jsi = JSInterpreter('function f(){return 42 + null;}') + self.assertEqual(jsi.call_function('f'), 42) + + def test_sub(self): + jsi = JSInterpreter('function f(){return 42 - 7;}') + self.assertEqual(jsi.call_function('f'), 35) + jsi = JSInterpreter('function f(){return 42 - undefined;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + jsi = JSInterpreter('function f(){return 42 - null;}') + self.assertEqual(jsi.call_function('f'), 42) + + def test_mul(self): + jsi = JSInterpreter('function f(){return 42 * 7;}') + self.assertEqual(jsi.call_function('f'), 294) + jsi = JSInterpreter('function f(){return 42 * undefined;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + jsi = JSInterpreter('function f(){return 42 * null;}') + self.assertEqual(jsi.call_function('f'), 0) + + def test_div(self): + jsi = JSInterpreter('function f(a, b){return a / b;}') + self.assertTrue(math.isnan(jsi.call_function('f', 0, 0))) + self.assertTrue(math.isnan(jsi.call_function('f', JS_Undefined, 1))) + self.assertTrue(math.isinf(jsi.call_function('f', 2, 0))) + self.assertEqual(jsi.call_function('f', 0, 3), 0) + + def test_mod(self): + jsi = JSInterpreter('function f(){return 42 % 7;}') + self.assertEqual(jsi.call_function('f'), 0) + jsi = JSInterpreter('function f(){return 42 % 0;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + jsi = JSInterpreter('function f(){return 42 % undefined;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + + def test_exp(self): + jsi = JSInterpreter('function f(){return 42 ** 2;}') + self.assertEqual(jsi.call_function('f'), 1764) + jsi = JSInterpreter('function f(){return 42 ** undefined;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + jsi = JSInterpreter('function f(){return 42 ** null;}') + self.assertEqual(jsi.call_function('f'), 1) + jsi = JSInterpreter('function f(){return undefined ** 42;}') + self.assertTrue(math.isnan(jsi.call_function('f'))) + def test_empty_return(self): jsi = JSInterpreter('function f(){return; y()}') self.assertEqual(jsi.call_function('f'), None) @@ -516,6 +565,9 @@ class TestJSInterpreter(unittest.TestCase): jsi = JSInterpreter('function x(){return 42 << NaN}') self.assertEqual(jsi.call_function('x'), 42) + jsi = JSInterpreter('function x(){return 42 << Infinity}') + self.assertEqual(jsi.call_function('x'), 42) + def test_32066(self): jsi = JSInterpreter("function x(){return Math.pow(3, 5) + new Date('1970-01-01T08:01:42.000+08:00') / 1000 * -239 - -24205;}") self.assertEqual(jsi.call_function('x'), 70) diff --git a/test/test_youtube_signature.py b/test/test_youtube_signature.py index decf7ee38..e7bce9d68 100644 --- a/test/test_youtube_signature.py +++ b/test/test_youtube_signature.py @@ -143,6 +143,18 @@ _NSIG_TESTS = [ 'https://www.youtube.com/s/player/dac945fd/player_ias.vflset/en_US/base.js', 'o8BkRxXhuYsBCWi6RplPdP', '3Lx32v_hmzTm6A', ), + ( + 'https://www.youtube.com/s/player/6f20102c/player_ias.vflset/en_US/base.js', + 'lE8DhoDmKqnmJJ', 'pJTTX6XyJP2BYw', + ), + ( + 'https://www.youtube.com/s/player/cfa9e7cb/player_ias.vflset/en_US/base.js', + 'qO0NiMtYQ7TeJnfFG2', 'k9cuJDHNS5O7kQ', + ), + ( + 'https://www.youtube.com/s/player/8c7583ff/player_ias.vflset/en_US/base.js', + 'E2AQVN6y_zM7uN9w8z', '9A2dbY5GDZrt9A', + ), ] diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 212c04298..98b878fc1 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -102,6 +102,7 @@ from .utils import ( YoutubeDLCookieProcessor, YoutubeDLHandler, YoutubeDLRedirectHandler, + ytdl_is_updateable, ) from .cache import Cache from .extractor import get_info_extractor, gen_extractor_classes, _LAZY_LOADER @@ -2373,9 +2374,10 @@ class YoutubeDL(object): self.get_encoding())) write_string(encoding_str, encoding=None) - self._write_string('[debug] youtube-dl version ' + __version__ + '\n') + self._write_string('[debug] youtube-dl version ' + __version__ + (' (single file build)\n' if ytdl_is_updateable() else '\n')) if _LAZY_LOADER: - self._write_string('[debug] Lazy loading extractors enabled' + '\n') + self._write_string('[debug] Lazy loading extractors enabled\n') + writeln_debug = lambda *s: self._write_string('[debug] %s\n' % (''.join(s), )) # moved down for easier merge try: sp = subprocess.Popen( ['git', 'rev-parse', '--short', 'HEAD'], @@ -2384,7 +2386,7 @@ class YoutubeDL(object): out, err = process_communicate_or_kill(sp) out = out.decode().strip() if re.match('[0-9a-f]+', out): - self._write_string('[debug] Git HEAD: ' + out + '\n') + writeln_debug('Git HEAD: ', out) except Exception: try: sys.exc_clear() @@ -2403,13 +2405,15 @@ class YoutubeDL(object): except OSError: # We may not have access to the executable return [] - self._write_string('[debug] Python %s (%s %s) - %s (%s%s)\n' % ( + libc = join_nonempty(*libc_ver(), delim=' ') + writeln_debug('Python %s (%s %s %s) - %s - %s%s' % ( platform.python_version(), python_implementation(), + platform.machine(), platform.architecture()[0], platform_name(), OPENSSL_VERSION, - ', %s' % (join_nonempty(*libc_ver(), delim=' ') or '-'), + (' - %s' % (libc, )) if libc else '' )) exe_versions = FFmpegPostProcessor.get_versions(self) @@ -2422,17 +2426,17 @@ class YoutubeDL(object): ) if not exe_str: exe_str = 'none' - self._write_string('[debug] exe versions: %s\n' % exe_str) + writeln_debug('exe versions: %s' % (exe_str, )) proxy_map = {} for handler in self._opener.handlers: if hasattr(handler, 'proxies'): proxy_map.update(handler.proxies) - self._write_string('[debug] Proxy map: ' + compat_str(proxy_map) + '\n') + writeln_debug('Proxy map: ', compat_str(proxy_map)) if self.params.get('call_home', False): ipaddr = self.urlopen('https://yt-dl.org/ip').read().decode('utf-8') - self._write_string('[debug] Public IP address: %s\n' % ipaddr) + writeln_debug('Public IP address: %s' % (ipaddr, )) latest_version = self.urlopen( 'https://yt-dl.org/latest/version').read().decode('utf-8') if version_tuple(latest_version) > version_tuple(__version__): diff --git a/youtube_dl/casefold.py b/youtube_dl/casefold.py index 748c2d491..ad9c66f8e 100644 --- a/youtube_dl/casefold.py +++ b/youtube_dl/casefold.py @@ -1663,5 +1663,5 @@ def casefold(s): __all__ = [ - casefold + 'casefold', ] diff --git a/youtube_dl/extractor/itv.py b/youtube_dl/extractor/itv.py index 7026139ea..c64af3be6 100644 --- a/youtube_dl/extractor/itv.py +++ b/youtube_dl/extractor/itv.py @@ -59,7 +59,7 @@ class ITVBaseIE(InfoExtractor): @staticmethod def _vanilla_ua_header(): - return {'User-agent': 'Mozilla/5.0'} + return {'User-Agent': 'Mozilla/5.0'} def _download_webpage_handle(self, url, video_id, *args, **kwargs): # specialised to (a) use vanilla UA (b) detect geo-block @@ -69,7 +69,7 @@ class ITVBaseIE(InfoExtractor): 'user_agent' not in params and not any(re.match(r'(?i)user-agent\s*:', h) for h in (params.get('headers') or [])) - and 'User-agent' not in (kwargs.get('headers') or {})): + and 'User-Agent' not in (kwargs.get('headers') or {})): kwargs.setdefault('headers', {}) kwargs['headers'] = self._vanilla_ua_header() diff --git a/youtube_dl/jsinterp.py b/youtube_dl/jsinterp.py index f837865c4..9d4a5bc57 100644 --- a/youtube_dl/jsinterp.py +++ b/youtube_dl/jsinterp.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals import itertools import json -import math import operator import re @@ -52,6 +51,10 @@ def wraps_op(op): return update_and_rename_wrapper +# NB In principle NaN cannot be checked by membership. +# Here all NaN values are actually this one, so _NaN is _NaN, +# although _NaN != _NaN. + _NaN = float('nan') @@ -79,7 +82,7 @@ def _js_arith_op(op): def _js_div(a, b): - if JS_Undefined in (a, b) or not (a and b): + if JS_Undefined in (a, b) or not (a or b): return _NaN return operator.truediv(a or 0, b) if b else float('inf') @@ -126,13 +129,8 @@ def _js_comp_op(op): def _js_ternary(cndn, if_true=True, if_false=False): """Simulate JS's ternary operator (cndn?if_true:if_false)""" - if cndn in (False, None, 0, '', JS_Undefined): + if cndn in (False, None, 0, '', JS_Undefined, _NaN): return if_false - try: - if math.isnan(cndn): # NB: NaN cannot be checked by membership - return if_false - except TypeError: - pass return if_true