diff --git a/README.md b/README.md index 1db4ed2a5d..3605e3978b 100644 --- a/README.md +++ b/README.md @@ -2319,7 +2319,7 @@ These options are not intended to be used by the end-user --test Download only part of video for testing extractors --load-pages Load pages dumped by --write-pages --youtube-print-sig-code For testing youtube signatures - --allow-unplayable-formats List unplayable formats also + --allow-unplayable-formats List unplayable formats also. Implies `--simulate` and `--list-formats`. --no-allow-unplayable-formats Default #### Old aliases diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 65b72e026c..b215fc9fef 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -240,7 +240,7 @@ class YoutubeDL: You can also pass a function. The function takes 'ctx' as argument and returns the formats to download. See "build_format_selector" for an implementation - allow_unplayable_formats: Allow unplayable formats to be extracted and downloaded. + allow_unplayable_formats: Allow unplayable formats to be extracted. ignore_no_formats_error: Ignore "No video formats" error. Usefull for extracting metadata even if the video is not actually available for download (experimental) @@ -683,11 +683,14 @@ class YoutubeDL: self.deprecated_feature(system_deprecation.replace('\n', '\n ')) if self.params.get('allow_unplayable_formats'): + from . import _IN_CLI + + switch = '--allow-unplayable-formats' if _IN_CLI else 'allow_unplayable_formats' self.report_warning( - f'You have asked for {self._format_err("UNPLAYABLE", self.Styles.EMPHASIS)} formats to be listed/downloaded. ' - 'This is a developer option intended for debugging. \n' - ' If you experience any issues while using this option, ' - f'{self._format_err("DO NOT", self.Styles.ERROR)} open a bug report') + f'{switch} is a {self._format_err("developer option", self.Styles.EMPHASIS)} intended for {self._format_err("debugging", self.Styles.EMPHASIS)}. \n' + f' If you experience issues {self._format_err("DO NOT", self.Styles.ERROR)} open a bug report.') + self.params['listformats'] = True + self.params['simulate'] = 'list_only' if self.params.get('bidi_workaround', False): try: @@ -2811,6 +2814,7 @@ class YoutubeDL: info_dict['_has_drm'] = any( # or None ensures --clean-infojson removes it f.get('has_drm') and f['has_drm'] != 'maybe' for f in formats) or None if not self.params.get('allow_unplayable_formats'): + # Allow bypassing flaky `has_drm` detection formats = [f for f in formats if not f.get('has_drm') or f['has_drm'] == 'maybe'] if formats and all(f.get('acodec') == f.get('vcodec') == 'none' for f in formats): @@ -3426,12 +3430,7 @@ class YoutubeDL: success, real_download = self.dl(temp_filename, info_dict) info_dict['__real_download'] = real_download else: - if self.params.get('allow_unplayable_formats'): - self.report_warning( - 'You have requested merging of multiple formats ' - 'while also allowing unplayable formats to be downloaded. ' - 'The formats won\'t be merged to prevent data corruption.') - elif not merger.available: + if not merger.available: msg = 'You have requested merging of multiple formats but ffmpeg is not installed' if not self.params.get('ignoreerrors'): self.report_error(f'{msg}. Aborting due to --abort-on-error') @@ -3462,7 +3461,7 @@ class YoutubeDL: info_dict['__real_download'] = info_dict['__real_download'] or real_download success = success and partial_success - if downloaded and merger.available and not self.params.get('allow_unplayable_formats'): + if downloaded and merger.available: info_dict['__postprocessors'].append(merger) info_dict['__files_to_merge'] = downloaded # Even if there were no downloads, it is being merged only now diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 20111175b1..63625115f9 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -504,8 +504,7 @@ def validate_options(opts): opts.postprocessor_args['default'] = opts.postprocessor_args.pop('default-compat') opts.postprocessor_args.setdefault('sponskrub', []) - def report_conflict(arg1, opt1, arg2='--allow-unplayable-formats', opt2='allow_unplayable_formats', - val1=NO_DEFAULT, val2=NO_DEFAULT, default=False): + def report_conflict(arg1, opt1, arg2, opt2, val1=NO_DEFAULT, val2=NO_DEFAULT, default=False): if val2 is NO_DEFAULT: val2 = getattr(opts, opt2) if not val2: @@ -533,21 +532,6 @@ def validate_options(opts): report_conflict('--sponskrub-cut', 'sponskrub_cut', '--split-chapter', 'split_chapters', val1=opts.sponskrub and opts.sponskrub_cut) - # Conflicts with --allow-unplayable-formats - report_conflict('--embed-metadata', 'addmetadata') - report_conflict('--embed-chapters', 'addchapters') - report_conflict('--embed-info-json', 'embed_infojson') - report_conflict('--embed-subs', 'embedsubtitles') - report_conflict('--embed-thumbnail', 'embedthumbnail') - report_conflict('--extract-audio', 'extractaudio') - report_conflict('--fixup', 'fixup', val1=opts.fixup not in (None, 'never', 'ignore'), default='never') - report_conflict('--recode-video', 'recodevideo') - report_conflict('--remove-chapters', 'remove_chapters', default=[]) - report_conflict('--remux-video', 'remuxvideo') - report_conflict('--sponskrub', 'sponskrub') - report_conflict('--sponsorblock-remove', 'sponsorblock_remove', default=set()) - report_conflict('--xattrs', 'xattrs') - # Fully deprecated options def report_deprecation(val, old, new=None): if not val: diff --git a/yt_dlp/downloader/external.py b/yt_dlp/downloader/external.py index 7f6b5b45cc..7afee9727e 100644 --- a/yt_dlp/downloader/external.py +++ b/yt_dlp/downloader/external.py @@ -470,7 +470,6 @@ class FFmpegFD(ExternalFD): return ( info_dict.get('requested_formats') and info_dict.get('protocol') - and not params.get('allow_unplayable_formats') and 'no-direct-merge' not in params.get('compat_opts', []) and cls.can_download(info_dict)) diff --git a/yt_dlp/downloader/f4m.py b/yt_dlp/downloader/f4m.py index 22d0ebd265..9d3fb4a46d 100644 --- a/yt_dlp/downloader/f4m.py +++ b/yt_dlp/downloader/f4m.py @@ -256,14 +256,13 @@ class F4mFD(FragmentFD): media = doc.findall(_add_ns('media')) if not media: self.report_error('No media found') - if not self.params.get('allow_unplayable_formats'): - for e in (doc.findall(_add_ns('drmAdditionalHeader')) - + doc.findall(_add_ns('drmAdditionalHeaderSet'))): - # If id attribute is missing it's valid for all media nodes - # without drmAdditionalHeaderId or drmAdditionalHeaderSetId attribute - if 'id' not in e.attrib: - self.report_error('Missing ID in f4m DRM') - media = remove_encrypted_media(media) + for e in (doc.findall(_add_ns('drmAdditionalHeader')) + + doc.findall(_add_ns('drmAdditionalHeaderSet'))): + # If id attribute is missing it's valid for all media nodes + # without drmAdditionalHeaderId or drmAdditionalHeaderSetId attribute + if 'id' not in e.attrib: + self.report_error('Missing ID in f4m DRM') + media = remove_encrypted_media(media) if not media: self.report_error('Unsupported DRM') return media diff --git a/yt_dlp/downloader/hls.py b/yt_dlp/downloader/hls.py index da2574da72..0573730896 100644 --- a/yt_dlp/downloader/hls.py +++ b/yt_dlp/downloader/hls.py @@ -15,6 +15,7 @@ from ..utils import ( traverse_obj, update_url_query, urljoin, + deprecation_warning, ) @@ -38,6 +39,8 @@ class HlsFD(FragmentFD): @classmethod def can_download(cls, manifest, info_dict, allow_unplayable_formats=False): + if allow_unplayable_formats: + deprecation_warning('allow_unplayable_formats is not supported', stacklevel=1) UNSUPPORTED_FEATURES = [ # r'#EXT-X-BYTERANGE', # playlists composed of byte ranges of media files [2] @@ -57,17 +60,15 @@ class HlsFD(FragmentFD): # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5 # 5. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.5 ] - if not allow_unplayable_formats: - UNSUPPORTED_FEATURES += [ - r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)', # encrypted streams [1], but not necessarily DRM - ] + UNSUPPORTED_FEATURES += [ + r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)', # encrypted streams [1], but not necessarily DRM + ] def check_results(): yield not info_dict.get('is_live') for feature in UNSUPPORTED_FEATURES: yield not re.search(feature, manifest) - if not allow_unplayable_formats: - yield not cls._has_drm(manifest) + yield not cls._has_drm(manifest) return all(check_results()) def real_download(self, filename, info_dict): @@ -78,7 +79,7 @@ class HlsFD(FragmentFD): man_url = urlh.url s = urlh.read().decode('utf-8', 'ignore') - can_download, message = self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')), None + can_download, message = self.can_download(s, info_dict), None if can_download: has_ffmpeg = FFmpegFD.available() no_crypto = not Cryptodome.AES and '#EXT-X-KEY:METHOD=AES-128' in s @@ -92,7 +93,7 @@ class HlsFD(FragmentFD): message = ('Live HLS streams are not supported by the native downloader. If this is a livestream, ' f'please {install_ffmpeg}add "--downloader ffmpeg --hls-use-mpegts" to your command') if not can_download: - if self._has_drm(s) and not self.params.get('allow_unplayable_formats'): + if self._has_drm(s): if info_dict.get('has_drm') and self.params.get('test'): self.to_screen(f'[{self.FD_NAME}] This format is DRM protected', skip_eol=True) else: