python-lastfm/lastfm/artist.py

491 lines
17 KiB
Python

#!/usr/bin/env python
"""Module for calling Artist related last.fm web services API methods"""
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2"
__license__ = "GNU Lesser General Public License"
__package__ = "lastfm"
from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, sharable, shoutable, taggable, crawlable
from lastfm.decorators import cached_property, top_property
@crawlable
@shoutable
@sharable
@taggable
@searchable
@cacheable
class Artist(LastfmBase):
"""A class representing an artist."""
def init(self,
api,
name = None,
mbid = None,
url = None,
image = None,
streamable = None,
stats = None,
similar = None,
top_tags = None,
bio = None,
subject = None):
"""
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
self._name = name
self._mbid = mbid
self._url = url
self._image = image
self._streamable = streamable
self._stats = stats and Stats(
subject = self,
listeners = stats.listeners,
playcount = stats.playcount,
weight = stats.weight,
match = stats.match,
rank = stats.rank
)
self._similar = similar
self._top_tags = top_tags
self._bio = bio and Wiki(
subject = self,
published = bio.published,
summary = bio.summary,
content = bio.content
)
self._subject = subject
@property
def name(self):
"""
name of the artist
@rtype: L{str}
"""
return self._name
@property
def mbid(self):
"""
MBID of the artist
@rtype: L{str}
"""
if self._mbid is None:
self._fill_info()
return self._mbid
@property
def url(self):
"""
url of the artist's page
@rtype: L{str}
"""
if self._url is None:
self._fill_info()
return self._url
@property
def image(self):
"""
images of the artist
@rtype: L{dict}
"""
if self._image is None:
self._fill_info()
return self._image
@property
def streamable(self):
"""
is the artist streamable on last.fm
@rtype: L{bool}
"""
if self._streamable is None:
self._fill_info()
return self._streamable
@property
def stats(self):
"""
stats for the artist
@rtype: L{Stats}
"""
if self._stats is None:
self._fill_info()
return self._stats
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 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 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
@property
def bio(self):
"""
biography of the artist
@rtype: L{Wiki}
"""
if self._bio is None:
self._fill_info()
return self._bio
@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 "<lastfm.Artist: %s>" % 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