mirror of
https://github.com/yt-dlp/yt-dlp
synced 2024-12-29 22:24:25 +01:00
[vevo] Support 1080p videos (Fixes #3656)
This commit is contained in:
parent
2f771f6c99
commit
f0b5d6af74
4 changed files with 102 additions and 4 deletions
|
@ -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')
|
||||||
|
|
Loading…
Reference in a new issue