Skip to content

Commit

Permalink
[jsinterp] Overhaul JSInterp to handle new YT players 4c3f79c5, 324f6…
Browse files Browse the repository at this point in the history
…7b9 (#31170)

* back-port from yt-dlp 8f53dc4, thanks pukkandan
* also support void, improve <</>> precedence, improve expressions in comma-list
* add more tests
  • Loading branch information
dirkf authored Aug 14, 2022
1 parent e6a836d commit d231b56
Show file tree
Hide file tree
Showing 6 changed files with 504 additions and 251 deletions.
49 changes: 43 additions & 6 deletions test/test_jsinterp.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def test_basic(self):
jsi = JSInterpreter('function x3(){return 42;}')
self.assertEqual(jsi.call_function('x3'), 42)

jsi = JSInterpreter('function x3(){42}')
self.assertEqual(jsi.call_function('x3'), None)

jsi = JSInterpreter('var x5 = function(){return 42;}')
self.assertEqual(jsi.call_function('x5'), 42)

Expand Down Expand Up @@ -51,8 +54,11 @@ def test_operators(self):
jsi = JSInterpreter('function f(){return 11 >> 2;}')
self.assertEqual(jsi.call_function('f'), 2)

jsi = JSInterpreter('function f(){return []? 2+3: 4;}')
self.assertEqual(jsi.call_function('f'), 5)

def test_array_access(self):
jsi = JSInterpreter('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;}')
jsi = JSInterpreter('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2.0] = 7; return x;}')
self.assertEqual(jsi.call_function('f'), [5, 2, 7])

def test_parens(self):
Expand All @@ -62,6 +68,10 @@ def test_parens(self):
jsi = JSInterpreter('function f(){return (1 + 2) * 3;}')
self.assertEqual(jsi.call_function('f'), 9)

def test_quotes(self):
jsi = JSInterpreter(r'function f(){return "a\"\\("}')

This comment has been minimized.

Copy link
@pukkandan

pukkandan Aug 14, 2022

Contributor

Conventionally r'' is used for regex and R'' for other raw strings

self.assertEqual(jsi.call_function('f'), r'a"\(')

def test_assignments(self):
jsi = JSInterpreter('function f(){var x = 20; x = 30 + 1; return x;}')
self.assertEqual(jsi.call_function('f'), 31)
Expand Down Expand Up @@ -104,18 +114,29 @@ def test_precedence(self):
}''')
self.assertEqual(jsi.call_function('x'), [20, 20, 30, 40, 50])

def test_builtins(self):
jsi = JSInterpreter('''
function x() { return new Date('Wednesday 31 December 1969 18:01:26 MDT') - 0; }
''')
self.assertEqual(jsi.call_function('x'), 86000)
jsi = JSInterpreter('''
function x(dt) { return new Date(dt) - 0; }
''')
self.assertEqual(jsi.call_function('x', 'Wednesday 31 December 1969 18:01:26 MDT'), 86000)

def test_call(self):
jsi = JSInterpreter('''
function x() { return 2; }
function y(a) { return x() + a; }
function y(a) { return x() + (a?a:0); }
function z() { return y(3); }
''')
self.assertEqual(jsi.call_function('z'), 5)
self.assertEqual(jsi.call_function('y'), 2)

def test_for_loop(self):
# function x() { a=0; for (i=0; i-10; i++) {a++} a }
jsi = JSInterpreter('''
function x() { a=0; for (i=0; i-10; i = i + 1) {a++} a }
function x() { a=0; for (i=0; i-10; i++) {a++} return a }
''')
self.assertEqual(jsi.call_function('x'), 10)

Expand Down Expand Up @@ -156,19 +177,19 @@ def test_try(self):

def test_for_loop_continue(self):
jsi = JSInterpreter('''
function x() { a=0; for (i=0; i-10; i++) { continue; a++ } a }
function x() { a=0; for (i=0; i-10; i++) { continue; a++ } return a }
''')
self.assertEqual(jsi.call_function('x'), 0)

def test_for_loop_break(self):
jsi = JSInterpreter('''
function x() { a=0; for (i=0; i-10; i++) { break; a++ } a }
function x() { a=0; for (i=0; i-10; i++) { break; a++ } return a }
''')
self.assertEqual(jsi.call_function('x'), 0)

def test_literal_list(self):
jsi = JSInterpreter('''
function x() { [1, 2, "asdf", [5, 6, 7]][3] }
function x() { return [1, 2, "asdf", [5, 6, 7]][3] }
''')
self.assertEqual(jsi.call_function('x'), [5, 6, 7])

Expand All @@ -177,6 +198,22 @@ def test_comma(self):
function x() { a=5; a -= 1, a+=3; return a }
''')
self.assertEqual(jsi.call_function('x'), 7)
jsi = JSInterpreter('''
function x() { a=5; return (a -= 1, a+=3, a); }
''')
self.assertEqual(jsi.call_function('x'), 7)

