From ed5835b4513aed59ff29f4bdb7e07456cd041ee0 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Mon, 10 Jan 2022 23:57:59 +0530 Subject: [PATCH] Allow `--print` to be run at any post-processing stage --- README.md | 17 ++++++++---- yt_dlp/YoutubeDL.py | 67 ++++++++++++++++++++++----------------------- yt_dlp/options.py | 6 ++-- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index b40f5c693..31e5aad3a 100644 --- a/README.md +++ b/README.md @@ -667,11 +667,13 @@ You can also fork the project on github and run your fork's [build workflow](.gi --skip-download Do not download the video but write all related files (Alias: --no-download) -O, --print [WHEN:]TEMPLATE 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 + screen, optionally prefixed with when to + print it, separated by a ":". 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 -j, --dump-json Quiet, but print JSON information for each video. Simulate unless --no-simulate is used. See "OUTPUT TEMPLATE" for a @@ -1221,6 +1223,11 @@ Available only when used in `--print`: - `urls` (string): The URLs of all requested formats, one in each line - `filename` (string): Name of the video file. Note that the actual filename may be different due to post-processing. Use `--exec echo` to get the name after all postprocessing is complete + - `formats_table` (table): The video format table as printed by `--list-formats` + - `thumbnails_table` (table): The thumbnail format table as printed by `--list-thumbnails` + - `subtitles_table` (table): The subtitle format table as printed by `--list-subs` + - `automatic_captions_table` (table): The automatic subtitle format table as printed by `--list-subs` + Available only in `--sponsorblock-chapter-title`: diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index dff4b17b3..a239f1c3c 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -1772,13 +1772,8 @@ class YoutubeDL(object): self.prepare_filename(ie_copy, 'pl_infojson'), overwrite=True) is None: return - for tmpl in self.params['forceprint'].get('playlist', []): - self._forceprint(tmpl, ie_result) - - for pp in self._pps['playlist']: - ie_result = self.run_pp(pp, ie_result) - - self.to_screen('[download] Finished downloading playlist: %s' % playlist) + ie_result = self.run_all_pps('playlist', ie_result) + self.to_screen(f'[download] Finished downloading playlist: {playlist}') return ie_result @__handle_extraction_exceptions @@ -2600,8 +2595,7 @@ class YoutubeDL(object): self.record_download_archive(info_dict) info_dict['requested_downloads'] = formats_to_download - for pp in self._pps['after_video']: - info_dict = self.run_pp(pp, info_dict) + info_dict = self.run_all_pps('after_video', info_dict) if max_downloads_reached: raise MaxDownloadsReached() @@ -3228,6 +3222,26 @@ class YoutubeDL(object): ''' Alias of sanitize_info for backward compatibility ''' return YoutubeDL.sanitize_info(info_dict, actually_filter) + @staticmethod + def post_extract(info_dict): + def actual_post_extract(info_dict): + if info_dict.get('_type') in ('playlist', 'multi_video'): + for video_dict in info_dict.get('entries', {}): + actual_post_extract(video_dict or {}) + return + + post_extractor = info_dict.get('__post_extractor') or (lambda: {}) + extra = post_extractor().items() + info_dict.update(extra) + info_dict.pop('__post_extractor', None) + + original_infodict = info_dict.get('__original_infodict') or {} + original_infodict.update(extra) + original_infodict.pop('__post_extractor', None) + + actual_post_extract(info_dict or {}) + + def run_pp(self, pp, infodict): files_to_delete = [] if '__files_to_move' not in infodict: @@ -3257,44 +3271,27 @@ class YoutubeDL(object): del infodict['__files_to_move'][old_filename] return infodict - @staticmethod - def post_extract(info_dict): - def actual_post_extract(info_dict): - if info_dict.get('_type') in ('playlist', 'multi_video'): - for video_dict in info_dict.get('entries', {}): - actual_post_extract(video_dict or {}) - return - - post_extractor = info_dict.get('__post_extractor') or (lambda: {}) - extra = post_extractor().items() - info_dict.update(extra) - info_dict.pop('__post_extractor', None) - - original_infodict = info_dict.get('__original_infodict') or {} - original_infodict.update(extra) - original_infodict.pop('__post_extractor', None) - - actual_post_extract(info_dict or {}) + def run_all_pps(self, key, info, *, additional_pps=None): + for tmpl in self.params['forceprint'].get(key, []): + self._forceprint(tmpl, info) + for pp in (additional_pps or []) + self._pps[key]: + info = self.run_pp(info) + return info def pre_process(self, ie_info, key='pre_process', files_to_move=None): info = dict(ie_info) info['__files_to_move'] = files_to_move or {} - for pp in self._pps[key]: - info = self.run_pp(pp, info) + info = self.run_all_pps(key, info) return info, info.pop('__files_to_move', None) def post_process(self, filename, info, files_to_move=None): """Run all the postprocessors on the given file.""" info['filepath'] = filename info['__files_to_move'] = files_to_move or {} - - for pp in info.get('__postprocessors', []) + self._pps['post_process']: - info = self.run_pp(pp, info) + info = self.run_all_pps('post_process', info, additional_pps=info.get('__postprocessors')) info = self.run_pp(MoveFilesAfterDownloadPP(self), info) del info['__files_to_move'] - for pp in self._pps['after_move']: - info = self.run_pp(pp, info) - return info + return self.run_all_pps('after_move', info) def _make_archive_id(self, info_dict): video_id = info_dict.get('id') diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 15c480e6d..4dd7c6dbd 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -910,13 +910,13 @@ def create_parser(): metavar='[WHEN:]TEMPLATE', dest='forceprint', default={}, type='str', action='callback', callback=_dict_from_options_callback, callback_kwargs={ - 'allowed_keys': 'video|playlist', + 'allowed_keys': 'video|' + '|'.join(map(re.escape, POSTPROCESS_WHEN)), '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. ' + 'Field name or output template to print to screen, optionally prefixed with when to print it, separated by a ":". ' + '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( '-g', '--get-url',