yading@7
|
1 #!/usr/bin/env python
|
yading@7
|
2 """A python interface to 7Digital web service (non-premium services)"""
|
yading@7
|
3 import os
|
yading@7
|
4 import urllib2, urllib
|
yading@7
|
5 import re
|
yading@7
|
6 import urlparse
|
yading@7
|
7 from xml.dom import minidom
|
yading@7
|
8 try:
|
yading@7
|
9 from hashlib import md5
|
yading@7
|
10 except ImportError:
|
yading@7
|
11 from md5 import md5
|
yading@7
|
12
|
yading@7
|
13 __name__ = 'py7digital'
|
yading@7
|
14 __doc__ = 'A python interface to 7Digital web service'
|
yading@7
|
15 __author__ = 'Oscar Celma, Pau Capella'
|
yading@7
|
16 __version__ = '0.0.1'
|
yading@7
|
17 __license__ = 'GPL'
|
yading@7
|
18 __maintainer__ = 'Oscar Celma'
|
yading@7
|
19 __email__ = 'ocelma@bmat.com'
|
yading@7
|
20 __status__ = 'Beta'
|
yading@7
|
21
|
yading@7
|
22 API_VERSION = '1.2'
|
yading@7
|
23 HOST_NAME = 'api.7digital.com/' + API_VERSION
|
yading@7
|
24 OAUTHKEY = '' #TODO Put your oauth key here
|
yading@7
|
25 COUNTRY = '' # ISO Country
|
yading@7
|
26
|
yading@7
|
27 __cache_dir = './cache' # Set cache directory
|
yading@7
|
28 __cache_enabled = False # Enable cache? if set to True, make sure that __cache_dir exists! (e.g. $ mkdir ./cache)
|
yading@7
|
29
|
yading@7
|
30 class ServiceException(Exception):
|
yading@7
|
31 """Exception related to the web service."""
|
yading@7
|
32
|
yading@7
|
33 def __init__(self, type, message):
|
yading@7
|
34 self._type = type
|
yading@7
|
35 self._message = message
|
yading@7
|
36
|
yading@7
|
37 def __str__(self):
|
yading@7
|
38 return self._type + ': ' + self._message
|
yading@7
|
39
|
yading@7
|
40 def get_message(self):
|
yading@7
|
41 return self._message
|
yading@7
|
42
|
yading@7
|
43 def get_type(self):
|
yading@7
|
44 return self._type
|
yading@7
|
45
|
yading@7
|
46 class _Request(object):
|
yading@7
|
47 """Representing an abstract web service operation."""
|
yading@7
|
48
|
yading@7
|
49 def __init__(self, method_name, params):
|
yading@7
|
50 self.params = params
|
yading@7
|
51 self.method = method_name
|
yading@7
|
52
|
yading@7
|
53 def _download_response(self):
|
yading@7
|
54 """Returns a response"""
|
yading@7
|
55 data = []
|
yading@7
|
56 for name in self.params.keys():
|
yading@7
|
57 data.append('='.join((name, urllib.quote_plus(self.params[name].replace('&', '&').encode('utf8')))))
|
yading@7
|
58 data = '&'.join(data)
|
yading@7
|
59
|
yading@7
|
60 url = HOST_NAME
|
yading@7
|
61 parsed_url = urlparse.urlparse(url)
|
yading@7
|
62 if not parsed_url.scheme:
|
yading@7
|
63 url = "http://" + url
|
yading@7
|
64 url += self.method + '?oauth_consumer_key=' + OAUTHKEY + '&'
|
yading@7
|
65 if COUNTRY:
|
yading@7
|
66 url += 'country=' + COUNTRY + '&'
|
yading@7
|
67 url += data
|
yading@7
|
68 #print url
|
yading@7
|
69
|
yading@7
|
70 request = urllib2.Request(url)
|
yading@7
|
71 response = urllib2.urlopen(request)
|
yading@7
|
72 return response.read()
|
yading@7
|
73
|
yading@7
|
74 def execute(self, cacheable=False):
|
yading@7
|
75 try:
|
yading@7
|
76 if is_caching_enabled() and cacheable:
|
yading@7
|
77 response = self._get_cached_response()
|
yading@7
|
78 else:
|
yading@7
|
79 response = self._download_response()
|
yading@7
|
80 return minidom.parseString(response)
|
yading@7
|
81 except urllib2.HTTPError, e:
|
yading@7
|
82 raise self._get_error(e.fp.read())
|
yading@7
|
83
|
yading@7
|
84 def _get_cache_key(self):
|
yading@7
|
85 """Cache key"""
|
yading@7
|
86 keys = self.params.keys()[:]
|
yading@7
|
87 keys.sort()
|
yading@7
|
88 string = self.method
|
yading@7
|
89 for name in keys:
|
yading@7
|
90 string += name
|
yading@7
|
91 string += self.params[name]
|
yading@7
|
92 return get_md5(string)
|
yading@7
|
93
|
yading@7
|
94 def _is_cached(self):
|
yading@7
|
95 """Returns True if the request is available in the cache."""
|
yading@7
|
96 return os.path.exists(os.path.join(_get_cache_dir(), self._get_cache_key()))
|
yading@7
|
97
|
yading@7
|
98 def _get_cached_response(self):
|
yading@7
|
99 """Returns a file object of the cached response."""
|
yading@7
|
100 if not self._is_cached():
|
yading@7
|
101 response = self._download_response()
|
yading@7
|
102 response_file = open(os.path.join(_get_cache_dir(), self._get_cache_key()), "w")
|
yading@7
|
103 response_file.write(response)
|
yading@7
|
104 response_file.close()
|
yading@7
|
105 return open(os.path.join(_get_cache_dir(), self._get_cache_key()), "r").read()
|
yading@7
|
106
|
yading@7
|
107 def _get_error(self, text):
|
yading@7
|
108 return ServiceException('Error', text)
|
yading@7
|
109 raise
|
yading@7
|
110
|
yading@7
|
111
|
yading@7
|
112 class _BaseObject(object):
|
yading@7
|
113 """An abstract webservices object."""
|
yading@7
|
114
|
yading@7
|
115 def __init__(self, method):
|
yading@7
|
116 self._method = method
|
yading@7
|
117 self._xml = None
|
yading@7
|
118
|
yading@7
|
119 def _request(self, method_name , cacheable = False, params = None):
|
yading@7
|
120 if not params:
|
yading@7
|
121 params = self._get_params()
|
yading@7
|
122 return _Request(method_name, params).execute(cacheable)
|
yading@7
|
123
|
yading@7
|
124 def _get_params(self):
|
yading@7
|
125 return dict()
|
yading@7
|
126
|
yading@7
|
127
|
yading@7
|
128 class Artist(_BaseObject):
|
yading@7
|
129 """ A 7digital artist """
|
yading@7
|
130
|
yading@7
|
131 def __init__(self, id):
|
yading@7
|
132 _BaseObject.__init__(self, '/artist/')
|
yading@7
|
133
|
yading@7
|
134 self.name = None
|
yading@7
|
135 self.id = id
|
yading@7
|
136 self._url = None
|
yading@7
|
137 self._image = None
|
yading@7
|
138 self._albums = None
|
yading@7
|
139 self._top_tracks = None
|
yading@7
|
140 self._tags = None
|
yading@7
|
141 self._recommended_albums = None
|
yading@7
|
142
|
yading@7
|
143 def __repr__(self):
|
yading@7
|
144 return self.get_name().encode('utf8')
|
yading@7
|
145
|
yading@7
|
146 def __eq__(self, other):
|
yading@7
|
147 return self.get_id() == other.get_id()
|
yading@7
|
148
|
yading@7
|
149 def __ne__(self, other):
|
yading@7
|
150 return self.get_id() != other.get_id()
|
yading@7
|
151
|
yading@7
|
152 def get_id(self):
|
yading@7
|
153 """ Returns the 7digital artist id """
|
yading@7
|
154 return self.id
|
yading@7
|
155
|
yading@7
|
156 def get_name(self):
|
yading@7
|
157 """ Returns the name of the artist """
|
yading@7
|
158 if self.name is None:
|
yading@7
|
159 self.name = ''
|
yading@7
|
160 try:
|
yading@7
|
161 if not self._xml : self._xml = self._request(self._method, + 'details', True, {'artistid': self.id})
|
yading@7
|
162 self.name = _extract(self._xml, 'artist', 1) or ''
|
yading@7
|
163 except:
|
yading@7
|
164 return self.name
|
yading@7
|
165 return self.name
|
yading@7
|
166
|
yading@7
|
167 def set_name(self, name) :
|
yading@7
|
168 self.name = name
|
yading@7
|
169
|
yading@7
|
170 def get_image(self):
|
yading@7
|
171 """ Returns the image url of an artist """
|
yading@7
|
172 if self._image is None :
|
yading@7
|
173 if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id})
|
yading@7
|
174 self._image = _extract(self._xml, 'image')
|
yading@7
|
175 return self._image
|
yading@7
|
176
|
yading@7
|
177 def set_image(self, image) :
|
yading@7
|
178 self._image = image
|
yading@7
|
179
|
yading@7
|
180 def get_url(self):
|
yading@7
|
181 """ Returns the url of an artist """
|
yading@7
|
182 if self._url is None :
|
yading@7
|
183 if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id})
|
yading@7
|
184 self._url = _extract(self._xml, 'url')
|
yading@7
|
185 return self._url
|
yading@7
|
186
|
yading@7
|
187 def set_url(self, url) :
|
yading@7
|
188 self._url = url
|
yading@7
|
189
|
yading@7
|
190 def get_tags(self, pageSize=10):
|
yading@7
|
191 """ Returns the tags of an artist """
|
yading@7
|
192 if self._tags is None:
|
yading@7
|
193 self._tags = []
|
yading@7
|
194 xml = self._request(self._method + 'tags', True, {'artistid': self.id})
|
yading@7
|
195 for node in xml.getElementsByTagName('tag') :
|
yading@7
|
196 self._tags.append(_get_tag(node))
|
yading@7
|
197 if self._tags:
|
yading@7
|
198 self._tags.sort()
|
yading@7
|
199 return self._tags[:pageSize]
|
yading@7
|
200
|
yading@7
|
201 def get_albums(self, pageSize=10):
|
yading@7
|
202 """ Returns the albums of an artist """
|
yading@7
|
203 if self._albums is not None: return self._albums
|
yading@7
|
204
|
yading@7
|
205 results = []
|
yading@7
|
206 xml = self._request(self._method + 'releases', True, {'artistid': self.id, 'pageSize': str(pageSize)})
|
yading@7
|
207 for node in xml.getElementsByTagName('release'):
|
yading@7
|
208 album = _get_album(node, self)
|
yading@7
|
209 results.append(album)
|
yading@7
|
210 self._albums = results
|
yading@7
|
211 return self._albums
|
yading@7
|
212
|
yading@7
|
213 def get_recommended_albums(self, pageSize=10):
|
yading@7
|
214 """ Returns a list of recommended albums based on the seed artist """
|
yading@7
|
215 if self._recommended_albums is not None: return self._recommended_albums
|
yading@7
|
216
|
yading@7
|
217 results = []
|
yading@7
|
218 xml = self._request('/release/recommend', True, {'artistid': self.id, 'pageSize': str(pageSize)}) # TODO if country is set gives different results
|
yading@7
|
219 for node in xml.getElementsByTagName('release'):
|
yading@7
|
220 results.append(_get_album(node, _get_artist(node.getElementsByTagName('artist')[0])))
|
yading@7
|
221 self._recommended_albums = results
|
yading@7
|
222 return self._recommended_albums
|
yading@7
|
223
|
yading@7
|
224 def get_top_tracks(self, pageSize=10):
|
yading@7
|
225 """ Returns the top tracks of an artist """
|
yading@7
|
226 if self._top_tracks is not None: return self._top_tracks
|
yading@7
|
227
|
yading@7
|
228 results = []
|
yading@7
|
229 xml = self._request(self._method + 'toptracks', True, {'artistid': self.id, 'pageSize': str(pageSize)})
|
yading@7
|
230 for node in xml.getElementsByTagName('track'):
|
yading@7
|
231 results.append(_get_track(node, None, self))
|
yading@7
|
232 self._top_tracks = results
|
yading@7
|
233 return self._top_tracks
|
yading@7
|
234
|
yading@7
|
235 class Album(_BaseObject):
|
yading@7
|
236 """ A 7digital album """
|
yading@7
|
237 def __init__(self, id=HOST_NAME):
|
yading@7
|
238 _BaseObject.__init__(self, '/release/')
|
yading@7
|
239 self.id = id
|
yading@7
|
240 self.artist = None
|
yading@7
|
241 self.title = None
|
yading@7
|
242
|
yading@7
|
243 self._url = None
|
yading@7
|
244 self._type = None
|
yading@7
|
245 self._barcode = None
|
yading@7
|
246 self._year = None
|
yading@7
|
247 self._image = None
|
yading@7
|
248 self._label = None
|
yading@7
|
249 self._tags = None
|
yading@7
|
250 self._tracks = None
|
yading@7
|
251 self._similar = None
|
yading@7
|
252 self._release_date = None
|
yading@7
|
253 self._added_date = None
|
yading@7
|
254
|
yading@7
|
255 def __repr__(self):
|
yading@7
|
256 if self.get_artist():
|
yading@7
|
257 return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8')
|
yading@7
|
258 return self.get_title().encode('utf8')
|
yading@7
|
259
|
yading@7
|
260 def __eq__(self, other):
|
yading@7
|
261 return self.get_id() == other.get_id()
|
yading@7
|
262
|
yading@7
|
263 def __ne__(self, other):
|
yading@7
|
264 return self.get_id() != other.get_id()
|
yading@7
|
265
|
yading@7
|
266 def get_id(self):
|
yading@7
|
267 """ Returns the 7digital album id """
|
yading@7
|
268 return self.id
|
yading@7
|
269
|
yading@7
|
270 def get_title(self):
|
yading@7
|
271 """ Returns the name of the album """
|
yading@7
|
272 if self.title is None:
|
yading@7
|
273 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
274 self.title = _extract(self._xml, 'release', 1)
|
yading@7
|
275 return self.title
|
yading@7
|
276
|
yading@7
|
277 def set_title(self, title):
|
yading@7
|
278 if title is None:
|
yading@7
|
279 title = ''
|
yading@7
|
280 self.title = title
|
yading@7
|
281
|
yading@7
|
282 def get_type(self):
|
yading@7
|
283 """ Returns the type (CD, DVD, etc.) of the album """
|
yading@7
|
284 if self._type is None:
|
yading@7
|
285 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
286 self._type = _extract(self._xml, 'type')
|
yading@7
|
287 return self._type
|
yading@7
|
288
|
yading@7
|
289 def set_type(self, type):
|
yading@7
|
290 self._type = type
|
yading@7
|
291
|
yading@7
|
292 def get_year(self):
|
yading@7
|
293 """ Returns the year of the album """
|
yading@7
|
294 if self._year is None:
|
yading@7
|
295 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
296 self._year = _extract(self._xml, 'year')
|
yading@7
|
297 return self._year
|
yading@7
|
298
|
yading@7
|
299 def set_year(self, year):
|
yading@7
|
300 self._year = year
|
yading@7
|
301
|
yading@7
|
302 def get_barcode(self):
|
yading@7
|
303 """ Returns the barcode of the album """
|
yading@7
|
304 if self._barcode is None:
|
yading@7
|
305 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
306 self.barcode = _extract(self._xml, 'barcode')
|
yading@7
|
307 return self._barcode
|
yading@7
|
308
|
yading@7
|
309 def set_barcode(self, barcode):
|
yading@7
|
310 self._barcode = barcode
|
yading@7
|
311
|
yading@7
|
312 def get_url(self):
|
yading@7
|
313 """ Returns the url of the album """
|
yading@7
|
314 if self._url is None :
|
yading@7
|
315 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
316 self._url = _extract(self._xml, 'url')
|
yading@7
|
317 return self._url
|
yading@7
|
318
|
yading@7
|
319 def set_url(self, url) :
|
yading@7
|
320 self._url = url
|
yading@7
|
321
|
yading@7
|
322 def get_label(self):
|
yading@7
|
323 """ Returns the label of the album """
|
yading@7
|
324 if self._label is None:
|
yading@7
|
325 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
326 self.set_label(_get_label(self._xml.getElementsByTagName('label')))
|
yading@7
|
327 return self._label
|
yading@7
|
328
|
yading@7
|
329 def set_label(self, label):
|
yading@7
|
330 self._label = label
|
yading@7
|
331
|
yading@7
|
332 def get_release_date(self):
|
yading@7
|
333 """ Returns the release date of the album """
|
yading@7
|
334 if self._release_date is None :
|
yading@7
|
335 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
336 self._release_date = _extract(self._xml, 'releaseDate')
|
yading@7
|
337 return self._release_date
|
yading@7
|
338
|
yading@7
|
339 def set_release_date(self, release_date):
|
yading@7
|
340 self._release_date = release_date
|
yading@7
|
341
|
yading@7
|
342 def get_added_date(self):
|
yading@7
|
343 """ Returns the added date of the album """
|
yading@7
|
344 if self._added_date is None :
|
yading@7
|
345 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
346 self._added_date = _extract(self._xml, 'addedDate')
|
yading@7
|
347 return self._added_date
|
yading@7
|
348
|
yading@7
|
349 def set_added_date(self, added_date):
|
yading@7
|
350 self._added_date = added_date
|
yading@7
|
351
|
yading@7
|
352 def get_artist(self):
|
yading@7
|
353 """ Returns the Artist of the album """
|
yading@7
|
354 if not self.artist:
|
yading@7
|
355 self.set_artist(_get_artist(self._xml.getElementsByTagName('artist')))
|
yading@7
|
356 return self.artist
|
yading@7
|
357
|
yading@7
|
358 def set_artist(self, artist):
|
yading@7
|
359 """ Sets the Artist object of the track """
|
yading@7
|
360 self.artist = artist
|
yading@7
|
361
|
yading@7
|
362 def get_image(self):
|
yading@7
|
363 """ Returns album image url """
|
yading@7
|
364 if self._image is None:
|
yading@7
|
365 if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id})
|
yading@7
|
366 self._image = _extract(self._xml, 'release_small_image')
|
yading@7
|
367 return self._image
|
yading@7
|
368
|
yading@7
|
369 def set_image(self, image):
|
yading@7
|
370 if image is None: image = ''
|
yading@7
|
371 self._image = image
|
yading@7
|
372
|
yading@7
|
373 def get_tags(self, pageSize=10):
|
yading@7
|
374 """ Returns the tags of the album """
|
yading@7
|
375 if self._tags is None:
|
yading@7
|
376 self._tags = []
|
yading@7
|
377 xml = self._request(self._method + 'tags', True, {'releaseid': self.id})
|
yading@7
|
378 for node in xml.getElementsByTagName('tag') :
|
yading@7
|
379 self._tags.append(_get_tag(node))
|
yading@7
|
380 if self._tags:
|
yading@7
|
381 self._tags.sort()
|
yading@7
|
382 return self._tags[:pageSize]
|
yading@7
|
383
|
yading@7
|
384 def get_tracks(self, pageSize=10):
|
yading@7
|
385 """ Returns the tracks of the album """
|
yading@7
|
386 if self._tracks is not None: return self._tracks
|
yading@7
|
387
|
yading@7
|
388 results = []
|
yading@7
|
389 xml = self._request(self._method + 'tracks', True, {'releaseid': self.id, 'pageSize': str(pageSize)})
|
yading@7
|
390 for node in xml.getElementsByTagName('track'):
|
yading@7
|
391 if self.artist is None:
|
yading@7
|
392 self.set_artist(_get_artist(node.getElementsByTagName('artist')))
|
yading@7
|
393 track = _get_track(node, self, self.get_artist())
|
yading@7
|
394 results.append(track)
|
yading@7
|
395 self._tracks = results
|
yading@7
|
396 return self._tracks
|
yading@7
|
397
|
yading@7
|
398 def get_similar(self, pageSize=10):
|
yading@7
|
399 """ Returns a list similar albums """
|
yading@7
|
400 if self._similar is not None: return self._similar
|
yading@7
|
401
|
yading@7
|
402 results = []
|
yading@7
|
403 xml = self._request(self._method + 'recommend', True, {'releaseid': self.id, 'pageSize': str(pageSize)})
|
yading@7
|
404 for node in xml.getElementsByTagName('release'):
|
yading@7
|
405 album = _get_album(node, _get_artist(node.getElementsByTagName('artist')[0]))
|
yading@7
|
406 if self == album:
|
yading@7
|
407 continue #Same album!
|
yading@7
|
408 results.append(album)
|
yading@7
|
409 self._similar = results
|
yading@7
|
410 return self._similar
|
yading@7
|
411
|
yading@7
|
412
|
yading@7
|
413 class Track(_BaseObject):
|
yading@7
|
414 """ A Bmat track. """
|
yading@7
|
415 def __init__(self, id, artist=None):
|
yading@7
|
416 _BaseObject.__init__(self, '/track/')
|
yading@7
|
417
|
yading@7
|
418 if isinstance(artist, Artist):
|
yading@7
|
419 self.artist = artist
|
yading@7
|
420 else:
|
yading@7
|
421 self.artist = None
|
yading@7
|
422 self.id = id
|
yading@7
|
423 self.title = None
|
yading@7
|
424 self.artist = None
|
yading@7
|
425 self.album = None
|
yading@7
|
426
|
yading@7
|
427 self._isrc = None
|
yading@7
|
428 self._url = None
|
yading@7
|
429 self._preview = 'http://api.7digital.com/1.2/track/preview?trackid=' + self.id
|
yading@7
|
430 self._image = None
|
yading@7
|
431 self._tags = None
|
yading@7
|
432 self._duration = None
|
yading@7
|
433 self._version = None
|
yading@7
|
434 self._explicit = None
|
yading@7
|
435 self._position = None
|
yading@7
|
436
|
yading@7
|
437 def __repr__(self):
|
yading@7
|
438 return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8')
|
yading@7
|
439
|
yading@7
|
440 def __eq__(self, other):
|
yading@7
|
441 return self.get_id() == other.get_id()
|
yading@7
|
442
|
yading@7
|
443 def __ne__(self, other):
|
yading@7
|
444 return self.get_id() != other.get_id()
|
yading@7
|
445
|
yading@7
|
446 def get_id(self):
|
yading@7
|
447 """ Returns the track id """
|
yading@7
|
448 return self.id
|
yading@7
|
449
|
yading@7
|
450 def get_title(self):
|
yading@7
|
451 """ Returns the track title """
|
yading@7
|
452 if self.title is None:
|
yading@7
|
453 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
454 self.title = _extract(self._xml, 'track', 1)
|
yading@7
|
455 return self.title
|
yading@7
|
456
|
yading@7
|
457 def set_title(self, title):
|
yading@7
|
458 self.title = title
|
yading@7
|
459
|
yading@7
|
460 def get_isrc(self):
|
yading@7
|
461 """ Returns the ISRC of the track """
|
yading@7
|
462 if self._isrc is None:
|
yading@7
|
463 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
464 self._isrc = _extract(self._xml, 'isrc')
|
yading@7
|
465 return self._isrc
|
yading@7
|
466
|
yading@7
|
467 def set_isrc(self, isrc):
|
yading@7
|
468 self._isrc = isrc
|
yading@7
|
469
|
yading@7
|
470 def get_url(self):
|
yading@7
|
471 """ Returns the url of the track """
|
yading@7
|
472 if self._url is None :
|
yading@7
|
473 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
474 self._url = _extract(self._xml, 'url')
|
yading@7
|
475 return self._url
|
yading@7
|
476
|
yading@7
|
477 def set_url(self, url):
|
yading@7
|
478 self._url = url
|
yading@7
|
479
|
yading@7
|
480 def get_audio(self):
|
yading@7
|
481 return self.get_preview()
|
yading@7
|
482
|
yading@7
|
483 def get_preview(self):
|
yading@7
|
484 """ Returns the url of the track """
|
yading@7
|
485 if self._preview is None :
|
yading@7
|
486 if not self._xml : self._xml = self._request(self._method + 'preview', True, {'trackid': self.id})
|
yading@7
|
487 self._preview = _extract(self._xml, 'url')
|
yading@7
|
488 return self._preview
|
yading@7
|
489
|
yading@7
|
490 def get_duration(self):
|
yading@7
|
491 """ Returns the duration of the track """
|
yading@7
|
492 if self._duration is None :
|
yading@7
|
493 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
494 self._duration = _extract(self._xml, 'duration')
|
yading@7
|
495 return self._duration
|
yading@7
|
496
|
yading@7
|
497 def set_duration(self, duration):
|
yading@7
|
498 self._duration = duration
|
yading@7
|
499
|
yading@7
|
500 def get_position(self):
|
yading@7
|
501 """ Returns the track number in the release """
|
yading@7
|
502 if self._position is None :
|
yading@7
|
503 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
504 self._position = _extract(self._xml, 'trackNumber')
|
yading@7
|
505 return self._position
|
yading@7
|
506
|
yading@7
|
507 def set_position(self, track_number):
|
yading@7
|
508 self._position = track_number
|
yading@7
|
509
|
yading@7
|
510 def get_explicit(self):
|
yading@7
|
511 """ Returns whether the track contains explicit content """
|
yading@7
|
512 if self._explicit is None :
|
yading@7
|
513 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
514 self._explicit = _extract(self._xml, 'explicitContent')
|
yading@7
|
515 return self._explicit
|
yading@7
|
516
|
yading@7
|
517 def set_explicit(self, explicit):
|
yading@7
|
518 self._explicit = explicit
|
yading@7
|
519
|
yading@7
|
520 def get_version(self):
|
yading@7
|
521 """ Returns the version of the track """
|
yading@7
|
522 if self._version is None :
|
yading@7
|
523 if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id})
|
yading@7
|
524 self._version = _extract(self._xml, 'version')
|
yading@7
|
525 return self._version
|
yading@7
|
526
|
yading@7
|
527 def set_version(self, version):
|
yading@7
|
528 self._version = version
|
yading@7
|
529
|
yading@7
|
530 def get_artist(self):
|
yading@7
|
531 """ Returns the Artist of the track """
|
yading@7
|
532 if not self.artist:
|
yading@7
|
533 self.set_artist(_get_artist(self._xml.getElementsByTagName('artist')))
|
yading@7
|
534 return self.artist
|
yading@7
|
535
|
yading@7
|
536 def set_artist(self, artist):
|
yading@7
|
537 """ Sets the Artist object of the track """
|
yading@7
|
538 self.artist = artist
|
yading@7
|
539
|
yading@7
|
540 def get_album(self):
|
yading@7
|
541 """ Returns the associated Album object """
|
yading@7
|
542 if not self.album:
|
yading@7
|
543 self.set_album(_get_album(self._xml.getElementsByTagName('release')))
|
yading@7
|
544 return self.album
|
yading@7
|
545
|
yading@7
|
546 def set_album(self, album):
|
yading@7
|
547 """ Sets the Album object of the track """
|
yading@7
|
548 self.album = album
|
yading@7
|
549
|
yading@7
|
550
|
yading@7
|
551 class Tag(_BaseObject):
|
yading@7
|
552 """ A Tag """
|
yading@7
|
553 def __init__(self, id):
|
yading@7
|
554 _BaseObject.__init__(self, '/tag/')
|
yading@7
|
555
|
yading@7
|
556 self.id = id
|
yading@7
|
557 self.name = None
|
yading@7
|
558 self._url = None
|
yading@7
|
559
|
yading@7
|
560 def __repr__(self):
|
yading@7
|
561 return self.get_name().encode('utf8')
|
yading@7
|
562
|
yading@7
|
563 def __eq__(self, other):
|
yading@7
|
564 return self.get_id() == other.get_id()
|
yading@7
|
565
|
yading@7
|
566 def __ne__(self, other):
|
yading@7
|
567 return self.get_id() != other.get_id()
|
yading@7
|
568
|
yading@7
|
569 def get_id(self):
|
yading@7
|
570 """ Returns the tag id """
|
yading@7
|
571 return self.id
|
yading@7
|
572
|
yading@7
|
573 def get_name(self):
|
yading@7
|
574 """ Returns the tag name """
|
yading@7
|
575 if self.name is None:
|
yading@7
|
576 if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id})
|
yading@7
|
577 self.name = _extract(self._xml, 'name')
|
yading@7
|
578 return self.name
|
yading@7
|
579
|
yading@7
|
580 def set_name(self, name):
|
yading@7
|
581 self.name = name
|
yading@7
|
582
|
yading@7
|
583 def get_url(self):
|
yading@7
|
584 """ Returns the url of the tag"""
|
yading@7
|
585 if self._url is None:
|
yading@7
|
586 if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id})
|
yading@7
|
587 self._url = _extract(self._xml, 'url')
|
yading@7
|
588 return self._url
|
yading@7
|
589
|
yading@7
|
590 def set_url(self, url):
|
yading@7
|
591 self._url = url
|
yading@7
|
592
|
yading@7
|
593 class Label(_BaseObject):
|
yading@7
|
594 """ A Label """
|
yading@7
|
595 def __init__(self, id):
|
yading@7
|
596 _BaseObject.__init__(self, '')
|
yading@7
|
597
|
yading@7
|
598 self.id = id
|
yading@7
|
599 self.name = None
|
yading@7
|
600
|
yading@7
|
601 def __repr__(self):
|
yading@7
|
602 return self.get_name().encode('utf8')
|
yading@7
|
603
|
yading@7
|
604 def __eq__(self, other):
|
yading@7
|
605 return self.get_id() == other.get_id()
|
yading@7
|
606
|
yading@7
|
607 def __ne__(self, other):
|
yading@7
|
608 return self.get_id() != other.get_id()
|
yading@7
|
609
|
yading@7
|
610 def get_id(self):
|
yading@7
|
611 """ Returns the label id """
|
yading@7
|
612 return self.id
|
yading@7
|
613
|
yading@7
|
614 def get_name(self):
|
yading@7
|
615 """ Returns the label name """
|
yading@7
|
616 return self.name
|
yading@7
|
617
|
yading@7
|
618 def set_name(self, name):
|
yading@7
|
619 self.name = name
|
yading@7
|
620
|
yading@7
|
621
|
yading@7
|
622 class _Search(_BaseObject):
|
yading@7
|
623 """ An abstract class for search """
|
yading@7
|
624
|
yading@7
|
625 def __init__(self, method, search_terms, xml_tag):
|
yading@7
|
626 _BaseObject.__init__(self, method)
|
yading@7
|
627
|
yading@7
|
628 self._search_terms = search_terms
|
yading@7
|
629 self._xml_tag = xml_tag
|
yading@7
|
630
|
yading@7
|
631 self._results_per_page = 10
|
yading@7
|
632 if self._search_terms.has_key('pageSize'):
|
yading@7
|
633 self._results_per_page = str(self._search_terms['pageSize'])
|
yading@7
|
634 else:
|
yading@7
|
635 self._search_terms['pageSize'] = str(self._results_per_page)
|
yading@7
|
636 self._last_page_index = 0
|
yading@7
|
637 self._hits = None
|
yading@7
|
638
|
yading@7
|
639 def get_total_result_count(self):
|
yading@7
|
640 if self._hits is not None:
|
yading@7
|
641 return self._hits
|
yading@7
|
642 params = self._get_params()
|
yading@7
|
643 params['pageSize'] = '1'
|
yading@7
|
644 xml = self._request(self._method, True, params)
|
yading@7
|
645 hits = int(_extract(xml, 'totalItems'))
|
yading@7
|
646 self._hits = hits
|
yading@7
|
647 return self._hits
|
yading@7
|
648
|
yading@7
|
649 def _get_params(self):
|
yading@7
|
650 params = {}
|
yading@7
|
651 for key in self._search_terms.keys():
|
yading@7
|
652 params[key] = self._search_terms[key]
|
yading@7
|
653 return params
|
yading@7
|
654
|
yading@7
|
655 def _retrieve_page(self, page_index):
|
yading@7
|
656 """ Returns the xml nodes to process """
|
yading@7
|
657 params = self._get_params()
|
yading@7
|
658
|
yading@7
|
659 if page_index != 0:
|
yading@7
|
660 #offset = self._results_per_page * page_index
|
yading@7
|
661 params["page"] = str(page_index)
|
yading@7
|
662
|
yading@7
|
663 doc = self._request(self._method, True, params)
|
yading@7
|
664 return doc.getElementsByTagName(self._xml_tag)[0]
|
yading@7
|
665
|
yading@7
|
666 def _retrieve_next_page(self):
|
yading@7
|
667 self._last_page_index += 1
|
yading@7
|
668 return self._retrieve_page(self._last_page_index)
|
yading@7
|
669
|
yading@7
|
670 def has_results(self):
|
yading@7
|
671 return self.get_total_result_count() > (self._results_per_page * self._last_page_index)
|
yading@7
|
672
|
yading@7
|
673 def get_next_page(self):
|
yading@7
|
674 master_node = self._retrieve_next_page()
|
yading@7
|
675 return self._get_results(master_node)
|
yading@7
|
676
|
yading@7
|
677 def get_page(self, page=0):
|
yading@7
|
678 if page < 0: page = 0
|
yading@7
|
679 if page > 0: page = page-1
|
yading@7
|
680
|
yading@7
|
681 master_node = self._retrieve_page(page)
|
yading@7
|
682 return self._get_results(master_node)
|
yading@7
|
683
|
yading@7
|
684
|
yading@7
|
685 class ArtistSearch(_Search):
|
yading@7
|
686 """ Search artists """
|
yading@7
|
687
|
yading@7
|
688 def __init__(self, query):
|
yading@7
|
689 _Search.__init__(self, '/artist/search', {'q': query}, 'searchResults')
|
yading@7
|
690
|
yading@7
|
691 def _get_results(self, master_node):
|
yading@7
|
692 results = []
|
yading@7
|
693 for node in master_node.getElementsByTagName('artist'):
|
yading@7
|
694 artist = _get_artist(node)
|
yading@7
|
695 results.append(artist)
|
yading@7
|
696 return results
|
yading@7
|
697
|
yading@7
|
698
|
yading@7
|
699 class ArtistBrowse(_Search):
|
yading@7
|
700 """ Browse artists """
|
yading@7
|
701
|
yading@7
|
702 def __init__(self, letter):
|
yading@7
|
703 _Search.__init__(self, '/artist/browse', {'letter': letter}, 'artists')
|
yading@7
|
704
|
yading@7
|
705 def _get_results(self, master_node):
|
yading@7
|
706 results = []
|
yading@7
|
707 for node in master_node.getElementsByTagName('artist'):
|
yading@7
|
708 artist = _get_artist(node)
|
yading@7
|
709 results.append(artist)
|
yading@7
|
710 return results
|
yading@7
|
711
|
yading@7
|
712 class ArtistDetail(_Search):
|
yading@7
|
713 def __init__(self, artist_id):
|
yading@7
|
714 _Search.__init__(self, '/artist/details', {'artistid': artist_id}, 'artist')
|
yading@7
|
715
|
yading@7
|
716 def _get_results(self, master_node):
|
yading@7
|
717 results = []
|
yading@7
|
718 node = master_node
|
yading@7
|
719
|
yading@7
|
720 artist = _get_artist(node)
|
yading@7
|
721
|
yading@7
|
722 results.append(artist)
|
yading@7
|
723 return results
|
yading@7
|
724
|
yading@7
|
725 class AlbumSearch(_Search):
|
yading@7
|
726 """ Search albums """
|
yading@7
|
727
|
yading@7
|
728 def __init__(self, query):
|
yading@7
|
729 _Search.__init__(self, '/release/search', {'q': query}, 'searchResults')
|
yading@7
|
730
|
yading@7
|
731 def _get_results(self, master_node):
|
yading@7
|
732 results = []
|
yading@7
|
733 for node in master_node.getElementsByTagName('release'):
|
yading@7
|
734 artist = _get_artist(node.getElementsByTagName('artist')[0])
|
yading@7
|
735 album = _get_album(node, artist)
|
yading@7
|
736 results.append(album)
|
yading@7
|
737 return results
|
yading@7
|
738
|
yading@7
|
739 class AlbumCharts(_Search):
|
yading@7
|
740 """ Chart albums """
|
yading@7
|
741
|
yading@7
|
742 def __init__(self, period, todate):
|
yading@7
|
743 _Search.__init__(self, '/release/chart', {'period': period, 'todate': todate}, 'chart')
|
yading@7
|
744
|
yading@7
|
745 def _get_results(self, master_node):
|
yading@7
|
746 results = []
|
yading@7
|
747 for node in master_node.getElementsByTagName('release'):
|
yading@7
|
748 artist = _get_artist(node.getElementsByTagName('artist')[0])
|
yading@7
|
749 album = _get_album(node, artist)
|
yading@7
|
750 results.append(album)
|
yading@7
|
751 return results
|
yading@7
|
752
|
yading@7
|
753 class AlbumReleases(_Search):
|
yading@7
|
754 """ Release albums by date """
|
yading@7
|
755
|
yading@7
|
756 def __init__(self, fromdate, todate):
|
yading@7
|
757 _Search.__init__(self, '/release/bydate', {'fromDate': fromdate, 'toDate': todate}, 'releases')
|
yading@7
|
758
|
yading@7
|
759 def _get_results(self, master_node):
|
yading@7
|
760 results = []
|
yading@7
|
761 for node in master_node.getElementsByTagName('release'):
|
yading@7
|
762 artist = _get_artist(node.getElementsByTagName('artist')[0])
|
yading@7
|
763 album = _get_album(node, artist)
|
yading@7
|
764 results.append(album)
|
yading@7
|
765 return results
|
yading@7
|
766
|
yading@7
|
767 class TrackSearch(_Search):
|
yading@7
|
768 """ Search for tracks """
|
yading@7
|
769
|
yading@7
|
770 def __init__(self, query):
|
yading@7
|
771 _Search.__init__(self, '/track/search', {'q': query}, 'searchResults')
|
yading@7
|
772
|
yading@7
|
773 def _get_results(self, master_node):
|
yading@7
|
774 results = []
|
yading@7
|
775 for node in master_node.getElementsByTagName('track'):
|
yading@7
|
776 artist = _get_artist(node.getElementsByTagName('artist')[0])
|
yading@7
|
777 album = _get_album(node.getElementsByTagName('release')[0], artist)
|
yading@7
|
778 track = _get_track(node, album, artist)
|
yading@7
|
779 results.append(track)
|
yading@7
|
780 return results
|
yading@7
|
781
|
yading@7
|
782 def get_artist_detail(artistId):
|
yading@7
|
783 return ArtistDetail(artistId)
|
yading@7
|
784
|
yading@7
|
785 def search_artist(query):
|
yading@7
|
786 """Search artists by query. Returns an ArtistSearch object.
|
yading@7
|
787 Use get_next_page() to retrieve sequences of results."""
|
yading@7
|
788 return ArtistSearch(query)
|
yading@7
|
789
|
yading@7
|
790 def browse_artists(letter):
|
yading@7
|
791 """Browse artists by letter [a..z]. Returns an ArtistBrowse object.
|
yading@7
|
792 Use get_next_page() to retrieve sequences of results."""
|
yading@7
|
793 return ArtistBrowse(letter)
|
yading@7
|
794
|
yading@7
|
795 def search_album(query):
|
yading@7
|
796 """Search albums by query. Returns the albumSearch object.
|
yading@7
|
797 Use get_next_page() to retrieve sequences of results."""
|
yading@7
|
798 return AlbumSearch(query)
|
yading@7
|
799
|
yading@7
|
800 def album_charts(period, todate):
|
yading@7
|
801 """Get chart albums in a given period of time """
|
yading@7
|
802 return AlbumCharts(period, todate)
|
yading@7
|
803
|
yading@7
|
804 def album_releases(fromdate, todate):
|
yading@7
|
805 """Get releases in a given period of time"""
|
yading@7
|
806 return AlbumReleases(fromdate, todate)
|
yading@7
|
807
|
yading@7
|
808 def search_track(query):
|
yading@7
|
809 """Search tracks by query. Returns a TrackSearch object.
|
yading@7
|
810 Use get_next_page() to retrieve sequences of results."""
|
yading@7
|
811 return TrackSearch(query)
|
yading@7
|
812
|
yading@7
|
813
|
yading@7
|
814 # XML
|
yading@7
|
815 def _extract(node, name, index = 0):
|
yading@7
|
816 """Extracts a value from the xml string"""
|
yading@7
|
817 try:
|
yading@7
|
818 nodes = node.getElementsByTagName(name)
|
yading@7
|
819
|
yading@7
|
820 if len(nodes):
|
yading@7
|
821 if nodes[index].firstChild:
|
yading@7
|
822 return nodes[index].firstChild.data.strip()
|
yading@7
|
823 else:
|
yading@7
|
824 return None
|
yading@7
|
825 except:
|
yading@7
|
826 return None
|
yading@7
|
827
|
yading@7
|
828 def _extract_all(node, name, pageSize_count = None):
|
yading@7
|
829 """Extracts all the values from the xml string. It returns a list."""
|
yading@7
|
830 results = []
|
yading@7
|
831 for i in range(0, len(node.getElementsByTagName(name))):
|
yading@7
|
832 if len(results) == pageSize_count:
|
yading@7
|
833 break
|
yading@7
|
834 results.append(_extract(node, name, i))
|
yading@7
|
835 return results
|
yading@7
|
836
|
yading@7
|
837 def _get_artist(xml):
|
yading@7
|
838 artist_id = xml.getAttribute('id')
|
yading@7
|
839 artist = Artist(artist_id)
|
yading@7
|
840 artist.set_name(_extract(xml, 'name'))
|
yading@7
|
841 return artist
|
yading@7
|
842
|
yading@7
|
843 def _get_album(xml, artist):
|
yading@7
|
844 album_id = xml.getAttribute('id')
|
yading@7
|
845 album = Album(album_id)
|
yading@7
|
846 album.set_artist(artist)
|
yading@7
|
847 album.set_title(_extract(xml, 'title'))
|
yading@7
|
848 album.set_type(_extract(xml, 'type'))
|
yading@7
|
849 album.set_image(_extract(xml, 'image'))
|
yading@7
|
850 album.set_year(_extract(xml, 'year'))
|
yading@7
|
851 album.set_barcode(_extract(xml, 'barcode'))
|
yading@7
|
852 album.set_url(_extract(xml, 'url'))
|
yading@7
|
853 album.set_release_date(_extract(xml, 'releaseDate'))
|
yading@7
|
854 album.set_added_date(_extract(xml, 'addedDate'))
|
yading@7
|
855 #TODO price, formats, artist appears_as
|
yading@7
|
856 try :
|
yading@7
|
857 album.set_label(_get_label(xml.getElementsByTagName('label')[0])) #In some cases that are albums with no label (record company) attached! :-(
|
yading@7
|
858 except:
|
yading@7
|
859 pass
|
yading@7
|
860 return album
|
yading@7
|
861
|
yading@7
|
862 def _get_track(xml, album, artist):
|
yading@7
|
863 track_id = xml.getAttribute('id')
|
yading@7
|
864 track = Track(track_id)
|
yading@7
|
865 track.set_title(_extract(xml, 'title'))
|
yading@7
|
866 track.set_url(_extract(xml, 'url'))
|
yading@7
|
867 track.set_isrc(_extract(xml, 'isrc'))
|
yading@7
|
868 track.set_duration(_extract(xml, 'duration'))
|
yading@7
|
869 track.set_position(_extract(xml, 'trackNumber'))
|
yading@7
|
870 track.set_explicit(_extract(xml, 'explicitContent'))
|
yading@7
|
871 track.set_version(_extract(xml, 'version'))
|
yading@7
|
872 track.set_album(album)
|
yading@7
|
873 track.set_artist(artist)
|
yading@7
|
874 #TODO price, formats
|
yading@7
|
875 return track
|
yading@7
|
876
|
yading@7
|
877 def _get_tag(xml):
|
yading@7
|
878 tag = Tag(_extract(xml, 'text'))
|
yading@7
|
879 tag.set_name(tag.get_id())
|
yading@7
|
880 return tag
|
yading@7
|
881
|
yading@7
|
882 def _get_label(xml):
|
yading@7
|
883 label = ''
|
yading@7
|
884 try:
|
yading@7
|
885 label = Label(xml.getAttribute('id'))
|
yading@7
|
886 label.set_name(_extract(xml, 'name'))
|
yading@7
|
887 except:
|
yading@7
|
888 pass
|
yading@7
|
889 return label
|
yading@7
|
890
|
yading@7
|
891 # CACHE
|
yading@7
|
892 def enable_caching(cache_dir = None):
|
yading@7
|
893 global __cache_dir
|
yading@7
|
894 global __cache_enabled
|
yading@7
|
895
|
yading@7
|
896 if cache_dir == None:
|
yading@7
|
897 import tempfile
|
yading@7
|
898 __cache_dir = tempfile.mkdtemp()
|
yading@7
|
899 else:
|
yading@7
|
900 if not os.path.exists(cache_dir):
|
yading@7
|
901 os.mkdir(cache_dir)
|
yading@7
|
902 __cache_dir = cache_dir
|
yading@7
|
903 __cache_enabled = True
|
yading@7
|
904
|
yading@7
|
905 def disable_caching():
|
yading@7
|
906 global __cache_enabled
|
yading@7
|
907 __cache_enabled = False
|
yading@7
|
908
|
yading@7
|
909 def is_caching_enabled():
|
yading@7
|
910 """Returns True if caching is enabled."""
|
yading@7
|
911 global __cache_enabled
|
yading@7
|
912 return __cache_enabled
|
yading@7
|
913
|
yading@7
|
914 def _get_cache_dir():
|
yading@7
|
915 """Returns the directory in which cache files are saved."""
|
yading@7
|
916 global __cache_dir
|
yading@7
|
917 global __cache_enabled
|
yading@7
|
918 return __cache_dir
|
yading@7
|
919
|
yading@7
|
920 def get_md5(text):
|
yading@7
|
921 """Returns the md5 hash of a string."""
|
yading@7
|
922 hash = md5()
|
yading@7
|
923 hash.update(text.encode('utf8'))
|
yading@7
|
924 return hash.hexdigest()
|
yading@7
|
925
|