]>
Commit | Line | Data |
---|---|---|
e7c14660 | 1 | # coding: utf-8 |
4c603938 MA |
2 | from __future__ import unicode_literals |
3 | ||
4 | import re | |
5 | import hashlib | |
4c603938 MA |
6 | |
7 | from .common import InfoExtractor | |
baf510bf S |
8 | from ..compat import ( |
9 | compat_str, | |
10 | compat_urllib_parse, | |
baf510bf | 11 | ) |
47fe42e1 S |
12 | from ..utils import ( |
13 | int_or_none, | |
14 | float_or_none, | |
5c2266df | 15 | sanitized_Request, |
47fe42e1 | 16 | ) |
4c603938 | 17 | |
4c603938 | 18 | |
e4df2f98 S |
19 | class YandexMusicTrackIE(InfoExtractor): |
20 | IE_NAME = 'yandexmusic:track' | |
21 | IE_DESC = 'Яндекс.Музыка - Трек' | |
22 | _VALID_URL = r'https?://music\.yandex\.(?:ru|kz|ua|by)/album/(?P<album_id>\d+)/track/(?P<id>\d+)' | |
23 | ||
24 | _TEST = { | |
25 | 'url': 'http://music.yandex.ru/album/540508/track/4878838', | |
26 | 'md5': 'f496818aa2f60b6c0062980d2e00dc20', | |
27 | 'info_dict': { | |
28 | 'id': '4878838', | |
29 | 'ext': 'mp3', | |
30 | 'title': 'Carlo Ambrosio - Gypsy Eyes 1', | |
31 | 'filesize': 4628061, | |
32 | 'duration': 193.04, | |
33 | } | |
34 | } | |
35 | ||
4c603938 | 36 | def _get_track_url(self, storage_dir, track_id): |
47fe42e1 S |
37 | data = self._download_json( |
38 | 'http://music.yandex.ru/api/v1.5/handlers/api-jsonp.jsx?action=getTrackSrc&p=download-info/%s' | |
39 | % storage_dir, | |
40 | track_id, 'Downloading track location JSON') | |
4c603938 | 41 | |
47fe42e1 | 42 | key = hashlib.md5(('XGRlBW9FXlekgbPrRHuSiA' + data['path'][1:] + data['s']).encode('utf-8')).hexdigest() |
4c603938 MA |
43 | storage = storage_dir.split('.') |
44 | ||
47fe42e1 S |
45 | return ('http://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default' |
46 | % (data['host'], key, data['ts'] + data['path'], storage[1])) | |
4c603938 | 47 | |
47fe42e1 | 48 | def _get_track_info(self, track): |
ab953c64 S |
49 | thumbnail = None |
50 | cover_uri = track.get('albums', [{}])[0].get('coverUri') | |
51 | if cover_uri: | |
52 | thumbnail = cover_uri.replace('%%', 'orig') | |
53 | if not thumbnail.startswith('http'): | |
54 | thumbnail = 'http://' + thumbnail | |
47fe42e1 S |
55 | return { |
56 | 'id': track['id'], | |
57 | 'ext': 'mp3', | |
58 | 'url': self._get_track_url(track['storageDir'], track['id']), | |
59 | 'title': '%s - %s' % (track['artists'][0]['name'], track['title']), | |
60 | 'filesize': int_or_none(track.get('fileSize')), | |
61 | 'duration': float_or_none(track.get('durationMs'), 1000), | |
ab953c64 | 62 | 'thumbnail': thumbnail, |
47fe42e1 | 63 | } |
4c603938 | 64 | |
4c603938 | 65 | def _real_extract(self, url): |
47fe42e1 S |
66 | mobj = re.match(self._VALID_URL, url) |
67 | album_id, track_id = mobj.group('album_id'), mobj.group('id') | |
4c603938 | 68 | |
47fe42e1 S |
69 | track = self._download_json( |
70 | 'http://music.yandex.ru/handlers/track.jsx?track=%s:%s' % (track_id, album_id), | |
71 | track_id, 'Downloading track JSON')['track'] | |
4c603938 | 72 | |
47fe42e1 | 73 | return self._get_track_info(track) |
4c603938 | 74 | |
4c603938 | 75 | |
e7c14660 S |
76 | class YandexMusicPlaylistBaseIE(InfoExtractor): |
77 | def _build_playlist(self, tracks): | |
78 | return [ | |
79 | self.url_result( | |
80 | 'http://music.yandex.ru/album/%s/track/%s' % (track['albums'][0]['id'], track['id'])) | |
6d53cdd6 | 81 | for track in tracks if track.get('albums') and isinstance(track.get('albums'), list)] |
e7c14660 S |
82 | |
83 | ||
84 | class YandexMusicAlbumIE(YandexMusicPlaylistBaseIE): | |
47fe42e1 S |
85 | IE_NAME = 'yandexmusic:album' |
86 | IE_DESC = 'Яндекс.Музыка - Альбом' | |
29171bc2 | 87 | _VALID_URL = r'https?://music\.yandex\.(?:ru|kz|ua|by)/album/(?P<id>\d+)/?(\?|$)' |
4c603938 | 88 | |
47fe42e1 S |
89 | _TEST = { |
90 | 'url': 'http://music.yandex.ru/album/540508', | |
91 | 'info_dict': { | |
92 | 'id': '540508', | |
93 | 'title': 'Carlo Ambrosio - Gypsy Soul (2009)', | |
94 | }, | |
95 | 'playlist_count': 50, | |
96 | } | |
4c603938 MA |
97 | |
98 | def _real_extract(self, url): | |
47fe42e1 | 99 | album_id = self._match_id(url) |
4c603938 | 100 | |
47fe42e1 S |
101 | album = self._download_json( |
102 | 'http://music.yandex.ru/handlers/album.jsx?album=%s' % album_id, | |
103 | album_id, 'Downloading album JSON') | |
4c603938 | 104 | |
e7c14660 | 105 | entries = self._build_playlist(album['volumes'][0]) |
47fe42e1 S |
106 | |
107 | title = '%s - %s' % (album['artists'][0]['name'], album['title']) | |
108 | year = album.get('year') | |
109 | if year: | |
110 | title += ' (%s)' % year | |
111 | ||
112 | return self.playlist_result(entries, compat_str(album['id']), title) | |
4c603938 | 113 | |
4c603938 | 114 | |
e7c14660 | 115 | class YandexMusicPlaylistIE(YandexMusicPlaylistBaseIE): |
47fe42e1 S |
116 | IE_NAME = 'yandexmusic:playlist' |
117 | IE_DESC = 'Яндекс.Музыка - Плейлист' | |
29171bc2 | 118 | _VALID_URL = r'https?://music\.yandex\.(?:ru|kz|ua|by)/users/[^/]+/playlists/(?P<id>\d+)' |
47fe42e1 | 119 | |
baf510bf | 120 | _TESTS = [{ |
47fe42e1 | 121 | 'url': 'http://music.yandex.ru/users/music.partners/playlists/1245', |
4c603938 | 122 | 'info_dict': { |
47fe42e1 S |
123 | 'id': '1245', |
124 | 'title': 'Что слушают Enter Shikari', | |
125 | 'description': 'md5:3b9f27b0efbe53f2ee1e844d07155cc9', | |
126 | }, | |
127 | 'playlist_count': 6, | |
baf510bf S |
128 | }, { |
129 | # playlist exceeding the limit of 150 tracks shipped with webpage (see | |
130 | # https://github.com/rg3/youtube-dl/issues/6666) | |
131 | 'url': 'https://music.yandex.ru/users/ya.playlist/playlists/1036', | |
132 | 'info_dict': { | |
133 | 'id': '1036', | |
134 | 'title': 'Музыка 90-х', | |
135 | }, | |
136 | 'playlist_count': 310, | |
137 | }] | |
4c603938 MA |
138 | |
139 | def _real_extract(self, url): | |
47fe42e1 | 140 | playlist_id = self._match_id(url) |
4c603938 | 141 | |
47fe42e1 | 142 | webpage = self._download_webpage(url, playlist_id) |
4c603938 | 143 | |
baf510bf | 144 | mu = self._parse_json( |
47fe42e1 S |
145 | self._search_regex( |
146 | r'var\s+Mu\s*=\s*({.+?});\s*</script>', webpage, 'player'), | |
baf510bf S |
147 | playlist_id) |
148 | ||
149 | playlist = mu['pageData']['playlist'] | |
150 | tracks, track_ids = playlist['tracks'], playlist['trackIds'] | |
151 | ||
152 | # tracks dictionary shipped with webpage is limited to 150 tracks, | |
153 | # missing tracks should be retrieved manually. | |
154 | if len(tracks) < len(track_ids): | |
155 | present_track_ids = set([compat_str(track['id']) for track in tracks if track.get('id')]) | |
156 | missing_track_ids = set(map(compat_str, track_ids)) - set(present_track_ids) | |
5c2266df | 157 | request = sanitized_Request( |
baf510bf S |
158 | 'https://music.yandex.ru/handlers/track-entries.jsx', |
159 | compat_urllib_parse.urlencode({ | |
160 | 'entries': ','.join(missing_track_ids), | |
161 | 'lang': mu.get('settings', {}).get('lang', 'en'), | |
162 | 'external-domain': 'music.yandex.ru', | |
163 | 'overembed': 'false', | |
164 | 'sign': mu.get('authData', {}).get('user', {}).get('sign'), | |
165 | 'strict': 'true', | |
166 | }).encode('utf-8')) | |
167 | request.add_header('Referer', url) | |
168 | request.add_header('X-Requested-With', 'XMLHttpRequest') | |
169 | ||
170 | missing_tracks = self._download_json( | |
171 | request, playlist_id, 'Downloading missing tracks JSON', fatal=False) | |
172 | if missing_tracks: | |
173 | tracks.extend(missing_tracks) | |
47fe42e1 | 174 | |
47fe42e1 | 175 | return self.playlist_result( |
baf510bf | 176 | self._build_playlist(tracks), |
e7c14660 | 177 | compat_str(playlist_id), |
47fe42e1 | 178 | playlist['title'], playlist.get('description')) |