aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpukkandan <pukkandan.ytdlp@gmail.com>2022-01-24 03:04:19 +0530
committerpukkandan <pukkandan.ytdlp@gmail.com>2022-01-24 03:24:15 +0530
commitbb66c24797edd8740a27efb8d77669dbb0e859b7 (patch)
tree651177ab2d75883775606133644884ac0a4900af
parent2edb38e8caa8cadd3a2effd75d8bf47e31f94f9c (diff)
downloadhypervideo-pre-bb66c24797edd8740a27efb8d77669dbb0e859b7.tar.lz
hypervideo-pre-bb66c24797edd8740a27efb8d77669dbb0e859b7.tar.xz
hypervideo-pre-bb66c24797edd8740a27efb8d77669dbb0e859b7.zip
Add option `--print-to-file`
Closes #2372
-rw-r--r--README.md6
-rw-r--r--yt_dlp/YoutubeDL.py62
-rw-r--r--yt_dlp/__init__.py5
-rw-r--r--yt_dlp/options.py19
4 files changed, 69 insertions, 23 deletions
diff --git a/README.md b/README.md
index d3fbdd6e4..5641a868f 100644
--- a/README.md
+++ b/README.md
@@ -686,6 +686,12 @@ You can also fork the project on github and run your fork's [build workflow](.gi
Implies --quiet and --simulate (unless
--no-simulate is used). This option can be
used multiple times
+ --print-to-file [WHEN:]TEMPLATE FILE
+ Append given template to the file. The
+ values of WHEN and TEMPLATE are same as
+ that of --print. FILE uses the same syntax
+ as the output template. This option can be
+ used multiple times
-j, --dump-json Quiet, but print JSON information for each
video. Simulate unless --no-simulate is
used. See "OUTPUT TEMPLATE" for a
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index d1fbd9a87..97f9099ff 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -201,9 +201,12 @@ class YoutubeDL(object):
verbose: Print additional info to stdout.
quiet: Do not print messages to stdout.
no_warnings: Do not print out anything for warnings.
- forceprint: A dict with keys video/playlist mapped to
- a list of templates to force print to stdout
+ forceprint: A dict with keys WHEN mapped to a list of templates to
+ print to stdout. The allowed keys are video or any of the
+ items in utils.POSTPROCESS_WHEN.
For compatibility, a single list is also accepted
+ print_to_file: A dict with keys WHEN (same as forceprint) mapped to
+ a list of tuples with (template, filename)
forceurl: Force printing final URL. (Deprecated)
forcetitle: Force printing title. (Deprecated)
forceid: Force printing ID. (Deprecated)
@@ -349,8 +352,8 @@ class YoutubeDL(object):
postprocessors: A list of dictionaries, each with an entry
* key: The name of the postprocessor. See
yt_dlp/postprocessor/__init__.py for a list.
- * when: When to run the postprocessor. Can be one of
- pre_process|before_dl|post_process|after_move.
+ * when: When to run the postprocessor. Allowed values are
+ the entries of utils.POSTPROCESS_WHEN
Assumed to be 'post_process' if not given
post_hooks: Deprecated - Register a custom postprocessor instead
A list of functions that get called as the final step
@@ -592,8 +595,10 @@ class YoutubeDL(object):
else:
self.params['nooverwrites'] = not self.params['overwrites']
- # Compatibility with older syntax
params.setdefault('forceprint', {})
+ params.setdefault('print_to_file', {})
+
+ # Compatibility with older syntax
if not isinstance(params['forceprint'], dict):
params['forceprint'] = {'video': params['forceprint']}
@@ -2683,19 +2688,32 @@ class YoutubeDL(object):
subs[lang] = f
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]})r'
- elif mobj:
- tmpl = '%({})s'.format(tmpl)
+ def _forceprint(self, key, info_dict):
+ if info_dict is None:
+ return
+ info_copy = info_dict.copy()
+ info_copy['formats_table'] = self.render_formats_table(info_dict)
+ info_copy['thumbnails_table'] = self.render_thumbnails_table(info_dict)
+ info_copy['subtitles_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('subtitles'))
+ info_copy['automatic_captions_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('automatic_captions'))
+
+ def format_tmpl(tmpl):
+ mobj = re.match(r'\w+(=?)$', tmpl)
+ if mobj and mobj.group(1):
+ return f'{tmpl[:-1]} = %({tmpl[:-1]})r'
+ elif mobj:
+ return f'%({tmpl})s'
+ return tmpl
- info_dict = info_dict.copy()
- info_dict['formats_table'] = self.render_formats_table(info_dict)
- info_dict['thumbnails_table'] = self.render_thumbnails_table(info_dict)
- info_dict['subtitles_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('subtitles'))
- info_dict['automatic_captions_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('automatic_captions'))
- self.to_stdout(self.evaluate_outtmpl(tmpl, info_dict))
+ for tmpl in self.params['forceprint'].get(key, []):
+ self.to_stdout(self.evaluate_outtmpl(format_tmpl(tmpl), info_copy))
+
+ for tmpl, file_tmpl in self.params['print_to_file'].get(key, []):
+ filename = self.evaluate_outtmpl(file_tmpl, info_dict)
+ tmpl = format_tmpl(tmpl)
+ self.to_screen(f'[info] Writing {tmpl!r} to: {filename}')
+ with io.open(filename, 'a', encoding='utf-8') as f:
+ f.write(self.evaluate_outtmpl(tmpl, info_copy) + '\n')
def __forced_printings(self, info_dict, filename, incomplete):
def print_mandatory(field, actual_field=None):
@@ -2719,10 +2737,11 @@ class YoutubeDL(object):
elif 'url' in info_dict:
info_dict['urls'] = info_dict['url'] + info_dict.get('play_path', '')
- if self.params['forceprint'].get('video') or self.params.get('forcejson'):
+ if (self.params.get('forcejson')
+ or self.params['forceprint'].get('video')
+ or self.params['print_to_file'].get('video')):
self.post_extract(info_dict)
- for tmpl in self.params['forceprint'].get('video', []):
- self._forceprint(tmpl, info_dict)
+ self._forceprint('video', info_dict)
print_mandatory('title')
print_mandatory('id')
@@ -3290,8 +3309,7 @@ class YoutubeDL(object):
return infodict
def run_all_pps(self, key, info, *, additional_pps=None):
- for tmpl in self.params['forceprint'].get(key, []):
- self._forceprint(tmpl, info)
+ self._forceprint(key, info)
for pp in (additional_pps or []) + self._pps[key]:
info = self.run_pp(pp, info)
return info
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index c5e647c23..f9ebfa053 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -356,6 +356,10 @@ def _real_main(argv=None):
for type_, tmpl_list in opts.forceprint.items():
for tmpl in tmpl_list:
validate_outtmpl(tmpl, f'{type_} print template')
+ for type_, tmpl_list in opts.print_to_file.items():
+ for tmpl, file in tmpl_list:
+ validate_outtmpl(tmpl, f'{type_} print-to-file template')
+ validate_outtmpl(file, f'{type_} print-to-file filename')
validate_outtmpl(opts.sponsorblock_chapter_title, 'SponsorBlock chapter title')
for k, tmpl in opts.progress_template.items():
k = f'{k[:-6]} console title' if '-title' in k else f'{k} progress'
@@ -663,6 +667,7 @@ def _real_main(argv=None):
'forcefilename': opts.getfilename,
'forceformat': opts.getformat,
'forceprint': opts.forceprint,
+ 'print_to_file': opts.print_to_file,
'forcejson': opts.dumpjson or opts.print_json,
'dump_single_json': opts.dump_single_json,
'force_write_download_archive': opts.force_write_download_archive,
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 289804945..df8fb6f63 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -173,11 +173,16 @@ def create_parser():
process_key=str.lower, append=False):
out_dict = dict(getattr(parser.values, option.dest))
+ multiple_args = not isinstance(value, str)
if multiple_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[0] if multiple_args else value)
if mobj is not None:
keys, val = mobj.group('keys').split(','), mobj.group('val')
+ if multiple_args:
+ val = [val, *value[1:]]
elif default_key is not None:
keys, val = [default_key], value
else:
@@ -924,6 +929,18 @@ def create_parser():
'Supported values of "WHEN" are the same as that of --use-postprocessor, and "video" (default). '
'Implies --quiet and --simulate (unless --no-simulate is used). This option can be used multiple times'))
verbosity.add_option(
+ '--print-to-file',
+ metavar='[WHEN:]TEMPLATE FILE', dest='print_to_file', default={}, type='str', nargs=2,
+ action='callback', callback=_dict_from_options_callback,
+ callback_kwargs={
+ 'allowed_keys': 'video|' + '|'.join(map(re.escape, POSTPROCESS_WHEN)),
+ 'default_key': 'video',
+ 'multiple_keys': False,
+ 'append': True,
+ }, help=(
+ 'Append given template to the file. The values of WHEN and TEMPLATE are same as that of --print. '
+ 'FILE uses the same syntax as the output template. This option can be used multiple times'))
+ verbosity.add_option(
'-g', '--get-url',
action='store_true', dest='geturl', default=False,
help=optparse.SUPPRESS_HELP)