def test_void(self):
jsi = JSInterpreter('''
function x() { return void 42; }
''')
self.assertEqual(jsi.call_function('x'), None)

def test_return_function(self):
jsi = JSInterpreter('''
function x() { return [1, function(){return 1}][1] }
''')
self.assertEqual(jsi.call_function('x')([]), 1)


if __name__ == '__main__':
Expand Down
3 changes: 3 additions & 0 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ def test_unified_timestamps(self):
self.assertEqual(unified_timestamp('Sep 11, 2013 | 5:49 AM'), 1378878540)
self.assertEqual(unified_timestamp('December 15, 2017 at 7:49 am'), 1513324140)
self.assertEqual(unified_timestamp('2018-03-14T08:32:43.1493874+00:00'), 1521016363)
self.assertEqual(unified_timestamp('December 31 1969 20:00:01 EDT'), 1)
self.assertEqual(unified_timestamp('Wednesday 31 December 1969 18:01:26 MDT'), 86)
self.assertEqual(unified_timestamp('12/31/1969 20:01:18 EDT', False), 78)

def test_determine_ext(self):
self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
Expand Down
13 changes: 13 additions & 0 deletions test/test_youtube_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,25 @@
'https://www.youtube.com/s/player/e06dea74/player_ias.vflset/en_US/base.js',
'AiuodmaDDYw8d3y4bf', 'ankd8eza2T6Qmw',
),
(
'https://www.youtube.com/s/player/5dd88d1d/player-plasma-ias-phone-en_US.vflset/base.js',
'kSxKFLeqzv_ZyHSAt', 'n8gS8oRlHOxPFA',
),
(
'https://www.youtube.com/s/player/324f67b9/player_ias.vflset/en_US/base.js',
'xdftNy7dh9QGnhW', '22qLGxrmX8F1rA',
),
(
'https://www.youtube.com/s/player/4c3f79c5/player_ias.vflset/en_US/base.js',
'TDCstCG66tEAO5pR9o', 'dbxNtZ14c-yWyw',
),
]


class TestPlayerInfo(unittest.TestCase):
def test_youtube_extract_player_info(self):
PLAYER_URLS = (
('https://www.youtube.com/s/player/4c3f79c5/player_ias.vflset/en_US/base.js', '4c3f79c5'),
('https://www.youtube.com/s/player/64dddad9/player_ias.vflset/en_US/base.js', '64dddad9'),
('https://www.youtube.com/s/player/64dddad9/player_ias.vflset/fr_FR/base.js', '64dddad9'),
('https://www.youtube.com/s/player/64dddad9/player-plasma-ias-phone-en_US.vflset/base.js', '64dddad9'),
Expand Down
54 changes: 53 additions & 1 deletion youtube_dl/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2985,7 +2985,6 @@ def unpack(self, string):
except ImportError:
compat_filter = filter


try:
from future_builtins import zip as compat_zip
except ImportError: # not 2.6+ or is 3.x
Expand All @@ -2995,6 +2994,57 @@ def unpack(self, string):
compat_zip = zip


# method renamed between Py2/3
try:
from itertools import zip_longest as compat_itertools_zip_longest
except ImportError:
from itertools import izip_longest as compat_itertools_zip_longest


# new class in collections
try:
from collections import ChainMap as compat_collections_chain_map
except ImportError:
# Py < 3.3
class compat_collections_chain_map(compat_collections_abc.MutableMapping):

maps = [{}]

def __init__(self, *maps):
self.maps = list(maps) or [{}]

def __getitem__(self, k):
for m in self.maps:
if k in m:
return m[k]
raise KeyError(k)

def __setitem__(self, k, v):
self.maps[0].__setitem__(k, v)
return

def __delitem__(self, k):
if k in self.maps[0]:
del self.maps[0][k]
return
raise KeyError(k)

def __iter__(self):
return itertools.chain(*reversed(self.maps))

def __len__(self):
return len(iter(self))

def new_child(self, m=None, **kwargs):
m = m or {}
m.update(kwargs)
return compat_collections_chain_map(m, *self.maps)

@property
def parents(self):
return compat_collections_chain_map(*(self.maps[1:]))


if sys.version_info < (3, 3):
def compat_b64decode(s, *args, **kwargs):
if isinstance(s, compat_str):
Expand Down Expand Up @@ -3031,6 +3081,7 @@ def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
'compat_basestring',
'compat_chr',
'compat_collections_abc',
'compat_collections_chain_map',
'compat_cookiejar',
'compat_cookiejar_Cookie',
'compat_cookies',
Expand All @@ -3051,6 +3102,7 @@ def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
'compat_input',
'compat_integer_types',
'compat_itertools_count',
'compat_itertools_zip_longest',
'compat_kwargs',
'compat_map',
'compat_numeric_types',
Expand Down
Loading

0 comments on commit d231b56

Please sign in to comment.