[utils] Generalize traverse_dict
to traverse_obj
This commit is contained in:
parent
beb982bead
commit
324ad82006
3 changed files with 34 additions and 17 deletions
|
@ -101,7 +101,7 @@ from .utils import (
|
||||||
strftime_or_none,
|
strftime_or_none,
|
||||||
subtitles_filename,
|
subtitles_filename,
|
||||||
to_high_limit_path,
|
to_high_limit_path,
|
||||||
traverse_dict,
|
traverse_obj,
|
||||||
UnavailableVideoError,
|
UnavailableVideoError,
|
||||||
url_basename,
|
url_basename,
|
||||||
version_tuple,
|
version_tuple,
|
||||||
|
@ -855,7 +855,7 @@ class YoutubeDL(object):
|
||||||
def get_value(mdict):
|
def get_value(mdict):
|
||||||
# Object traversal
|
# Object traversal
|
||||||
fields = mdict['fields'].split('.')
|
fields = mdict['fields'].split('.')
|
||||||
value = traverse_dict(info_dict, fields)
|
value = traverse_obj(info_dict, fields)
|
||||||
# Negative
|
# Negative
|
||||||
if mdict['negate']:
|
if mdict['negate']:
|
||||||
value = float_or_none(value)
|
value = float_or_none(value)
|
||||||
|
@ -872,7 +872,7 @@ class YoutubeDL(object):
|
||||||
item, multiplier = (item[1:], -1) if item[0] == '-' else (item, 1)
|
item, multiplier = (item[1:], -1) if item[0] == '-' else (item, 1)
|
||||||
offset = float_or_none(item)
|
offset = float_or_none(item)
|
||||||
if offset is None:
|
if offset is None:
|
||||||
offset = float_or_none(traverse_dict(info_dict, item.split('.')))
|
offset = float_or_none(traverse_obj(info_dict, item.split('.')))
|
||||||
try:
|
try:
|
||||||
value = operator(value, multiplier * offset)
|
value = operator(value, multiplier * offset)
|
||||||
except (TypeError, ZeroDivisionError):
|
except (TypeError, ZeroDivisionError):
|
||||||
|
|
|
@ -23,7 +23,7 @@ from ..utils import (
|
||||||
ISO639Utils,
|
ISO639Utils,
|
||||||
process_communicate_or_kill,
|
process_communicate_or_kill,
|
||||||
replace_extension,
|
replace_extension,
|
||||||
traverse_dict,
|
traverse_obj,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ class FFmpegPostProcessor(PostProcessor):
|
||||||
def get_stream_number(self, path, keys, value):
|
def get_stream_number(self, path, keys, value):
|
||||||
streams = self.get_metadata_object(path)['streams']
|
streams = self.get_metadata_object(path)['streams']
|
||||||
num = next(
|
num = next(
|
||||||
(i for i, stream in enumerate(streams) if traverse_dict(stream, keys, casesense=False) == value),
|
(i for i, stream in enumerate(streams) if traverse_obj(stream, keys, casesense=False) == value),
|
||||||
None)
|
None)
|
||||||
return num, len(streams)
|
return num, len(streams)
|
||||||
|
|
||||||
|
|
|
@ -6181,21 +6181,38 @@ def load_plugins(name, suffix, namespace):
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
|
|
||||||
def traverse_dict(dictn, keys, casesense=True):
|
def traverse_obj(obj, keys, *, casesense=True, is_user_input=False, traverse_string=False):
|
||||||
|
''' Traverse nested list/dict/tuple
|
||||||
|
@param casesense Whether to consider dictionary keys as case sensitive
|
||||||
|
@param is_user_input Whether the keys are generated from user input. If True,
|
||||||
|
strings are converted to int/slice if necessary
|
||||||
|
@param traverse_string Whether to traverse inside strings. If True, any
|
||||||
|
non-compatible object will also be converted into a string
|
||||||
|
'''
|
||||||
keys = list(keys)[::-1]
|
keys = list(keys)[::-1]
|
||||||
while keys:
|
while keys:
|
||||||
key = keys.pop()
|
key = keys.pop()
|
||||||
if isinstance(dictn, dict):
|
if isinstance(obj, dict):
|
||||||
|
assert isinstance(key, compat_str)
|
||||||
if not casesense:
|
if not casesense:
|
||||||
dictn = {k.lower(): v for k, v in dictn.items()}
|
obj = {k.lower(): v for k, v in obj.items()}
|
||||||
key = key.lower()
|
key = key.lower()
|
||||||
dictn = dictn.get(key)
|
obj = obj.get(key)
|
||||||
elif isinstance(dictn, (list, tuple, compat_str)):
|
|
||||||
if ':' in key:
|
|
||||||
key = slice(*map(int_or_none, key.split(':')))
|
|
||||||
else:
|
|
||||||
key = int_or_none(key)
|
|
||||||
dictn = try_get(dictn, lambda x: x[key])
|
|
||||||
else:
|
else:
|
||||||
return None
|
if is_user_input:
|
||||||
return dictn
|
key = (int_or_none(key) if ':' not in key
|
||||||
|
else slice(*map(int_or_none, key.split(':'))))
|
||||||
|
if not isinstance(obj, (list, tuple)):
|
||||||
|
if traverse_string:
|
||||||
|
obj = compat_str(obj)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
assert isinstance(key, (int, slice))
|
||||||
|
obj = try_get(obj, lambda x: x[key])
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def traverse_dict(dictn, keys, casesense=True):
|
||||||
|
''' For backward compatibility. Do not use '''
|
||||||
|
return traverse_obj(dictn, keys, casesense=casesense,
|
||||||
|
is_user_input=True, traverse_string=True)
|
||||||
|
|
Loading…
Reference in a new issue