yading@7: #!/usr/bin/env python yading@7: """A python interface to 7Digital web service (non-premium services)""" yading@7: import os yading@7: import urllib2, urllib yading@7: import re yading@7: import urlparse yading@7: from xml.dom import minidom yading@7: try: yading@7: from hashlib import md5 yading@7: except ImportError: yading@7: from md5 import md5 yading@7: yading@7: __name__ = 'py7digital' yading@7: __doc__ = 'A python interface to 7Digital web service' yading@7: __author__ = 'Oscar Celma, Pau Capella' yading@7: __version__ = '0.0.1' yading@7: __license__ = 'GPL' yading@7: __maintainer__ = 'Oscar Celma' yading@7: __email__ = 'ocelma@bmat.com' yading@7: __status__ = 'Beta' yading@7: yading@7: API_VERSION = '1.2' yading@7: HOST_NAME = 'api.7digital.com/' + API_VERSION yading@7: OAUTHKEY = '' #TODO Put your oauth key here yading@7: COUNTRY = '' # ISO Country yading@7: yading@7: __cache_dir = './cache' # Set cache directory yading@7: __cache_enabled = False # Enable cache? if set to True, make sure that __cache_dir exists! (e.g. $ mkdir ./cache) yading@7: yading@7: class ServiceException(Exception): yading@7: """Exception related to the web service.""" yading@7: yading@7: def __init__(self, type, message): yading@7: self._type = type yading@7: self._message = message yading@7: yading@7: def __str__(self): yading@7: return self._type + ': ' + self._message yading@7: yading@7: def get_message(self): yading@7: return self._message yading@7: yading@7: def get_type(self): yading@7: return self._type yading@7: yading@7: class _Request(object): yading@7: """Representing an abstract web service operation.""" yading@7: yading@7: def __init__(self, method_name, params): yading@7: self.params = params yading@7: self.method = method_name yading@7: yading@7: def _download_response(self): yading@7: """Returns a response""" yading@7: data = [] yading@7: for name in self.params.keys(): yading@7: data.append('='.join((name, urllib.quote_plus(self.params[name].replace('&', '&').encode('utf8'))))) yading@7: data = '&'.join(data) yading@7: yading@7: url = HOST_NAME yading@7: parsed_url = urlparse.urlparse(url) yading@7: if not parsed_url.scheme: yading@7: url = "http://" + url yading@7: url += self.method + '?oauth_consumer_key=' + OAUTHKEY + '&' yading@7: if COUNTRY: yading@7: url += 'country=' + COUNTRY + '&' yading@7: url += data yading@7: #print url yading@7: yading@7: request = urllib2.Request(url) yading@7: response = urllib2.urlopen(request) yading@7: return response.read() yading@7: yading@7: def execute(self, cacheable=False): yading@7: try: yading@7: if is_caching_enabled() and cacheable: yading@7: response = self._get_cached_response() yading@7: else: yading@7: response = self._download_response() yading@7: return minidom.parseString(response) yading@7: except urllib2.HTTPError, e: yading@7: raise self._get_error(e.fp.read()) yading@7: yading@7: def _get_cache_key(self): yading@7: """Cache key""" yading@7: keys = self.params.keys()[:] yading@7: keys.sort() yading@7: string = self.method yading@7: for name in keys: yading@7: string += name yading@7: string += self.params[name] yading@7: return get_md5(string) yading@7: yading@7: def _is_cached(self): yading@7: """Returns True if the request is available in the cache.""" yading@7: return os.path.exists(os.path.join(_get_cache_dir(), self._get_cache_key())) yading@7: yading@7: def _get_cached_response(self): yading@7: """Returns a file object of the cached response.""" yading@7: if not self._is_cached(): yading@7: response = self._download_response() yading@7: response_file = open(os.path.join(_get_cache_dir(), self._get_cache_key()), "w") yading@7: response_file.write(response) yading@7: response_file.close() yading@7: return open(os.path.join(_get_cache_dir(), self._get_cache_key()), "r").read() yading@7: yading@7: def _get_error(self, text): yading@7: return ServiceException('Error', text) yading@7: raise yading@7: yading@7: yading@7: class _BaseObject(object): yading@7: """An abstract webservices object.""" yading@7: yading@7: def __init__(self, method): yading@7: self._method = method yading@7: self._xml = None yading@7: yading@7: def _request(self, method_name , cacheable = False, params = None): yading@7: if not params: yading@7: params = self._get_params() yading@7: return _Request(method_name, params).execute(cacheable) yading@7: yading@7: def _get_params(self): yading@7: return dict() yading@7: yading@7: yading@7: class Artist(_BaseObject): yading@7: """ A 7digital artist """ yading@7: yading@7: def __init__(self, id): yading@7: _BaseObject.__init__(self, '/artist/') yading@7: yading@7: self.name = None yading@7: self.id = id yading@7: self._url = None yading@7: self._image = None yading@7: self._albums = None yading@7: self._top_tracks = None yading@7: self._tags = None yading@7: self._recommended_albums = None yading@7: yading@7: def __repr__(self): yading@7: return self.get_name().encode('utf8') yading@7: yading@7: def __eq__(self, other): yading@7: return self.get_id() == other.get_id() yading@7: yading@7: def __ne__(self, other): yading@7: return self.get_id() != other.get_id() yading@7: yading@7: def get_id(self): yading@7: """ Returns the 7digital artist id """ yading@7: return self.id yading@7: yading@7: def get_name(self): yading@7: """ Returns the name of the artist """ yading@7: if self.name is None: yading@7: self.name = '' yading@7: try: yading@7: if not self._xml : self._xml = self._request(self._method, + 'details', True, {'artistid': self.id}) yading@7: self.name = _extract(self._xml, 'artist', 1) or '' yading@7: except: yading@7: return self.name yading@7: return self.name yading@7: yading@7: def set_name(self, name) : yading@7: self.name = name yading@7: yading@7: def get_image(self): yading@7: """ Returns the image url of an artist """ yading@7: if self._image is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) yading@7: self._image = _extract(self._xml, 'image') yading@7: return self._image yading@7: yading@7: def set_image(self, image) : yading@7: self._image = image yading@7: yading@7: def get_url(self): yading@7: """ Returns the url of an artist """ yading@7: if self._url is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'artistid': self.id}) yading@7: self._url = _extract(self._xml, 'url') yading@7: return self._url yading@7: yading@7: def set_url(self, url) : yading@7: self._url = url yading@7: yading@7: def get_tags(self, pageSize=10): yading@7: """ Returns the tags of an artist """ yading@7: if self._tags is None: yading@7: self._tags = [] yading@7: xml = self._request(self._method + 'tags', True, {'artistid': self.id}) yading@7: for node in xml.getElementsByTagName('tag') : yading@7: self._tags.append(_get_tag(node)) yading@7: if self._tags: yading@7: self._tags.sort() yading@7: return self._tags[:pageSize] yading@7: yading@7: def get_albums(self, pageSize=10): yading@7: """ Returns the albums of an artist """ yading@7: if self._albums is not None: return self._albums yading@7: yading@7: results = [] yading@7: xml = self._request(self._method + 'releases', True, {'artistid': self.id, 'pageSize': str(pageSize)}) yading@7: for node in xml.getElementsByTagName('release'): yading@7: album = _get_album(node, self) yading@7: results.append(album) yading@7: self._albums = results yading@7: return self._albums yading@7: yading@7: def get_recommended_albums(self, pageSize=10): yading@7: """ Returns a list of recommended albums based on the seed artist """ yading@7: if self._recommended_albums is not None: return self._recommended_albums yading@7: yading@7: results = [] yading@7: xml = self._request('/release/recommend', True, {'artistid': self.id, 'pageSize': str(pageSize)}) # TODO if country is set gives different results yading@7: for node in xml.getElementsByTagName('release'): yading@7: results.append(_get_album(node, _get_artist(node.getElementsByTagName('artist')[0]))) yading@7: self._recommended_albums = results yading@7: return self._recommended_albums yading@7: yading@7: def get_top_tracks(self, pageSize=10): yading@7: """ Returns the top tracks of an artist """ yading@7: if self._top_tracks is not None: return self._top_tracks yading@7: yading@7: results = [] yading@7: xml = self._request(self._method + 'toptracks', True, {'artistid': self.id, 'pageSize': str(pageSize)}) yading@7: for node in xml.getElementsByTagName('track'): yading@7: results.append(_get_track(node, None, self)) yading@7: self._top_tracks = results yading@7: return self._top_tracks yading@7: yading@7: class Album(_BaseObject): yading@7: """ A 7digital album """ yading@7: def __init__(self, id=HOST_NAME): yading@7: _BaseObject.__init__(self, '/release/') yading@7: self.id = id yading@7: self.artist = None yading@7: self.title = None yading@7: yading@7: self._url = None yading@7: self._type = None yading@7: self._barcode = None yading@7: self._year = None yading@7: self._image = None yading@7: self._label = None yading@7: self._tags = None yading@7: self._tracks = None yading@7: self._similar = None yading@7: self._release_date = None yading@7: self._added_date = None yading@7: yading@7: def __repr__(self): yading@7: if self.get_artist(): yading@7: return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') yading@7: return self.get_title().encode('utf8') yading@7: yading@7: def __eq__(self, other): yading@7: return self.get_id() == other.get_id() yading@7: yading@7: def __ne__(self, other): yading@7: return self.get_id() != other.get_id() yading@7: yading@7: def get_id(self): yading@7: """ Returns the 7digital album id """ yading@7: return self.id yading@7: yading@7: def get_title(self): yading@7: """ Returns the name of the album """ yading@7: if self.title is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self.title = _extract(self._xml, 'release', 1) yading@7: return self.title yading@7: yading@7: def set_title(self, title): yading@7: if title is None: yading@7: title = '' yading@7: self.title = title yading@7: yading@7: def get_type(self): yading@7: """ Returns the type (CD, DVD, etc.) of the album """ yading@7: if self._type is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._type = _extract(self._xml, 'type') yading@7: return self._type yading@7: yading@7: def set_type(self, type): yading@7: self._type = type yading@7: yading@7: def get_year(self): yading@7: """ Returns the year of the album """ yading@7: if self._year is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._year = _extract(self._xml, 'year') yading@7: return self._year yading@7: yading@7: def set_year(self, year): yading@7: self._year = year yading@7: yading@7: def get_barcode(self): yading@7: """ Returns the barcode of the album """ yading@7: if self._barcode is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self.barcode = _extract(self._xml, 'barcode') yading@7: return self._barcode yading@7: yading@7: def set_barcode(self, barcode): yading@7: self._barcode = barcode yading@7: yading@7: def get_url(self): yading@7: """ Returns the url of the album """ yading@7: if self._url is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._url = _extract(self._xml, 'url') yading@7: return self._url yading@7: yading@7: def set_url(self, url) : yading@7: self._url = url yading@7: yading@7: def get_label(self): yading@7: """ Returns the label of the album """ yading@7: if self._label is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self.set_label(_get_label(self._xml.getElementsByTagName('label'))) yading@7: return self._label yading@7: yading@7: def set_label(self, label): yading@7: self._label = label yading@7: yading@7: def get_release_date(self): yading@7: """ Returns the release date of the album """ yading@7: if self._release_date is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._release_date = _extract(self._xml, 'releaseDate') yading@7: return self._release_date yading@7: yading@7: def set_release_date(self, release_date): yading@7: self._release_date = release_date yading@7: yading@7: def get_added_date(self): yading@7: """ Returns the added date of the album """ yading@7: if self._added_date is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._added_date = _extract(self._xml, 'addedDate') yading@7: return self._added_date yading@7: yading@7: def set_added_date(self, added_date): yading@7: self._added_date = added_date yading@7: yading@7: def get_artist(self): yading@7: """ Returns the Artist of the album """ yading@7: if not self.artist: yading@7: self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) yading@7: return self.artist yading@7: yading@7: def set_artist(self, artist): yading@7: """ Sets the Artist object of the track """ yading@7: self.artist = artist yading@7: yading@7: def get_image(self): yading@7: """ Returns album image url """ yading@7: if self._image is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'releaseid': self.id}) yading@7: self._image = _extract(self._xml, 'release_small_image') yading@7: return self._image yading@7: yading@7: def set_image(self, image): yading@7: if image is None: image = '' yading@7: self._image = image yading@7: yading@7: def get_tags(self, pageSize=10): yading@7: """ Returns the tags of the album """ yading@7: if self._tags is None: yading@7: self._tags = [] yading@7: xml = self._request(self._method + 'tags', True, {'releaseid': self.id}) yading@7: for node in xml.getElementsByTagName('tag') : yading@7: self._tags.append(_get_tag(node)) yading@7: if self._tags: yading@7: self._tags.sort() yading@7: return self._tags[:pageSize] yading@7: yading@7: def get_tracks(self, pageSize=10): yading@7: """ Returns the tracks of the album """ yading@7: if self._tracks is not None: return self._tracks yading@7: yading@7: results = [] yading@7: xml = self._request(self._method + 'tracks', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) yading@7: for node in xml.getElementsByTagName('track'): yading@7: if self.artist is None: yading@7: self.set_artist(_get_artist(node.getElementsByTagName('artist'))) yading@7: track = _get_track(node, self, self.get_artist()) yading@7: results.append(track) yading@7: self._tracks = results yading@7: return self._tracks yading@7: yading@7: def get_similar(self, pageSize=10): yading@7: """ Returns a list similar albums """ yading@7: if self._similar is not None: return self._similar yading@7: yading@7: results = [] yading@7: xml = self._request(self._method + 'recommend', True, {'releaseid': self.id, 'pageSize': str(pageSize)}) yading@7: for node in xml.getElementsByTagName('release'): yading@7: album = _get_album(node, _get_artist(node.getElementsByTagName('artist')[0])) yading@7: if self == album: yading@7: continue #Same album! yading@7: results.append(album) yading@7: self._similar = results yading@7: return self._similar yading@7: yading@7: yading@7: class Track(_BaseObject): yading@7: """ A Bmat track. """ yading@7: def __init__(self, id, artist=None): yading@7: _BaseObject.__init__(self, '/track/') yading@7: yading@7: if isinstance(artist, Artist): yading@7: self.artist = artist yading@7: else: yading@7: self.artist = None yading@7: self.id = id yading@7: self.title = None yading@7: self.artist = None yading@7: self.album = None yading@7: yading@7: self._isrc = None yading@7: self._url = None yading@7: self._preview = 'http://api.7digital.com/1.2/track/preview?trackid=' + self.id yading@7: self._image = None yading@7: self._tags = None yading@7: self._duration = None yading@7: self._version = None yading@7: self._explicit = None yading@7: self._position = None yading@7: yading@7: def __repr__(self): yading@7: return self.get_artist().get_name().encode('utf8') + ' - ' + self.get_title().encode('utf8') yading@7: yading@7: def __eq__(self, other): yading@7: return self.get_id() == other.get_id() yading@7: yading@7: def __ne__(self, other): yading@7: return self.get_id() != other.get_id() yading@7: yading@7: def get_id(self): yading@7: """ Returns the track id """ yading@7: return self.id yading@7: yading@7: def get_title(self): yading@7: """ Returns the track title """ yading@7: if self.title is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self.title = _extract(self._xml, 'track', 1) yading@7: return self.title yading@7: yading@7: def set_title(self, title): yading@7: self.title = title yading@7: yading@7: def get_isrc(self): yading@7: """ Returns the ISRC of the track """ yading@7: if self._isrc is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._isrc = _extract(self._xml, 'isrc') yading@7: return self._isrc yading@7: yading@7: def set_isrc(self, isrc): yading@7: self._isrc = isrc yading@7: yading@7: def get_url(self): yading@7: """ Returns the url of the track """ yading@7: if self._url is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._url = _extract(self._xml, 'url') yading@7: return self._url yading@7: yading@7: def set_url(self, url): yading@7: self._url = url yading@7: yading@7: def get_audio(self): yading@7: return self.get_preview() yading@7: yading@7: def get_preview(self): yading@7: """ Returns the url of the track """ yading@7: if self._preview is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'preview', True, {'trackid': self.id}) yading@7: self._preview = _extract(self._xml, 'url') yading@7: return self._preview yading@7: yading@7: def get_duration(self): yading@7: """ Returns the duration of the track """ yading@7: if self._duration is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._duration = _extract(self._xml, 'duration') yading@7: return self._duration yading@7: yading@7: def set_duration(self, duration): yading@7: self._duration = duration yading@7: yading@7: def get_position(self): yading@7: """ Returns the track number in the release """ yading@7: if self._position is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._position = _extract(self._xml, 'trackNumber') yading@7: return self._position yading@7: yading@7: def set_position(self, track_number): yading@7: self._position = track_number yading@7: yading@7: def get_explicit(self): yading@7: """ Returns whether the track contains explicit content """ yading@7: if self._explicit is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._explicit = _extract(self._xml, 'explicitContent') yading@7: return self._explicit yading@7: yading@7: def set_explicit(self, explicit): yading@7: self._explicit = explicit yading@7: yading@7: def get_version(self): yading@7: """ Returns the version of the track """ yading@7: if self._version is None : yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'trackid': self.id}) yading@7: self._version = _extract(self._xml, 'version') yading@7: return self._version yading@7: yading@7: def set_version(self, version): yading@7: self._version = version yading@7: yading@7: def get_artist(self): yading@7: """ Returns the Artist of the track """ yading@7: if not self.artist: yading@7: self.set_artist(_get_artist(self._xml.getElementsByTagName('artist'))) yading@7: return self.artist yading@7: yading@7: def set_artist(self, artist): yading@7: """ Sets the Artist object of the track """ yading@7: self.artist = artist yading@7: yading@7: def get_album(self): yading@7: """ Returns the associated Album object """ yading@7: if not self.album: yading@7: self.set_album(_get_album(self._xml.getElementsByTagName('release'))) yading@7: return self.album yading@7: yading@7: def set_album(self, album): yading@7: """ Sets the Album object of the track """ yading@7: self.album = album yading@7: yading@7: yading@7: class Tag(_BaseObject): yading@7: """ A Tag """ yading@7: def __init__(self, id): yading@7: _BaseObject.__init__(self, '/tag/') yading@7: yading@7: self.id = id yading@7: self.name = None yading@7: self._url = None yading@7: yading@7: def __repr__(self): yading@7: return self.get_name().encode('utf8') yading@7: yading@7: def __eq__(self, other): yading@7: return self.get_id() == other.get_id() yading@7: yading@7: def __ne__(self, other): yading@7: return self.get_id() != other.get_id() yading@7: yading@7: def get_id(self): yading@7: """ Returns the tag id """ yading@7: return self.id yading@7: yading@7: def get_name(self): yading@7: """ Returns the tag name """ yading@7: if self.name is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) yading@7: self.name = _extract(self._xml, 'name') yading@7: return self.name yading@7: yading@7: def set_name(self, name): yading@7: self.name = name yading@7: yading@7: def get_url(self): yading@7: """ Returns the url of the tag""" yading@7: if self._url is None: yading@7: if not self._xml : self._xml = self._request(self._method + 'details', True, {'tagid': self.id}) yading@7: self._url = _extract(self._xml, 'url') yading@7: return self._url yading@7: yading@7: def set_url(self, url): yading@7: self._url = url yading@7: yading@7: class Label(_BaseObject): yading@7: """ A Label """ yading@7: def __init__(self, id): yading@7: _BaseObject.__init__(self, '') yading@7: yading@7: self.id = id yading@7: self.name = None yading@7: yading@7: def __repr__(self): yading@7: return self.get_name().encode('utf8') yading@7: yading@7: def __eq__(self, other): yading@7: return self.get_id() == other.get_id() yading@7: yading@7: def __ne__(self, other): yading@7: return self.get_id() != other.get_id() yading@7: yading@7: def get_id(self): yading@7: """ Returns the label id """ yading@7: return self.id yading@7: yading@7: def get_name(self): yading@7: """ Returns the label name """ yading@7: return self.name yading@7: yading@7: def set_name(self, name): yading@7: self.name = name yading@7: yading@7: yading@7: class _Search(_BaseObject): yading@7: """ An abstract class for search """ yading@7: yading@7: def __init__(self, method, search_terms, xml_tag): yading@7: _BaseObject.__init__(self, method) yading@7: yading@7: self._search_terms = search_terms yading@7: self._xml_tag = xml_tag yading@7: yading@7: self._results_per_page = 10 yading@7: if self._search_terms.has_key('pageSize'): yading@7: self._results_per_page = str(self._search_terms['pageSize']) yading@7: else: yading@7: self._search_terms['pageSize'] = str(self._results_per_page) yading@7: self._last_page_index = 0 yading@7: self._hits = None yading@7: yading@7: def get_total_result_count(self): yading@7: if self._hits is not None: yading@7: return self._hits yading@7: params = self._get_params() yading@7: params['pageSize'] = '1' yading@7: xml = self._request(self._method, True, params) yading@7: hits = int(_extract(xml, 'totalItems')) yading@7: self._hits = hits yading@7: return self._hits yading@7: yading@7: def _get_params(self): yading@7: params = {} yading@7: for key in self._search_terms.keys(): yading@7: params[key] = self._search_terms[key] yading@7: return params yading@7: yading@7: def _retrieve_page(self, page_index): yading@7: """ Returns the xml nodes to process """ yading@7: params = self._get_params() yading@7: yading@7: if page_index != 0: yading@7: #offset = self._results_per_page * page_index yading@7: params["page"] = str(page_index) yading@7: yading@7: doc = self._request(self._method, True, params) yading@7: return doc.getElementsByTagName(self._xml_tag)[0] yading@7: yading@7: def _retrieve_next_page(self): yading@7: self._last_page_index += 1 yading@7: return self._retrieve_page(self._last_page_index) yading@7: yading@7: def has_results(self): yading@7: return self.get_total_result_count() > (self._results_per_page * self._last_page_index) yading@7: yading@7: def get_next_page(self): yading@7: master_node = self._retrieve_next_page() yading@7: return self._get_results(master_node) yading@7: yading@7: def get_page(self, page=0): yading@7: if page < 0: page = 0 yading@7: if page > 0: page = page-1 yading@7: yading@7: master_node = self._retrieve_page(page) yading@7: return self._get_results(master_node) yading@7: yading@7: yading@7: class ArtistSearch(_Search): yading@7: """ Search artists """ yading@7: yading@7: def __init__(self, query): yading@7: _Search.__init__(self, '/artist/search', {'q': query}, 'searchResults') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('artist'): yading@7: artist = _get_artist(node) yading@7: results.append(artist) yading@7: return results yading@7: yading@7: yading@7: class ArtistBrowse(_Search): yading@7: """ Browse artists """ yading@7: yading@7: def __init__(self, letter): yading@7: _Search.__init__(self, '/artist/browse', {'letter': letter}, 'artists') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('artist'): yading@7: artist = _get_artist(node) yading@7: results.append(artist) yading@7: return results yading@7: yading@7: class ArtistDetail(_Search): yading@7: def __init__(self, artist_id): yading@7: _Search.__init__(self, '/artist/details', {'artistid': artist_id}, 'artist') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: node = master_node yading@7: yading@7: artist = _get_artist(node) yading@7: yading@7: results.append(artist) yading@7: return results yading@7: yading@7: class AlbumSearch(_Search): yading@7: """ Search albums """ yading@7: yading@7: def __init__(self, query): yading@7: _Search.__init__(self, '/release/search', {'q': query}, 'searchResults') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('release'): yading@7: artist = _get_artist(node.getElementsByTagName('artist')[0]) yading@7: album = _get_album(node, artist) yading@7: results.append(album) yading@7: return results yading@7: yading@7: class AlbumCharts(_Search): yading@7: """ Chart albums """ yading@7: yading@7: def __init__(self, period, todate): yading@7: _Search.__init__(self, '/release/chart', {'period': period, 'todate': todate}, 'chart') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('release'): yading@7: artist = _get_artist(node.getElementsByTagName('artist')[0]) yading@7: album = _get_album(node, artist) yading@7: results.append(album) yading@7: return results yading@7: yading@7: class AlbumReleases(_Search): yading@7: """ Release albums by date """ yading@7: yading@7: def __init__(self, fromdate, todate): yading@7: _Search.__init__(self, '/release/bydate', {'fromDate': fromdate, 'toDate': todate}, 'releases') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('release'): yading@7: artist = _get_artist(node.getElementsByTagName('artist')[0]) yading@7: album = _get_album(node, artist) yading@7: results.append(album) yading@7: return results yading@7: yading@7: class TrackSearch(_Search): yading@7: """ Search for tracks """ yading@7: yading@7: def __init__(self, query): yading@7: _Search.__init__(self, '/track/search', {'q': query}, 'searchResults') yading@7: yading@7: def _get_results(self, master_node): yading@7: results = [] yading@7: for node in master_node.getElementsByTagName('track'): yading@7: artist = _get_artist(node.getElementsByTagName('artist')[0]) yading@7: album = _get_album(node.getElementsByTagName('release')[0], artist) yading@7: track = _get_track(node, album, artist) yading@7: results.append(track) yading@7: return results yading@7: yading@7: def get_artist_detail(artistId): yading@7: return ArtistDetail(artistId) yading@7: yading@7: def search_artist(query): yading@7: """Search artists by query. Returns an ArtistSearch object. yading@7: Use get_next_page() to retrieve sequences of results.""" yading@7: return ArtistSearch(query) yading@7: yading@7: def browse_artists(letter): yading@7: """Browse artists by letter [a..z]. Returns an ArtistBrowse object. yading@7: Use get_next_page() to retrieve sequences of results.""" yading@7: return ArtistBrowse(letter) yading@7: yading@7: def search_album(query): yading@7: """Search albums by query. Returns the albumSearch object. yading@7: Use get_next_page() to retrieve sequences of results.""" yading@7: return AlbumSearch(query) yading@7: yading@7: def album_charts(period, todate): yading@7: """Get chart albums in a given period of time """ yading@7: return AlbumCharts(period, todate) yading@7: yading@7: def album_releases(fromdate, todate): yading@7: """Get releases in a given period of time""" yading@7: return AlbumReleases(fromdate, todate) yading@7: yading@7: def search_track(query): yading@7: """Search tracks by query. Returns a TrackSearch object. yading@7: Use get_next_page() to retrieve sequences of results.""" yading@7: return TrackSearch(query) yading@7: yading@7: yading@7: # XML yading@7: def _extract(node, name, index = 0): yading@7: """Extracts a value from the xml string""" yading@7: try: yading@7: nodes = node.getElementsByTagName(name) yading@7: yading@7: if len(nodes): yading@7: if nodes[index].firstChild: yading@7: return nodes[index].firstChild.data.strip() yading@7: else: yading@7: return None yading@7: except: yading@7: return None yading@7: yading@7: def _extract_all(node, name, pageSize_count = None): yading@7: """Extracts all the values from the xml string. It returns a list.""" yading@7: results = [] yading@7: for i in range(0, len(node.getElementsByTagName(name))): yading@7: if len(results) == pageSize_count: yading@7: break yading@7: results.append(_extract(node, name, i)) yading@7: return results yading@7: yading@7: def _get_artist(xml): yading@7: artist_id = xml.getAttribute('id') yading@7: artist = Artist(artist_id) yading@7: artist.set_name(_extract(xml, 'name')) yading@7: return artist yading@7: yading@7: def _get_album(xml, artist): yading@7: album_id = xml.getAttribute('id') yading@7: album = Album(album_id) yading@7: album.set_artist(artist) yading@7: album.set_title(_extract(xml, 'title')) yading@7: album.set_type(_extract(xml, 'type')) yading@7: album.set_image(_extract(xml, 'image')) yading@7: album.set_year(_extract(xml, 'year')) yading@7: album.set_barcode(_extract(xml, 'barcode')) yading@7: album.set_url(_extract(xml, 'url')) yading@7: album.set_release_date(_extract(xml, 'releaseDate')) yading@7: album.set_added_date(_extract(xml, 'addedDate')) yading@7: #TODO price, formats, artist appears_as yading@7: try : yading@7: album.set_label(_get_label(xml.getElementsByTagName('label')[0])) #In some cases that are albums with no label (record company) attached! :-( yading@7: except: yading@7: pass yading@7: return album yading@7: yading@7: def _get_track(xml, album, artist): yading@7: track_id = xml.getAttribute('id') yading@7: track = Track(track_id) yading@7: track.set_title(_extract(xml, 'title')) yading@7: track.set_url(_extract(xml, 'url')) yading@7: track.set_isrc(_extract(xml, 'isrc')) yading@7: track.set_duration(_extract(xml, 'duration')) yading@7: track.set_position(_extract(xml, 'trackNumber')) yading@7: track.set_explicit(_extract(xml, 'explicitContent')) yading@7: track.set_version(_extract(xml, 'version')) yading@7: track.set_album(album) yading@7: track.set_artist(artist) yading@7: #TODO price, formats yading@7: return track yading@7: yading@7: def _get_tag(xml): yading@7: tag = Tag(_extract(xml, 'text')) yading@7: tag.set_name(tag.get_id()) yading@7: return tag yading@7: yading@7: def _get_label(xml): yading@7: label = '' yading@7: try: yading@7: label = Label(xml.getAttribute('id')) yading@7: label.set_name(_extract(xml, 'name')) yading@7: except: yading@7: pass yading@7: return label yading@7: yading@7: # CACHE yading@7: def enable_caching(cache_dir = None): yading@7: global __cache_dir yading@7: global __cache_enabled yading@7: yading@7: if cache_dir == None: yading@7: import tempfile yading@7: __cache_dir = tempfile.mkdtemp() yading@7: else: yading@7: if not os.path.exists(cache_dir): yading@7: os.mkdir(cache_dir) yading@7: __cache_dir = cache_dir yading@7: __cache_enabled = True yading@7: yading@7: def disable_caching(): yading@7: global __cache_enabled yading@7: __cache_enabled = False yading@7: yading@7: def is_caching_enabled(): yading@7: """Returns True if caching is enabled.""" yading@7: global __cache_enabled yading@7: return __cache_enabled yading@7: yading@7: def _get_cache_dir(): yading@7: """Returns the directory in which cache files are saved.""" yading@7: global __cache_dir yading@7: global __cache_enabled yading@7: return __cache_dir yading@7: yading@7: def get_md5(text): yading@7: """Returns the md5 hash of a string.""" yading@7: hash = md5() yading@7: hash.update(text.encode('utf8')) yading@7: return hash.hexdigest() yading@7: