Add --print playlist: to print fields per playlist

This commit is contained in:
pukkandan 2022-01-02 16:22:00 +05:30
parent af3cbd8782
commit ca30f449a1
No known key found for this signature in database
GPG key ID: 0F00D95A001F4698
4 changed files with 50 additions and 24 deletions

View file

@ -662,10 +662,12 @@ ## Verbosity and Simulation Options:
formats are found (default) formats are found (default)
--skip-download Do not download the video but write all --skip-download Do not download the video but write all
related files (Alias: --no-download) related files (Alias: --no-download)
-O, --print TEMPLATE Quiet, but print the given fields for each -O, --print [WHEN:]TEMPLATE Field name or output template to print to
video. Simulate unless --no-simulate is screen per video. Prefix the template with
used. Either a field name or same syntax as "playlist:" to print it once per playlist
the output template can be used instead. Implies --quiet and --simulate
(unless --no-simulate is used). This option
can be used multiple times
-j, --dump-json Quiet, but print JSON information for each -j, --dump-json Quiet, but print JSON information for each
video. Simulate unless --no-simulate is video. Simulate unless --no-simulate is
used. See "OUTPUT TEMPLATE" for a used. See "OUTPUT TEMPLATE" for a

View file

@ -199,7 +199,9 @@ class YoutubeDL(object):
verbose: Print additional info to stdout. verbose: Print additional info to stdout.
quiet: Do not print messages to stdout. quiet: Do not print messages to stdout.
no_warnings: Do not print out anything for warnings. no_warnings: Do not print out anything for warnings.
forceprint: A list of templates to force print forceprint: A dict with keys video/playlist mapped to
a list of templates to force print to stdout
For compatibility, a single list is also accepted
forceurl: Force printing final URL. (Deprecated) forceurl: Force printing final URL. (Deprecated)
forcetitle: Force printing title. (Deprecated) forcetitle: Force printing title. (Deprecated)
forceid: Force printing ID. (Deprecated) forceid: Force printing ID. (Deprecated)
@ -585,6 +587,11 @@ def check_deprecated(param, option, suggestion):
else: else:
self.params['nooverwrites'] = not self.params['overwrites'] self.params['nooverwrites'] = not self.params['overwrites']
# Compatibility with older syntax
params.setdefault('forceprint', {})
if not isinstance(params['forceprint'], dict):
params['forceprint'] = {'video': params['forceprint']}
if params.get('bidi_workaround', False): if params.get('bidi_workaround', False):
try: try:
import pty import pty
@ -1755,6 +1762,9 @@ def get_entry(i):
'updated playlist', ie_result, 'updated playlist', ie_result,
self.prepare_filename(ie_copy, 'pl_infojson'), overwrite=True) is None: self.prepare_filename(ie_copy, 'pl_infojson'), overwrite=True) is None:
return return
for tmpl in self.params['forceprint'].get('playlist', []):
self._forceprint(tmpl, ie_result)
self.to_screen('[download] Finished downloading playlist: %s' % playlist) self.to_screen('[download] Finished downloading playlist: %s' % playlist)
return ie_result return ie_result
@ -2626,6 +2636,14 @@ def process_subtitles(self, video_id, normal_subtitles, automatic_captions):
subs[lang] = f subs[lang] = f
return subs return subs
def _forceprint(self, tmpl, info_dict):
mobj = re.match(r'\w+(=?)$', tmpl)
if mobj and mobj.group(1):
tmpl = f'{tmpl[:-1]} = %({tmpl[:-1]})s'
elif mobj:
tmpl = '%({})s'.format(tmpl)
self.to_stdout(self.evaluate_outtmpl(tmpl, info_dict))
def __forced_printings(self, info_dict, filename, incomplete): def __forced_printings(self, info_dict, filename, incomplete):
def print_mandatory(field, actual_field=None): def print_mandatory(field, actual_field=None):
if actual_field is None: if actual_field is None:
@ -2648,15 +2666,10 @@ def print_optional(field):
elif 'url' in info_dict: elif 'url' in info_dict:
info_dict['urls'] = info_dict['url'] + info_dict.get('play_path', '') info_dict['urls'] = info_dict['url'] + info_dict.get('play_path', '')
if self.params.get('forceprint') or self.params.get('forcejson'): if self.params['forceprint'].get('video') or self.params.get('forcejson'):
self.post_extract(info_dict) self.post_extract(info_dict)
for tmpl in self.params.get('forceprint', []): for tmpl in self.params['forceprint'].get('video', []):
mobj = re.match(r'\w+(=?)$', tmpl) self._forceprint(tmpl, info_dict)
if mobj and mobj.group(1):
tmpl = f'{tmpl[:-1]} = %({tmpl[:-1]})s'
elif mobj:
tmpl = '%({})s'.format(tmpl)
self.to_stdout(self.evaluate_outtmpl(tmpl, info_dict))
print_mandatory('title') print_mandatory('title')
print_mandatory('id') print_mandatory('id')

