diff --git a/README.md b/README.md index 418203eea9..6c34ba46dc 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,10 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git available. Pass the minimum number of seconds (or range) to wait between retries --no-wait-for-video Do not wait for scheduled streams (default) + --wait-retries RETRIES Number of retries while waiting for + scheduled streams to become available + (default is infinite). --wait-for-video must + also be set --mark-watched Mark videos watched (even with --simulate) --no-mark-watched Do not mark videos watched (default) --color [STREAM:]POLICY Whether to emit color codes in output, diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index f08a31afac..2dcfde92de 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -1620,17 +1620,26 @@ class YoutubeDL: def _handle_extraction_exceptions(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): + wait_retries = 1 + max_wait_retries = self.params.get('wait_retries') while True: try: return func(self, *args, **kwargs) except (CookieLoadError, DownloadCancelled, LazyList.IndexError, PagedList.IndexError): raise except ReExtractInfo as e: + if wait_retries > max_wait_retries: + if max_wait_retries > 0: + self.report_error(f'Giving up after {wait_retries - 1} {"retries" if wait_retries > 2 else "retry"} while waiting.') + else: + self.report_error(f'Video is still unavailable after waiting.') + raise UserNotLive('Maximum number of retries exceeded while waiting for stream') if e.expected: self.to_screen(f'{e}; Re-extracting data') else: self.to_stderr('\r') self.report_warning(f'{e}; Re-extracting data') + wait_retries += 1 continue except GeoRestrictedError as e: msg = e.msg diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 9b3bd4acd2..5dc989873c 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -266,6 +266,7 @@ def validate_options(opts): opts.retries = parse_retries('download', opts.retries) opts.fragment_retries = parse_retries('fragment', opts.fragment_retries) + opts.wait_retries = parse_retries('waiting', opts.wait_retries) opts.extractor_retries = parse_retries('extractor', opts.extractor_retries) opts.file_access_retries = parse_retries('file access', opts.file_access_retries) @@ -926,6 +927,7 @@ def parse_options(argv=None): 'extract_flat': opts.extract_flat, 'live_from_start': opts.live_from_start, 'wait_for_video': opts.wait_for_video, + 'wait_retries': opts.wait_retries, 'mark_watched': opts.mark_watched, 'merge_output_format': opts.merge_output_format, 'final_ext': final_ext, diff --git a/yt_dlp/options.py b/yt_dlp/options.py index c4d2a72743..e4df4e59eb 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -442,6 +442,10 @@ def create_parser(): '--no-wait-for-video', dest='wait_for_video', action='store_const', const=None, help='Do not wait for scheduled streams (default)') + general.add_option( + '--wait-retries', + dest='wait_retries', metavar='RETRIES', default="infinite", + help='Number of retries while waiting for scheduled streams to become available (default is %default). --wait-for-video must also be set') general.add_option( '--mark-watched', action='store_true', dest='mark_watched', default=False,