]>
Commit | Line | Data |
---|---|---|
e452345f RA |
1 | import hashlib |
2 | import random | |
b1742275 | 3 | |
e452345f | 4 | from ..compat import compat_str |
b1742275 | 5 | from .common import InfoExtractor |
e452345f RA |
6 | from ..utils import ( |
7 | clean_html, | |
8 | int_or_none, | |
9 | try_get, | |
10 | ) | |
11 | ||
12 | ||
13 | class JamendoIE(InfoExtractor): | |
3c7da54c S |
14 | _VALID_URL = r'''(?x) |
15 | https?:// | |
16 | (?: | |
17 | licensing\.jamendo\.com/[^/]+| | |
18 | (?:www\.)?jamendo\.com | |
19 | ) | |
e452345f | 20 | /track/(?P<id>[0-9]+)(?:/(?P<display_id>[^/?#&]+))? |
3c7da54c S |
21 | ''' |
22 | _TESTS = [{ | |
b1742275 T |
23 | 'url': 'https://www.jamendo.com/track/196219/stories-from-emona-i', |
24 | 'md5': '6e9e82ed6db98678f171c25a8ed09ffd', | |
25 | 'info_dict': { | |
26 | 'id': '196219', | |
27 | 'display_id': 'stories-from-emona-i', | |
28 | 'ext': 'flac', | |
f7ad7160 | 29 | # 'title': 'Maya Filipič - Stories from Emona I', |
30 | 'title': 'Stories from Emona I', | |
5f8ea7e0 ES |
31 | 'artist': 'Maya Filipič', |
32 | 'album': 'Between two worlds', | |
3cbecdd1 S |
33 | 'track': 'Stories from Emona I', |
34 | 'duration': 210, | |
5f8ea7e0 | 35 | 'thumbnail': 'https://usercontent.jamendo.com?type=album&id=29279&width=300&trackid=196219', |
e452345f RA |
36 | 'timestamp': 1217438117, |
37 | 'upload_date': '20080730', | |
f7ad7160 | 38 | 'license': 'by-nc-nd', |
39 | 'view_count': int, | |
40 | 'like_count': int, | |
41 | 'average_rating': int, | |
42 | 'tags': ['piano', 'peaceful', 'newage', 'strings', 'upbeat'], | |
b1742275 | 43 | } |
3c7da54c S |
44 | }, { |
45 | 'url': 'https://licensing.jamendo.com/en/track/1496667/energetic-rock', | |
46 | 'only_matching': True, | |
47 | }] | |
b1742275 | 48 | |
5f8ea7e0 | 49 | def _call_api(self, resource, resource_id, fatal=True): |
f7ad7160 | 50 | path = '/api/%ss' % resource |
51 | rand = compat_str(random.random()) | |
52 | return self._download_json( | |
5f8ea7e0 | 53 | 'https://www.jamendo.com' + path, resource_id, fatal=fatal, query={ |
f7ad7160 | 54 | 'id[]': resource_id, |
55 | }, headers={ | |
56 | 'X-Jam-Call': '$%s*%s~' % (hashlib.sha1((path + rand).encode()).hexdigest(), rand) | |
57 | })[0] | |
58 | ||
b1742275 | 59 | def _real_extract(self, url): |
bdbafb39 | 60 | track_id, display_id = self._match_valid_url(url).groups() |
f7ad7160 | 61 | # webpage = self._download_webpage( |
62 | # 'https://www.jamendo.com/track/' + track_id, track_id) | |
63 | # models = self._parse_json(self._html_search_regex( | |
64 | # r"data-bundled-models='([^']+)", | |
65 | # webpage, 'bundled models'), track_id) | |
66 | # track = models['track']['models'][0] | |
67 | track = self._call_api('track', track_id) | |
e452345f | 68 | title = track_name = track['name'] |
f7ad7160 | 69 | # get_model = lambda x: try_get(models, lambda y: y[x]['models'][0], dict) or {} |
70 | # artist = get_model('artist') | |
71 | # artist_name = artist.get('name') | |
72 | # if artist_name: | |
73 | # title = '%s - %s' % (artist_name, title) | |
74 | # album = get_model('album') | |
5f8ea7e0 ES |
75 | artist = self._call_api("artist", track.get('artistId'), fatal=False) |
76 | album = self._call_api("album", track.get('albumId'), fatal=False) | |
d3b6b3b9 S |
77 | |
78 | formats = [{ | |
79 | 'url': 'https://%s.jamendo.com/?trackid=%s&format=%s&from=app-97dab294' | |
80 | % (sub_domain, track_id, format_id), | |
81 | 'format_id': format_id, | |
82 | 'ext': ext, | |
83 | 'quality': quality, | |
84 | } for quality, (format_id, sub_domain, ext) in enumerate(( | |
85 | ('mp31', 'mp3l', 'mp3'), | |
86 | ('mp32', 'mp3d', 'mp3'), | |
87 | ('ogg1', 'ogg', 'ogg'), | |
88 | ('flac', 'flac', 'flac'), | |
89 | ))] | |
90 | self._sort_formats(formats) | |
91 | ||
e452345f RA |
92 | urls = [] |
93 | thumbnails = [] | |
f7ad7160 | 94 | for covers in (track.get('cover') or {}).values(): |
e452345f RA |
95 | for cover_id, cover_url in covers.items(): |
96 | if not cover_url or cover_url in urls: | |
97 | continue | |
98 | urls.append(cover_url) | |
99 | size = int_or_none(cover_id.lstrip('size')) | |
100 | thumbnails.append({ | |
101 | 'id': cover_id, | |
102 | 'url': cover_url, | |
103 | 'width': size, | |
104 | 'height': size, | |
105 | }) | |
106 | ||
107 | tags = [] | |
f7ad7160 | 108 | for tag in (track.get('tags') or []): |
e452345f RA |
109 | tag_name = tag.get('name') |
110 | if not tag_name: | |
111 | continue | |
112 | tags.append(tag_name) | |
113 | ||
114 | stats = track.get('stats') or {} | |
f7ad7160 | 115 | license = track.get('licenseCC') or [] |
b1742275 | 116 | |
b1742275 T |
117 | return { |
118 | 'id': track_id, | |
119 | 'display_id': display_id, | |
e452345f | 120 | 'thumbnails': thumbnails, |
b1742275 | 121 | 'title': title, |
e452345f RA |
122 | 'description': track.get('description'), |
123 | 'duration': int_or_none(track.get('duration')), | |
5f8ea7e0 | 124 | 'artist': artist.get('name'), |
e452345f | 125 | 'track': track_name, |
5f8ea7e0 | 126 | 'album': album.get('name'), |
e452345f | 127 | 'formats': formats, |
f7ad7160 | 128 | 'license': '-'.join(license) if license else None, |
e452345f RA |
129 | 'timestamp': int_or_none(track.get('dateCreated')), |
130 | 'view_count': int_or_none(stats.get('listenedAll')), | |
131 | 'like_count': int_or_none(stats.get('favorited')), | |
132 | 'average_rating': int_or_none(stats.get('averageNote')), | |
133 | 'tags': tags, | |
b1742275 T |
134 | } |
135 | ||
136 | ||
6368e2e6 | 137 | class JamendoAlbumIE(JamendoIE): # XXX: Do not subclass from concrete IE |
e452345f | 138 | _VALID_URL = r'https?://(?:www\.)?jamendo\.com/album/(?P<id>[0-9]+)' |
f7ad7160 | 139 | _TESTS = [{ |
b1742275 T |
140 | 'url': 'https://www.jamendo.com/album/121486/duck-on-cover', |
141 | 'info_dict': { | |
142 | 'id': '121486', | |
e452345f RA |
143 | 'title': 'Duck On Cover', |
144 | 'description': 'md5:c2920eaeef07d7af5b96d7c64daf1239', | |
b1742275 | 145 | }, |
d3b6b3b9 S |
146 | 'playlist': [{ |
147 | 'md5': 'e1a2fcb42bda30dfac990212924149a8', | |
148 | 'info_dict': { | |
149 | 'id': '1032333', | |
150 | 'ext': 'flac', | |
5f8ea7e0 | 151 | 'title': 'Warmachine', |
3cbecdd1 S |
152 | 'artist': 'Shearer', |
153 | 'track': 'Warmachine', | |
e452345f RA |
154 | 'timestamp': 1368089771, |
155 | 'upload_date': '20130509', | |
5f8ea7e0 ES |
156 | 'view_count': int, |
157 | 'thumbnail': 'https://usercontent.jamendo.com?type=album&id=121486&width=300&trackid=1032333', | |
158 | 'duration': 190, | |
159 | 'license': 'by', | |
160 | 'album': 'Duck On Cover', | |
161 | 'average_rating': 4, | |
162 | 'tags': ['rock', 'drums', 'bass', 'world', 'punk', 'neutral'], | |
163 | 'like_count': int, | |
d3b6b3b9 S |
164 | } |
165 | }, { | |
166 | 'md5': '1f358d7b2f98edfe90fd55dac0799d50', | |
167 | 'info_dict': { | |
168 | 'id': '1032330', | |
169 | 'ext': 'flac', | |
5f8ea7e0 | 170 | 'title': 'Without Your Ghost', |
3cbecdd1 S |
171 | 'artist': 'Shearer', |
172 | 'track': 'Without Your Ghost', | |
e452345f RA |
173 | 'timestamp': 1368089771, |
174 | 'upload_date': '20130509', | |
5f8ea7e0 ES |
175 | 'duration': 192, |
176 | 'tags': ['rock', 'drums', 'bass', 'world', 'punk'], | |
177 | 'album': 'Duck On Cover', | |
178 | 'thumbnail': 'https://usercontent.jamendo.com?type=album&id=121486&width=300&trackid=1032330', | |
179 | 'view_count': int, | |
180 | 'average_rating': 4, | |
181 | 'license': 'by', | |
182 | 'like_count': int, | |
b1742275 | 183 | } |
d3b6b3b9 | 184 | }], |
b1742275 T |
185 | 'params': { |
186 | 'playlistend': 2 | |
187 | } | |
f7ad7160 | 188 | }] |
e452345f | 189 | |
b1742275 | 190 | def _real_extract(self, url): |
e452345f RA |
191 | album_id = self._match_id(url) |
192 | album = self._call_api('album', album_id) | |
193 | album_name = album.get('name') | |
194 | ||
195 | entries = [] | |
f7ad7160 | 196 | for track in (album.get('tracks') or []): |
e452345f RA |
197 | track_id = track.get('id') |
198 | if not track_id: | |
199 | continue | |
200 | track_id = compat_str(track_id) | |
201 | entries.append({ | |
202 | '_type': 'url_transparent', | |
203 | 'url': 'https://www.jamendo.com/track/' + track_id, | |
204 | 'ie_key': JamendoIE.ie_key(), | |
205 | 'id': track_id, | |
206 | 'album': album_name, | |
207 | }) | |
208 | ||
209 | return self.playlist_result( | |
210 | entries, album_id, album_name, | |
211 | clean_html(try_get(album, lambda x: x['description']['en'], compat_str))) |