View file

@ -351,9 +351,9 @@ def validate_outtmpl(tmpl, msg):
for k, tmpl in opts.outtmpl.items(): for k, tmpl in opts.outtmpl.items():
validate_outtmpl(tmpl, f'{k} output template') validate_outtmpl(tmpl, f'{k} output template')
opts.forceprint = opts.forceprint or [] for type_, tmpl_list in opts.forceprint.items():
for tmpl in opts.forceprint or []: for tmpl in tmpl_list:
validate_outtmpl(tmpl, 'print template') validate_outtmpl(tmpl, f'{type_} print template')
validate_outtmpl(opts.sponsorblock_chapter_title, 'SponsorBlock chapter title') validate_outtmpl(opts.sponsorblock_chapter_title, 'SponsorBlock chapter title')
for k, tmpl in opts.progress_template.items(): for k, tmpl in opts.progress_template.items():
k = f'{k[:-6]} console title' if '-title' in k else f'{k} progress' k = f'{k[:-6]} console title' if '-title' in k else f'{k} progress'
@ -395,7 +395,10 @@ def metadataparser_actions(f):
opts.parse_metadata.append('title:%s' % opts.metafromtitle) opts.parse_metadata.append('title:%s' % opts.metafromtitle)
opts.parse_metadata = list(itertools.chain(*map(metadataparser_actions, opts.parse_metadata))) opts.parse_metadata = list(itertools.chain(*map(metadataparser_actions, opts.parse_metadata)))
any_getting = opts.forceprint or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration or opts.dumpjson or opts.dump_single_json any_getting = (any(opts.forceprint.values()) or opts.dumpjson or opts.dump_single_json
or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail
or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration)
any_printing = opts.print_json any_printing = opts.print_json
download_archive_fn = expand_path(opts.download_archive) if opts.download_archive is not None else opts.download_archive download_archive_fn = expand_path(opts.download_archive) if opts.download_archive is not None else opts.download_archive

View file

@ -152,9 +152,9 @@ def _set_from_options_callback(
def _dict_from_options_callback( def _dict_from_options_callback(
option, opt_str, value, parser, option, opt_str, value, parser,
allowed_keys=r'[\w-]+', delimiter=':', default_key=None, process=None, multiple_keys=True, allowed_keys=r'[\w-]+', delimiter=':', default_key=None, process=None, multiple_keys=True,
process_key=str.lower): process_key=str.lower, append=False):
out_dict = getattr(parser.values, option.dest) out_dict = dict(getattr(parser.values, option.dest))
if multiple_keys: if multiple_keys:
allowed_keys = r'(%s)(,(%s))*' % (allowed_keys, allowed_keys) allowed_keys = r'(%s)(,(%s))*' % (allowed_keys, allowed_keys)
mobj = re.match(r'(?i)(?P<keys>%s)%s(?P<val>.*)$' % (allowed_keys, delimiter), value) mobj = re.match(r'(?i)(?P<keys>%s)%s(?P<val>.*)$' % (allowed_keys, delimiter), value)
@ -171,7 +171,8 @@ def _dict_from_options_callback(
except Exception as err: except Exception as err:
raise optparse.OptionValueError(f'wrong {opt_str} formatting; {err}') raise optparse.OptionValueError(f'wrong {opt_str} formatting; {err}')
for key in keys: for key in keys:
out_dict[key] = val out_dict[key] = out_dict.get(key, []) + [val] if append else val
setattr(parser.values, option.dest, out_dict)
# No need to wrap help messages if we're on a wide console # No need to wrap help messages if we're on a wide console
columns = compat_get_terminal_size().columns columns = compat_get_terminal_size().columns
@ -882,10 +883,17 @@ def _dict_from_options_callback(
help='Do not download the video but write all related files (Alias: --no-download)') help='Do not download the video but write all related files (Alias: --no-download)')
verbosity.add_option( verbosity.add_option(
'-O', '--print', '-O', '--print',
metavar='TEMPLATE', action='append', dest='forceprint', metavar='[WHEN:]TEMPLATE', dest='forceprint', default={}, type='str',
help=( action='callback', callback=_dict_from_options_callback,
'Quiet, but print the given fields for each video. Simulate unless --no-simulate is used. ' callback_kwargs={
'Either a field name or same syntax as the output template can be used')) 'allowed_keys': 'video|playlist',
'default_key': 'video',
'multiple_keys': False,
'append': True,
}, help=(
'Field name or output template to print to screen per video. '
'Prefix the template with "playlist:" to print it once per playlist instead. '
'Implies --quiet and --simulate (unless --no-simulate is used). This option can be used multiple times'))
verbosity.add_option( verbosity.add_option(
'-g', '--get-url', '-g', '--get-url',
action='store_true', dest='geturl', default=False, action='store_true', dest='geturl', default=False,