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