|
|
@ -920,6 +920,7 @@ class YoutubeDL(object): |
|
|
|
PICKFIRST = 'PICKFIRST' |
|
|
|
MERGE = 'MERGE' |
|
|
|
SINGLE = 'SINGLE' |
|
|
|
GROUP = 'GROUP' |
|
|
|
FormatSelector = collections.namedtuple('FormatSelector', ['type', 'selector', 'filters']) |
|
|
|
|
|
|
|
def _parse_filter(tokens): |
|
|
@ -942,6 +943,10 @@ class YoutubeDL(object): |
|
|
|
elif type == tokenize.OP: |
|
|
|
if string in endwith: |
|
|
|
break |
|
|
|
elif string == ')': |
|
|
|
# ')' will be handled by the parentheses group |
|
|
|
tokens.restore_last_token() |
|
|
|
break |
|
|
|
if string == ',': |
|
|
|
selectors.append(current_selector) |
|
|
|
current_selector = None |
|
|
@ -955,6 +960,10 @@ class YoutubeDL(object): |
|
|
|
current_selector = FormatSelector(SINGLE, 'best', []) |
|
|
|
format_filter = _parse_filter(tokens) |
|
|
|
current_selector.filters.append(format_filter) |
|
|
|
elif string == '(': |
|
|
|
if current_selector: |
|
|
|
raise syntax_error('Unexpected "("', start) |
|
|
|
current_selector = FormatSelector(GROUP, _parse_format_selection(tokens, [')']), []) |
|
|
|
elif string == '+': |
|
|
|
video_selector = current_selector |
|
|
|
audio_selector = _parse_format_selection(tokens, [',']) |
|
|
@ -977,6 +986,8 @@ class YoutubeDL(object): |
|
|
|
for format in f(formats): |
|
|
|
yield format |
|
|
|
return selector_function |
|
|
|
elif selector.type == GROUP: |
|
|
|
selector_function = _build_selector_function(selector.selector) |
|
|
|
elif selector.type == PICKFIRST: |
|
|
|
fs = [_build_selector_function(s) for s in selector.selector] |
|
|
|
|
|
|
@ -1084,8 +1095,32 @@ class YoutubeDL(object): |
|
|
|
return final_selector |
|
|
|
|
|
|
|
stream = io.BytesIO(format_spec.encode('utf-8')) |
|
|
|
tokens = compat_tokenize_tokenize(stream.readline) |
|
|
|
parsed_selector = _parse_format_selection(tokens) |
|
|
|
try: |
|
|
|
tokens = list(compat_tokenize_tokenize(stream.readline)) |
|
|
|
except tokenize.TokenError: |
|
|
|
raise syntax_error('Missing closing/opening brackets or parenthesis', (0, len(format_spec))) |
|
|
|
|
|
|
|
class TokenIterator(object): |
|
|
|
def __init__(self, tokens): |
|
|
|
self.tokens = tokens |
|
|
|
self.counter = 0 |
|
|
|
|
|
|
|
def __iter__(self): |
|
|
|
return self |
|
|
|
|
|
|
|
def __next__(self): |
|
|
|
if self.counter >= len(self.tokens): |
|
|
|
raise StopIteration() |
|
|
|
value = self.tokens[self.counter] |
|
|
|
self.counter += 1 |
|
|
|
return value |
|
|
|
|
|
|
|
next = __next__ |
|
|
|
|
|
|
|
def restore_last_token(self): |
|
|
|
self.counter -= 1 |
|
|
|
|
|
|
|
parsed_selector = _parse_format_selection(iter(TokenIterator(tokens))) |
|
|
|
return _build_selector_function(parsed_selector) |
|
|
|
|
|
|
|
def _calc_headers(self, info_dict): |
|
|
|