mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 22:55:18 +00:00 
			
		
		
		
	[vevo] Support 1080p videos (Fixes #3656)
This commit is contained in:
		| @@ -2,6 +2,7 @@ from __future__ import unicode_literals | |||||||
|  |  | ||||||
| from .common import FileDownloader | from .common import FileDownloader | ||||||
| from .hls import HlsFD | from .hls import HlsFD | ||||||
|  | from .hls import NativeHlsFD | ||||||
| from .http import HttpFD | from .http import HttpFD | ||||||
| from .mplayer import MplayerFD | from .mplayer import MplayerFD | ||||||
| from .rtmp import RtmpFD | from .rtmp import RtmpFD | ||||||
| @@ -19,6 +20,8 @@ def get_suitable_downloader(info_dict): | |||||||
|  |  | ||||||
|     if url.startswith('rtmp'): |     if url.startswith('rtmp'): | ||||||
|         return RtmpFD |         return RtmpFD | ||||||
|  |     if protocol == 'm3u8_native': | ||||||
|  |         return NativeHlsFD | ||||||
|     if (protocol == 'm3u8') or (protocol is None and determine_ext(url) == 'm3u8'): |     if (protocol == 'm3u8') or (protocol is None and determine_ext(url) == 'm3u8'): | ||||||
|         return HlsFD |         return HlsFD | ||||||
|     if url.startswith('mms') or url.startswith('rtsp'): |     if url.startswith('mms') or url.startswith('rtsp'): | ||||||
|   | |||||||
| @@ -1,8 +1,12 @@ | |||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
| import os | import os | ||||||
|  | import re | ||||||
| import subprocess | import subprocess | ||||||
|  |  | ||||||
| from .common import FileDownloader | from .common import FileDownloader | ||||||
| from ..utils import ( | from ..utils import ( | ||||||
|  |     compat_urlparse, | ||||||
|     check_executable, |     check_executable, | ||||||
|     encodeFilename, |     encodeFilename, | ||||||
| ) | ) | ||||||
| @@ -43,3 +47,46 @@ class HlsFD(FileDownloader): | |||||||
|             self.to_stderr(u"\n") |             self.to_stderr(u"\n") | ||||||
|             self.report_error(u'%s exited with code %d' % (program, retval)) |             self.report_error(u'%s exited with code %d' % (program, retval)) | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NativeHlsFD(FileDownloader): | ||||||
|  |     """ A more limited implementation that does not require ffmpeg """ | ||||||
|  |  | ||||||
|  |     def real_download(self, filename, info_dict): | ||||||
|  |         url = info_dict['url'] | ||||||
|  |         self.report_destination(filename) | ||||||
|  |         tmpfilename = self.temp_name(filename) | ||||||
|  |  | ||||||
|  |         self.to_screen( | ||||||
|  |             '[hlsnative] %s: Downloading m3u8 manifest' % info_dict['id']) | ||||||
|  |         data = self.ydl.urlopen(url).read() | ||||||
|  |         s = data.decode('utf-8', 'ignore') | ||||||
|  |         segment_urls = [] | ||||||
|  |         for line in s.splitlines(): | ||||||
|  |             line = line.strip() | ||||||
|  |             if line and not line.startswith('#'): | ||||||
|  |                 segment_url = ( | ||||||
|  |                     line | ||||||
|  |                     if re.match(r'^https?://', line) | ||||||
|  |                     else compat_urlparse.urljoin(url, line)) | ||||||
|  |                 segment_urls.append(segment_url) | ||||||
|  |  | ||||||
|  |         byte_counter = 0 | ||||||
|  |         with open(tmpfilename, 'wb') as outf: | ||||||
|  |             for i, segurl in enumerate(segment_urls): | ||||||
|  |                 segment = self.ydl.urlopen(segurl).read() | ||||||
|  |                 outf.write(segment) | ||||||
|  |                 byte_counter += len(segment) | ||||||
|  |                 self.to_screen( | ||||||
|  |                     '[hlsnative] %s: Downloading segment %d / %d' % | ||||||
|  |                     (info_dict['id'], i + 1, len(segment_urls))) | ||||||
|  |  | ||||||
|  |         self._hook_progress({ | ||||||
|  |             'downloaded_bytes': byte_counter, | ||||||
|  |             'total_bytes': byte_counter, | ||||||
|  |             'filename': filename, | ||||||
|  |             'status': 'finished', | ||||||
|  |         }) | ||||||
|  |         self.try_rename(tmpfilename, filename) | ||||||
|  |         return True | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ from ..utils import ( | |||||||
|     compat_http_client, |     compat_http_client, | ||||||
|     compat_urllib_error, |     compat_urllib_error, | ||||||
|     compat_urllib_parse_urlparse, |     compat_urllib_parse_urlparse, | ||||||
|  |     compat_urlparse, | ||||||
|     compat_str, |     compat_str, | ||||||
|  |  | ||||||
|     clean_html, |     clean_html, | ||||||
| @@ -640,7 +641,9 @@ class InfoExtractor(object): | |||||||
|  |  | ||||||
|         return formats |         return formats | ||||||
|  |  | ||||||
|     def _extract_m3u8_formats(self, m3u8_url, video_id, ext=None): |     def _extract_m3u8_formats(self, m3u8_url, video_id, ext=None, | ||||||
|  |                               entry_protocol='m3u8', preference=None): | ||||||
|  |  | ||||||
|         formats = [{ |         formats = [{ | ||||||
|             'format_id': 'm3u8-meta', |             'format_id': 'm3u8-meta', | ||||||
|             'url': m3u8_url, |             'url': m3u8_url, | ||||||
| @@ -651,6 +654,11 @@ class InfoExtractor(object): | |||||||
|             'format_note': 'Quality selection URL', |             'format_note': 'Quality selection URL', | ||||||
|         }] |         }] | ||||||
|  |  | ||||||
|  |         format_url = lambda u: ( | ||||||
|  |             u | ||||||
|  |             if re.match(r'^https?://', u) | ||||||
|  |             else compat_urlparse.urljoin(m3u8_url, u)) | ||||||
|  |  | ||||||
|         m3u8_doc = self._download_webpage(m3u8_url, video_id) |         m3u8_doc = self._download_webpage(m3u8_url, video_id) | ||||||
|         last_info = None |         last_info = None | ||||||
|         kv_rex = re.compile( |         kv_rex = re.compile( | ||||||
| @@ -667,15 +675,17 @@ class InfoExtractor(object): | |||||||
|                 continue |                 continue | ||||||
|             else: |             else: | ||||||
|                 if last_info is None: |                 if last_info is None: | ||||||
|                     formats.append({'url': line}) |                     formats.append({'url': format_url(line)}) | ||||||
|                     continue |                     continue | ||||||
|                 tbr = int_or_none(last_info.get('BANDWIDTH'), scale=1000) |                 tbr = int_or_none(last_info.get('BANDWIDTH'), scale=1000) | ||||||
|  |  | ||||||
|                 f = { |                 f = { | ||||||
|                     'format_id': 'm3u8-%d' % (tbr if tbr else len(formats)), |                     'format_id': 'm3u8-%d' % (tbr if tbr else len(formats)), | ||||||
|                     'url': line.strip(), |                     'url': format_url(line.strip()), | ||||||
|                     'tbr': tbr, |                     'tbr': tbr, | ||||||
|                     'ext': ext, |                     'ext': ext, | ||||||
|  |                     'protocol': entry_protocol, | ||||||
|  |                     'preference': preference, | ||||||
|                 } |                 } | ||||||
|                 codecs = last_info.get('CODECS') |                 codecs = last_info.get('CODECS') | ||||||
|                 if codecs: |                 if codecs: | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import xml.etree.ElementTree | |||||||
| from .common import InfoExtractor | from .common import InfoExtractor | ||||||
| from ..utils import ( | from ..utils import ( | ||||||
|     compat_HTTPError, |     compat_HTTPError, | ||||||
|  |     compat_urllib_request, | ||||||
|     ExtractorError, |     ExtractorError, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -69,6 +70,21 @@ class VevoIE(InfoExtractor): | |||||||
|     }] |     }] | ||||||
|     _SMIL_BASE_URL = 'http://smil.lvl3.vevo.com/' |     _SMIL_BASE_URL = 'http://smil.lvl3.vevo.com/' | ||||||
|  |  | ||||||
|  |     def _real_initialize(self): | ||||||
|  |         req = compat_urllib_request.Request( | ||||||
|  |             'http://www.vevo.com/auth', data=b'') | ||||||
|  |         webpage = self._download_webpage( | ||||||
|  |             req, None, | ||||||
|  |             note='Retrieving oauth token', | ||||||
|  |             errnote='Unable to retrieve oauth token', | ||||||
|  |             fatal=False) | ||||||
|  |         if webpage is False: | ||||||
|  |             self._oauth_token = None | ||||||
|  |         else: | ||||||
|  |             self._oauth_token = self._search_regex( | ||||||
|  |                 r'access_token":\s*"([^"]+)"', | ||||||
|  |                 webpage, 'access token', fatal=False) | ||||||
|  |  | ||||||
|     def _formats_from_json(self, video_info): |     def _formats_from_json(self, video_info): | ||||||
|         last_version = {'version': -1} |         last_version = {'version': -1} | ||||||
|         for version in video_info['videoVersions']: |         for version in video_info['videoVersions']: | ||||||
| @@ -129,6 +145,26 @@ class VevoIE(InfoExtractor): | |||||||
|             }) |             }) | ||||||
|         return formats |         return formats | ||||||
|  |  | ||||||
|  |     def _download_api_formats(self, video_id): | ||||||
|  |         if not self._oauth_token: | ||||||
|  |             self._downloader.report_warning( | ||||||
|  |                 'No oauth token available, skipping API HLS download') | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|  |         api_url = 'https://apiv2.vevo.com/video/%s/streams/hls?token=%s' % ( | ||||||
|  |             video_id, self._oauth_token) | ||||||
|  |         api_data = self._download_json( | ||||||
|  |             api_url, video_id, | ||||||
|  |             note='Downloading HLS formats', | ||||||
|  |             errnote='Failed to download HLS format list', fatal=False) | ||||||
|  |         if api_data is None: | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|  |         m3u8_url = api_data[0]['url'] | ||||||
|  |         return self._extract_m3u8_formats( | ||||||
|  |             m3u8_url, video_id, entry_protocol='m3u8_native', ext='mp4', | ||||||
|  |             preference=0) | ||||||
|  |  | ||||||
|     def _real_extract(self, url): |     def _real_extract(self, url): | ||||||
|         mobj = re.match(self._VALID_URL, url) |         mobj = re.match(self._VALID_URL, url) | ||||||
|         video_id = mobj.group('id') |         video_id = mobj.group('id') | ||||||
| @@ -152,6 +188,9 @@ class VevoIE(InfoExtractor): | |||||||
|         else: |         else: | ||||||
|             age_limit = None |             age_limit = None | ||||||
|  |  | ||||||
|  |         # Download via HLS API | ||||||
|  |         formats.extend(self._download_api_formats(video_id)) | ||||||
|  |  | ||||||
|         # Download SMIL |         # Download SMIL | ||||||
|         smil_blocks = sorted(( |         smil_blocks = sorted(( | ||||||
|             f for f in video_info['videoVersions'] |             f for f in video_info['videoVersions'] | ||||||
| @@ -166,7 +205,6 @@ class VevoIE(InfoExtractor): | |||||||
|                 fatal=False) |                 fatal=False) | ||||||
|             if smil_url_m is not None: |             if smil_url_m is not None: | ||||||
|                 smil_url = smil_url_m |                 smil_url = smil_url_m | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             smil_xml = self._download_webpage(smil_url, video_id, |             smil_xml = self._download_webpage(smil_url, video_id, | ||||||
|                                               'Downloading SMIL info') |                                               'Downloading SMIL info') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Hagemeister
					Philipp Hagemeister