mirror of
https://github.com/ytdl-org/youtube-dl.git
synced 2024-11-18 16:26:23 +00:00
Merge branch 'ytdl-org:master' into master
This commit is contained in:
commit
93e0d820ce
5 changed files with 61 additions and 17 deletions
|
@ -505,6 +505,17 @@ class TestJSInterpreter(unittest.TestCase):
|
||||||
jsi = JSInterpreter('function x(){return 1236566549 << 5}')
|
jsi = JSInterpreter('function x(){return 1236566549 << 5}')
|
||||||
self.assertEqual(jsi.call_function('x'), 915423904)
|
self.assertEqual(jsi.call_function('x'), 915423904)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_unary_operators(self):
|
||||||
|
jsi = JSInterpreter('function f(){return 2 - - - 2;}')
|
||||||
|
self.assertEqual(jsi.call_function('f'), 0)
|
||||||
|
# fails
|
||||||
|
# jsi = JSInterpreter('function f(){return 2 + - + - - 2;}')
|
||||||
|
# self.assertEqual(jsi.call_function('f'), 0)
|
||||||
|
|
||||||
""" # fails so far
|
""" # fails so far
|
||||||
def test_packed(self):
|
def test_packed(self):
|
||||||
jsi = JSInterpreter('''function x(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}''')
|
jsi = JSInterpreter('''function x(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}''')
|
||||||
|
|
|
@ -1777,7 +1777,7 @@ class YoutubeDL(object):
|
||||||
self.to_stdout(formatSeconds(info_dict['duration']))
|
self.to_stdout(formatSeconds(info_dict['duration']))
|
||||||
print_mandatory('format')
|
print_mandatory('format')
|
||||||
if self.params.get('forcejson', False):
|
if self.params.get('forcejson', False):
|
||||||
self.to_stdout(json.dumps(info_dict))
|
self.to_stdout(json.dumps(self.sanitize_info(info_dict)))
|
||||||
|
|
||||||
def process_info(self, info_dict):
|
def process_info(self, info_dict):
|
||||||
"""Process a single resolved IE result."""
|
"""Process a single resolved IE result."""
|
||||||
|
@ -2091,7 +2091,7 @@ class YoutubeDL(object):
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
if self.params.get('dump_single_json', False):
|
if self.params.get('dump_single_json', False):
|
||||||
self.to_stdout(json.dumps(res))
|
self.to_stdout(json.dumps(self.sanitize_info(res)))
|
||||||
|
|
||||||
return self._download_retcode
|
return self._download_retcode
|
||||||
|
|
||||||
|
@ -2100,6 +2100,7 @@ class YoutubeDL(object):
|
||||||
[info_filename], mode='r',
|
[info_filename], mode='r',
|
||||||
openhook=fileinput.hook_encoded('utf-8'))) as f:
|
openhook=fileinput.hook_encoded('utf-8'))) as f:
|
||||||
# FileInput doesn't have a read method, we can't call json.load
|
# FileInput doesn't have a read method, we can't call json.load
|
||||||
|
# TODO: let's use io.open(), then
|
||||||
info = self.filter_requested_info(json.loads('\n'.join(f)))
|
info = self.filter_requested_info(json.loads('\n'.join(f)))
|
||||||
try:
|
try:
|
||||||
self.process_ie_result(info, download=True)
|
self.process_ie_result(info, download=True)
|
||||||
|
|
|
@ -38,12 +38,13 @@ class DashSegmentsFD(FragmentFD):
|
||||||
# In DASH, the first segment contains necessary headers to
|
# In DASH, the first segment contains necessary headers to
|
||||||
# generate a valid MP4 file, so always abort for the first segment
|
# generate a valid MP4 file, so always abort for the first segment
|
||||||
fatal = frag_index == 1 or not skip_unavailable_fragments
|
fatal = frag_index == 1 or not skip_unavailable_fragments
|
||||||
for count in itertools.count():
|
|
||||||
try:
|
|
||||||
fragment_url = fragment.get('url')
|
fragment_url = fragment.get('url')
|
||||||
if not fragment_url:
|
if not fragment_url:
|
||||||
assert fragment_base_url
|
assert fragment_base_url
|
||||||
fragment_url = urljoin(fragment_base_url, fragment['path'])
|
fragment_url = urljoin(fragment_base_url, fragment['path'])
|
||||||
|
success = False
|
||||||
|
for count in itertools.count():
|
||||||
|
try:
|
||||||
success, frag_content = self._download_fragment(ctx, fragment_url, info_dict)
|
success, frag_content = self._download_fragment(ctx, fragment_url, info_dict)
|
||||||
if not success:
|
if not success:
|
||||||
return False
|
return False
|
||||||
|
@ -63,14 +64,13 @@ class DashSegmentsFD(FragmentFD):
|
||||||
# itself since it has its own retry settings
|
# itself since it has its own retry settings
|
||||||
if fatal:
|
if fatal:
|
||||||
raise
|
raise
|
||||||
self.report_skip_fragment(frag_index)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if count >= fragment_retries:
|
if not success:
|
||||||
if not fatal:
|
if not fatal:
|
||||||
self.report_skip_fragment(frag_index)
|
self.report_skip_fragment(frag_index)
|
||||||
continue
|
continue
|
||||||
self.report_error('giving up after %s fragment retries' % fragment_retries)
|
self.report_error('giving up after %s fragment retries' % count)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._finish_frag_download(ctx)
|
self._finish_frag_download(ctx)
|
||||||
|
|
|
@ -19,6 +19,7 @@ from ..compat import (
|
||||||
compat_urllib_parse_parse_qs as compat_parse_qs,
|
compat_urllib_parse_parse_qs as compat_parse_qs,
|
||||||
compat_urllib_parse_unquote_plus,
|
compat_urllib_parse_unquote_plus,
|
||||||
compat_urllib_parse_urlparse,
|
compat_urllib_parse_urlparse,
|
||||||
|
compat_zip as zip,
|
||||||
)
|
)
|
||||||
from ..jsinterp import JSInterpreter
|
from ..jsinterp import JSInterpreter
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
@ -1555,17 +1556,14 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||||
r'\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'\bm=(?P<sig>[a-zA-Z0-9$]{2,})\(decodeURIComponent\(h\.s\)\)',
|
r'\bm=(?P<sig>[a-zA-Z0-9$]{2,})\(decodeURIComponent\(h\.s\)\)',
|
||||||
r'\bc&&\(c=(?P<sig>[a-zA-Z0-9$]{2,})\(decodeURIComponent\(c\)\)',
|
r'\bc&&\(c=(?P<sig>[a-zA-Z0-9$]{2,})\(decodeURIComponent\(c\)\)',
|
||||||
r'(?:\b|[^a-zA-Z0-9$])(?P<sig>[a-zA-Z0-9$]{2,})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\);[a-zA-Z0-9$]{2}\.[a-zA-Z0-9$]{2}\(a,\d+\)',
|
r'(?:\b|[^a-zA-Z0-9$])(?P<sig>[a-zA-Z0-9$]{2,})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)(?:;[a-zA-Z0-9$]{2}\.[a-zA-Z0-9$]{2}\(a,\d+\))?',
|
||||||
r'(?:\b|[^a-zA-Z0-9$])(?P<sig>[a-zA-Z0-9$]{2,})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)',
|
|
||||||
r'(?P<sig>[a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)',
|
r'(?P<sig>[a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)',
|
||||||
# Obsolete patterns
|
# Obsolete patterns
|
||||||
r'(["\'])signature\1\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'("|\')signature\1\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'\.sig\|\|(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'\.sig\|\|(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'yt\.akamaized\.net/\)\s*\|\|\s*.*?\s*[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?:encodeURIComponent\s*\()?\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'yt\.akamaized\.net/\)\s*\|\|\s*.*?\s*[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?:encodeURIComponent\s*\()?\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
r'\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
||||||
r'\bc\s*&&\s*a\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
|
||||||
r'\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(',
|
|
||||||
r'\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\('),
|
r'\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\('),
|
||||||
jscode, 'Initial JS player signature function name', group='sig')
|
jscode, 'Initial JS player signature function name', group='sig')
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from functools import update_wrapper
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
@ -23,11 +24,23 @@ from .compat import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def wraps_op(op):
|
||||||
|
|
||||||
|
def update_and_rename_wrapper(w):
|
||||||
|
f = update_wrapper(w, op)
|
||||||
|
# fn names are str in both Py 2/3
|
||||||
|
f.__name__ = str('JS_') + f.__name__
|
||||||
|
return f
|
||||||
|
|
||||||
|
return update_and_rename_wrapper
|
||||||
|
|
||||||
|
|
||||||
def _js_bit_op(op):
|
def _js_bit_op(op):
|
||||||
|
|
||||||
def zeroise(x):
|
def zeroise(x):
|
||||||
return 0 if x in (None, JS_Undefined) else x
|
return 0 if x in (None, JS_Undefined) else x
|
||||||
|
|
||||||
|
@wraps_op(op)
|
||||||
def wrapped(a, b):
|
def wrapped(a, b):
|
||||||
return op(zeroise(a), zeroise(b)) & 0xffffffff
|
return op(zeroise(a), zeroise(b)) & 0xffffffff
|
||||||
|
|
||||||
|
@ -36,6 +49,7 @@ def _js_bit_op(op):
|
||||||
|
|
||||||
def _js_arith_op(op):
|
def _js_arith_op(op):
|
||||||
|
|
||||||
|
@wraps_op(op)
|
||||||
def wrapped(a, b):
|
def wrapped(a, b):
|
||||||
if JS_Undefined in (a, b):
|
if JS_Undefined in (a, b):
|
||||||
return float('nan')
|
return float('nan')
|
||||||
|
@ -66,6 +80,7 @@ def _js_exp(a, b):
|
||||||
|
|
||||||
def _js_eq_op(op):
|
def _js_eq_op(op):
|
||||||
|
|
||||||
|
@wraps_op(op)
|
||||||
def wrapped(a, b):
|
def wrapped(a, b):
|
||||||
if set((a, b)) <= set((None, JS_Undefined)):
|
if set((a, b)) <= set((None, JS_Undefined)):
|
||||||
return op(a, a)
|
return op(a, a)
|
||||||
|
@ -76,6 +91,7 @@ def _js_eq_op(op):
|
||||||
|
|
||||||
def _js_comp_op(op):
|
def _js_comp_op(op):
|
||||||
|
|
||||||
|
@wraps_op(op)
|
||||||
def wrapped(a, b):
|
def wrapped(a, b):
|
||||||
if JS_Undefined in (a, b):
|
if JS_Undefined in (a, b):
|
||||||
return False
|
return False
|
||||||
|
@ -356,6 +372,7 @@ class JSInterpreter(object):
|
||||||
return right_val
|
return right_val
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# print('Eval:', opfunc.__name__, left_val, right_val)
|
||||||
return opfunc(left_val, right_val)
|
return opfunc(left_val, right_val)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise self.Exception('Failed to evaluate {left_val!r:.50} {op} {right_val!r:.50}'.format(**locals()), expr, cause=e)
|
raise self.Exception('Failed to evaluate {left_val!r:.50} {op} {right_val!r:.50}'.format(**locals()), expr, cause=e)
|
||||||
|
@ -395,6 +412,7 @@ class JSInterpreter(object):
|
||||||
raise self.Exception('Recursion limit reached')
|
raise self.Exception('Recursion limit reached')
|
||||||
allow_recursion -= 1
|
allow_recursion -= 1
|
||||||
|
|
||||||
|
# print('At: ' + stmt[:60])
|
||||||
should_return = False
|
should_return = False
|
||||||
# fails on (eg) if (...) stmt1; else stmt2;
|
# fails on (eg) if (...) stmt1; else stmt2;
|
||||||
sub_statements = list(self._separate(stmt, ';')) or ['']
|
sub_statements = list(self._separate(stmt, ';')) or ['']
|
||||||
|
@ -702,9 +720,24 @@ class JSInterpreter(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
right_expr = separated.pop()
|
right_expr = separated.pop()
|
||||||
while op == '-' and len(separated) > 1 and not separated[-1].strip():
|
# handle operators that are both unary and binary, minimal BODMAS
|
||||||
right_expr = '-' + right_expr
|
if op in ('+', '-'):
|
||||||
|
undone = 0
|
||||||
|
while len(separated) > 1 and not separated[-1].strip():
|
||||||
|
undone += 1
|
||||||
separated.pop()
|
separated.pop()
|
||||||
|
if op == '-' and undone % 2 != 0:
|
||||||
|
right_expr = op + right_expr
|
||||||
|
left_val = separated[-1]
|
||||||
|
for dm_op in ('*', '%', '/', '**'):
|
||||||
|
bodmas = tuple(self._separate(left_val, dm_op, skip_delims=skip_delim))
|
||||||
|
if len(bodmas) > 1 and not bodmas[-1].strip():
|
||||||
|
expr = op.join(separated) + op + right_expr
|
||||||
|
right_expr = None
|
||||||
|
break
|
||||||
|
if right_expr is None:
|
||||||
|
continue
|
||||||
|
|
||||||
left_val = self.interpret_expression(op.join(separated), local_vars, allow_recursion)
|
left_val = self.interpret_expression(op.join(separated), local_vars, allow_recursion)
|
||||||
return self._operator(op, left_val, right_expr, expr, local_vars, allow_recursion), should_return
|
return self._operator(op, left_val, right_expr, expr, local_vars, allow_recursion), should_return
|
||||||
|
|
||||||
|
@ -955,6 +988,7 @@ class JSInterpreter(object):
|
||||||
def build_function(self, argnames, code, *global_stack):
|
def build_function(self, argnames, code, *global_stack):
|
||||||
global_stack = list(global_stack) or [{}]
|
global_stack = list(global_stack) or [{}]
|
||||||
argnames = tuple(argnames)
|
argnames = tuple(argnames)
|
||||||
|
# import pdb; pdb.set_trace()
|
||||||
|
|
||||||
def resf(args, kwargs={}, allow_recursion=100):
|
def resf(args, kwargs={}, allow_recursion=100):
|
||||||
global_stack[0].update(
|
global_stack[0].update(
|
||||||
|
|
Loading…
Reference in a new issue