#!/usr/bin/env python """Module for calling Artist related last.fm web services API methods""" __author__ = "Abhinav Sarkar " __version__ = "0.2" __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase from lastfm.mixin import mixin from lastfm.decorators import cached_property, top_property @mixin("crawlable", "shoutable", "sharable", "taggable", "searchable", "cacheable", "property_adder") class Artist(LastfmBase): """A class representing an artist.""" class Meta(object): properties = ["name", "similar", "top_tags"] fillable_properties = ["mbid", "url", "image", "streamable", "stats", "bio"] def init(self, api, subject = None, **kwargs): """ Create an Artist object by providing all the data related to it. @param api: an instance of L{Api} @type api: L{Api} @param name: the artist name @type name: L{str} @param mbid: MBID of the artist @type mbid: L{str} @param url: URL of the artist on last.fm @type url: L{str} @param image: the images of the artist in various sizes @type image: L{dict} @param streamable: flag indicating if the artist is streamable from last.fm @type streamable: L{bool} @param stats: the artist statistics @type stats: L{Stats} @param similar: artists similar to the provided artist @type similar: L{list} of L{Artist} @param top_tags: top tags for the artist @type top_tags: L{list} of L{Tag} @param bio: biography of the artist @type bio: L{Wiki} @param subject: the subject to which this instance belongs to @type subject: L{User} OR L{Artist} OR L{Tag} OR L{Track} OR L{WeeklyChart} @raise InvalidParametersError: If an instance of L{Api} is not provided as the first parameter then an Exception is raised. """ if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api super(Artist, self).init(**kwargs) self._stats = hasattr(self, "_stats") and Stats( subject = self, listeners = self._stats.listeners, playcount = self._stats.playcount, weight = self._stats.weight, match = self._stats.match, rank = self._stats.rank ) or None self._bio = hasattr(self, "_bio") and Wiki( subject = self, published = self._bio.published, summary = self._bio.summary, content = self._bio.content ) or None self._subject = subject def get_similar(self, limit = None): """ Get the artists similar to this artist. @param limit: the number of artists returned (optional) @type limit: L{int} @return: artists similar to this artist @rtype: L{list} of L{Artist} """ params = self._default_params({'method': 'artist.getSimilar'}) if limit is not None: params.update({'limit': limit}) data = self._api._fetch_data(params).find('similarartists') self._similar = [ Artist( self._api, subject = self, name = a.findtext('name'), mbid = a.findtext('mbid'), stats = Stats( subject = a.findtext('name'), match = float(a.findtext('match')), ), url = 'http://' + a.findtext('url'), image = {'large': a.findtext('image')} ) for a in data.findall('artist') ] return self._similar[:] @property def similar(self): """ artists similar to this artist @rtype: L{list} of L{Artist} """ if not hasattr(self, "_similar") or self._similar is None or len(self._similar) < 6: return self.get_similar() return self._similar[:] @top_property("similar") def most_similar(self): """ artist most similar to this artist @rtype: L{Artist} """ pass @property def top_tags(self): """ top tags for the artist @rtype: L{list} of L{Tag} """ if not hasattr(self, "_top_tags") or self._top_tags is None or len(self._top_tags) < 6: params = self._default_params({'method': 'artist.getTopTags'}) data = self._api._fetch_data(params).find('toptags') self._top_tags = [ Tag( self._api, subject = self, name = t.findtext('name'), url = t.findtext('url') ) for t in data.findall('tag') ] return self._top_tags[:] @top_property("top_tags") def top_tag(self): """ top tag for the artist @rtype: L{Tag} """ pass @cached_property def events(self): """ events for the artist @rtype: L{lazylist} of L{Event} """ params = self._default_params({'method': 'artist.getEvents'}) data = self._api._fetch_data(params).find('events') return [ Event.create_from_data(self._api, e) for e in data.findall('event') ] @cached_property def top_albums(self): """ top albums of the artist @rtype: L{list} of L{Album} """ params = self._default_params({'method': 'artist.getTopAlbums'}) data = self._api._fetch_data(params).find('topalbums') return [ Album( self._api, subject = self, name = a.findtext('name'), artist = self, mbid = a.findtext('mbid'), url = a.findtext('url'), image = dict([(i.get('size'), i.text) for i in a.findall('image')]), stats = Stats( subject = a.findtext('name'), playcount = int(a.findtext('playcount')), rank = int(a.attrib['rank']) ) ) for a in data.findall('album') ] @top_property("top_albums") def top_album(self): """ top album of the artist @rtype: L{Album} """ pass @cached_property def top_fans(self): """ top fans of the artist @rtype: L{list} of L{User} """ params = self._default_params({'method': 'artist.getTopFans'}) data = self._api._fetch_data(params).find('topfans') return [ User( self._api, subject = self, name = u.findtext('name'), url = u.findtext('url'), image = dict([(i.get('size'), i.text) for i in u.findall('image')]), stats = Stats( subject = u.findtext('name'), weight = int(u.findtext('weight')) ) ) for u in data.findall('user') ] @top_property("top_fans") def top_fan(self): """ top fan of the artist @rtype: L{User}""" pass @cached_property def top_tracks(self): """ top tracks of the artist @rtype: L{list} of L{Track} """ params = self._default_params({'method': 'artist.getTopTracks'}) data = self._api._fetch_data(params).find('toptracks') return [ Track( self._api, subject = self, name = t.findtext('name'), artist = self, mbid = t.findtext('mbid'), stats = Stats( subject = t.findtext('name'), playcount = int(t.findtext('playcount')), rank = int(t.attrib['rank']) ), streamable = (t.findtext('streamable') == '1'), full_track = (t.find('streamable').attrib['fulltrack'] == '1'), image = dict([(i.get('size'), i.text) for i in t.findall('image')]), ) for t in data.findall('track') ] @top_property("top_tracks") def top_track(self): """ topmost track of the artist @rtype: L{Track} """ pass @staticmethod def get_info(api, artist = None, mbid = None): """ Get the data for the artist. @param api: an instance of L{Api} @type api: L{Api} @param artist: the name of the artist @type artist: L{str} @param mbid: MBID of the artist @type mbid: L{str} @return: an Artist object corresponding the provided artist name @rtype: L{Artist} @raise lastfm.InvalidParametersError: Either artist or mbid parameter has to be provided. Otherwise exception is raised. @note: Use the L{Api.get_artist} method instead of using this method directly. """ data = Artist._fetch_data(api, artist, mbid) a = Artist(api, name = data.findtext('name')) a._fill_info() return a @staticmethod def _get_all(seed_artist): return (seed_artist, ['name'], lambda api, hsh: Artist(api, **hsh).similar) def _default_params(self, extra_params = None): if not self.name: raise InvalidParametersError("artist has to be provided.") params = {'artist': self.name} if extra_params is not None: params.update(extra_params) return params @staticmethod def _fetch_data(api, artist = None, mbid = None): params = {'method': 'artist.getInfo'} if not (artist or mbid): raise InvalidParametersError("either artist or mbid has to be given as argument.") if artist: params.update({'artist': artist}) elif mbid: params.update({'mbid': mbid}) return api._fetch_data(params).find('artist') def _fill_info(self): data = Artist._fetch_data(self._api, self.name) self._name = data.findtext('name') self._mbid = data.findtext('mbid') self._url = data.findtext('url') self._image = dict([(i.get('size'), i.text) for i in data.findall('image')]) self._streamable = (data.findtext('streamable') == 1) if not self._stats: self._stats = Stats( subject = self, listeners = int(data.findtext('stats/listeners')), playcount = int(data.findtext('stats/playcount')) ) # self._similar = [ # Artist( # self._api, # subject = self, # name = a.findtext('name'), # url = a.findtext('url'), # image = dict([(i.get('size'), i.text) for i in a.findall('image')]) # ) # for a in data.findall('similar/artist') # ] self._top_tags = [ Tag( self._api, subject = self, name = t.findtext('name'), url = t.findtext('url') ) for t in data.findall('tags/tag') ] self._bio = Wiki( self, published = data.findtext('bio/published').strip() and datetime(*(time.strptime( data.findtext('bio/published').strip(), '%a, %d %b %Y %H:%M:%S +0000' )[0:6])), summary = data.findtext('bio/summary'), content = data.findtext('bio/content') ) @staticmethod def _search_yield_func(api, artist): return Artist( api, name = artist.findtext('name'), mbid = artist.findtext('mbid'), url = artist.findtext('url'), image = dict([(i.get('size'), i.text) for i in artist.findall('image')]), streamable = (artist.findtext('streamable') == '1'), ) @staticmethod def _hash_func(*args, **kwds): try: return hash(kwds['name'].lower()) except KeyError: try: return hash(args[1].lower()) except IndexError: raise InvalidParametersError("name has to be provided for hashing") def __hash__(self): return self.__class__._hash_func(name = self.name) def __eq__(self, other): if self.mbid and other.mbid: return self.mbid == other.mbid if self.url and other.url: return self.url == other.url return self.name == other.name def __lt__(self, other): return self.name < other.name def __repr__(self): return "" % self._name from datetime import datetime import time from lastfm.album import Album from lastfm.api import Api from lastfm.error import InvalidParametersError from lastfm.event import Event from lastfm.stats import Stats from lastfm.tag import Tag from lastfm.track import Track from lastfm.user import User from lastfm.wiki import Wiki