From b19404591a8ad4d0c7e962931ea809221e3f0b8e Mon Sep 17 00:00:00 2001 From: pukkandan Date: Fri, 24 Sep 2021 05:51:54 +0530 Subject: [PATCH] Separate the options `--ignore-errors` and `--no-abort-on-error` In youtube-dl, `-i` ignores both download and post-processing error, and treats the download as successful even if the post-processor fails. yt-dlp used to skip the entire video on either error and there was no option to ignore the post-processing errors like youtube-dl does. By splitting the option into two, now either just the download errors (--no-abort-on-error, default on CLI) or all errors (--ignore-errors) can be ignored as per the users' needs Closes #893 --- README.md | 9 ++++++--- yt_dlp/YoutubeDL.py | 21 ++++++++++++++------- yt_dlp/__init__.py | 2 +- yt_dlp/options.py | 10 +++++++--- yt_dlp/postprocessor/common.py | 1 + yt_dlp/postprocessor/ffmpeg.py | 3 +-- yt_dlp/postprocessor/xattrpp.py | 5 ++--- 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 641b672e0..07a8e5ef2 100644 --- a/README.md +++ b/README.md @@ -243,9 +243,12 @@ Then simply run `make`. You can also run `make yt-dlp` instead to compile only t -U, --update Update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed) - -i, --ignore-errors Continue on download errors, for example to - skip unavailable videos in a playlist - (default) (Alias: --no-abort-on-error) + -i, --ignore-errors Ignore download and postprocessing errors. + The download will be considered successfull + even if the postprocessing fails + --no-abort-on-error Continue with next video on download + errors; e.g. to skip unavailable videos in + a playlist (default) --abort-on-error Abort downloading of further videos if an error occurs (Alias: --no-ignore-errors) --dump-user-agent Display the current user-agent and exit diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 117461f5a..8df8f1675 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -226,9 +226,9 @@ class YoutubeDL(object): restrictfilenames: Do not allow "&" and spaces in file names trim_file_name: Limit length of filename (extension excluded) windowsfilenames: Force the filenames to be windows compatible - ignoreerrors: Do not stop on download errors - (Default True when running yt-dlp, - but False when directly accessing YoutubeDL class) + ignoreerrors: Do not stop on download/postprocessing errors. + Can be 'only_download' to ignore only download errors. + Default is 'only_download' for CLI, but False for API skip_playlist_after_errors: Number of allowed failures until the rest of the playlist is skipped force_generic_extractor: Force downloader to use the generic extractor @@ -776,7 +776,7 @@ class YoutubeDL(object): tb = ''.join(tb_data) if tb: self.to_stderr(tb) - if not self.params.get('ignoreerrors', False): + if not self.params.get('ignoreerrors'): if sys.exc_info()[0] and hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]: exc_info = sys.exc_info()[1].exc_info else: @@ -1241,7 +1241,7 @@ class YoutubeDL(object): except (MaxDownloadsReached, ExistingVideoReached, RejectedVideoReached, LazyList.IndexError): raise except Exception as e: - if self.params.get('ignoreerrors', False): + if self.params.get('ignoreerrors'): self.report_error(error_to_compat_str(e), tb=encode_compat_str(traceback.format_exc())) else: raise @@ -2989,10 +2989,17 @@ class YoutubeDL(object): files_to_delete = [] if '__files_to_move' not in infodict: infodict['__files_to_move'] = {} - files_to_delete, infodict = pp.run(infodict) + try: + files_to_delete, infodict = pp.run(infodict) + except PostProcessingError as e: + # Must be True and not 'only_download' + if self.params.get('ignoreerrors') is True: + self.report_error(e) + return infodict + raise + if not files_to_delete: return infodict - if self.params.get('keepvideo', False): for f in files_to_delete: infodict['__files_to_move'].setdefault(f, '') diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 5168ed0f7..9987c6472 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -279,7 +279,7 @@ def _real_main(argv=None): setattr(opts, opt_name, default) return None - set_default_compat('abort-on-error', 'ignoreerrors') + set_default_compat('abort-on-error', 'ignoreerrors', 'only_download') set_default_compat('no-playlist-metafiles', 'allow_playlist_files') set_default_compat('no-clean-infojson', 'clean_infojson') if 'format-sort' in compat_opts: diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 80b3df74f..57e25a518 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -206,9 +206,13 @@ def parseOpts(overrideArguments=None): action='store_true', dest='update_self', help='Update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)') general.add_option( - '-i', '--ignore-errors', '--no-abort-on-error', - action='store_true', dest='ignoreerrors', default=None, - help='Continue on download errors, for example to skip unavailable videos in a playlist (default) (Alias: --no-abort-on-error)') + '-i', '--ignore-errors', + action='store_true', dest='ignoreerrors', + help='Ignore download and postprocessing errors. The download will be considered successfull even if the postprocessing fails') + general.add_option( + '--no-abort-on-error', + action='store_const', dest='ignoreerrors', const='only_download', + help='Continue with next video on download errors; e.g. to skip unavailable videos in a playlist (default)') general.add_option( '--abort-on-error', '--no-ignore-errors', action='store_false', dest='ignoreerrors', diff --git a/yt_dlp/postprocessor/common.py b/yt_dlp/postprocessor/common.py index aa4715b06..d8ec997d9 100644 --- a/yt_dlp/postprocessor/common.py +++ b/yt_dlp/postprocessor/common.py @@ -52,6 +52,7 @@ class PostProcessor(object): return self._downloader.report_warning(text, *args, **kwargs) def report_error(self, text, *args, **kwargs): + # Exists only for compatibility. Do not use if self._downloader: return self._downloader.report_error(text, *args, **kwargs) diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py index 7ea01620e..679377aa6 100644 --- a/yt_dlp/postprocessor/ffmpeg.py +++ b/yt_dlp/postprocessor/ffmpeg.py @@ -288,8 +288,7 @@ class FFmpegPostProcessor(PostProcessor): stdout, stderr = process_communicate_or_kill(p) if p.returncode not in variadic(expected_retcodes): stderr = stderr.decode('utf-8', 'replace').strip() - if self.get_param('verbose', False): - self.report_error(stderr) + self.write_debug(stderr) raise FFmpegPostProcessorError(stderr.split('\n')[-1]) for out_path, _ in output_path_opts: if out_path: diff --git a/yt_dlp/postprocessor/xattrpp.py b/yt_dlp/postprocessor/xattrpp.py index 3d31f0ce5..95afa1c4f 100644 --- a/yt_dlp/postprocessor/xattrpp.py +++ b/yt_dlp/postprocessor/xattrpp.py @@ -57,8 +57,7 @@ class XAttrMetadataPP(PostProcessor): return [], info except XAttrUnavailableError as e: - self.report_error(str(e)) - return [], info + raise PostProcessingError(str(e)) except XAttrMetadataError as e: if e.reason == 'NO_SPACE': @@ -74,5 +73,5 @@ class XAttrMetadataPP(PostProcessor): msg += 'You need to use NTFS.' else: msg += '(You may have to enable them in your /etc/fstab)' - self.report_error(msg) + raise PostProcessingError(str(e)) return [], info