mirror of
https://github.com/yt-dlp/yt-dlp
synced 2025-01-19 07:13:05 +01:00
Communicate needed wait time through an exception.
Instead of waiting before throwing `ReExtractInfo`, `_wait_until` will now immediately throw a new exception `ReExtractInfoLater`. The `_handle_extraction_exceptions` decorator will then either bail out if the maximum retry count is exceeded, or wait until the specified waiting period expires.
This commit is contained in:
parent
ad808c570b
commit
de4e709220
2 changed files with 51 additions and 36 deletions
|
@ -97,6 +97,7 @@ from .utils import (
|
||||||
Popen,
|
Popen,
|
||||||
PostProcessingError,
|
PostProcessingError,
|
||||||
ReExtractInfo,
|
ReExtractInfo,
|
||||||
|
ReExtractInfoLater,
|
||||||
RejectedVideoReached,
|
RejectedVideoReached,
|
||||||
SameFileError,
|
SameFileError,
|
||||||
UnavailableVideoError,
|
UnavailableVideoError,
|
||||||
|
@ -1619,11 +1620,24 @@ class YoutubeDL:
|
||||||
def _handle_extraction_exceptions(func):
|
def _handle_extraction_exceptions(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
|
wait_retries = 0
|
||||||
|
max_retries = self.params.get('wait_retries')
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
return func(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
except (CookieLoadError, DownloadCancelled, LazyList.IndexError, PagedList.IndexError):
|
except (CookieLoadError, DownloadCancelled, LazyList.IndexError, PagedList.IndexError):
|
||||||
raise
|
raise
|
||||||
|
except ReExtractInfoLater as e:
|
||||||
|
if wait_retries > max_retries:
|
||||||
|
if max_retries > 0:
|
||||||
|
self.report_error(f'[wait] Giving up after {wait_retries-1} {"retries" if wait_retries != 2 else "retry"} while waiting.')
|
||||||
|
else:
|
||||||
|
self.report_error('[wait] Video is still unavailable after waiting.')
|
||||||
|
return
|
||||||
|
self._wait_until(e.time)
|
||||||
|
wait_retries += 1
|
||||||
|
self.to_screen('[wait] Re-extracting data')
|
||||||
|
continue
|
||||||
except ReExtractInfo as e:
|
except ReExtractInfo as e:
|
||||||
if e.expected:
|
if e.expected:
|
||||||
self.to_screen(f'{e}; Re-extracting data')
|
self.to_screen(f'{e}; Re-extracting data')
|
||||||
|
@ -1648,24 +1662,9 @@ class YoutubeDL:
|
||||||
break
|
break
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def _wait_for_video(self, ie_result={}, extra_info={}):
|
def _wait_until(self, till):
|
||||||
if (not self.params.get('wait_for_video')
|
|
||||||
or ie_result.get('_type', 'video') != 'video'
|
|
||||||
or ie_result.get('formats') or ie_result.get('url')):
|
|
||||||
return
|
|
||||||
|
|
||||||
max_wait_retries = self.params.get('wait_retries')
|
|
||||||
wait_retries = extra_info.get('__attempt') or 0
|
|
||||||
if wait_retries > max_wait_retries:
|
|
||||||
if max_wait_retries > 0:
|
|
||||||
raise UserNotLive(f'[wait] Giving up after {wait_retries-1} {"retries" if wait_retries != 2 else "retry"} while waiting.')
|
|
||||||
else:
|
|
||||||
raise UserNotLive('[wait] Video is still unavailable after waiting.')
|
|
||||||
extra_info['__attempt'] = wait_retries + 1
|
|
||||||
|
|
||||||
format_dur = lambda dur: '%02d:%02d:%02d' % timetuple_from_msec(dur * 1000)[:-1]
|
format_dur = lambda dur: '%02d:%02d:%02d' % timetuple_from_msec(dur * 1000)[:-1]
|
||||||
last_msg = ''
|
last_msg = ''
|
||||||
|
|
||||||
def progress(msg):
|
def progress(msg):
|
||||||
nonlocal last_msg
|
nonlocal last_msg
|
||||||
full_msg = f'{msg}\n'
|
full_msg = f'{msg}\n'
|
||||||
|
@ -1676,6 +1675,30 @@ class YoutubeDL:
|
||||||
self.to_screen(full_msg, skip_eol=True)
|
self.to_screen(full_msg, skip_eol=True)
|
||||||
last_msg = msg
|
last_msg = msg
|
||||||
|
|
||||||
|
diff = till - time.time()
|
||||||
|
self.to_screen(f'[wait] Waiting for {format_dur(diff)} - Press Ctrl+C to interrupt')
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
diff = till - time.time()
|
||||||
|
if diff <= 0:
|
||||||
|
progress('')
|
||||||
|
self.to_screen('[wait] Wait period ended')
|
||||||
|
return
|
||||||
|
progress(f'[wait] Remaining time until next attempt: {self._format_screen(format_dur(diff), self.Styles.EMPHASIS)}')
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
progress('')
|
||||||
|
self.to_screen('[wait] Interrupted by user')
|
||||||
|
except BaseException as e:
|
||||||
|
self.to_screen('')
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _wait_for_video(self, ie_result={}):
|
||||||
|
if (not self.params.get('wait_for_video')
|
||||||
|
or ie_result.get('_type', 'video') != 'video'
|
||||||
|
or ie_result.get('formats') or ie_result.get('url')):
|
||||||
|
return
|
||||||
|
|
||||||
min_wait, max_wait = self.params.get('wait_for_video')
|
min_wait, max_wait = self.params.get('wait_for_video')
|
||||||
diff = try_get(ie_result, lambda x: x['release_timestamp'] - time.time())
|
diff = try_get(ie_result, lambda x: x['release_timestamp'] - time.time())
|
||||||
if diff is None and ie_result.get('live_status') == 'is_upcoming':
|
if diff is None and ie_result.get('live_status') == 'is_upcoming':
|
||||||
|
@ -1684,24 +1707,7 @@ class YoutubeDL:
|
||||||
elif ie_result and (diff or 0) <= 0:
|
elif ie_result and (diff or 0) <= 0:
|
||||||
self.report_warning('Video should already be available according to extracted info')
|
self.report_warning('Video should already be available according to extracted info')
|
||||||
diff = min(max(diff or 0, min_wait or 0), max_wait or float('inf'))
|
diff = min(max(diff or 0, min_wait or 0), max_wait or float('inf'))
|
||||||
self.to_screen(f'[wait] Waiting for {format_dur(diff)} - Press Ctrl+C to try now')
|
raise ReExtractInfoLater(time.time() + diff)
|
||||||
|
|
||||||
wait_till = time.time() + diff
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
diff = wait_till - time.time()
|
|
||||||
if diff <= 0:
|
|
||||||
progress('')
|
|
||||||
raise ReExtractInfo('[wait] Wait period ended', expected=True)
|
|
||||||
progress(f'[wait] Remaining time until next attempt: {self._format_screen(format_dur(diff), self.Styles.EMPHASIS)}')
|
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
progress('')
|
|
||||||
raise ReExtractInfo('[wait] Interrupted by user', expected=True)
|
|
||||||
except BaseException as e:
|
|
||||||
if not isinstance(e, ReExtractInfo):
|
|
||||||
self.to_screen('')
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _load_cookies(self, data, *, autoscope=True):
|
def _load_cookies(self, data, *, autoscope=True):
|
||||||
"""Loads cookies from a `Cookie` header
|
"""Loads cookies from a `Cookie` header
|
||||||
|
@ -1770,7 +1776,7 @@ class YoutubeDL:
|
||||||
if process:
|
if process:
|
||||||
if self.params.get('wait_for_video'):
|
if self.params.get('wait_for_video'):
|
||||||
self.report_warning(e)
|
self.report_warning(e)
|
||||||
self._wait_for_video(extra_info=extra_info)
|
self._wait_for_video()
|
||||||
raise
|
raise
|
||||||
if ie_result is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
if ie_result is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
||||||
self.report_warning(f'Extractor {ie.IE_NAME} returned nothing{bug_reports_message()}')
|
self.report_warning(f'Extractor {ie.IE_NAME} returned nothing{bug_reports_message()}')
|
||||||
|
@ -1785,7 +1791,7 @@ class YoutubeDL:
|
||||||
ie_result.setdefault('original_url', extra_info['original_url'])
|
ie_result.setdefault('original_url', extra_info['original_url'])
|
||||||
self.add_default_extra_info(ie_result, ie, url)
|
self.add_default_extra_info(ie_result, ie, url)
|
||||||
if process:
|
if process:
|
||||||
self._wait_for_video(ie_result, extra_info=extra_info)
|
self._wait_for_video(ie_result)
|
||||||
return self.process_ie_result(ie_result, download, extra_info)
|
return self.process_ie_result(ie_result, download, extra_info)
|
||||||
else:
|
else:
|
||||||
return ie_result
|
return ie_result
|
||||||
|
|
|
@ -1110,6 +1110,15 @@ class ReExtractInfo(YoutubeDLError):
|
||||||
self.expected = expected
|
self.expected = expected
|
||||||
|
|
||||||
|
|
||||||
|
class ReExtractInfoLater(ReExtractInfo):
|
||||||
|
""" Video info needs to be re-extracted after a waiting period. """
|
||||||
|
msg = 'Video is not available yet'
|
||||||
|
|
||||||
|
def __init__(self, time):
|
||||||
|
super().__init__(self.msg, expected=True)
|
||||||
|
self.time = time
|
||||||
|
|
||||||
|
|
||||||
class ThrottledDownload(ReExtractInfo):
|
class ThrottledDownload(ReExtractInfo):
|
||||||
""" Download speed below --throttled-rate. """
|
""" Download speed below --throttled-rate. """
|
||||||
msg = 'The download speed is below throttle limit'
|
msg = 'The download speed is below throttle limit'
|
||||||
|
|
Loading…
Add table
Reference in a new issue