From 63ad4d43ebb826725539235f4fa08c85c046fdc1 Mon Sep 17 00:00:00 2001 From: shirt-dev <2660574+shirt-dev@users.noreply.github.com> Date: Thu, 11 Feb 2021 22:51:59 -0500 Subject: [PATCH] #70 Allow downloading of unplayable video formats Video postprocessors are also turned off when this option is used Co-authored-by: shirtjs <2660574+shirtjs@users.noreply.github.com> Co-authored-by: pukkandan --- README.md | 5 ++++ youtube_dlc/YoutubeDL.py | 16 +++++++---- youtube_dlc/__init__.py | 38 +++++++++++++++++++++++-- youtube_dlc/downloader/f4m.py | 15 +++++----- youtube_dlc/downloader/hls.py | 13 +++++---- youtube_dlc/extractor/brightcove.py | 6 ++-- youtube_dlc/extractor/ceskatelevize.py | 2 +- youtube_dlc/extractor/common.py | 8 ++++-- youtube_dlc/extractor/crackle.py | 2 +- youtube_dlc/extractor/globo.py | 2 +- youtube_dlc/extractor/hotstar.py | 2 +- youtube_dlc/extractor/ivi.py | 2 +- youtube_dlc/extractor/kaltura.py | 2 +- youtube_dlc/extractor/limelight.py | 2 +- youtube_dlc/extractor/ninecninemedia.py | 2 +- youtube_dlc/extractor/ninenow.py | 2 +- youtube_dlc/extractor/npo.py | 2 +- youtube_dlc/extractor/prosiebensat1.py | 2 +- youtube_dlc/extractor/rtbf.py | 2 +- youtube_dlc/extractor/ruutu.py | 2 +- youtube_dlc/extractor/shahid.py | 2 +- youtube_dlc/extractor/sonyliv.py | 2 +- youtube_dlc/extractor/toggle.py | 2 +- youtube_dlc/extractor/toutv.py | 2 +- youtube_dlc/extractor/tvnow.py | 2 +- youtube_dlc/extractor/viki.py | 2 +- youtube_dlc/extractor/wakanim.py | 2 +- youtube_dlc/extractor/youtube.py | 2 +- youtube_dlc/options.py | 10 +++++++ 29 files changed, 106 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 0303f1d50..0f062c2cf 100644 --- a/README.md +++ b/README.md @@ -537,6 +537,11 @@ ## Video Format Options: bestvideo+bestaudio), output to given container format. One of mkv, mp4, ogg, webm, flv. Ignored if no merge is required + --allow-unplayable-formats Allow unplayable formats to be listed and + downloaded. All video postprocessing will + also be turned off + --no-allow-unplayable-formats Do not allow unplayable formats to be + listed or downloaded (default) ## Subtitle Options: --write-subs Write subtitle file diff --git a/youtube_dlc/YoutubeDL.py b/youtube_dlc/YoutubeDL.py index 8156a8a28..922cf269b 100644 --- a/youtube_dlc/YoutubeDL.py +++ b/youtube_dlc/YoutubeDL.py @@ -179,6 +179,7 @@ class YoutubeDL(object): of 'skip_download' or 'simulate'. 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. 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" @@ -2291,10 +2292,15 @@ def existing_file(*filepaths): if info_dict.get('requested_formats') is not None: downloaded = [] merger = FFmpegMergerPP(self) - if not merger.available: - self.report_warning('You have requested multiple ' - 'formats but ffmpeg is not installed.' - ' The formats won\'t be merged.') + 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: + self.report_warning( + 'You have requested merging of multiple formats but ffmpeg is not installed. ' + 'The formats won\'t be merged.') def compatible_formats(formats): # TODO: some formats actually allow this (mkv, webm, ogg, mp4), but not all of them. @@ -2346,7 +2352,7 @@ def correct_ext(filename): downloaded.append(fname) partial_success, real_download = dl(fname, new_info) success = success and partial_success - if merger.available: + if merger.available and not self.params.get('allow_unplayable_formats'): 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/youtube_dlc/__init__.py b/youtube_dlc/__init__.py index eeb7b6f74..7b2e63fd3 100644 --- a/youtube_dlc/__init__.py +++ b/youtube_dlc/__init__.py @@ -212,9 +212,6 @@ def parse_retries(retries): if opts.recodevideo is not None: if opts.recodevideo not in REMUX_EXTENSIONS: parser.error('invalid video recode format specified') - if opts.remuxvideo and opts.recodevideo: - opts.remuxvideo = None - write_string('WARNING: --remux-video is ignored since --recode-video was given\n', out=sys.stderr) if opts.remuxvideo is not None: opts.remuxvideo = opts.remuxvideo.replace(' ', '') remux_regex = r'{0}(?:/{0})*$'.format(r'(?:\w+>)?(?:%s)' % '|'.join(REMUX_EXTENSIONS)) @@ -265,6 +262,40 @@ def parse_retries(retries): any_printing = opts.print_json download_archive_fn = expand_path(opts.download_archive) if opts.download_archive is not None else opts.download_archive + def report_conflict(arg1, arg2): + write_string('WARNING: %s is ignored since %s was given\n' % (arg2, arg1), out=sys.stderr) + if opts.remuxvideo and opts.recodevideo: + report_conflict('--recode-video', '--remux-video') + opts.remuxvideo = False + if opts.allow_unplayable_formats: + if opts.extractaudio: + report_conflict('--allow-unplayable-formats', '--extract-audio') + opts.extractaudio = False + if opts.remuxvideo: + report_conflict('--allow-unplayable-formats', '--remux-video') + opts.remuxvideo = False + if opts.recodevideo: + report_conflict('--allow-unplayable-formats', '--recode-video') + opts.recodevideo = False + if opts.addmetadata: + report_conflict('--allow-unplayable-formats', '--add-metadata') + opts.addmetadata = False + if opts.embedsubtitles: + report_conflict('--allow-unplayable-formats', '--embed-subs') + opts.embedsubtitles = False + if opts.embedthumbnail: + report_conflict('--allow-unplayable-formats', '--embed-thumbnail') + opts.embedthumbnail = False + if opts.xattrs: + report_conflict('--allow-unplayable-formats', '--xattrs') + opts.xattrs = False + if opts.fixup and opts.fixup.lower() not in ('never', 'ignore'): + report_conflict('--allow-unplayable-formats', '--fixup') + opts.fixup = 'never' + if opts.sponskrub: + report_conflict('--allow-unplayable-formats', '--sponskrub') + opts.sponskrub = False + # PostProcessors postprocessors = [] if opts.metafromfield: @@ -393,6 +424,7 @@ def parse_retries(retries): 'simulate': opts.simulate or any_getting, 'skip_download': opts.skip_download, 'format': opts.format, + 'allow_unplayable_formats': opts.allow_unplayable_formats, 'format_sort': opts.format_sort, 'format_sort_force': opts.format_sort_force, 'allow_multiple_video_streams': opts.allow_multiple_video_streams, diff --git a/youtube_dlc/downloader/f4m.py b/youtube_dlc/downloader/f4m.py index 8dd3c2eeb..3eb406152 100644 --- a/youtube_dlc/downloader/f4m.py +++ b/youtube_dlc/downloader/f4m.py @@ -267,13 +267,14 @@ def _get_unencrypted_media(self, doc): media = doc.findall(_add_ns('media')) if not media: self.report_error('No media found') - 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 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) if not media: self.report_error('Unsupported DRM') return media diff --git a/youtube_dlc/downloader/hls.py b/youtube_dlc/downloader/hls.py index c3c862410..ea515a48e 100644 --- a/youtube_dlc/downloader/hls.py +++ b/youtube_dlc/downloader/hls.py @@ -29,9 +29,8 @@ class HlsFD(FragmentFD): FD_NAME = 'hlsnative' @staticmethod - def can_download(manifest, info_dict): - UNSUPPORTED_FEATURES = ( - r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)', # encrypted streams [1] + def can_download(manifest, info_dict, allow_unplayable_formats=False): + UNSUPPORTED_FEATURES = [ # r'#EXT-X-BYTERANGE', # playlists composed of byte ranges of media files [2] # Live streams heuristic does not always work (e.g. geo restricted to Germany @@ -50,7 +49,11 @@ def can_download(manifest, info_dict): # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2 # 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] + ] check_results = [not re.search(feature, manifest) for feature in UNSUPPORTED_FEATURES] is_aes128_enc = '#EXT-X-KEY:METHOD=AES-128' in manifest check_results.append(can_decrypt_frag or not is_aes128_enc) @@ -66,7 +69,7 @@ def real_download(self, filename, info_dict): man_url = urlh.geturl() s = urlh.read().decode('utf-8', 'ignore') - if not self.can_download(s, info_dict): + if not self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')): if info_dict.get('extra_param_to_segment_url') or info_dict.get('_decryption_key_url'): self.report_error('pycrypto not found. Please install it.') return False diff --git a/youtube_dlc/extractor/brightcove.py b/youtube_dlc/extractor/brightcove.py index 6022076ac..901bfa585 100644 --- a/youtube_dlc/extractor/brightcove.py +++ b/youtube_dlc/extractor/brightcove.py @@ -479,10 +479,10 @@ def _parse_brightcove_metadata(self, json_data, video_id, headers={}): ext = mimetype2ext(source.get('type')) src = source.get('src') # https://support.brightcove.com/playback-api-video-fields-reference#key_systems_object - if container == 'WVM' or source.get('key_systems'): + if not self._downloader.params.get('allow_unplayable_formats') and (container == 'WVM' or source.get('key_systems')): num_drm_sources += 1 continue - elif ext == 'ism': + elif ext == 'ism' and self._downloader.params.get('allow_unplayable_formats'): continue elif ext == 'm3u8' or container == 'M2TS': if not src: @@ -546,7 +546,7 @@ def build_format_id(kind): error = errors[0] raise ExtractorError( error.get('message') or error.get('error_subcode') or error['error_code'], expected=True) - if sources and num_drm_sources == len(sources): + if 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) self._sort_formats(formats) diff --git a/youtube_dlc/extractor/ceskatelevize.py b/youtube_dlc/extractor/ceskatelevize.py index 7cb4efb74..dc8b04ec6 100644 --- a/youtube_dlc/extractor/ceskatelevize.py +++ b/youtube_dlc/extractor/ceskatelevize.py @@ -147,7 +147,7 @@ def _real_extract(self, url): is_live = item.get('type') == 'LIVE' formats = [] for format_id, stream_url in item.get('streamUrls', {}).items(): - if 'drmOnly=true' in stream_url: + if not self._downloader.params.get('allow_unplayable_formats') and 'drmOnly=true' in stream_url: continue if 'playerType=flash' in stream_url: stream_formats = self._extract_m3u8_formats( diff --git a/youtube_dlc/extractor/common.py b/youtube_dlc/extractor/common.py index 0304b2133..1fe2d0a93 100644 --- a/youtube_dlc/extractor/common.py +++ b/youtube_dlc/extractor/common.py @@ -2358,6 +2358,8 @@ def extract_Initialization(source): extract_Initialization(segment_template) return ms_info + allow_unplayable_formats = self._downloader.params.get('allow_unplayable_formats') + mpd_duration = parse_duration(mpd_doc.get('mediaPresentationDuration')) formats = [] for period in mpd_doc.findall(_add_ns('Period')): @@ -2367,11 +2369,11 @@ def extract_Initialization(source): 'timescale': 1, }) for adaptation_set in period.findall(_add_ns('AdaptationSet')): - if is_drm_protected(adaptation_set): + if is_drm_protected(adaptation_set) and allow_unplayable_formats is False: continue adaption_set_ms_info = extract_multisegment_info(adaptation_set, period_ms_info) for representation in adaptation_set.findall(_add_ns('Representation')): - if is_drm_protected(representation): + if is_drm_protected(representation) and allow_unplayable_formats is False: continue representation_attrib = adaptation_set.attrib.copy() representation_attrib.update(representation.attrib) @@ -2585,7 +2587,7 @@ def _parse_ism_formats(self, ism_doc, ism_url, ism_id=None): 1. [MS-SSTR]: Smooth Streaming Protocol, https://msdn.microsoft.com/en-us/library/ff469518.aspx """ - if ism_doc.get('IsLive') == 'TRUE' or ism_doc.find('Protection') is not None: + if ism_doc.get('IsLive') == 'TRUE' or (ism_doc.find('Protection') is not None and not self._downloader.params.get('allow_unplayable_formats')): return [] duration = int(ism_doc.attrib['Duration']) diff --git a/youtube_dlc/extractor/crackle.py b/youtube_dlc/extractor/crackle.py index 49bf3a4f9..231d52218 100644 --- a/youtube_dlc/extractor/crackle.py +++ b/youtube_dlc/extractor/crackle.py @@ -103,7 +103,7 @@ def _real_extract(self, url): formats = [] for e in media['MediaURLs']: - if e.get('UseDRM') is True: + if not self._downloader.params.get('allow_unplayable_formats') and e.get('UseDRM') is True: continue format_url = url_or_none(e.get('Path')) if not format_url: diff --git a/youtube_dlc/extractor/globo.py b/youtube_dlc/extractor/globo.py index 60d842d3a..3dbe759be 100644 --- a/youtube_dlc/extractor/globo.py +++ b/youtube_dlc/extractor/globo.py @@ -96,7 +96,7 @@ def _real_extract(self, url): video = self._download_json( 'http://api.globovideos.com/videos/%s/playlist' % video_id, video_id)['videos'][0] - if video.get('encrypted') is True: + if not self._downloader.params.get('allow_unplayable_formats') and video.get('encrypted') is True: raise ExtractorError('This video is DRM protected.', expected=True) title = video['title'] diff --git a/youtube_dlc/extractor/hotstar.py b/youtube_dlc/extractor/hotstar.py index 1fb4d2d41..e2e923539 100644 --- a/youtube_dlc/extractor/hotstar.py +++ b/youtube_dlc/extractor/hotstar.py @@ -141,7 +141,7 @@ def _real_extract(self, url): title = video_data['title'] - if video_data.get('drmProtected'): + if not self._downloader.params.get('allow_unplayable_formats') and video_data.get('drmProtected'): raise ExtractorError('This video is DRM protected.', expected=True) headers = {'Referer': url} diff --git a/youtube_dlc/extractor/ivi.py b/youtube_dlc/extractor/ivi.py index b9cb5a8e6..7952ab9e6 100644 --- a/youtube_dlc/extractor/ivi.py +++ b/youtube_dlc/extractor/ivi.py @@ -163,7 +163,7 @@ def _real_extract(self, url): for f in result.get('files', []): f_url = f.get('url') content_format = f.get('content_format') - if not f_url or '-MDRM-' in content_format or '-FPS-' in content_format: + if not f_url or (not self._downloader.params.get('allow_unplayable_formats') and ('-MDRM-' in content_format or '-FPS-' in content_format)): continue formats.append({ 'url': f_url, diff --git a/youtube_dlc/extractor/kaltura.py b/youtube_dlc/extractor/kaltura.py index 49d13460d..c8097249e 100644 --- a/youtube_dlc/extractor/kaltura.py +++ b/youtube_dlc/extractor/kaltura.py @@ -309,7 +309,7 @@ def sign_url(unsigned_url): if f.get('fileExt') == 'chun': continue # DRM-protected video, cannot be decrypted - if f.get('fileExt') == 'wvm': + if not self._downloader.params.get('allow_unplayable_formats') and f.get('fileExt') == 'wvm': continue if not f.get('fileExt'): # QT indicates QuickTime; some videos have broken fileExt diff --git a/youtube_dlc/extractor/limelight.py b/youtube_dlc/extractor/limelight.py index 39f74d282..6592f60da 100644 --- a/youtube_dlc/extractor/limelight.py +++ b/youtube_dlc/extractor/limelight.py @@ -96,7 +96,7 @@ def _extract_info(self, pc, mobile, i, referer): urls = [] for stream in pc_item.get('streams', []): stream_url = stream.get('url') - if not stream_url or stream.get('drmProtected') or stream_url in urls: + if not stream_url or (not self._downloader.params.get('allow_unplayable_formats') and stream.get('drmProtected')) or stream_url in urls: continue urls.append(stream_url) ext = determine_ext(stream_url) diff --git a/youtube_dlc/extractor/ninecninemedia.py b/youtube_dlc/extractor/ninecninemedia.py index a569c889e..39ae4c66e 100644 --- a/youtube_dlc/extractor/ninecninemedia.py +++ b/youtube_dlc/extractor/ninecninemedia.py @@ -36,7 +36,7 @@ def _real_extract(self, url): '$include': '[HasClosedCaptions]', }) - if try_get(content_package, lambda x: x['Constraints']['Security']['Type']): + if not self._downloader.params.get('allow_unplayable_formats') and try_get(content_package, lambda x: x['Constraints']['Security']['Type']): raise ExtractorError('This video is DRM protected.', expected=True) manifest_base_url = content_package_url + 'manifest.' diff --git a/youtube_dlc/extractor/ninenow.py b/youtube_dlc/extractor/ninenow.py index 6157dc7c1..fc3a398ad 100644 --- a/youtube_dlc/extractor/ninenow.py +++ b/youtube_dlc/extractor/ninenow.py @@ -66,7 +66,7 @@ def _real_extract(self, url): video_data = common_data['video'] - if video_data.get('drm'): + if not self._downloader.params.get('allow_unplayable_formats') and video_data.get('drm'): raise ExtractorError('This video is DRM protected.', expected=True) brightcove_id = video_data.get('brightcoveId') or 'ref:' + video_data['referenceId'] diff --git a/youtube_dlc/extractor/npo.py b/youtube_dlc/extractor/npo.py index e525ad928..416b6acfc 100644 --- a/youtube_dlc/extractor/npo.py +++ b/youtube_dlc/extractor/npo.py @@ -246,7 +246,7 @@ def _get_info(self, url, video_id): }) if not formats: - if drm: + if not self._downloader.params.get('allow_unplayable_formats') and drm: raise ExtractorError('This video is DRM protected.', expected=True) return diff --git a/youtube_dlc/extractor/prosiebensat1.py b/youtube_dlc/extractor/prosiebensat1.py index e47088292..307ab81e9 100644 --- a/youtube_dlc/extractor/prosiebensat1.py +++ b/youtube_dlc/extractor/prosiebensat1.py @@ -34,7 +34,7 @@ def _extract_video_info(self, url, clip_id): 'ids': clip_id, })[0] - if video.get('is_protected') is True: + if not self._downloader.params.get('allow_unplayable_formats') and video.get('is_protected') is True: raise ExtractorError('This video is DRM protected.', expected=True) formats = [] diff --git a/youtube_dlc/extractor/rtbf.py b/youtube_dlc/extractor/rtbf.py index 3b0f3080b..3c6c656ea 100644 --- a/youtube_dlc/extractor/rtbf.py +++ b/youtube_dlc/extractor/rtbf.py @@ -125,7 +125,7 @@ def _real_extract(self, url): }) mpd_url = data.get('urlDash') - if not data.get('drm') and mpd_url: + if (not self._downloader.params.get('allow_unplayable_formats') and not data.get('drm')) and mpd_url: formats.extend(self._extract_mpd_formats( mpd_url, media_id, mpd_id='dash', fatal=False)) diff --git a/youtube_dlc/extractor/ruutu.py b/youtube_dlc/extractor/ruutu.py index c50cd3ecd..5db83a4e1 100644 --- a/youtube_dlc/extractor/ruutu.py +++ b/youtube_dlc/extractor/ruutu.py @@ -201,7 +201,7 @@ def pv(name): if not formats: drm = xpath_text(video_xml, './Clip/DRM', default=None) - if drm: + if not self._downloader.params.get('allow_unplayable_formats') and drm: raise ExtractorError('This video is DRM protected.', expected=True) ns_st_cds = pv('ns_st_cds') if ns_st_cds != 'free': diff --git a/youtube_dlc/extractor/shahid.py b/youtube_dlc/extractor/shahid.py index 5c2a6206b..c1d6aba2c 100644 --- a/youtube_dlc/extractor/shahid.py +++ b/youtube_dlc/extractor/shahid.py @@ -111,7 +111,7 @@ def _real_extract(self, url): playout = self._call_api( 'playout/url/' + video_id, video_id)['playout'] - if playout.get('drm'): + if not self._downloader.params.get('allow_unplayable_formats') and playout.get('drm'): raise ExtractorError('This video is DRM protected.', expected=True) formats = self._extract_m3u8_formats(playout['url'], video_id, 'mp4') diff --git a/youtube_dlc/extractor/sonyliv.py b/youtube_dlc/extractor/sonyliv.py index fedfceb62..f0c17b256 100644 --- a/youtube_dlc/extractor/sonyliv.py +++ b/youtube_dlc/extractor/sonyliv.py @@ -75,7 +75,7 @@ def _real_extract(self, url): video_id = self._match_id(url) content = self._call_api( '1.5', 'IN/CONTENT/VIDEOURL/VOD/' + video_id, video_id) - if content.get('isEncrypted'): + if not self._downloader.params.get('allow_unplayable_formats') and content.get('isEncrypted'): raise ExtractorError('This video is DRM protected.', expected=True) dash_url = content['videoURL'] headers = { diff --git a/youtube_dlc/extractor/toggle.py b/youtube_dlc/extractor/toggle.py index 270c84daa..1ba55b555 100644 --- a/youtube_dlc/extractor/toggle.py +++ b/youtube_dlc/extractor/toggle.py @@ -154,7 +154,7 @@ def _real_extract(self, url): }) if not formats: for meta in (info.get('Metas') or []): - if meta.get('Key') == 'Encryption' and meta.get('Value') == '1': + if not self._downloader.params.get('allow_unplayable_formats') and meta.get('Key') == 'Encryption' and meta.get('Value') == '1': raise ExtractorError( 'This video is DRM protected.', expected=True) # Most likely because geo-blocked diff --git a/youtube_dlc/extractor/toutv.py b/youtube_dlc/extractor/toutv.py index 44b022fca..aba87051a 100644 --- a/youtube_dlc/extractor/toutv.py +++ b/youtube_dlc/extractor/toutv.py @@ -74,7 +74,7 @@ def _real_extract(self, url): }) # IsDrm does not necessarily mean the video is DRM protected (see # https://github.com/ytdl-org/youtube-dl/issues/13994). - if metadata.get('IsDrm'): + if not self._downloader.params.get('allow_unplayable_formats') and metadata.get('IsDrm'): self.report_warning('This video is probably DRM protected.', path) video_id = metadata['IdMedia'] details = metadata['Details'] diff --git a/youtube_dlc/extractor/tvnow.py b/youtube_dlc/extractor/tvnow.py index e2bb62ae8..9b90a2b26 100644 --- a/youtube_dlc/extractor/tvnow.py +++ b/youtube_dlc/extractor/tvnow.py @@ -69,7 +69,7 @@ def make_urls(proto, suffix): if formats: break else: - if info.get('isDrm'): + if not self._downloader.params.get('allow_unplayable_formats') and info.get('isDrm'): raise ExtractorError( 'Video %s is DRM protected' % video_id, expected=True) if info.get('geoblocked'): diff --git a/youtube_dlc/extractor/viki.py b/youtube_dlc/extractor/viki.py index fd1c305b1..50208db6e 100644 --- a/youtube_dlc/extractor/viki.py +++ b/youtube_dlc/extractor/viki.py @@ -315,7 +315,7 @@ def add_format(format_id, format_dict, protocol='http'): # Despite CODECS metadata in m3u8 all video-only formats # are actually video+audio for f in m3u8_formats: - if '_drm/index_' in f['url']: + if not self._downloader.params.get('allow_unplayable_formats') and '_drm/index_' in f['url']: continue if f.get('acodec') == 'none' and f.get('vcodec') != 'none': f['acodec'] = None diff --git a/youtube_dlc/extractor/wakanim.py b/youtube_dlc/extractor/wakanim.py index f9a2395d9..a8963d769 100644 --- a/youtube_dlc/extractor/wakanim.py +++ b/youtube_dlc/extractor/wakanim.py @@ -45,7 +45,7 @@ def _real_extract(self, url): encryption = self._search_regex( r'encryption%3D(c(?:enc|bc(?:s-aapl)?))', m3u8_url, 'encryption', default=None) - if encryption and encryption in ('cenc', 'cbcs-aapl'): + if not self._downloader.params.get('allow_unplayable_formats') and encryption and encryption in ('cenc', 'cbcs-aapl'): raise ExtractorError('This video is DRM protected.', expected=True) formats = self._extract_m3u8_formats( diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index b973e5d81..7f199ad88 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -1618,7 +1618,7 @@ def feed_entry(name): formats.append(f) if not formats: - if streaming_data.get('licenseInfos'): + if not self._downloader.params.get('allow_unplayable_formats') and streaming_data.get('licenseInfos'): raise ExtractorError( 'This video is DRM protected.', expected=True) pemr = try_get( diff --git a/youtube_dlc/options.py b/youtube_dlc/options.py index abbd1927d..cb8e8236a 100644 --- a/youtube_dlc/options.py +++ b/youtube_dlc/options.py @@ -519,6 +519,16 @@ def _dict_from_multiple_values_options_callback( 'If a merge is required (e.g. bestvideo+bestaudio), ' 'output to given container format. One of mkv, mp4, ogg, webm, flv. ' 'Ignored if no merge is required')) + video_format.add_option( + '--allow-unplayable-formats', + action='store_true', dest='allow_unplayable_formats', default=False, + help=( + 'Allow unplayable formats to be listed and downloaded. ' + 'All video postprocessing will also be turned off')) + video_format.add_option( + '--no-allow-unplayable-formats', + action='store_false', dest='allow_unplayable_formats', + help='Do not allow unplayable formats to be listed or downloaded (default)') subtitles = optparse.OptionGroup(parser, 'Subtitle Options') subtitles.add_option(