Browse Source

[vevo] Support 1080p videos (Fixes #3656)

totalwebcasting
Philipp Hagemeister 10 years ago
parent
commit
f0b5d6af74
4 changed files with 102 additions and 4 deletions
  1. +3
    -0
      youtube_dl/downloader/__init__.py
  2. +47
    -0
      youtube_dl/downloader/hls.py
  3. +13
    -3
      youtube_dl/extractor/common.py
  4. +39
    -1
      youtube_dl/extractor/vevo.py

+ 3
- 0
youtube_dl/downloader/__init__.py View File

@ -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'):


+ 47
- 0
youtube_dl/downloader/hls.py View File

@ -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

+ 13
- 3
youtube_dl/extractor/common.py View File

@ -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:


+ 39
- 1
youtube_dl/extractor/vevo.py View File

@ -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…
Cancel
Save