diff --git a/youtube_dl/extractor/naver.py b/youtube_dl/extractor/naver.py index aef8f01f5..25d3ed06d 100644 --- a/youtube_dl/extractor/naver.py +++ b/youtube_dl/extractor/naver.py @@ -84,49 +84,53 @@ class NaverIE(InfoExtractor): }, }] - def _extract_video_formats(self, formats_list): + def _extract_video_formats(self, formats_list, vid): formats = [] for format_el in formats_list: url = format_el.get('source') if url: - encoding_option = format_el.get('encodingOption') - bitrate = format_el.get('bitrate') - formats.append({ - 'format_id': encoding_option.get('id') or encoding_option.get('name'), - 'url': format_el['source'], - 'width': int_or_none(encoding_option.get('width')), - 'height': int_or_none(encoding_option.get('height')), - 'vbr': float_or_none(bitrate.get('video')), - 'abr': float_or_none(bitrate.get('audio')), - 'filesize': int_or_none(format_el.get('size')), - 'vcodec': format_el.get('type'), - 'ext': determine_ext(url, 'mp4'), - }) + if format_el.get('type') == 'HLS': + key = format_el.get('key') + if key: + url += '?%s=%s' % (key['name'], key['value']) + formats.extend(self._extract_m3u8_formats(url, vid, 'mp4', m3u8_id='hls')) + else: + encoding_option = format_el.get('encodingOption') + bitrate = format_el.get('bitrate') + formats.append({ + 'format_id': encoding_option.get('id') or encoding_option.get('name'), + 'url': format_el['source'], + 'width': int_or_none(encoding_option.get('width')), + 'height': int_or_none(encoding_option.get('height')), + 'vbr': float_or_none(bitrate.get('video')), + 'abr': float_or_none(bitrate.get('audio')), + 'filesize': int_or_none(format_el.get('size')), + 'vcodec': format_el.get('type'), + 'ext': determine_ext(url, 'mp4'), + }) if formats: self._sort_formats(formats) return formats - def _extract_video_info(self, vid, key): - play_data = self._download_json( - 'http://global.apis.naver.com/linetv/rmcnmv/vod_play_videoInfo.json?' + compat_urllib_parse.urlencode({'videoId': vid, 'key': key}), - vid, 'Downloading video info') + def _parse_video_info(self, play_data, vid): meta = play_data.get('meta') - user = meta.get('user') + user = meta.get('user', {}) thumbnails = [] - for thumbnail in play_data['thumbnails']['list']: + for thumbnail in play_data.get('thumbnails', {}).get('list', []): thumbnails.append({'url': thumbnail['source']}) - formats = self._extract_video_formats(play_data['videos']['list']) - if not formats: - video_info = self._download_json( - 'http://serviceapi.rmcnmv.naver.com/mobile/getVideoInfo.nhn?' + compat_urllib_parse.urlencode({'videoId': vid, 'inKey': key, 'protocol': 'http'}), - vid, 'Downloading video info') - formats = self._extract_video_formats(video_info['videos']['list']) + subtitles = {} + for caption in play_data.get('captions', {}).get('list', []): + subtitles[caption['language']] = [ + {'ext': determine_ext(caption['source'], default_ext='vtt'), + 'url': caption['source']}] + + formats = self._extract_video_formats(play_data['videos']['list'] + play_data.get('streams', []), vid) return { 'id': vid, - 'title': meta['subject'], + 'title': meta.get('subject'), 'formats': formats, 'thumbnail': meta.get('cover', {}).get('source'), 'thumbnails': thumbnails, @@ -135,6 +139,18 @@ class NaverIE(InfoExtractor): 'uploader': user.get('name'), } + def _extract_video_info(self, vid, key): + play_data = self._download_json( + 'http://global.apis.naver.com/rmcnmv/rmcnmv/vod_play_videoInfo.json?' + compat_urllib_parse.urlencode({'videoId': vid, 'key': key}), + vid, 'Downloading video info') + info = self._parse_video_info(play_data, vid) + if not info['formats']: + play_data = self._download_json( + 'http://serviceapi.rmcnmv.naver.com/mobile/getVideoInfo.nhn?' + compat_urllib_parse.urlencode({'videoId': vid, 'inKey': key, 'protocol': 'http'}), + vid, 'Downloading video info') + info['formats'] = self._extract_video_formats(play_data['videos']['list'] + play_data.get('streams', []), vid) + return info + def _extract_id_and_key(self, webpage): m_id = re.search(r'(?s)new\s+nhn.rmcnmv.RMCVideoPlayer\(\s*["\']([^"\']+)["\']\s*,\s*(?:{[^}]*?value[^:]*?:\s*?)?["\']([^"\']+)["\']', webpage) if not m_id: diff --git a/youtube_dl/extractor/vlive.py b/youtube_dl/extractor/vlive.py index 86c1cb5ef..8755cdc6a 100644 --- a/youtube_dl/extractor/vlive.py +++ b/youtube_dl/extractor/vlive.py @@ -6,19 +6,19 @@ from hashlib import sha1 from base64 import b64encode from time import time -from .common import InfoExtractor +from .naver import NaverIE from ..utils import ( ExtractorError, - determine_ext + int_or_none, ) from ..compat import compat_urllib_parse -class VLiveIE(InfoExtractor): +class VLiveIE(NaverIE): IE_NAME = 'vlive' # www.vlive.tv/video/ links redirect to m.vlive.tv/video/ for mobile devices _VALID_URL = r'https?://(?:(www|m)\.)?vlive\.tv/video/(?P[0-9]+)' - _TEST = { + _TESTS = [{ 'url': 'http://m.vlive.tv/video/1326', 'md5': 'cc7314812855ce56de70a06a27314983', 'info_dict': { @@ -27,60 +27,55 @@ class VLiveIE(InfoExtractor): 'title': '[V] Girl\'s Day\'s Broadcast', 'creator': 'Girl\'s Day', }, - } + }] _SECRET = 'rFkwZet6pqk1vQt6SxxUkAHX7YL3lmqzUMrU4IDusTo4jEBdtOhNfT4BYYAdArwH' def _real_extract(self, url): video_id = self._match_id(url) - webpage = self._download_webpage( - 'http://m.vlive.tv/video/%s' % video_id, - video_id, note='Download video page') + status = self._download_json( + 'http://www.vlive.tv/video/status?videoSeq=%s' % video_id, + video_id, note='Download status metadata') - title = self._og_search_title(webpage) - thumbnail = self._og_search_thumbnail(webpage) - creator = self._html_search_regex( - r']+class="name">([^<>]+)', webpage, 'creator') - - url = 'http://global.apis.naver.com/globalV/globalV/vod/%s/playinfo?' % video_id - msgpad = '%.0f' % (time() * 1000) - md = b64encode( - hmac.new(self._SECRET.encode('ascii'), - (url[:255] + msgpad).encode('ascii'), sha1).digest() - ) - url += '&' + compat_urllib_parse.urlencode({'msgpad': msgpad, 'md': md}) - playinfo = self._download_json(url, video_id, 'Downloading video json') - - if playinfo.get('message', '') != 'success': - raise ExtractorError(playinfo.get('message', 'JSON request unsuccessful')) - - if not playinfo.get('result'): - raise ExtractorError('No videos found.') - - formats = [] - for vid in playinfo['result'].get('videos', {}).get('list', []): - formats.append({ - 'url': vid['source'], - 'ext': 'mp4', - 'abr': vid.get('bitrate', {}).get('audio'), - 'vbr': vid.get('bitrate', {}).get('video'), - 'format_id': vid['encodingOption']['name'], - 'height': vid.get('height'), - 'width': vid.get('width'), + vid = status.get('vodId') + if vid: + key = status.get('vodInKey') + if not key: + key = self._download_webpage('http://www.vlive.tv/video/inkey?vodId=%s' % vid, video_id) + if key: + video_info = self._extract_video_info(vid, key) + elif status['status'] not in ('CANCELED', 'COMING_SOON', 'NOT_FOUND'): + webpage = self._download_webpage( + 'http://m.vlive.tv/video/%s' % video_id, + video_id, note='Download video page') + title = self._og_search_title(webpage) + thumbnail = self._og_search_thumbnail(webpage) + creator = self._html_search_regex( + r']+class="name">([^<>]+)', webpage, 'creator') + url = 'http://global.apis.naver.com/globalV/globalV/vod/%s/playinfo?' % video_id + msgpad = '%.0f' % (time() * 1000) + md = b64encode( + hmac.new(self._SECRET.encode('ascii'), + (url[:255] + msgpad).encode('ascii'), sha1).digest() + ) + url += '&' + compat_urllib_parse.urlencode({'msgpad': msgpad, 'md': md}) + playinfo = self._download_json(url, video_id, 'Downloading video json') + if playinfo.get('message', '') != 'success': + raise ExtractorError(playinfo.get('message', 'JSON request unsuccessful')) + result = playinfo.get('result') + if not result: + raise ExtractorError('No videos found.') + video_info = self._parse_video_info(result, video_id) + video_info.update({ + 'title': title, + 'thumbnail': thumbnail, + 'creator': creator, }) - self._sort_formats(formats) - - subtitles = {} - for caption in playinfo['result'].get('captions', {}).get('list', []): - subtitles[caption['language']] = [ - {'ext': determine_ext(caption['source'], default_ext='vtt'), - 'url': caption['source']}] - - return { - 'id': video_id, - 'title': title, - 'creator': creator, - 'thumbnail': thumbnail, - 'formats': formats, - 'subtitles': subtitles, - } + if video_info: + video_info.update({ + 'id': video_id, + 'view_count': int_or_none(status.get('playCount')), + 'likes': int_or_none(status.get('likeCount')), + }) + return video_info + raise ExtractorError(status['status'])