Compare commits

...

2 Commits

Author SHA1 Message Date
dirkf
d619dd712f [jsinterp] Fix bug in operator precedence
* from 164b03c486
* added tests
2022-08-25 12:16:10 +01:00
dirkf
573b13410e [YouTube] Improve error check for n-sig processing 2022-08-25 12:14:59 +01:00
4 changed files with 47 additions and 7 deletions

View File

@ -192,6 +192,31 @@ class TestJSInterpreter(unittest.TestCase):
''') ''')
self.assertEqual(jsi.call_function('x'), 10) self.assertEqual(jsi.call_function('x'), 10)
def test_catch(self):
jsi = JSInterpreter('''
function x() { try{throw 10} catch(e){return 5} }
''')
self.assertEqual(jsi.call_function('x'), 5)
@unittest.expectedFailure
def test_finally(self):
jsi = JSInterpreter('''
function x() { try{throw 10} finally {return 42} }
''')
self.assertEqual(jsi.call_function('x'), 42)
jsi = JSInterpreter('''
function x() { try{throw 10} catch(e){return 5} finally {return 42} }
''')
self.assertEqual(jsi.call_function('x'), 42)
def test_nested_try(self):
jsi = JSInterpreter('''
function x() {try {
try{throw 10} finally {throw 42}
} catch(e){return 5} }
''')
self.assertEqual(jsi.call_function('x'), 5)
def test_for_loop_continue(self): def test_for_loop_continue(self):
jsi = JSInterpreter(''' jsi = JSInterpreter('''
function x() { a=0; for (i=0; i-10; i++) { continue; a++ } return a } function x() { a=0; for (i=0; i-10; i++) { continue; a++ } return a }

View File

@ -111,6 +111,10 @@ _NSIG_TESTS = [
'https://www.youtube.com/s/player/1f7d5369/player_ias.vflset/en_US/base.js', 'https://www.youtube.com/s/player/1f7d5369/player_ias.vflset/en_US/base.js',
'batNX7sYqIJdkJ', 'IhOkL_zxbkOZBw', 'batNX7sYqIJdkJ', 'IhOkL_zxbkOZBw',
), ),
(
'https://www.youtube.com/s/player/dc0c6770/player_ias.vflset/en_US/base.js',
'5EHDMgYLV6HPGk_Mu-kk', 'n9lUJLHbxUI0GQ',
),
] ]

View File

@ -1500,7 +1500,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
return lambda s: jsi.extract_function_from_code(*func_code)([s]) return lambda s: jsi.extract_function_from_code(*func_code)([s])
def _n_descramble(self, n_param, player_url, video_id): def _n_descramble(self, n_param, player_url, video_id):
"""Compute the response to YT's "n" parameter challenge """Compute the response to YT's "n" parameter challenge,
or None
Args: Args:
n_param -- challenge string that is the value of the n_param -- challenge string that is the value of the
@ -1518,7 +1519,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
if player_id not in self._player_cache: if player_id not in self._player_cache:
self._player_cache[player_id] = self._extract_n_function(video_id, player_url) self._player_cache[player_id] = self._extract_n_function(video_id, player_url)
func = self._player_cache[player_id] func = self._player_cache[player_id]
self._player_cache[sig_id] = func(n_param) ret = func(n_param)
if ret.startswith('enhanced_except_'):
raise ExtractorError('Unhandled exception in decode')
self._player_cache[sig_id] = ret
if self._downloader.params.get('verbose', False): if self._downloader.params.get('verbose', False):
self._downloader.to_screen('[debug] [%s] %s' % (self.IE_NAME, 'Decrypted nsig {0} => {1}'.format(n_param, self._player_cache[sig_id]))) self._downloader.to_screen('[debug] [%s] %s' % (self.IE_NAME, 'Decrypted nsig {0} => {1}'.format(n_param, self._player_cache[sig_id])))
return self._player_cache[sig_id] return self._player_cache[sig_id]
@ -1539,10 +1543,12 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
continue continue
n_param = n_param[-1] n_param = n_param[-1]
n_response = self._n_descramble(n_param, player_url, video_id) n_response = self._n_descramble(n_param, player_url, video_id)
if n_response: if n_response is None:
qs['n'] = [n_response] # give up if descrambling failed
fmt['url'] = compat_urlparse.urlunparse( break
parsed_fmt_url._replace(query=compat_urllib_parse_urlencode(qs, True))) qs['n'] = [n_response]
fmt['url'] = compat_urlparse.urlunparse(
parsed_fmt_url._replace(query=compat_urllib_parse_urlencode(qs, True)))
def _mark_watched(self, video_id, player_response): def _mark_watched(self, video_id, player_response):
playback_url = url_or_none(try_get( playback_url = url_or_none(try_get(

View File

@ -5,6 +5,7 @@ import json
import math import math
import operator import operator
import re import re
from collections import Counter
from .utils import ( from .utils import (
error_to_compat_str, error_to_compat_str,
@ -108,8 +109,8 @@ _OPERATORS = (
_COMP_OPERATORS = ( _COMP_OPERATORS = (
('===', operator.is_), ('===', operator.is_),
('==', _js_eq_op(operator.eq)),
('!==', operator.is_not), ('!==', operator.is_not),
('==', _js_eq_op(operator.eq)),
('!=', _js_eq_op(operator.ne)), ('!=', _js_eq_op(operator.ne)),
('<=', _js_comp_op(operator.le)), ('<=', _js_comp_op(operator.le)),
('>=', _js_comp_op(operator.ge)), ('>=', _js_comp_op(operator.ge)),
@ -241,7 +242,9 @@ class JSInterpreter(object):
def _separate(cls, expr, delim=',', max_split=None, skip_delims=None): def _separate(cls, expr, delim=',', max_split=None, skip_delims=None):
if not expr: if not expr:
return return
# collections.Counter() is ~10% slower
counters = {k: 0 for k in _MATCHING_PARENS.values()} counters = {k: 0 for k in _MATCHING_PARENS.values()}
# counters = Counter()
start, splits, pos, delim_len = 0, 0, 0, len(delim) - 1 start, splits, pos, delim_len = 0, 0, 0, len(delim) - 1
in_quote, escaping, skipping = None, False, 0 in_quote, escaping, skipping = None, False, 0
after_op, in_regex_char_group, skip_re = True, False, 0 after_op, in_regex_char_group, skip_re = True, False, 0
@ -442,6 +445,7 @@ class JSInterpreter(object):
return ret, should_abort or should_return return ret, should_abort or should_return
elif md.get('catch'): elif md.get('catch'):
catch_expr, expr = self._separate_at_paren(expr[m.end():], '}') catch_expr, expr = self._separate_at_paren(expr[m.end():], '}')
if self._EXC_NAME in local_vars: if self._EXC_NAME in local_vars:
catch_vars = local_vars.new_child({m.group('err'): local_vars.pop(self._EXC_NAME)}) catch_vars = local_vars.new_child({m.group('err'): local_vars.pop(self._EXC_NAME)})
@ -450,6 +454,7 @@ class JSInterpreter(object):
return ret, True return ret, True
ret, should_abort = self.interpret_statement(expr, local_vars, allow_recursion) ret, should_abort = self.interpret_statement(expr, local_vars, allow_recursion)
return ret, should_abort or should_return return ret, should_abort or should_return
elif md.get('for'): elif md.get('for'):