parent
d47f46e17e
commit
7c37ff97d3
3 changed files with 20 additions and 8 deletions
|
@ -958,12 +958,13 @@ The field names themselves (the part inside the parenthesis) can also have some
|
||||||
1. **Object traversal**: The dictionaries and lists available in metadata can be traversed by using a `.` (dot) separator. You can also do python slicing using `:`. Eg: `%(tags.0)s`, `%(subtitles.en.-1.ext)s`, `%(id.3:7:-1)s`, `%(formats.:.format_id)s`. `%()s` refers to the entire infodict. Note that all the fields that become available using this method are not listed below. Use `-j` to see such fields
|
1. **Object traversal**: The dictionaries and lists available in metadata can be traversed by using a `.` (dot) separator. You can also do python slicing using `:`. Eg: `%(tags.0)s`, `%(subtitles.en.-1.ext)s`, `%(id.3:7:-1)s`, `%(formats.:.format_id)s`. `%()s` refers to the entire infodict. Note that all the fields that become available using this method are not listed below. Use `-j` to see such fields
|
||||||
1. **Addition**: Addition and subtraction of numeric fields can be done using `+` and `-` respectively. Eg: `%(playlist_index+10)03d`, `%(n_entries+1-playlist_index)d`
|
1. **Addition**: Addition and subtraction of numeric fields can be done using `+` and `-` respectively. Eg: `%(playlist_index+10)03d`, `%(n_entries+1-playlist_index)d`
|
||||||
1. **Date/time Formatting**: Date/time fields can be formatted according to [strftime formatting](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes) by specifying it separated from the field name using a `>`. Eg: `%(duration>%H-%M-%S)s`, `%(upload_date>%Y-%m-%d)s`, `%(epoch-3600>%H-%M-%S)s`
|
1. **Date/time Formatting**: Date/time fields can be formatted according to [strftime formatting](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes) by specifying it separated from the field name using a `>`. Eg: `%(duration>%H-%M-%S)s`, `%(upload_date>%Y-%m-%d)s`, `%(epoch-3600>%H-%M-%S)s`
|
||||||
1. **Default**: A default value can be specified for when the field is empty using a `|` seperator. This overrides `--output-na-template`. Eg: `%(uploader|Unknown)s`
|
1. **Alternatives**: Alternate fields can be specified seperated with a `,`. Eg: `%(release_date>%Y,upload_date>%Y|Unknown)s`
|
||||||
|
1. **Default**: A literal default value can be specified for when the field is empty using a `|` seperator. This overrides `--output-na-template`. Eg: `%(uploader|Unknown)s`
|
||||||
1. **More Conversions**: In addition to the normal format types `diouxXeEfFgGcrs`, `B`, `j`, `l`, `q` can be used for converting to **B**ytes, **j**son, a comma seperated **l**ist and a string **q**uoted for the terminal respectively
|
1. **More Conversions**: In addition to the normal format types `diouxXeEfFgGcrs`, `B`, `j`, `l`, `q` can be used for converting to **B**ytes, **j**son, a comma seperated **l**ist and a string **q**uoted for the terminal respectively
|
||||||
|
|
||||||
To summarize, the general syntax for a field is:
|
To summarize, the general syntax for a field is:
|
||||||
```
|
```
|
||||||
%(name[.keys][addition][>strf][|default])[flags][width][.precision][length]type
|
%(name[.keys][addition][>strf][,alternate][|default])[flags][width][.precision][length]type
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally, you can set different output templates for the various metadata files separately from the general output template by specifying the type of file followed by the template separated by a colon `:`. The different file types supported are `subtitle`, `thumbnail`, `description`, `annotation` (deprecated), `infojson`, `pl_thumbnail`, `pl_description`, `pl_infojson`, `chapter`. For example, `-o '%(title)s.%(ext)s' -o 'thumbnail:%(title)s\%(title)s.%(ext)s'` will put the thumbnails in a folder with the same name as the video.
|
Additionally, you can set different output templates for the various metadata files separately from the general output template by specifying the type of file followed by the template separated by a colon `:`. The different file types supported are `subtitle`, `thumbnail`, `description`, `annotation` (deprecated), `infojson`, `pl_thumbnail`, `pl_description`, `pl_infojson`, `chapter`. For example, `-o '%(title)s.%(ext)s' -o 'thumbnail:%(title)s\%(title)s.%(ext)s'` will put the thumbnails in a folder with the same name as the video.
|
||||||
|
|
|
@ -790,6 +790,12 @@ class TestYoutubeDL(unittest.TestCase):
|
||||||
test('%(formats.0.id.-1+id)f', '1235.000000')
|
test('%(formats.0.id.-1+id)f', '1235.000000')
|
||||||
test('%(formats.0.id.-1+formats.1.id.-1)d', '3')
|
test('%(formats.0.id.-1+formats.1.id.-1)d', '3')
|
||||||
|
|
||||||
|
# Alternates
|
||||||
|
test('%(title,id)s', '1234')
|
||||||
|
test('%(width-100,height+20|def)d', '1100')
|
||||||
|
test('%(width-100,height+width|def)s', 'def')
|
||||||
|
test('%(timestamp-x>%H\\,%M\\,%S,timestamp>%H\\,%M\\,%S)s', '12,00,00')
|
||||||
|
|
||||||
# Laziness
|
# Laziness
|
||||||
def gen():
|
def gen():
|
||||||
yield from range(5)
|
yield from range(5)
|
||||||
|
|
|
@ -955,6 +955,7 @@ class YoutubeDL(object):
|
||||||
(?P<fields>{field})
|
(?P<fields>{field})
|
||||||
(?P<maths>(?:{math_op}{math_field})*)
|
(?P<maths>(?:{math_op}{math_field})*)
|
||||||
(?:>(?P<strf_format>.+?))?
|
(?:>(?P<strf_format>.+?))?
|
||||||
|
(?P<alternate>(?<!\\),[^|)]+)?
|
||||||
(?:\|(?P<default>.*?))?
|
(?:\|(?P<default>.*?))?
|
||||||
$'''.format(field=FIELD_RE, math_op=MATH_OPERATORS_RE, math_field=MATH_FIELD_RE))
|
$'''.format(field=FIELD_RE, math_op=MATH_OPERATORS_RE, math_field=MATH_FIELD_RE))
|
||||||
|
|
||||||
|
@ -996,7 +997,7 @@ class YoutubeDL(object):
|
||||||
operator = None
|
operator = None
|
||||||
# Datetime formatting
|
# Datetime formatting
|
||||||
if mdict['strf_format']:
|
if mdict['strf_format']:
|
||||||
value = strftime_or_none(value, mdict['strf_format'])
|
value = strftime_or_none(value, mdict['strf_format'].replace('\\,', ','))
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -1012,12 +1013,16 @@ class YoutubeDL(object):
|
||||||
return f'%{outer_mobj.group(0)}'
|
return f'%{outer_mobj.group(0)}'
|
||||||
key = outer_mobj.group('key')
|
key = outer_mobj.group('key')
|
||||||
mobj = re.match(INTERNAL_FORMAT_RE, key)
|
mobj = re.match(INTERNAL_FORMAT_RE, key)
|
||||||
if mobj is None:
|
initial_field = mobj.group('fields').split('.')[-1] if mobj else ''
|
||||||
value, default, mobj = None, na, {'fields': ''}
|
value, default = None, na
|
||||||
else:
|
while mobj:
|
||||||
mobj = mobj.groupdict()
|
mobj = mobj.groupdict()
|
||||||
default = mobj['default'] if mobj['default'] is not None else na
|
default = mobj['default'] if mobj['default'] is not None else default
|
||||||
value = get_value(mobj)
|
value = get_value(mobj)
|
||||||
|
if value is None and mobj['alternate']:
|
||||||
|
mobj = re.match(INTERNAL_FORMAT_RE, mobj['alternate'][1:])
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
fmt = outer_mobj.group('format')
|
fmt = outer_mobj.group('format')
|
||||||
if fmt == 's' and value is not None and key in field_size_compat_map.keys():
|
if fmt == 's' and value is not None and key in field_size_compat_map.keys():
|
||||||
|
@ -1052,7 +1057,7 @@ class YoutubeDL(object):
|
||||||
# So we convert it to repr first
|
# So we convert it to repr first
|
||||||
value, fmt = repr(value), str_fmt
|
value, fmt = repr(value), str_fmt
|
||||||
if fmt[-1] in 'csr':
|
if fmt[-1] in 'csr':
|
||||||
value = sanitize(mobj['fields'].split('.')[-1], value)
|
value = sanitize(initial_field, value)
|
||||||
|
|
||||||
key = '%s\0%s' % (key.replace('%', '%\0'), outer_mobj.group('format'))
|
key = '%s\0%s' % (key.replace('%', '%\0'), outer_mobj.group('format'))
|
||||||
TMPL_DICT[key] = value
|
TMPL_DICT[key] = value
|
||||||
|
|
Loading…
Reference in a new issue