diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index d7cbdd047..7cda3fc8f 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -191,6 +191,9 @@ class YoutubeDL(object): simulate: Do not download the video files. format: Video format code. see "FORMAT SELECTION" for more details. allow_unplayable_formats: Allow unplayable formats to be extracted and downloaded. + ignore_no_formats_error: Ignore "No video formats" error. Usefull for + extracting metadata even if the video is not actually + available for download (experimental) format_sort: How to sort the video formats. see "Sorting Formats" for more details. format_sort_force: Force the given format_sort. see "Sorting Formats" @@ -1884,7 +1887,10 @@ def sanitize_numeric_fields(info): formats = info_dict['formats'] if not formats: - raise ExtractorError('No video formats found!') + if not self.params.get('ignore_no_formats_error'): + raise ExtractorError('No video formats found!') + else: + self.report_warning('No video formats found!') def is_wellformed(f): url = f.get('url') @@ -1948,13 +1954,15 @@ def is_wellformed(f): # TODO Central sorting goes here - if formats[0] is not info_dict: + if formats and formats[0] is not info_dict: # only set the 'formats' fields if the original info_dict list them # otherwise we end up with a circular reference, the first (and unique) # element in the 'formats' field in info_dict is info_dict itself, # which can't be exported to json info_dict['formats'] = formats if self.params.get('listformats'): + if not info_dict.get('formats'): + raise ExtractorError('No video formats found', expected=True) self.list_formats(info_dict) return @@ -1994,19 +2002,25 @@ def is_wellformed(f): formats_to_download = list(format_selector(ctx)) if not formats_to_download: - raise ExtractorError('requested format not available', - expected=True) - - if download: - self.to_screen('[info] Downloading format(s) %s' % ", ".join([f['format_id'] for f in formats_to_download])) + if not self.params.get('ignore_no_formats_error'): + raise ExtractorError('Requested format is not available', expected=True) + else: + self.report_warning('Requested format is not available') + elif download: + self.to_screen( + '[info] %s: Downloading format(s) %s' + % (info_dict['id'], ", ".join([f['format_id'] for f in formats_to_download]))) if len(formats_to_download) > 1: - self.to_screen('[info] %s: downloading video in %s formats' % (info_dict['id'], len(formats_to_download))) - for format in formats_to_download: + self.to_screen( + '[info] %s: Downloading video in %s formats' + % (info_dict['id'], len(formats_to_download))) + for fmt in formats_to_download: new_info = dict(info_dict) - new_info.update(format) + new_info.update(fmt) self.process_info(new_info) # We update the info dict with the best quality format (backwards compatibility) - info_dict.update(formats_to_download[-1]) + if formats_to_download: + info_dict.update(formats_to_download[-1]) return info_dict def process_subtitles(self, video_id, normal_subtitles, automatic_captions): diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 90a3116ea..4f0684236 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -466,6 +466,7 @@ def report_args_compat(arg, name): 'skip_download': opts.skip_download, 'format': opts.format, 'allow_unplayable_formats': opts.allow_unplayable_formats, + 'ignore_no_formats_error': opts.ignore_no_formats_error, 'format_sort': opts.format_sort, 'format_sort_force': opts.format_sort_force, 'allow_multiple_video_streams': opts.allow_multiple_video_streams, diff --git a/yt_dlp/extractor/afreecatv.py b/yt_dlp/extractor/afreecatv.py index af0587ae6..016a4d24a 100644 --- a/yt_dlp/extractor/afreecatv.py +++ b/yt_dlp/extractor/afreecatv.py @@ -323,7 +323,7 @@ def _real_extract(self, url): 'url': file_url, 'format_id': 'http', }] - if not formats: + if not formats and not self._downloader.params.get('ignore_no_formats'): continue self._sort_formats(formats) file_info = common_entry.copy() diff --git a/yt_dlp/extractor/ard.py b/yt_dlp/extractor/ard.py index 294da7c51..4d90be714 100644 --- a/yt_dlp/extractor/ard.py +++ b/yt_dlp/extractor/ard.py @@ -36,12 +36,12 @@ def _parse_media_info(self, media_info, video_id, fsk): if not formats: if fsk: - raise ExtractorError( + self.raise_no_formats( 'This video is only available after 20:00', expected=True) elif media_info.get('_geoblocked'): self.raise_geo_restricted( 'This video is not available due to geoblocking', - countries=self._GEO_COUNTRIES) + countries=self._GEO_COUNTRIES, metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/bbc.py b/yt_dlp/extractor/bbc.py index e8d000bbb..333796c80 100644 --- a/yt_dlp/extractor/bbc.py +++ b/yt_dlp/extractor/bbc.py @@ -1242,7 +1242,7 @@ def extract_all(pattern): entries = [] for num, media_meta in enumerate(medias, start=1): formats, subtitles = self._extract_from_media_meta(media_meta, playlist_id) - if not formats: + if not formats and not self._downloader.params.get('ignore_no_formats'): continue self._sort_formats(formats) diff --git a/yt_dlp/extractor/brightcove.py b/yt_dlp/extractor/brightcove.py index 8b29ca993..d2fd10064 100644 --- a/yt_dlp/extractor/brightcove.py +++ b/yt_dlp/extractor/brightcove.py @@ -545,9 +545,9 @@ def build_format_id(kind): errors = json_data.get('errors') if errors: error = errors[0] - raise ExtractorError( + self.raise_no_formats( error.get('message') or error.get('error_subcode') or error['error_code'], expected=True) - if (not self._downloader.params.get('allow_unplayable_formats') + elif (not self._downloader.params.get('allow_unplayable_formats') and sources and num_drm_sources == len(sources)): raise ExtractorError('This video is DRM protected.', expected=True) diff --git a/yt_dlp/extractor/channel9.py b/yt_dlp/extractor/channel9.py index 09cacf6d3..258e96ca6 100644 --- a/yt_dlp/extractor/channel9.py +++ b/yt_dlp/extractor/channel9.py @@ -5,7 +5,6 @@ from .common import InfoExtractor from ..utils import ( clean_html, - ExtractorError, int_or_none, parse_iso8601, qualities, @@ -187,14 +186,13 @@ def quality(quality_id, format_url): 'quality': quality(q, q_url), }) - self._sort_formats(formats) - slides = content_data.get('Slides') zip_file = content_data.get('ZipFile') if not formats and not slides and not zip_file: - raise ExtractorError( + self.raise_no_formats( 'None of recording, slides or zip are available for %s' % content_path) + self._sort_formats(formats) subtitles = {} for caption in content_data.get('Captions', []): diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 40ea9339f..9ead6db2d 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -968,15 +968,27 @@ def report_login(self): """Report attempt to log in.""" self.to_screen('Logging in') - @staticmethod - def raise_login_required(msg='This video is only available for registered users'): + def raise_login_required( + self, msg='This video is only available for registered users', metadata_available=False): + if metadata_available and self._downloader.params.get('ignore_no_formats_error'): + self.report_warning(msg) raise ExtractorError( - '%s. Use --username and --password or --netrc to provide account credentials.' % msg, + '%s. Use --cookies, --username and --password or --netrc to provide account credentials' % msg, expected=True) - @staticmethod - def raise_geo_restricted(msg='This video is not available from your location due to geo restriction', countries=None): - raise GeoRestrictedError(msg, countries=countries) + def raise_geo_restricted( + self, msg='This video is not available from your location due to geo restriction', + countries=None, metadata_available=False): + if metadata_available and self._downloader.params.get('ignore_no_formats_error'): + self.report_warning(msg) + else: + raise GeoRestrictedError(msg, countries=countries) + + def raise_no_formats(self, msg, expected=False, video_id=None): + if expected and self._downloader.params.get('ignore_no_formats_error'): + self.report_warning(msg, video_id) + else: + raise ExtractorError(msg, expected=expected, video_id=video_id) # Methods for following #608 @staticmethod @@ -1670,6 +1682,8 @@ def calculate_preference(self, format): def _sort_formats(self, formats, field_preference=[]): if not formats: + if self._downloader.params.get('ignore_no_formats_error'): + return raise ExtractorError('No video formats found') format_sort = self.FormatSort() # params and to_screen are taken from the downloader format_sort.evaluate_params(self._downloader.params, field_preference) diff --git a/yt_dlp/extractor/corus.py b/yt_dlp/extractor/corus.py index e11aadf14..de61f42e4 100644 --- a/yt_dlp/extractor/corus.py +++ b/yt_dlp/extractor/corus.py @@ -131,7 +131,7 @@ def _real_extract(self, url): formats.extend(self._parse_smil_formats( smil, smil_url, video_id, namespace)) if not formats and video.get('drm'): - raise ExtractorError('This video is DRM protected.', expected=True) + self.raise_no_formats('This video is DRM protected.', expected=True) self._sort_formats(formats) subtitles = {} diff --git a/yt_dlp/extractor/disney.py b/yt_dlp/extractor/disney.py index 0eee82fd6..e1ae62ac6 100644 --- a/yt_dlp/extractor/disney.py +++ b/yt_dlp/extractor/disney.py @@ -9,7 +9,6 @@ unified_strdate, compat_str, determine_ext, - ExtractorError, update_url_query, ) @@ -140,7 +139,7 @@ def _real_extract(self, url): 'vcodec': 'none' if (width == 0 and height == 0) else None, }) if not formats and video_data.get('expired'): - raise ExtractorError( + self.raise_no_formats( '%s said: %s' % (self.IE_NAME, page_data['translations']['video_expired']), expected=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/facebook.py b/yt_dlp/extractor/facebook.py index 7a76dbb22..b68b90bd3 100644 --- a/yt_dlp/extractor/facebook.py +++ b/yt_dlp/extractor/facebook.py @@ -625,8 +625,6 @@ def parse_attachment(attachment, key='media'): subtitles_src = f[0].get('subtitles_src') if subtitles_src: subtitles.setdefault('en', []).append({'url': subtitles_src}) - if not formats: - raise ExtractorError('Cannot find video formats') process_formats(formats) diff --git a/yt_dlp/extractor/googledrive.py b/yt_dlp/extractor/googledrive.py index 4eefcb70c..7b5bf280f 100644 --- a/yt_dlp/extractor/googledrive.py +++ b/yt_dlp/extractor/googledrive.py @@ -253,7 +253,7 @@ def add_source_format(urlh): or 'unable to extract confirmation code') if not formats and reason: - raise ExtractorError(reason, expected=True) + self.raise_no_formats(reason, expected=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/hotstar.py b/yt_dlp/extractor/hotstar.py index e2e923539..22cccf2b2 100644 --- a/yt_dlp/extractor/hotstar.py +++ b/yt_dlp/extractor/hotstar.py @@ -184,7 +184,7 @@ def _real_extract(self, url): geo_restricted = True continue if not formats and geo_restricted: - self.raise_geo_restricted(countries=['IN']) + self.raise_geo_restricted(countries=['IN'], metadata_available=True) self._sort_formats(formats) for f in formats: diff --git a/yt_dlp/extractor/iprima.py b/yt_dlp/extractor/iprima.py index 648ae6741..28e660972 100644 --- a/yt_dlp/extractor/iprima.py +++ b/yt_dlp/extractor/iprima.py @@ -136,7 +136,7 @@ def extract_formats(format_url, format_key=None, lang=None): extract_formats(src) if not formats and '>GEO_IP_NOT_ALLOWED<' in playerpage: - self.raise_geo_restricted(countries=['CZ']) + self.raise_geo_restricted(countries=['CZ'], metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/keezmovies.py b/yt_dlp/extractor/keezmovies.py index c3eb74c17..cfdd0eb8e 100644 --- a/yt_dlp/extractor/keezmovies.py +++ b/yt_dlp/extractor/keezmovies.py @@ -101,7 +101,7 @@ def extract_format(format_url, height=None): if not formats: if 'title="This video is no longer available"' in webpage: - raise ExtractorError( + self.raise_no_formats( 'Video %s is no longer available' % video_id, expected=True) try: diff --git a/yt_dlp/extractor/line.py b/yt_dlp/extractor/line.py index 2526daa77..41ac8d422 100644 --- a/yt_dlp/extractor/line.py +++ b/yt_dlp/extractor/line.py @@ -6,7 +6,6 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - ExtractorError, int_or_none, js_to_json, str_or_none, @@ -77,7 +76,7 @@ def _real_extract(self, url): self._sort_formats(formats) - if not formats[0].get('width'): + if formats and not formats[0].get('width'): formats[0]['vcodec'] = 'none' title = self._og_search_title(webpage) @@ -183,7 +182,7 @@ def _real_extract(self, url): if not formats: archive_status = item.get('archiveStatus') if archive_status != 'ARCHIVED': - raise ExtractorError('this video has been ' + archive_status.lower(), expected=True) + self.raise_no_formats('this video has been ' + archive_status.lower(), expected=True) self._sort_formats(formats) info['formats'] = formats return info diff --git a/yt_dlp/extractor/medaltv.py b/yt_dlp/extractor/medaltv.py index 1603b55f6..4bca6f053 100644 --- a/yt_dlp/extractor/medaltv.py +++ b/yt_dlp/extractor/medaltv.py @@ -97,11 +97,11 @@ def add_item(container, item_url, height, id_key='format_id', item_id=None): error = clip.get('error') if not formats and error: if error == 404: - raise ExtractorError( + self.raise_no_formats( 'That clip does not exist.', expected=True, video_id=video_id) else: - raise ExtractorError( + self.raise_no_formats( 'An unknown error occurred ({0}).'.format(error), video_id=video_id) diff --git a/yt_dlp/extractor/mixcloud.py b/yt_dlp/extractor/mixcloud.py index 69319857d..b8ccd0ab4 100644 --- a/yt_dlp/extractor/mixcloud.py +++ b/yt_dlp/extractor/mixcloud.py @@ -157,7 +157,7 @@ def _real_extract(self, url): }) if not formats and cloudcast.get('isExclusive'): - self.raise_login_required() + self.raise_login_required(metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/npo.py b/yt_dlp/extractor/npo.py index ca6dbfc81..573a89092 100644 --- a/yt_dlp/extractor/npo.py +++ b/yt_dlp/extractor/npo.py @@ -247,7 +247,7 @@ def _get_info(self, url, video_id): if not formats: if not self._downloader.params.get('allow_unplayable_formats') and drm: - raise ExtractorError('This video is DRM protected.', expected=True) + self.raise_no_formats('This video is DRM protected.', expected=True) return self._sort_formats(formats) diff --git a/yt_dlp/extractor/odnoklassniki.py b/yt_dlp/extractor/odnoklassniki.py index 7ed9fac55..0ce2e3776 100644 --- a/yt_dlp/extractor/odnoklassniki.py +++ b/yt_dlp/extractor/odnoklassniki.py @@ -260,7 +260,7 @@ def _real_extract(self, url): if not formats: payment_info = metadata.get('paymentInfo') if payment_info: - raise ExtractorError('This video is paid, subscribe to download it', expected=True) + self.raise_no_formats('This video is paid, subscribe to download it', expected=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/ooyala.py b/yt_dlp/extractor/ooyala.py index eb957b8fe..7204dfecd 100644 --- a/yt_dlp/extractor/ooyala.py +++ b/yt_dlp/extractor/ooyala.py @@ -10,7 +10,6 @@ ) from ..utils import ( determine_ext, - ExtractorError, float_or_none, int_or_none, try_get, @@ -85,7 +84,7 @@ def _extract(self, content_tree_url, video_id, domain=None, supportedformats=Non 'fps': float_or_none(stream.get('framerate')), }) if not formats and not auth_data.get('authorized'): - raise ExtractorError('%s said: %s' % ( + self.raise_no_formats('%s said: %s' % ( self.IE_NAME, auth_data['message']), expected=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/philharmoniedeparis.py b/yt_dlp/extractor/philharmoniedeparis.py index 03da64b11..9545adebf 100644 --- a/yt_dlp/extractor/philharmoniedeparis.py +++ b/yt_dlp/extractor/philharmoniedeparis.py @@ -79,7 +79,7 @@ def extract_entry(source): formats.extend(self._extract_m3u8_formats( m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False)) - if not formats: + if not formats and not self._downloader.params.get('ignore_no_formats'): return self._sort_formats(formats) return { diff --git a/yt_dlp/extractor/rai.py b/yt_dlp/extractor/rai.py index 6c2191bb3..05cf84ba5 100644 --- a/yt_dlp/extractor/rai.py +++ b/yt_dlp/extractor/rai.py @@ -94,7 +94,7 @@ def _extract_relinker_info(self, relinker_url, video_id): }) if not formats and geoprotection is True: - self.raise_geo_restricted(countries=self._GEO_COUNTRIES) + self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True) return dict((k, v) for k, v in { 'is_live': is_live, diff --git a/yt_dlp/extractor/ruutu.py b/yt_dlp/extractor/ruutu.py index f9f30e3dd..5030c01cd 100644 --- a/yt_dlp/extractor/ruutu.py +++ b/yt_dlp/extractor/ruutu.py @@ -202,7 +202,7 @@ def pv(name): if not formats: if (not self._downloader.params.get('allow_unplayable_formats') and xpath_text(video_xml, './Clip/DRM', default=None)): - raise ExtractorError('This video is DRM protected.', expected=True) + self.raise_no_formats('This video is DRM protected.', expected=True) ns_st_cds = pv('ns_st_cds') if ns_st_cds != 'free': raise ExtractorError('This video is %s.' % ns_st_cds, expected=True) diff --git a/yt_dlp/extractor/soundcloud.py b/yt_dlp/extractor/soundcloud.py index 103b23bf7..35d34af02 100644 --- a/yt_dlp/extractor/soundcloud.py +++ b/yt_dlp/extractor/soundcloud.py @@ -498,7 +498,7 @@ def add_format(f, protocol, is_preview=False): f['vcodec'] = 'none' if not formats and info.get('policy') == 'BLOCK': - self.raise_geo_restricted() + self.raise_geo_restricted(metadata_available=True) self._sort_formats(formats) user = info.get('user') or {} diff --git a/yt_dlp/extractor/sportdeutschland.py b/yt_dlp/extractor/sportdeutschland.py index 3e497a939..e70d1a477 100644 --- a/yt_dlp/extractor/sportdeutschland.py +++ b/yt_dlp/extractor/sportdeutschland.py @@ -77,7 +77,7 @@ def entries(): continue formats = self._extract_m3u8_formats( video_url.replace('.smil', '.m3u8'), video_id, 'mp4', fatal=False) - if not formats: + if not formats and not self._downloader.params.get('ignore_no_formats'): continue yield { 'id': video_id, diff --git a/yt_dlp/extractor/steam.py b/yt_dlp/extractor/steam.py index a6a191ceb..c70bdefe2 100644 --- a/yt_dlp/extractor/steam.py +++ b/yt_dlp/extractor/steam.py @@ -139,7 +139,7 @@ def _real_extract(self, url): 'format_id': ext + quality, 'url': video_url, }) - if not formats: + if not formats and not self._downloader.params.get('ignore_no_formats'): continue entry['formats'] = formats entries.append(entry) diff --git a/yt_dlp/extractor/svt.py b/yt_dlp/extractor/svt.py index aba9bb447..5b377ea83 100644 --- a/yt_dlp/extractor/svt.py +++ b/yt_dlp/extractor/svt.py @@ -49,7 +49,7 @@ def _extract_video(self, video_info, video_id): if not formats and rights.get('geoBlockedSweden'): self.raise_geo_restricted( 'This video is only available in Sweden', - countries=self._GEO_COUNTRIES) + countries=self._GEO_COUNTRIES, metadata_available=True) self._sort_formats(formats) subtitles = {} diff --git a/yt_dlp/extractor/toggle.py b/yt_dlp/extractor/toggle.py index 1e2a2d819..fe1841081 100644 --- a/yt_dlp/extractor/toggle.py +++ b/yt_dlp/extractor/toggle.py @@ -7,7 +7,6 @@ from .common import InfoExtractor from ..utils import ( determine_ext, - ExtractorError, float_or_none, int_or_none, parse_iso8601, @@ -156,10 +155,9 @@ def _real_extract(self, url): for meta in (info.get('Metas') or []): if (not self._downloader.params.get('allow_unplayable_formats') and meta.get('Key') == 'Encryption' and meta.get('Value') == '1'): - raise ExtractorError( + self.raise_no_formats( 'This video is DRM protected.', expected=True) - # Most likely because geo-blocked - raise ExtractorError('No downloadable videos found', expected=True) + # Most likely because geo-blocked if no formats and no DRM self._sort_formats(formats) thumbnails = [] diff --git a/yt_dlp/extractor/tv2.py b/yt_dlp/extractor/tv2.py index 334b7d540..f3480de56 100644 --- a/yt_dlp/extractor/tv2.py +++ b/yt_dlp/extractor/tv2.py @@ -103,7 +103,7 @@ def _real_extract(self, url): 'filesize': int_or_none(item.get('fileSize')), }) if not formats and data.get('drmProtected'): - raise ExtractorError('This video is DRM protected.', expected=True) + self.raise_no_formats('This video is DRM protected.', expected=True) self._sort_formats(formats) thumbnails = [{ diff --git a/yt_dlp/extractor/tv4.py b/yt_dlp/extractor/tv4.py index b73bab9a8..b8ad4fafc 100644 --- a/yt_dlp/extractor/tv4.py +++ b/yt_dlp/extractor/tv4.py @@ -107,7 +107,7 @@ def _real_extract(self, url): video_id, ism_id='mss', fatal=False)) if not formats and info.get('is_geo_restricted'): - self.raise_geo_restricted(countries=self._GEO_COUNTRIES) + self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/tvplay.py b/yt_dlp/extractor/tvplay.py index 0d858c025..739c61cdd 100644 --- a/yt_dlp/extractor/tvplay.py +++ b/yt_dlp/extractor/tvplay.py @@ -298,7 +298,8 @@ def _real_extract(self, url): if not formats and video.get('is_geo_blocked'): self.raise_geo_restricted( - 'This content might not be available in your country due to copyright reasons') + 'This content might not be available in your country due to copyright reasons', + metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/videomore.py b/yt_dlp/extractor/videomore.py index e0c10aa5b..05ae74e33 100644 --- a/yt_dlp/extractor/videomore.py +++ b/yt_dlp/extractor/videomore.py @@ -10,7 +10,6 @@ compat_urllib_parse_urlparse, ) from ..utils import ( - ExtractorError, int_or_none, ) @@ -193,8 +192,8 @@ def _real_extract(self, url): error = item.get('error') if error: if error in ('Данное видео недоступно для просмотра на территории этой страны', 'Данное видео доступно для просмотра только на территории России'): - self.raise_geo_restricted(countries=['RU']) - raise ExtractorError(error, expected=True) + self.raise_geo_restricted(countries=['RU'], metadata_available=True) + self.raise_no_formats(error, expected=True) self._sort_formats(formats) return { diff --git a/yt_dlp/extractor/vube.py b/yt_dlp/extractor/vube.py index 8ce3a6b81..c92b47e63 100644 --- a/yt_dlp/extractor/vube.py +++ b/yt_dlp/extractor/vube.py @@ -8,7 +8,6 @@ ) from ..utils import ( int_or_none, - ExtractorError, ) @@ -125,13 +124,13 @@ def _real_extract(self, url): }) formats.append(fmt) - self._sort_formats(formats) - if not formats and video.get('vst') == 'dmca': - raise ExtractorError( + self.raise_no_formats( 'This video has been removed in response to a complaint received under the US Digital Millennium Copyright Act.', expected=True) + self._sort_formats(formats) + title = video['title'] description = video.get('description') thumbnail = self._proto_relative_url(video.get('thumbnail_src'), scheme='http:') diff --git a/yt_dlp/extractor/wat.py b/yt_dlp/extractor/wat.py index f1bccc2d6..05dcc1f17 100644 --- a/yt_dlp/extractor/wat.py +++ b/yt_dlp/extractor/wat.py @@ -87,7 +87,7 @@ def extract_formats(manifest_urls): extract_formats({delivery.get('format'): delivery.get('url')}) if not formats: if delivery.get('drm'): - raise ExtractorError('This video is DRM protected.', expected=True) + self.raise_no_formats('This video is DRM protected.', expected=True) manifest_urls = self._download_json( 'http://www.wat.tv/get/webhtml/' + video_id, video_id, fatal=False) if manifest_urls: diff --git a/yt_dlp/extractor/yahoo.py b/yt_dlp/extractor/yahoo.py index a17b10d6e..ecf2f5f48 100644 --- a/yt_dlp/extractor/yahoo.py +++ b/yt_dlp/extractor/yahoo.py @@ -239,7 +239,7 @@ def _extract_yahoo_video(self, video_id, country): 'm3u8_native', m3u8_id='hls', fatal=False)) if not formats and msg == 'geo restricted': - self.raise_geo_restricted() + self.raise_geo_restricted(metadata_available=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/youtube.py b/yt_dlp/extractor/youtube.py index 6d5ef0193..6c1a5b881 100644 --- a/yt_dlp/extractor/youtube.py +++ b/yt_dlp/extractor/youtube.py @@ -2050,7 +2050,7 @@ def feed_entry(name): if not formats: if not self._downloader.params.get('allow_unplayable_formats') and streaming_data.get('licenseInfos'): - raise ExtractorError( + self.raise_no_formats( 'This video is DRM protected.', expected=True) pemr = try_get( playability_status, @@ -2065,11 +2065,10 @@ def feed_entry(name): if not countries: regions_allowed = search_meta('regionsAllowed') countries = regions_allowed.split(',') if regions_allowed else None - self.raise_geo_restricted( - subreason, countries) + self.raise_geo_restricted(subreason, countries, metadata_available=True) reason += '\n' + subreason if reason: - raise ExtractorError(reason, expected=True) + self.raise_no_formats(reason, expected=True) self._sort_formats(formats) diff --git a/yt_dlp/extractor/zingmp3.py b/yt_dlp/extractor/zingmp3.py index 207c04f5e..a3edc158f 100644 --- a/yt_dlp/extractor/zingmp3.py +++ b/yt_dlp/extractor/zingmp3.py @@ -3,7 +3,6 @@ from .common import InfoExtractor from ..utils import ( - ExtractorError, int_or_none, ) @@ -48,8 +47,8 @@ def _extract_item(self, item, fatal): return msg = item['msg'] if msg == 'Sorry, this content is not available in your country.': - self.raise_geo_restricted(countries=self._GEO_COUNTRIES) - raise ExtractorError(msg, expected=True) + self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True) + self.raise_no_formats(msg, expected=True) self._sort_formats(formats) subtitles = None diff --git a/yt_dlp/options.py b/yt_dlp/options.py index cced9fb89..fef1e4b15 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -749,6 +749,16 @@ def _dict_from_multiple_values_options_callback( '-s', '--simulate', action='store_true', dest='simulate', default=False, help='Do not download the video and do not write anything to disk') + verbosity.add_option( + '--ignore-no-formats-error', + action='store_true', dest='ignore_no_formats_error', default=False, + help=( + 'Ignore "No video formats" error. Usefull for extracting metadata ' + 'even if the video is not actually available for download (experimental)')) + verbosity.add_option( + '--no-ignore-no-formats-error', + action='store_false', dest='ignore_no_formats_error', + help='Throw error when no downloadable video formats are found (default)') verbosity.add_option( '--skip-download', '--no-download', action='store_true', dest='skip_download', default=False,