From 26e2805c3f11066c79ed29cf78511eae7fdc7a7b Mon Sep 17 00:00:00 2001 From: pukkandan Date: Wed, 21 Apr 2021 11:30:43 +0530 Subject: [PATCH] Add option `--skip-playlist-after-errors` Allows to skip the rest of a playlist after a given number of errors are encountered --- README.md | 2 ++ yt_dlp/YoutubeDL.py | 10 ++++++++++ yt_dlp/__init__.py | 1 + yt_dlp/options.py | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/README.md b/README.md index b1068421be..b4e60eff62 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,8 @@ ## Video Selection: a file that is in the archive --break-on-reject Stop the download process when encountering a file that has been filtered out + --skip-playlist-after-errors N Number of allowed failures until the rest + of the playlist is skipped --no-download-archive Do not use archive file (default) ## Download Options: diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 29931474d5..a3d7968ff9 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -214,6 +214,8 @@ class YoutubeDL(object): ignoreerrors: Do not stop on download errors (Default True when running yt-dlp, but False when directly accessing YoutubeDL class) + 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 overwrites: Overwrite all video and metadata files if True, overwrite only non-video files if None @@ -1327,6 +1329,8 @@ def make_playlistitems_entries(list_ie_entries): x_forwarded_for = ie_result.get('__x_forwarded_for_ip') self.to_screen('[%s] playlist %s: %s' % (ie_result['extractor'], playlist, msg)) + failures = 0 + max_failures = self.params.get('skip_playlist_after_errors') or float('inf') for i, entry in enumerate(entries, 1): self.to_screen('[download] Downloading video %s of %s' % (i, n_entries)) # This __x_forwarded_for_ip thing is a bit ugly but requires @@ -1351,6 +1355,12 @@ def make_playlistitems_entries(list_ie_entries): continue entry_result = self.__process_iterable_entry(entry, download, extra) + if not entry_result: + failures += 1 + if failures >= max_failures: + self.report_error( + 'Skipping the remaining entries in playlist "%s" since %d items failed extraction' % (playlist, failures)) + break # TODO: skip failed (empty) entries? playlist_results.append(entry_result) ie_result['entries'] = playlist_results diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 4f0684236e..bf5896f0c6 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -544,6 +544,7 @@ def report_args_compat(arg, name): 'download_archive': download_archive_fn, 'break_on_existing': opts.break_on_existing, 'break_on_reject': opts.break_on_reject, + 'skip_playlist_after_errors': opts.skip_playlist_after_errors, 'cookiefile': opts.cookiefile, 'nocheckcertificate': opts.no_check_certificate, 'prefer_insecure': opts.prefer_insecure, diff --git a/yt_dlp/options.py b/yt_dlp/options.py index a16604b733..c21c453edf 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -395,6 +395,10 @@ def _dict_from_multiple_values_options_callback( '--break-on-reject', action='store_true', dest='break_on_reject', default=False, help='Stop the download process when encountering a file that has been filtered out') + selection.add_option( + '--skip-playlist-after-errors', metavar='N', + dest='skip_playlist_after_errors', default=None, type=int, + help='Number of allowed failures until the rest of the playlist is skipped') selection.add_option( '--no-download-archive', dest='download_archive', action="store_const", const=None,