]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/vevo.py
Completely change project name to yt-dlp (#85)
[yt-dlp.git] / yt_dlp / extractor / vevo.py
CommitLineData
45d7bc2f
JMF
1from __future__ import unicode_literals
2
70d1924f 3import re
55992530 4import json
70d1924f
JMF
5
6from .common import InfoExtractor
e0da32df 7from ..compat import (
5c9ced95 8 compat_str,
e0da32df 9 compat_urlparse,
9bccdc70 10 compat_HTTPError,
e0da32df 11)
1cc79574 12from ..utils import (
70d1924f 13 ExtractorError,
7d3d06a1 14 int_or_none,
9165d6ba 15 parse_iso8601,
70d1924f
JMF
16)
17
88bd97e3 18
9618c448 19class VevoBaseIE(InfoExtractor):
9d0c08a0 20 def _extract_json(self, webpage, video_id):
9618c448
S
21 return self._parse_json(
22 self._search_regex(
23 r'window\.__INITIAL_STORE__\s*=\s*({.+?});\s*</script>',
24 webpage, 'initial store'),
9d0c08a0 25 video_id)
9618c448
S
26
27
28class VevoIE(VevoBaseIE):
2975fe1a 29 '''
0577177e 30 Accepts urls from vevo.com or in the format 'vevo:{id}'
3266f0c6 31 (currently used by MTVIE and MySpaceIE)
2975fe1a 32 '''
f25571ff 33 _VALID_URL = r'''(?x)
92519402 34 (?:https?://(?:www\.)?vevo\.com/watch/(?!playlist|genre)(?:[^/]+/(?:[^/]+/)?)?|
f25571ff 35 https?://cache\.vevo\.com/m/html/embed\.html\?video=|
ebce53b3 36 https?://videoplayer\.vevo\.com/embed/embedded\?videoId=|
d1e41164 37 https?://embed\.vevo\.com/.*?[?&]isrc=|
f25571ff
PH
38 vevo:)
39 (?P<id>[^&?#]+)'''
fd5e6f7e 40
dd2d55f1 41 _TESTS = []
2975fe1a 42 _VERSIONS = {
9165d6ba 43 0: 'youtube', # only in AuthenticateVideo videoVersions
2975fe1a 44 1: 'level3',
45 2: 'akamai',
46 3: 'level3',
47 4: 'amazon',
48 }
49
682f8c43 50 def _initialize_api(self, video_id):
9165d6ba 51 webpage = self._download_webpage(
b07ea5ea 52 'https://accounts.vevo.com/token', None,
9165d6ba 53 note='Retrieving oauth token',
b07ea5ea
S
54 errnote='Unable to retrieve oauth token',
55 data=json.dumps({
56 'client_id': 'SPupX1tvqFEopQ1YS6SS',
57 'grant_type': 'urn:vevo:params:oauth:grant-type:anonymous',
58 }).encode('utf-8'),
59 headers={
60 'Content-Type': 'application/json',
61 })
9165d6ba 62
621a2800 63 if re.search(r'(?i)THIS PAGE IS CURRENTLY UNAVAILABLE IN YOUR REGION', webpage):
bc7e77a0
S
64 self.raise_geo_restricted(
65 '%s said: This page is currently unavailable in your region' % self.IE_NAME)
9165d6ba 66
67 auth_info = self._parse_json(webpage, video_id)
55992530 68 self._api_url_template = self.http_scheme() + '//apiv2.vevo.com/%s?token=' + auth_info['legacy_token']
9165d6ba 69
516ea41a 70 def _call_api(self, path, *args, **kwargs):
9bccdc70
RA
71 try:
72 data = self._download_json(self._api_url_template % path, *args, **kwargs)
73 except ExtractorError as e:
74 if isinstance(e.cause, compat_HTTPError):
75 errors = self._parse_json(e.cause.read().decode(), None)['errors']
76 error_message = ', '.join([error['message'] for error in errors])
77 raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
78 raise
79 return data
9165d6ba 80
72321ead 81 def _real_extract(self, url):
4b942883 82 video_id = self._match_id(url)
72321ead 83
9bccdc70 84 self._initialize_api(video_id)
682f8c43 85
9bccdc70
RA
86 video_info = self._call_api(
87 'video/%s' % video_id, video_id, 'Downloading api video info',
88 'Failed to download video info')
2975fe1a 89
9bccdc70
RA
90 video_versions = self._call_api(
91 'video/%s/streams' % video_id, video_id,
92 'Downloading video versions info',
93 'Failed to download video versions info',
94 fatal=False)
ff51983e 95
9bccdc70 96 # Some videos are only available via webpage (e.g.
067aa17e 97 # https://github.com/ytdl-org/youtube-dl/issues/9366)
9bccdc70
RA
98 if not video_versions:
99 webpage = self._download_webpage(url, video_id)
9d0c08a0
YCH
100 json_data = self._extract_json(webpage, video_id)
101 if 'streams' in json_data.get('default', {}):
102 video_versions = json_data['default']['streams'][video_id][0]
103 else:
104 video_versions = [
105 value
106 for key, value in json_data['apollo']['data'].items()
107 if key.startswith('%s.streams' % video_id)]
9618c448 108
9bccdc70
RA
109 uploader = None
110 artist = None
111 featured_artist = None
112 artists = video_info.get('artists')
113 for curr_artist in artists:
114 if curr_artist.get('role') == 'Featured':
115 featured_artist = curr_artist['name']
116 else:
117 artist = uploader = curr_artist['name']
9165d6ba 118
9bccdc70
RA
119 formats = []
120 for video_version in video_versions:
9d0c08a0 121 version = self._VERSIONS.get(video_version.get('version'), 'generic')
9bccdc70
RA
122 version_url = video_version.get('url')
123 if not version_url:
124 continue
9165d6ba 125
9bccdc70
RA
126 if '.ism' in version_url:
127 continue
128 elif '.mpd' in version_url:
129 formats.extend(self._extract_mpd_formats(
130 version_url, video_id, mpd_id='dash-%s' % version,
131 note='Downloading %s MPD information' % version,
132 errnote='Failed to download %s MPD information' % version,
133 fatal=False))
134 elif '.m3u8' in version_url:
135 formats.extend(self._extract_m3u8_formats(
136 version_url, video_id, 'mp4', 'm3u8_native',
137 m3u8_id='hls-%s' % version,
138 note='Downloading %s m3u8 information' % version,
139 errnote='Failed to download %s m3u8 information' % version,
140 fatal=False))
141 else:
142 m = re.search(r'''(?xi)
143 _(?P<width>[0-9]+)x(?P<height>[0-9]+)
144 _(?P<vcodec>[a-z0-9]+)
145 _(?P<vbr>[0-9]+)
146 _(?P<acodec>[a-z0-9]+)
147 _(?P<abr>[0-9]+)
148 \.(?P<ext>[a-z0-9]+)''', version_url)
149 if not m:
9165d6ba 150 continue
9165d6ba 151
9bccdc70
RA
152 formats.append({
153 'url': version_url,
154 'format_id': 'http-%s-%s' % (version, video_version['quality']),
155 'vcodec': m.group('vcodec'),
156 'acodec': m.group('acodec'),
157 'vbr': int(m.group('vbr')),
158 'abr': int(m.group('abr')),
159 'ext': m.group('ext'),
160 'width': int(m.group('width')),
161 'height': int(m.group('height')),
162 })
2975fe1a 163 self._sort_formats(formats)
27579b9e 164
881dbc86 165 track = video_info['title']
9508738f
S
166 if featured_artist:
167 artist = '%s ft. %s' % (artist, featured_artist)
168 title = '%s - %s' % (artist, track) if artist else track
5c9ced95
S
169
170 genres = video_info.get('genres')
171 genre = (
3089bc74
S
172 genres[0] if genres and isinstance(genres, list)
173 and isinstance(genres[0], compat_str) else None)
9165d6ba 174
6cadf8c8
PH
175 is_explicit = video_info.get('isExplicit')
176 if is_explicit is True:
177 age_limit = 18
178 elif is_explicit is False:
179 age_limit = 0
180 else:
181 age_limit = None
182
45d7bc2f 183 return {
88bd97e3 184 'id': video_id,
ff51983e 185 'title': title,
88bd97e3 186 'formats': formats,
9165d6ba 187 'thumbnail': video_info.get('imageUrl') or video_info.get('thumbnailUrl'),
9bccdc70 188 'timestamp': parse_iso8601(video_info.get('releaseDate')),
9165d6ba 189 'uploader': uploader,
9bccdc70
RA
190 'duration': int_or_none(video_info.get('duration')),
191 'view_count': int_or_none(video_info.get('views', {}).get('total')),
6cadf8c8 192 'age_limit': age_limit,
881dbc86
S
193 'track': track,
194 'artist': uploader,
195 'genre': genre,
88bd97e3 196 }
e0da32df
S
197
198
9618c448 199class VevoPlaylistIE(VevoBaseIE):
92519402 200 _VALID_URL = r'https?://(?:www\.)?vevo\.com/watch/(?P<kind>playlist|genre)/(?P<id>[^/?#&]+)'
e0da32df
S
201
202 _TESTS = [{
e2bd301c
S
203 'url': 'http://www.vevo.com/watch/genre/rock',
204 'info_dict': {
205 'id': 'rock',
206 'title': 'Rock',
207 },
208 'playlist_count': 20,
e0da32df
S
209 }, {
210 'url': 'http://www.vevo.com/watch/genre/rock?index=0',
211 'only_matching': True,
212 }]
213
214 def _real_extract(self, url):
e2bd301c
S
215 mobj = re.match(self._VALID_URL, url)
216 playlist_id = mobj.group('id')
217 playlist_kind = mobj.group('kind')
e0da32df
S
218
219 webpage = self._download_webpage(url, playlist_id)
220
221 qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
222 index = qs.get('index', [None])[0]
223
224 if index:
225 video_id = self._search_regex(
226 r'<meta[^>]+content=(["\'])vevo://video/(?P<id>.+?)\1[^>]*>',
227 webpage, 'video id', default=None, group='id')
228 if video_id:
229 return self.url_result('vevo:%s' % video_id, VevoIE.ie_key())
230
9d0c08a0 231 playlists = self._extract_json(webpage, playlist_id)['default']['%ss' % playlist_kind]
e0da32df 232
e2bd301c
S
233 playlist = (list(playlists.values())[0]
234 if playlist_kind == 'playlist' else playlists[playlist_id])
e0da32df
S
235
236 entries = [
237 self.url_result('vevo:%s' % src, VevoIE.ie_key())
238 for src in playlist['isrcs']]
239
240 return self.playlist_result(
78a3ff33 241 entries, playlist.get('playlistId') or playlist_id,
e0da32df 242 playlist.get('name'), playlist.get('description'))