diff --git a/.gitignore b/.gitignore index a550c83af9..7562032942 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,9 @@ youtube-dlc *.swf *.part *.ytdl +*.frag +*.frag.urls +*.aria2 *.swp *.ogg *.opus diff --git a/Changelog.md b/Changelog.md index 87aff1107e..cc3c0a1f1e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -205,7 +205,7 @@ ### 2021.01.05 * Changed video format sorting to show video only files and video+audio files together. * Added `--video-multistreams`, `--no-video-multistreams`, `--audio-multistreams`, `--no-audio-multistreams` * Added `b`,`w`,`v`,`a` as alias for `best`, `worst`, `video` and `audio` respectively -* **Shortcut Options:** Added `--write-link`, `--write-url-link`, `--write-webloc-link`, `--write-desktop-link` by [h-h-h-h](https://github.com/h-h-h-h) - See [Internet Shortcut Options]README.md(#internet-shortcut-options) for details +* **Shortcut Options:** Added `--write-link`, `--write-url-link`, `--write-webloc-link`, `--write-desktop-link` by [h-h-h-h](https://github.com/h-h-h-h) - See [Internet Shortcut Options](README.md#internet-shortcut-options) for details * **Sponskrub integration:** Added `--sponskrub`, `--sponskrub-cut`, `--sponskrub-force`, `--sponskrub-location`, `--sponskrub-args` - See [SponSkrub Options](README.md#sponskrub-options-sponsorblock) for details * Added `--force-download-archive` (`--force-write-archive`) by [h-h-h-h](https://github.com/h-h-h-h) * Added `--list-formats-as-table`, `--list-formats-old` diff --git a/Makefile b/Makefile index 4dc5e517cf..f5390c46db 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ man: README.txt youtube-dlc.1 youtube-dlc.bash-completion youtube-dlc.zsh youtub clean: - rm -rf youtube-dlc.1.temp.md youtube-dlc.1 youtube-dlc.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dlc.tar.gz youtube-dlc.zsh youtube-dlc.fish youtube_dlc/extractor/lazy_extractors.py *.dump *.part* *.ytdl *.info.json *.mp4 *.m4a *.flv *.mp3 *.avi *.mkv *.webm *.3gp *.wav *.ape *.swf *.jpg *.png *.spec CONTRIBUTING.md.tmp youtube-dlc youtube-dlc.exe + rm -rf youtube-dlc.1.temp.md youtube-dlc.1 youtube-dlc.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dlc.tar.gz youtube-dlc.zsh youtube-dlc.fish youtube_dlc/extractor/lazy_extractors.py *.dump *.part* *.ytdl *.info.json *.mp4 *.m4a *.flv *.mp3 *.avi *.mkv *.webm *.3gp *.wav *.ape *.swf *.jpg *.png *.spec *.frag *.frag.urls *.frag.aria2 CONTRIBUTING.md.tmp youtube-dlc youtube-dlc.exe find . -name "*.pyc" -delete find . -name "*.class" -delete diff --git a/youtube_dlc/__init__.py b/youtube_dlc/__init__.py index c681c8edbc..4e55cf3371 100644 --- a/youtube_dlc/__init__.py +++ b/youtube_dlc/__init__.py @@ -379,12 +379,15 @@ def report_conflict(arg1, arg2): 'when': 'aftermove' }) - _args_compat_warning = 'WARNING: %s given without specifying name. The arguments will be given to all %s\n' + def report_args_compat(arg, name): + write_string( + 'WARNING: %s given without specifying name. The arguments will be given to all %s\n' % (arg, name), + out=sys.stderr) if 'default' in opts.external_downloader_args: - write_string(_args_compat_warning % ('--external-downloader-args', 'external downloaders'), out=sys.stderr), + report_args_compat('--external-downloader-args', 'external downloaders') if 'default-compat' in opts.postprocessor_args and 'default' not in opts.postprocessor_args: - write_string(_args_compat_warning % ('--post-processor-args', 'post-processors'), out=sys.stderr), + report_args_compat('--post-processor-args', 'post-processors') opts.postprocessor_args.setdefault('sponskrub', []) opts.postprocessor_args['default'] = opts.postprocessor_args['default-compat'] diff --git a/youtube_dlc/extractor/brightcove.py b/youtube_dlc/extractor/brightcove.py index 091992ebd3..8b29ca9937 100644 --- a/youtube_dlc/extractor/brightcove.py +++ b/youtube_dlc/extractor/brightcove.py @@ -478,11 +478,12 @@ def _parse_brightcove_metadata(self, json_data, video_id, headers={}): container = source.get('container') ext = mimetype2ext(source.get('type')) src = source.get('src') + skip_unplayable = not self._downloader.params.get('allow_unplayable_formats') # https://support.brightcove.com/playback-api-video-fields-reference#key_systems_object - if not self._downloader.params.get('allow_unplayable_formats') and (container == 'WVM' or source.get('key_systems')): + if skip_unplayable and (container == 'WVM' or source.get('key_systems')): num_drm_sources += 1 continue - elif ext == 'ism' and not self._downloader.params.get('allow_unplayable_formats'): + elif ext == 'ism' and skip_unplayable: continue elif ext == 'm3u8' or container == 'M2TS': if not src: @@ -546,7 +547,8 @@ 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 not self._downloader.params.get('allow_unplayable_formats') and 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 dc8b04ec6a..6bfb760fad 100644 --- a/youtube_dlc/extractor/ceskatelevize.py +++ b/youtube_dlc/extractor/ceskatelevize.py @@ -147,7 +147,8 @@ def _real_extract(self, url): is_live = item.get('type') == 'LIVE' formats = [] for format_id, stream_url in item.get('streamUrls', {}).items(): - if not self._downloader.params.get('allow_unplayable_formats') and '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 1fe2d0a93e..371c349299 100644 --- a/youtube_dlc/extractor/common.py +++ b/youtube_dlc/extractor/common.py @@ -2358,7 +2358,7 @@ def extract_Initialization(source): extract_Initialization(segment_template) return ms_info - allow_unplayable_formats = self._downloader.params.get('allow_unplayable_formats') + skip_unplayable = not self._downloader.params.get('allow_unplayable_formats') mpd_duration = parse_duration(mpd_doc.get('mediaPresentationDuration')) formats = [] @@ -2369,11 +2369,11 @@ def extract_Initialization(source): 'timescale': 1, }) for adaptation_set in period.findall(_add_ns('AdaptationSet')): - if is_drm_protected(adaptation_set) and allow_unplayable_formats is False: + if skip_unplayable and is_drm_protected(adaptation_set): 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) and allow_unplayable_formats is False: + if skip_unplayable and is_drm_protected(representation): continue representation_attrib = adaptation_set.attrib.copy() representation_attrib.update(representation.attrib) @@ -2587,7 +2587,10 @@ 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 and not self._downloader.params.get('allow_unplayable_formats')): + if ism_doc.get('IsLive') == 'TRUE': + return [] + if (not self._downloader.params.get('allow_unplayable_formats') + and ism_doc.find('Protection') is not None): return [] duration = int(ism_doc.attrib['Duration']) diff --git a/youtube_dlc/extractor/ivi.py b/youtube_dlc/extractor/ivi.py index 7952ab9e65..580cf41cd0 100644 --- a/youtube_dlc/extractor/ivi.py +++ b/youtube_dlc/extractor/ivi.py @@ -163,7 +163,10 @@ 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 (not self._downloader.params.get('allow_unplayable_formats') and ('-MDRM-' in content_format or '-FPS-' in content_format)): + if not f_url: + continue + if (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/limelight.py b/youtube_dlc/extractor/limelight.py index 6592f60da3..b95b001ad3 100644 --- a/youtube_dlc/extractor/limelight.py +++ b/youtube_dlc/extractor/limelight.py @@ -96,7 +96,9 @@ 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 (not self._downloader.params.get('allow_unplayable_formats') and stream.get('drmProtected')) or stream_url in urls: + if not stream_url or stream_url in urls: + continue + if not self._downloader.params.get('allow_unplayable_formats') and stream.get('drmProtected'): 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 39ae4c66e6..f98e8396be 100644 --- a/youtube_dlc/extractor/ninecninemedia.py +++ b/youtube_dlc/extractor/ninecninemedia.py @@ -36,7 +36,8 @@ def _real_extract(self, url): '$include': '[HasClosedCaptions]', }) - if not self._downloader.params.get('allow_unplayable_formats') and 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/ruutu.py b/youtube_dlc/extractor/ruutu.py index 5db83a4e11..f9f30e3dd2 100644 --- a/youtube_dlc/extractor/ruutu.py +++ b/youtube_dlc/extractor/ruutu.py @@ -200,8 +200,8 @@ def pv(name): return node.get('value') if not formats: - drm = xpath_text(video_xml, './Clip/DRM', default=None) - if not self._downloader.params.get('allow_unplayable_formats') and drm: + 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) ns_st_cds = pv('ns_st_cds') if ns_st_cds != 'free': diff --git a/youtube_dlc/extractor/toggle.py b/youtube_dlc/extractor/toggle.py index 1ba55b5552..1e2a2d819c 100644 --- a/youtube_dlc/extractor/toggle.py +++ b/youtube_dlc/extractor/toggle.py @@ -154,7 +154,8 @@ def _real_extract(self, url): }) if not formats: 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': + 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/wakanim.py b/youtube_dlc/extractor/wakanim.py index a8963d7698..507a28febb 100644 --- a/youtube_dlc/extractor/wakanim.py +++ b/youtube_dlc/extractor/wakanim.py @@ -41,12 +41,13 @@ def _real_extract(self, url): m3u8_url = urljoin(url, self._search_regex( r'file\s*:\s*(["\'])(?P(?:(?!\1).)+)\1', webpage, 'm3u8 url', group='url')) - # https://docs.microsoft.com/en-us/azure/media-services/previous/media-services-content-protection-overview#streaming-urls - encryption = self._search_regex( - r'encryption%3D(c(?:enc|bc(?:s-aapl)?))', - m3u8_url, 'encryption', default=None) - 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) + if not self._downloader.params.get('allow_unplayable_formats'): + # https://docs.microsoft.com/en-us/azure/media-services/previous/media-services-content-protection-overview#streaming-urls + encryption = self._search_regex( + r'encryption%3D(c(?:enc|bc(?:s-aapl)?))', + m3u8_url, 'encryption', default=None) + if encryption in ('cenc', 'cbcs-aapl'): + raise ExtractorError('This video is DRM protected.', expected=True) formats = self._extract_m3u8_formats( m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native', diff --git a/youtube_dlc/postprocessor/ffmpeg.py b/youtube_dlc/postprocessor/ffmpeg.py index cabe7266e2..0982bea813 100644 --- a/youtube_dlc/postprocessor/ffmpeg.py +++ b/youtube_dlc/postprocessor/ffmpeg.py @@ -221,8 +221,7 @@ def get_metadata_object(self, path, opts=[]): cmd += opts cmd.append(encodeFilename(self._ffmpeg_filename_argument(path), True)) - if self._downloader.params.get('verbose', False): - self._downloader.to_screen('[debug] ffprobe command line: %s' % shell_quote(cmd)) + self.write_debug('ffprobe command line: %s' % shell_quote(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate() return json.loads(stdout.decode('utf-8', 'replace')) @@ -261,7 +260,7 @@ def run_ffmpeg_multiple_files(self, input_paths, out_path, opts): stdout, stderr = process_communicate_or_kill(p) if p.returncode != 0: stderr = stderr.decode('utf-8', 'replace').strip() - if self._downloader.params.get('verbose', False): + if self.get_param('verbose', False): self.report_error(stderr) raise FFmpegPostProcessorError(stderr.split('\n')[-1]) self.try_utime(out_path, oldest_mtime, oldest_mtime) diff --git a/youtube_dlc/update.py b/youtube_dlc/update.py index 69bc5d2537..b9d3c76248 100644 --- a/youtube_dlc/update.py +++ b/youtube_dlc/update.py @@ -59,7 +59,7 @@ def sha256sum(): if verbose: to_screen(encode_compat_str(traceback.format_exc())) to_screen('ERROR: can\'t obtain versions info. Please try again later.') - to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/lastest') + to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest') return version_id = version_info['tag_name'] @@ -107,7 +107,7 @@ def version_tuple(version_str): if verbose: to_screen(encode_compat_str(traceback.format_exc())) to_screen('ERROR: unable to download latest version') - to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/lastest') + to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest') return try: @@ -150,7 +150,7 @@ def version_tuple(version_str): if verbose: to_screen(encode_compat_str(traceback.format_exc())) to_screen('ERROR: unable to download latest version') - to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/lastest') + to_screen('Visit https://github.com/pukkandan/yt-dlp/releases/latest') return try: