[cleanup] Minor improvements to error and debug messages

This commit is contained in:
pukkandan 2021-11-10 04:19:33 +05:30
parent d54c6003ab
commit aa9369a2d8
No known key found for this signature in database
GPG key ID: 0F00D95A001F4698
5 changed files with 98 additions and 39 deletions

View file

@ -1318,9 +1318,9 @@ def wrapper(self, *args, **kwargs):
self.report_error(msg) self.report_error(msg)
except ExtractorError as e: # An error we somewhat expected except ExtractorError as e: # An error we somewhat expected
self.report_error(compat_str(e), e.format_traceback()) self.report_error(compat_str(e), e.format_traceback())
except ThrottledDownload: except ThrottledDownload as e:
self.to_stderr('\r') self.to_stderr('\r')
self.report_warning('The download speed is below throttle limit. Re-extracting data') self.report_warning(f'{e}; Re-extracting data')
return wrapper(self, *args, **kwargs) return wrapper(self, *args, **kwargs)
except (DownloadCancelled, LazyList.IndexError): except (DownloadCancelled, LazyList.IndexError):
raise raise
@ -1499,7 +1499,7 @@ def __process_playlist(self, ie_result, download):
self.to_screen('[download] Downloading playlist: %s' % playlist) self.to_screen('[download] Downloading playlist: %s' % playlist)
if 'entries' not in ie_result: if 'entries' not in ie_result:
raise EntryNotInPlaylist() raise EntryNotInPlaylist('There are no entries')
incomplete_entries = bool(ie_result.get('requested_entries')) incomplete_entries = bool(ie_result.get('requested_entries'))
if incomplete_entries: if incomplete_entries:
def fill_missing_entries(entries, indexes): def fill_missing_entries(entries, indexes):
@ -1561,7 +1561,7 @@ def get_entry(i):
raise EntryNotInPlaylist() raise EntryNotInPlaylist()
except (IndexError, EntryNotInPlaylist): except (IndexError, EntryNotInPlaylist):
if incomplete_entries: if incomplete_entries:
raise EntryNotInPlaylist() raise EntryNotInPlaylist(f'Entry {i} cannot be found')
elif not playlistitems: elif not playlistitems:
break break
entries.append(entry) entries.append(entry)
@ -2935,8 +2935,25 @@ def ffmpeg_fixup(cndn, msg, cls):
if max_downloads is not None and self._num_downloads >= int(max_downloads): if max_downloads is not None and self._num_downloads >= int(max_downloads):
raise MaxDownloadsReached() raise MaxDownloadsReached()
def __download_wrapper(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
res = func(*args, **kwargs)
except UnavailableVideoError as e:
self.report_error(e)
except DownloadCancelled as e:
self.to_screen(f'[info] {e}')
raise
else:
if self.params.get('dump_single_json', False):
self.post_extract(res)
self.to_stdout(json.dumps(self.sanitize_info(res)))
return wrapper
def download(self, url_list): def download(self, url_list):
"""Download a given list of URLs.""" """Download a given list of URLs."""
url_list = variadic(url_list) # Passing a single URL is a common mistake
outtmpl = self.outtmpl_dict['default'] outtmpl = self.outtmpl_dict['default']
if (len(url_list) > 1 if (len(url_list) > 1
and outtmpl != '-' and outtmpl != '-'
@ -2945,19 +2962,8 @@ def download(self, url_list):
raise SameFileError(outtmpl) raise SameFileError(outtmpl)
for url in url_list: for url in url_list:
try: self.__download_wrapper(self.extract_info)(
# It also downloads the videos
res = self.extract_info(
url, force_generic_extractor=self.params.get('force_generic_extractor', False)) url, force_generic_extractor=self.params.get('force_generic_extractor', False))
except UnavailableVideoError:
self.report_error('unable to download video')
except DownloadCancelled as e:
self.to_screen(f'[info] {e.msg}')
raise
else:
if self.params.get('dump_single_json', False):
self.post_extract(res)
self.to_stdout(json.dumps(self.sanitize_info(res)))
return self._download_retcode return self._download_retcode
@ -2968,11 +2974,12 @@ def download_with_info_file(self, info_filename):
# FileInput doesn't have a read method, we can't call json.load # FileInput doesn't have a read method, we can't call json.load
info = self.sanitize_info(json.loads('\n'.join(f)), self.params.get('clean_infojson', True)) info = self.sanitize_info(json.loads('\n'.join(f)), self.params.get('clean_infojson', True))
try: try:
self.process_ie_result(info, download=True) self.__download_wrapper(self.process_ie_result)(info, download=True)
except (DownloadError, EntryNotInPlaylist, ThrottledDownload): except (DownloadError, EntryNotInPlaylist, ThrottledDownload) as e:
self.to_stderr('\r')
webpage_url = info.get('webpage_url') webpage_url = info.get('webpage_url')
if webpage_url is not None: if webpage_url is not None:
self.report_warning('The info failed to download, trying with "%s"' % webpage_url) self.report_warning(f'The info failed to download: {e}; trying with URL {webpage_url}')
return self.download([webpage_url]) return self.download([webpage_url])
else: else:
raise raise
@ -3566,14 +3573,15 @@ def _write_thumbnails(self, label, info_dict, filename, thumb_filename_base=None
for t in thumbnails[::-1]: for t in thumbnails[::-1]:
thumb_ext = (f'{t["id"]}.' if multiple else '') + determine_ext(t['url'], 'jpg') thumb_ext = (f'{t["id"]}.' if multiple else '') + determine_ext(t['url'], 'jpg')
thumb_display_id = f'{label} thumbnail' + (f' {t["id"]}' if multiple else '') thumb_display_id = f'{label} thumbnail {t["id"]}'
thumb_filename = replace_extension(filename, thumb_ext, info_dict.get('ext')) thumb_filename = replace_extension(filename, thumb_ext, info_dict.get('ext'))
thumb_filename_final = replace_extension(thumb_filename_base, thumb_ext, info_dict.get('ext')) thumb_filename_final = replace_extension(thumb_filename_base, thumb_ext, info_dict.get('ext'))
if not self.params.get('overwrites', True) and os.path.exists(thumb_filename): if not self.params.get('overwrites', True) and os.path.exists(thumb_filename):
ret.append((thumb_filename, thumb_filename_final)) ret.append((thumb_filename, thumb_filename_final))
t['filepath'] = thumb_filename t['filepath'] = thumb_filename
self.to_screen(f'[info] {thumb_display_id.title()} is already present') self.to_screen('[info] %s is already present' % (
thumb_display_id if multiple else f'{label} thumbnail').capitalize())
else: else:
self.to_screen(f'[info] Downloading {thumb_display_id} ...') self.to_screen(f'[info] Downloading {thumb_display_id} ...')
try: try:

View file

@ -795,15 +795,15 @@ def main(argv=None):
_real_main(argv) _real_main(argv)
except DownloadError: except DownloadError:
sys.exit(1) sys.exit(1)
except SameFileError: except SameFileError as e:
sys.exit('ERROR: fixed output name but more than one file to download') sys.exit(f'ERROR: {e}')
except KeyboardInterrupt: except KeyboardInterrupt:
sys.exit('\nERROR: Interrupted by user') sys.exit('\nERROR: Interrupted by user')
except BrokenPipeError: except BrokenPipeError as e:
# https://docs.python.org/3/library/signal.html#note-on-sigpipe # https://docs.python.org/3/library/signal.html#note-on-sigpipe
devnull = os.open(os.devnull, os.O_WRONLY) devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno()) os.dup2(devnull, sys.stdout.fileno())
sys.exit(r'\nERROR: {err}') sys.exit(f'\nERROR: {e}')
__all__ = ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors'] __all__ = ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors']

View file

@ -2337,6 +2337,9 @@ def report_following_redirect(self, new_url):
"""Report information extraction.""" """Report information extraction."""
self._downloader.to_screen('[redirect] Following redirect to %s' % new_url) self._downloader.to_screen('[redirect] Following redirect to %s' % new_url)
def report_detected(self, name):
self._downloader.write_debug(f'Identified a {name}')
def _extract_rss(self, url, video_id, doc): def _extract_rss(self, url, video_id, doc):
playlist_title = doc.find('./channel/title').text playlist_title = doc.find('./channel/title').text
playlist_desc_el = doc.find('./channel/description') playlist_desc_el = doc.find('./channel/description')
@ -2552,6 +2555,7 @@ def _real_extract(self, url):
content_type = head_response.headers.get('Content-Type', '').lower() content_type = head_response.headers.get('Content-Type', '').lower()
m = re.match(r'^(?P<type>audio|video|application(?=/(?:ogg$|(?:vnd\.apple\.|x-)?mpegurl)))/(?P<format_id>[^;\s]+)', content_type) m = re.match(r'^(?P<type>audio|video|application(?=/(?:ogg$|(?:vnd\.apple\.|x-)?mpegurl)))/(?P<format_id>[^;\s]+)', content_type)
if m: if m:
self.report_detected('direct video link')
format_id = compat_str(m.group('format_id')) format_id = compat_str(m.group('format_id'))
subtitles = {} subtitles = {}
if format_id.endswith('mpegurl'): if format_id.endswith('mpegurl'):
@ -2592,6 +2596,7 @@ def _real_extract(self, url):
# Is it an M3U playlist? # Is it an M3U playlist?
if first_bytes.startswith(b'#EXTM3U'): if first_bytes.startswith(b'#EXTM3U'):
self.report_detected('M3U playlist')
info_dict['formats'], info_dict['subtitles'] = self._extract_m3u8_formats_and_subtitles(url, video_id, 'mp4') info_dict['formats'], info_dict['subtitles'] = self._extract_m3u8_formats_and_subtitles(url, video_id, 'mp4')
self._sort_formats(info_dict['formats']) self._sort_formats(info_dict['formats'])
return info_dict return info_dict
@ -2622,16 +2627,20 @@ def _real_extract(self, url):
except compat_xml_parse_error: except compat_xml_parse_error:
doc = compat_etree_fromstring(webpage.encode('utf-8')) doc = compat_etree_fromstring(webpage.encode('utf-8'))
if doc.tag == 'rss': if doc.tag == 'rss':
self.report_detected('RSS feed')
return self._extract_rss(url, video_id, doc) return self._extract_rss(url, video_id, doc)
elif doc.tag == 'SmoothStreamingMedia': elif doc.tag == 'SmoothStreamingMedia':
info_dict['formats'], info_dict['subtitles'] = self._parse_ism_formats_and_subtitles(doc, url) info_dict['formats'], info_dict['subtitles'] = self._parse_ism_formats_and_subtitles(doc, url)
self.report_detected('ISM manifest')
self._sort_formats(info_dict['formats']) self._sort_formats(info_dict['formats'])
return info_dict return info_dict
elif re.match(r'^(?:{[^}]+})?smil$', doc.tag): elif re.match(r'^(?:{[^}]+})?smil$', doc.tag):
smil = self._parse_smil(doc, url, video_id) smil = self._parse_smil(doc, url, video_id)
self.report_detected('SMIL file')
self._sort_formats(smil['formats']) self._sort_formats(smil['formats'])
return smil return smil
elif doc.tag == '{http://xspf.org/ns/0/}playlist': elif doc.tag == '{http://xspf.org/ns/0/}playlist':
self.report_detected('XSPF playlist')
return self.playlist_result( return self.playlist_result(
self._parse_xspf( self._parse_xspf(
doc, video_id, xspf_url=url, doc, video_id, xspf_url=url,
@ -2642,10 +2651,12 @@ def _real_extract(self, url):
doc, doc,
mpd_base_url=full_response.geturl().rpartition('/')[0], mpd_base_url=full_response.geturl().rpartition('/')[0],
mpd_url=url) mpd_url=url)
self.report_detected('DASH manifest')
self._sort_formats(info_dict['formats']) self._sort_formats(info_dict['formats'])
return info_dict return info_dict
elif re.match(r'^{http://ns\.adobe\.com/f4m/[12]\.0}manifest$', doc.tag): elif re.match(r'^{http://ns\.adobe\.com/f4m/[12]\.0}manifest$', doc.tag):
info_dict['formats'] = self._parse_f4m_formats(doc, url, video_id) info_dict['formats'] = self._parse_f4m_formats(doc, url, video_id)
self.report_detected('F4M manifest')
self._sort_formats(info_dict['formats']) self._sort_formats(info_dict['formats'])
return info_dict return info_dict
except compat_xml_parse_error: except compat_xml_parse_error:
@ -2654,6 +2665,7 @@ def _real_extract(self, url):
# Is it a Camtasia project? # Is it a Camtasia project?
camtasia_res = self._extract_camtasia(url, video_id, webpage) camtasia_res = self._extract_camtasia(url, video_id, webpage)
if camtasia_res is not None: if camtasia_res is not None:
self.report_detected('Camtasia video')
return camtasia_res return camtasia_res
# Sometimes embedded video player is hidden behind percent encoding # Sometimes embedded video player is hidden behind percent encoding
@ -2704,6 +2716,8 @@ def _real_extract(self, url):
'age_limit': age_limit, 'age_limit': age_limit,
}) })
self._downloader.write_debug('Looking for video embeds')
# Look for Brightcove Legacy Studio embeds # Look for Brightcove Legacy Studio embeds
bc_urls = BrightcoveLegacyIE._extract_brightcove_urls(webpage) bc_urls = BrightcoveLegacyIE._extract_brightcove_urls(webpage)
if bc_urls: if bc_urls:
@ -3497,6 +3511,7 @@ def _real_extract(self, url):
# Look for HTML5 media # Look for HTML5 media
entries = self._parse_html5_media_entries(url, webpage, video_id, m3u8_id='hls') entries = self._parse_html5_media_entries(url, webpage, video_id, m3u8_id='hls')
if entries: if entries:
self.report_detected('HTML5 media')
if len(entries) == 1: if len(entries) == 1:
entries[0].update({ entries[0].update({
'id': video_id, 'id': video_id,
@ -3516,6 +3531,7 @@ def _real_extract(self, url):
webpage, video_id, transform_source=js_to_json) webpage, video_id, transform_source=js_to_json)
if jwplayer_data: if jwplayer_data:
if isinstance(jwplayer_data.get('playlist'), str): if isinstance(jwplayer_data.get('playlist'), str):
self.report_detected('JW Player playlist')
return { return {
**info_dict, **info_dict,
'_type': 'url', '_type': 'url',
@ -3525,6 +3541,7 @@ def _real_extract(self, url):
try: try:
info = self._parse_jwplayer_data( info = self._parse_jwplayer_data(
jwplayer_data, video_id, require_title=False, base_url=url) jwplayer_data, video_id, require_title=False, base_url=url)
self.report_detected('JW Player data')
return merge_dicts(info, info_dict) return merge_dicts(info, info_dict)
except ExtractorError: except ExtractorError:
# See https://github.com/ytdl-org/youtube-dl/pull/16735 # See https://github.com/ytdl-org/youtube-dl/pull/16735
@ -3574,6 +3591,7 @@ def _real_extract(self, url):
}, },
}) })
if formats or subtitles: if formats or subtitles:
self.report_detected('video.js embed')
self._sort_formats(formats) self._sort_formats(formats)
info_dict['formats'] = formats info_dict['formats'] = formats
info_dict['subtitles'] = subtitles info_dict['subtitles'] = subtitles
@ -3582,6 +3600,7 @@ def _real_extract(self, url):
# Looking for http://schema.org/VideoObject # Looking for http://schema.org/VideoObject
json_ld = self._search_json_ld(webpage, video_id, default={}) json_ld = self._search_json_ld(webpage, video_id, default={})
if json_ld.get('url'): if json_ld.get('url'):
self.report_detected('JSON LD')
return merge_dicts(json_ld, info_dict) return merge_dicts(json_ld, info_dict)
def check_video(vurl): def check_video(vurl):
@ -3598,7 +3617,9 @@ def filter_video(urls):
# Start with something easy: JW Player in SWFObject # Start with something easy: JW Player in SWFObject
found = filter_video(re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage)) found = filter_video(re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage))
if not found: if found:
self.report_detected('JW Player in SFWObject')
else:
# Look for gorilla-vid style embedding # Look for gorilla-vid style embedding
found = filter_video(re.findall(r'''(?sx) found = filter_video(re.findall(r'''(?sx)
(?: (?:
@ -3608,10 +3629,13 @@ def filter_video(urls):
) )
.*? .*?
['"]?file['"]?\s*:\s*["\'](.*?)["\']''', webpage)) ['"]?file['"]?\s*:\s*["\'](.*?)["\']''', webpage))
if found:
self.report_detected('JW Player embed')
if not found: if not found:
# Look for generic KVS player # Look for generic KVS player
found = re.search(r'<script [^>]*?src="https://.+?/kt_player\.js\?v=(?P<ver>(?P<maj_ver>\d+)(\.\d+)+)".*?>', webpage) found = re.search(r'<script [^>]*?src="https://.+?/kt_player\.js\?v=(?P<ver>(?P<maj_ver>\d+)(\.\d+)+)".*?>', webpage)
if found: if found:
self.report_detected('KWS Player')
if found.group('maj_ver') not in ['4', '5']: if found.group('maj_ver') not in ['4', '5']:
self.report_warning('Untested major version (%s) in player engine--Download may fail.' % found.group('ver')) self.report_warning('Untested major version (%s) in player engine--Download may fail.' % found.group('ver'))
flashvars = re.search(r'(?ms)<script.*?>.*?var\s+flashvars\s*=\s*(\{.*?\});.*?</script>', webpage) flashvars = re.search(r'(?ms)<script.*?>.*?var\s+flashvars\s*=\s*(\{.*?\});.*?</script>', webpage)
@ -3657,10 +3681,14 @@ def filter_video(urls):
if not found: if not found:
# Broaden the search a little bit # Broaden the search a little bit
found = filter_video(re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage)) found = filter_video(re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage))
if found:
self.report_detected('video file')
if not found: if not found:
# Broaden the findall a little bit: JWPlayer JS loader # Broaden the findall a little bit: JWPlayer JS loader
found = filter_video(re.findall( found = filter_video(re.findall(
r'[^A-Za-z0-9]?(?:file|video_url)["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage)) r'[^A-Za-z0-9]?(?:file|video_url)["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage))
if found:
self.report_detected('JW Player JS loader')
if not found: if not found:
# Flow player # Flow player
found = filter_video(re.findall(r'''(?xs) found = filter_video(re.findall(r'''(?xs)
@ -3669,10 +3697,14 @@ def filter_video(urls):
\s*\{[^}]+? ["']?clip["']?\s*:\s*\{\s* \s*\{[^}]+? ["']?clip["']?\s*:\s*\{\s*
["']?url["']?\s*:\s*["']([^"']+)["'] ["']?url["']?\s*:\s*["']([^"']+)["']
''', webpage)) ''', webpage))
if found:
self.report_detected('Flow Player')
if not found: if not found:
# Cinerama player # Cinerama player
found = re.findall( found = re.findall(
r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage) r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage)
if found:
self.report_detected('Cinerama player')
if not found: if not found:
# Try to find twitter cards info # Try to find twitter cards info
# twitter:player:stream should be checked before twitter:player since # twitter:player:stream should be checked before twitter:player since
@ -3680,6 +3712,8 @@ def filter_video(urls):
# https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser) # https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
found = filter_video(re.findall( found = filter_video(re.findall(
r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage)) r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
if found:
self.report_detected('Twitter card')
if not found: if not found:
# We look for Open Graph info: # We look for Open Graph info:
# We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am) # We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am)
@ -3687,6 +3721,8 @@ def filter_video(urls):
# We only look in og:video if the MIME type is a video, don't try if it's a Flash player: # We only look in og:video if the MIME type is a video, don't try if it's a Flash player:
if m_video_type is not None: if m_video_type is not None:
found = filter_video(re.findall(r'<meta.*?property="og:(?:video|audio)".*?content="(.*?)"', webpage)) found = filter_video(re.findall(r'<meta.*?property="og:(?:video|audio)".*?content="(.*?)"', webpage))
if found:
self.report_detected('Open Graph video info')
if not found: if not found:
REDIRECT_REGEX = r'[0-9]{,2};\s*(?:URL|url)=\'?([^\'"]+)' REDIRECT_REGEX = r'[0-9]{,2};\s*(?:URL|url)=\'?([^\'"]+)'
found = re.search( found = re.search(
@ -3718,6 +3754,7 @@ def filter_video(urls):
# https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser) # https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
embed_url = self._html_search_meta('twitter:player', webpage, default=None) embed_url = self._html_search_meta('twitter:player', webpage, default=None)
if embed_url and embed_url != url: if embed_url and embed_url != url:
self.report_detected('twitter:player iframe')
return self.url_result(embed_url) return self.url_result(embed_url)
if not found: if not found:

View file

@ -1864,7 +1864,7 @@ def _decrypt_nsig(self, s, video_id, player_url):
self.write_debug(f'Decrypted nsig {s} => {self._player_cache[sig_id]}') self.write_debug(f'Decrypted nsig {s} => {self._player_cache[sig_id]}')
return self._player_cache[sig_id] return self._player_cache[sig_id]
except Exception as e: except Exception as e:
raise ExtractorError(traceback.format_exc(), cause=e) raise ExtractorError(traceback.format_exc(), cause=e, video_id=video_id)
def _extract_n_function_name(self, jscode): def _extract_n_function_name(self, jscode):
return self._search_regex( return self._search_regex(
@ -2496,7 +2496,9 @@ def _extract_formats(self, streaming_data, video_id, player_url, is_live):
fmt_url = update_url_query(fmt_url, { fmt_url = update_url_query(fmt_url, {
'n': self._decrypt_nsig(query['n'][0], video_id, player_url)}) 'n': self._decrypt_nsig(query['n'][0], video_id, player_url)})
except ExtractorError as e: except ExtractorError as e:
self.report_warning(f'nsig extraction failed: You may experience throttling for some formats\n{e}', only_once=True) self.report_warning(
f'nsig extraction failed: You may experience throttling for some formats\n'
f'n = {query["n"][0]} ; player = {player_url}\n{e}', only_once=True)
throttled = True throttled = True
if itag: if itag:

View file

@ -2459,7 +2459,14 @@ def bug_reports_message(before=';'):
class YoutubeDLError(Exception): class YoutubeDLError(Exception):
"""Base exception for YoutubeDL errors.""" """Base exception for YoutubeDL errors."""
pass msg = None
def __init__(self, msg=None):
if msg is not None:
self.msg = msg
elif self.msg is None:
self.msg = type(self).__name__
super().__init__(self.msg)
network_exceptions = [compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error] network_exceptions = [compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error]
@ -2544,7 +2551,7 @@ class EntryNotInPlaylist(YoutubeDLError):
This exception will be thrown by YoutubeDL when a requested entry This exception will be thrown by YoutubeDL when a requested entry
is not found in the playlist info_dict is not found in the playlist info_dict
""" """
pass msg = 'Entry not found in info'
class SameFileError(YoutubeDLError): class SameFileError(YoutubeDLError):
@ -2553,7 +2560,12 @@ class SameFileError(YoutubeDLError):
This exception will be thrown by FileDownloader objects if they detect This exception will be thrown by FileDownloader objects if they detect
multiple files would have to be downloaded to the same file on disk. multiple files would have to be downloaded to the same file on disk.
""" """
pass msg = 'Fixed output name but more than one file to download'
def __init__(self, filename=None):
if filename is not None:
self.msg += f': {filename}'
super().__init__(self.msg)
class PostProcessingError(YoutubeDLError): class PostProcessingError(YoutubeDLError):
@ -2572,11 +2584,6 @@ class DownloadCancelled(YoutubeDLError):
""" Exception raised when the download queue should be interrupted """ """ Exception raised when the download queue should be interrupted """
msg = 'The download was cancelled' msg = 'The download was cancelled'
def __init__(self, msg=None):
if msg is not None:
self.msg = msg
YoutubeDLError.__init__(self, self.msg)
class ExistingVideoReached(DownloadCancelled): class ExistingVideoReached(DownloadCancelled):
""" --break-on-existing triggered """ """ --break-on-existing triggered """
@ -2595,7 +2602,7 @@ class MaxDownloadsReached(DownloadCancelled):
class ThrottledDownload(YoutubeDLError): class ThrottledDownload(YoutubeDLError):
""" Download speed below --throttled-rate. """ """ Download speed below --throttled-rate. """
pass msg = 'The download speed is below throttle limit'
class UnavailableVideoError(YoutubeDLError): class UnavailableVideoError(YoutubeDLError):
@ -2604,7 +2611,12 @@ class UnavailableVideoError(YoutubeDLError):
This exception will be thrown when a video is requested This exception will be thrown when a video is requested
in a format that is not available for that video. in a format that is not available for that video.
""" """
pass msg = 'Unable to download video'
def __init__(self, err=None):
if err is not None:
self.msg += f': {err}'
super().__init__(self.msg)
class ContentTooShortError(YoutubeDLError): class ContentTooShortError(YoutubeDLError):