From 55680ba9c8ade76b59de230f3e61236defbca241 Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Wed, 31 Dec 2008 20:09:06 +0000 Subject: [PATCH] implemented new methods and check for authenticated method calls. --- lastfm/album.py | 4 +- lastfm/api.py | 22 ++-- lastfm/artist.py | 1 + lastfm/base.py | 20 +++- lastfm/error.py | 4 + lastfm/geo.py | 247 ++++++++++++++++++++++++++++++++++++++++++ lastfm/group.py | 31 ++++++ lastfm/tag.py | 40 ++++++- lastfm/user.py | 192 +++++++++++++++++++++++++++----- lastfm/weeklychart.py | 16 +-- 10 files changed, 525 insertions(+), 52 deletions(-) diff --git a/lastfm/album.py b/lastfm/album.py index 6a24d03..61c4bbc 100644 --- a/lastfm/album.py +++ b/lastfm/album.py @@ -96,9 +96,7 @@ class Album(LastfmBase, Cacheable, Searchable, Taggable): @property def streamable(self): - """is the artist streamable""" - if self._streamable is None: - self._fill_info() + """is the album streamable""" return self._streamable @LastfmBase.cached_property diff --git a/lastfm/api.py b/lastfm/api.py index 3641298..4a36246 100644 --- a/lastfm/api.py +++ b/lastfm/api.py @@ -245,14 +245,14 @@ class Api(object): return urllib.urlencode([(k, self._encode(parameters[k])) for k in keys if parameters[k] is not None]) def _read_url_data(self, opener, url, data = None): - now = datetime.now() - delta = now - self._last_fetch_time - delta = delta.seconds + float(delta.microseconds)/1000000 - if delta < Api.FETCH_INTERVAL: - time.sleep(Api.FETCH_INTERVAL - delta) - url_data = opener.open(url, data).read() - self._last_fetch_time = datetime.now() - return url_data + now = datetime.now() + delta = now - self._last_fetch_time + delta = delta.seconds + float(delta.microseconds)/1000000 + if delta < Api.FETCH_INTERVAL: + time.sleep(Api.FETCH_INTERVAL - delta) + url_data = opener.open(url, data).read() + self._last_fetch_time = datetime.now() + return url_data def _fetch_url(self, url, @@ -307,6 +307,7 @@ class Api(object): sign = False, session = False, no_cache = False): + params = params.copy() params['api_key'] = self.api_key if session: @@ -325,9 +326,9 @@ class Api(object): url, parameters): url = self._build_url(url) - if self._debug: - print url data = self._encode_parameters(parameters) + if self._debug: + print data opener = self._get_opener(url) url_data = self._read_url_data(opener, url, data) return url_data @@ -350,6 +351,7 @@ class Api(object): keys.sort() sig = unicode() for name in keys: + if name == 'api_sig': continue sig += ("%s%s" % (name, params[name])) sig += self.secret hashed_sig = md5.new(sig).hexdigest() diff --git a/lastfm/artist.py b/lastfm/artist.py index ed211de..7763f4c 100644 --- a/lastfm/artist.py +++ b/lastfm/artist.py @@ -37,6 +37,7 @@ class Artist(LastfmBase, Cacheable, Sharable, Shoutable, Searchable, Taggable): subject = self, listeners = stats.listeners, playcount = stats.playcount, + weight = stats.weight, match = stats.match, rank = stats.rank ) diff --git a/lastfm/base.py b/lastfm/base.py index e9d09e9..46af6dd 100644 --- a/lastfm/base.py +++ b/lastfm/base.py @@ -33,6 +33,24 @@ class LastfmBase(object): return cache_attribute return property(fget = wrapper, doc = func.__doc__) + + @staticmethod + def autheticate(func): + def wrapper(self, *args, **kwargs): + from lastfm.user import User + user = None + if isinstance(self, User): + user = self.name + if self.autheticated: + return func(self, *args, **kwargs) + elif hasattr(self, 'user'): + user = self.user.name + if self.user.autheticated: + return func(self, *args, **kwargs) + + raise AuthenticationFailedError( + "user '%s' does not have permissions to access the service" % user) + return wrapper def __gt__(self, other): return not (self.__lt__(other) or self.__eq(other)) @@ -47,4 +65,4 @@ class LastfmBase(object): return not self.__gt__(other) import copy -from lastfm.error import LastfmError +from lastfm.error import LastfmError, AuthenticationFailedError \ No newline at end of file diff --git a/lastfm/error.py b/lastfm/error.py index dcf0e70..98e49f3 100644 --- a/lastfm/error.py +++ b/lastfm/error.py @@ -57,6 +57,9 @@ class ServiceOfflineError(LastfmError):#11 class SubscribersOnlyError(LastfmError):#12 pass +class InvalidMethodSignatureError(LastfmError):#13 + pass + class TokenNotAuthorizedError(LastfmError):#14 pass @@ -76,6 +79,7 @@ error_map = { 10: InvalidApiKeyError, 11: ServiceOfflineError, 12: SubscribersOnlyError, + 13: InvalidMethodSignatureError, 14: TokenNotAuthorizedError, 15: TokenExpiredError } \ No newline at end of file diff --git a/lastfm/geo.py b/lastfm/geo.py index c40379a..13c896a 100644 --- a/lastfm/geo.py +++ b/lastfm/geo.py @@ -257,6 +257,253 @@ class Location(LastfmBase, Cacheable): class Country(LastfmBase, Cacheable): """A class representing a country.""" + ISO_CODES = { + 'AD': 'Andorra', + 'AE': 'United Arab Emirates', + 'AF': 'Afghanistan', + 'AG': 'Antigua and Barbuda', + 'AI': 'Anguilla', + 'AL': 'Albania', + 'AM': 'Armenia', + 'AN': 'Netherlands Antilles', + 'AO': 'Angola', + 'AQ': 'Antarctica', + 'AR': 'Argentina', + 'AS': 'American Samoa', + 'AT': 'Austria', + 'AU': 'Australia', + 'AW': 'Aruba', + 'AX': 'land Islands', + 'AZ': 'Azerbaijan', + 'BA': 'Bosnia and Herzegovina', + 'BB': 'Barbados', + 'BD': 'Bangladesh', + 'BE': 'Belgium', + 'BF': 'Burkina Faso', + 'BG': 'Bulgaria', + 'BH': 'Bahrain', + 'BI': 'Burundi', + 'BJ': 'Benin', + 'BL': 'Saint Barthlemy', + 'BM': 'Bermuda', + 'BN': 'Brunei Darussalam', + 'BO': 'Bolivia', + 'BR': 'Brazil', + 'BS': 'Bahamas', + 'BT': 'Bhutan', + 'BV': 'Bouvet Island', + 'BW': 'Botswana', + 'BY': 'Belarus', + 'BZ': 'Belize', + 'CA': 'Canada', + 'CC': 'Cocos (Keeling) Islands', + 'CD': 'Congo, The Democratic Republic of the', + 'CF': 'Central African Republic', + 'CG': 'Congo', + 'CH': 'Switzerland', + 'CI': "Cte d'Ivoire", + 'CK': 'Cook Islands', + 'CL': 'Chile', + 'CM': 'Cameroon', + 'CN': 'China', + 'CO': 'Colombia', + 'CR': 'Costa Rica', + 'CU': 'Cuba', + 'CV': 'Cape Verde', + 'CX': 'Christmas Island', + 'CY': 'Cyprus', + 'CZ': 'Czech Republic', + 'DE': 'Germany', + 'DJ': 'Djibouti', + 'DK': 'Denmark', + 'DM': 'Dominica', + 'DO': 'Dominican Republic', + 'DZ': 'Algeria', + 'EC': 'Ecuador', + 'EE': 'Estonia', + 'EG': 'Egypt', + 'EH': 'Western Sahara', + 'ER': 'Eritrea', + 'ES': 'Spain', + 'ET': 'Ethiopia', + 'FI': 'Finland', + 'FJ': 'Fiji', + 'FK': 'Falkland Islands (Malvinas)', + 'FM': 'Micronesia, Federated States of', + 'FO': 'Faroe Islands', + 'FR': 'France', + 'GA': 'Gabon', + 'GB': 'United Kingdom', + 'GD': 'Grenada', + 'GE': 'Georgia', + 'GF': 'French Guiana', + 'GG': 'Guernsey', + 'GH': 'Ghana', + 'GI': 'Gibraltar', + 'GL': 'Greenland', + 'GM': 'Gambia', + 'GN': 'Guinea', + 'GP': 'Guadeloupe', + 'GQ': 'Equatorial Guinea', + 'GR': 'Greece', + 'GS': 'South Georgia and the South Sandwich Islands', + 'GT': 'Guatemala', + 'GU': 'Guam', + 'GW': 'Guinea-Bissau', + 'GY': 'Guyana', + 'HK': 'Hong Kong', + 'HM': 'Heard Island and McDonald Islands', + 'HN': 'Honduras', + 'HR': 'Croatia', + 'HT': 'Haiti', + 'HU': 'Hungary', + 'ID': 'Indonesia', + 'IE': 'Ireland', + 'IL': 'Israel', + 'IM': 'Isle of Man', + 'IN': 'India', + 'IO': 'British Indian Ocean Territory', + 'IQ': 'Iraq', + 'IR': 'Iran, Islamic Republic of', + 'IS': 'Iceland', + 'IT': 'Italy', + 'JE': 'Jersey', + 'JM': 'Jamaica', + 'JO': 'Jordan', + 'JP': 'Japan', + 'KE': 'Kenya', + 'KG': 'Kyrgyzstan', + 'KH': 'Cambodia', + 'KI': 'Kiribati', + 'KM': 'Comoros', + 'KN': 'Saint Kitts and Nevis', + 'KP': "Korea, Democratic People's Republic of", + 'KR': 'Korea, Republic of', + 'KW': 'Kuwait', + 'KY': 'Cayman Islands', + 'KZ': 'Kazakhstan', + 'LA': "Lao People's Democratic Republic", + 'LB': 'Lebanon', + 'LC': 'Saint Lucia', + 'LI': 'Liechtenstein', + 'LK': 'Sri Lanka', + 'LR': 'Liberia', + 'LS': 'Lesotho', + 'LT': 'Lithuania', + 'LU': 'Luxembourg', + 'LV': 'Latvia', + 'LY': 'Libyan Arab Jamahiriya', + 'MA': 'Morocco', + 'MC': 'Monaco', + 'MD': 'Moldova', + 'ME': 'Montenegro', + 'MF': 'Saint Martin', + 'MG': 'Madagascar', + 'MH': 'Marshall Islands', + 'MK': 'Macedonia, The Former Yugoslav Republic of', + 'ML': 'Mali', + 'MM': 'Myanmar', + 'MN': 'Mongolia', + 'MO': 'Macao', + 'MP': 'Northern Mariana Islands', + 'MQ': 'Martinique', + 'MR': 'Mauritania', + 'MS': 'Montserrat', + 'MT': 'Malta', + 'MU': 'Mauritius', + 'MV': 'Maldives', + 'MW': 'Malawi', + 'MX': 'Mexico', + 'MY': 'Malaysia', + 'MZ': 'Mozambique', + 'NA': 'Namibia', + 'NC': 'New Caledonia', + 'NE': 'Niger', + 'NF': 'Norfolk Island', + 'NG': 'Nigeria', + 'NI': 'Nicaragua', + 'NL': 'Netherlands', + 'NO': 'Norway', + 'NP': 'Nepal', + 'NR': 'Nauru', + 'NU': 'Niue', + 'NZ': 'New Zealand', + 'OM': 'Oman', + 'PA': 'Panama', + 'PE': 'Peru', + 'PF': 'French Polynesia', + 'PG': 'Papua New Guinea', + 'PH': 'Philippines', + 'PK': 'Pakistan', + 'PL': 'Poland', + 'PM': 'Saint Pierre and Miquelon', + 'PN': 'Pitcairn', + 'PR': 'Puerto Rico', + 'PS': 'Palestinian Territory, Occupied', + 'PT': 'Portugal', + 'PW': 'Palau', + 'PY': 'Paraguay', + 'QA': 'Qatar', + 'RE': 'Runion', + 'RO': 'Romania', + 'RS': 'Serbia', + 'RU': 'Russian Federation', + 'RW': 'Rwanda', + 'SA': 'Saudi Arabia', + 'SB': 'Solomon Islands', + 'SC': 'Seychelles', + 'SD': 'Sudan', + 'SE': 'Sweden', + 'SG': 'Singapore', + 'SH': 'Saint Helena', + 'SI': 'Slovenia', + 'SJ': 'Svalbard and Jan Mayen', + 'SK': 'Slovakia', + 'SL': 'Sierra Leone', + 'SM': 'San Marino', + 'SN': 'Senegal', + 'SO': 'Somalia', + 'SR': 'Suriname', + 'ST': 'Sao Tome and Principe', + 'SV': 'El Salvador', + 'SY': 'Syrian Arab Republic', + 'SZ': 'Swaziland', + 'TC': 'Turks and Caicos Islands', + 'TD': 'Chad', + 'TF': 'French Southern Territories', + 'TG': 'Togo', + 'TH': 'Thailand', + 'TJ': 'Tajikistan', + 'TK': 'Tokelau', + 'TL': 'Timor-Leste', + 'TM': 'Turkmenistan', + 'TN': 'Tunisia', + 'TO': 'Tonga', + 'TR': 'Turkey', + 'TT': 'Trinidad and Tobago', + 'TV': 'Tuvalu', + 'TW': 'Taiwan, Province of China', + 'TZ': 'Tanzania, United Republic of', + 'UA': 'Ukraine', + 'UG': 'Uganda', + 'UM': 'United States Minor Outlying Islands', + 'US': 'United States', + 'UY': 'Uruguay', + 'UZ': 'Uzbekistan', + 'VA': 'Holy See (Vatican City State)', + 'VC': 'Saint Vincent and the Grenadines', + 'VE': 'Venezuela', + 'VG': 'Virgin Islands, British', + 'VI': 'Virgin Islands, U.S.', + 'VN': 'Viet Nam', + 'VU': 'Vanuatu', + 'WF': 'Wallis and Futuna', + 'WS': 'Samoa', + 'YE': 'Yemen', + 'YT': 'Mayotte', + 'ZA': 'South Africa', + 'ZM': 'Zambia', + 'ZW': 'Zimbabwe'} def init(self, api, name = None): diff --git a/lastfm/group.py b/lastfm/group.py index 3f9c856..cbbf058 100644 --- a/lastfm/group.py +++ b/lastfm/group.py @@ -97,6 +97,36 @@ class Group(LastfmBase, Cacheable): yield self.get_weekly_track_chart(wc.start, wc.end) return gen() + @LastfmBase.cached_property + def members(self): + params = self._default_params({'method': 'group.getMembers'}) + + @lazylist + def gen(lst): + data = self._api._fetch_data(params).find('members') + total_pages = int(data.attrib['totalPages']) + + @lazylist + def gen2(lst, data): + for u in data.findall('user'): + yield User( + self._api, + name = u.findtext('name'), + real_name = u.findtext('realname'), + image = dict([(i.get('size'), i.text) for i in u.findall('image')]), + url = u.findtext('url') + ) + + for u in gen2(data): + yield u + + for page in xrange(1, total_pages+1): + params.update({'page': page}) + data = self._api._fetch_data(params).find('members') + for u in gen2(data): + yield u + return gen() + def _default_params(self, extra_params = {}): if not self.name: raise InvalidParametersError("group has to be provided.") @@ -125,4 +155,5 @@ class Group(LastfmBase, Cacheable): from lastfm.api import Api from lastfm.error import InvalidParametersError +from lastfm.user import User from lastfm.weeklychart import WeeklyChart, WeeklyAlbumChart, WeeklyArtistChart, WeeklyTrackChart diff --git a/lastfm/tag.py b/lastfm/tag.py index df896eb..ab3351f 100644 --- a/lastfm/tag.py +++ b/lastfm/tag.py @@ -169,6 +169,43 @@ class Tag(LastfmBase, Cacheable, Searchable): return Playlist.fetch(self._api, "lastfm://playlist/tag/%s/freetracks" % self.name) + @LastfmBase.cached_property + def weekly_chart_list(self): + params = self._default_params({'method': 'tag.getWeeklyChartList'}) + data = self._api._fetch_data(params).find('weeklychartlist') + return [ + WeeklyChart.create_from_data(self._api, self, c) + for c in data.findall('chart') + ] + + def get_weekly_artist_chart(self, + start = None, + end = None, + limit = None): + params = self._default_params({'method': 'tag.getWeeklyArtistChart'}) + if limit is not None: + params['limit'] = limit + params = WeeklyArtistChart._check_weekly_chart_params(params, start, end) + data = self._api._fetch_data(params).find('weeklyartistchart') + return WeeklyArtistChart.create_from_data(self._api, self, data) + + @LastfmBase.cached_property + def recent_weekly_artist_chart(self): + return self.get_weekly_artist_chart() + + @LastfmBase.cached_property + def weekly_artist_chart_list(self): + wcl = list(self.weekly_chart_list) + wcl.reverse() + @lazylist + def gen(lst): + for wc in wcl: + try: + yield self.get_weekly_artist_chart(wc.start, wc.end) + except LastfmError: + pass + return gen() + @staticmethod def get_top_tags(api): params = {'method': 'tag.getTopTags'} @@ -227,7 +264,8 @@ class Tag(LastfmBase, Cacheable, Searchable): from lastfm.album import Album from lastfm.api import Api from lastfm.artist import Artist -from lastfm.error import InvalidParametersError +from lastfm.error import LastfmError, InvalidParametersError from lastfm.playlist import Playlist from lastfm.stats import Stats from lastfm.track import Track +from lastfm.weeklychart import WeeklyChart, WeeklyArtistChart \ No newline at end of file diff --git a/lastfm/user.py b/lastfm/user.py index aed89dc..586f7c3 100644 --- a/lastfm/user.py +++ b/lastfm/user.py @@ -14,19 +14,16 @@ class User(LastfmBase, Cacheable, Shoutable): def init(self, api, name = None, + real_name = None, url = None, image = None, - stats = None, - language = None, - country = None, - age = None, - gender = None, - subscriber = None): + stats = None): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") Shoutable.init(self, api) self._api = api self._name = name + self._real_name = real_name self._url = url self._image = image self._stats = stats and Stats( @@ -36,17 +33,17 @@ class User(LastfmBase, Cacheable, Shoutable): playcount = stats.playcount ) self._library = User.Library(api, self) - self._language = language - self._country = country - self._age = age - self._gender = gender - self._subscriber = subscriber @property def name(self): """name of the user""" return self._name + @property + def real_name(self): + """real name of the user""" + return self._real_name + @property def url(self): """url of the user's page""" @@ -63,29 +60,39 @@ class User(LastfmBase, Cacheable, Shoutable): return self._stats @property + @LastfmBase.autheticate def language(self): """lang for the user""" return self._language @property + @LastfmBase.autheticate def country(self): """country for the user""" return self._country @property + @LastfmBase.autheticate def age(self): """age for the user""" return self._age @property + @LastfmBase.autheticate def gender(self): """stats for the user""" return self._gender @property + @LastfmBase.autheticate def subscriber(self): """is the user a subscriber""" return self._subscriber + + @property + def autheticated(self): + """is the user autheticated""" + return self._api.get_authenticated_user() == self @LastfmBase.cached_property def events(self): @@ -126,7 +133,37 @@ class User(LastfmBase, Cacheable, Shoutable): @LastfmBase.cached_property def past_events(self): return self.get_past_events() + + @LastfmBase.autheticate + def get_recommended_events(self, limit = None): + params = {'method': 'user.getRecommendedEvents'} + if limit is not None: + params.update({'limit': limit}) + @lazylist + def gen(lst): + data = self._api._fetch_data(params, sign = True, session = True).find('events') + total_pages = int(data.attrib['totalPages']) + + @lazylist + def gen2(lst, data): + for e in data.findall('event'): + yield Event.create_from_data(self._api, e) + + for e in gen2(data): + yield e + + for page in xrange(2, total_pages+1): + params.update({'page': page}) + data = self._api._fetch_data(params, sign = True, session = True).find('events') + for e in gen2(data): + yield e + return gen() + + @LastfmBase.cached_property + def recommended_events(self): + return self.get_recommended_events() + def get_friends(self, limit = None): params = self._default_params({'method': 'user.getFriends'}) @@ -202,6 +239,15 @@ class User(LastfmBase, Cacheable, Shoutable): for p in data.findall('playlist') ] + @LastfmBase.autheticate + def create_playlist(self, title, description = None): + params = {'method': 'playlist.create', + 'title': title} + if description is not None: + params['description'] = description + self._api._post_data(params) + self._playlists = None + @LastfmBase.cached_property def loved_tracks(self): params = self._default_params({'method': 'user.getLovedTracks'}) @@ -353,7 +399,39 @@ class User(LastfmBase, Cacheable, Shoutable): def top_artist(self): """top artist of the user""" pass + + @LastfmBase.cached_property + @LastfmBase.autheticate + def recommended_artists(self): + params = {'method': 'user.getRecommendedArtists'} + + @lazylist + def gen(lst): + data = self._api._fetch_data(params, sign = True, session = True).find('recommendations') + total_pages = int(data.attrib['totalPages']) + @lazylist + def gen2(lst, data): + for a in data.findall('artist'): + yield Artist( + self._api, + name = a.findtext('name'), + mbid = a.findtext('mbid'), + url = a.findtext('url'), + streamable = (a.findtext('streamable') == "1"), + image = dict([(i.get('size'), i.text) for i in a.findall('image')]), + ) + + for a in gen2(data): + yield a + + for page in xrange(2, total_pages+1): + params.update({'page': page}) + data = self._api._fetch_data(params, sign = True, session = True).find('recommendations') + for a in gen2(data): + yield a + return gen() + def get_top_tracks(self, period = None): params = self._default_params({'method': 'user.getTopTracks'}) if period is not None: @@ -516,24 +594,23 @@ class User(LastfmBase, Cacheable, Shoutable): def library(self): return self._library + @staticmethod def get_authenticated_user(api): data = api._fetch_data({'method': 'user.getInfo'}, sign = True, session = True).find('user') - return User( + user = User( api, name = data.findtext('name'), url = data.findtext('url'), - language = data.findtext('lang'), - country = Country(api, name = data.findtext('country')), - age = int(data.findtext('age')), - gender = data.findtext('gender'), - subscriber = (data.findtext('subscriber') == '1'), - stats = Stats( - subject = data.findtext('name'), - playcount = data.findtext('playcount') - ) ) - + user._language = data.findtext('lang') + user._country = Country(api, name = Country.ISO_CODES[data.findtext('country')]) + user._age = int(data.findtext('age')) + user._gender = data.findtext('gender') + user._subscriber = (data.findtext('subscriber') == "1") + user._stats = Stats(subject = user, playcount = data.findtext('playcount')) + return user + def _default_params(self, extra_params = {}): if not self.name: raise InvalidParametersError("user has to be provided.") @@ -589,15 +666,27 @@ class User(LastfmBase, Cacheable, Shoutable): @property def creator(self): return self._creator + + @property + def user(self): + return self._creator - def add_track(self, track): + @LastfmBase.autheticate + def add_track(self, track, artist = None): params = {'method': 'playlist.addTrack', 'playlistID': self.id} - if not isinstance(track, Track): - track = self._api.search_track(track)[0] - - params['artist'] = track.artist.name - params['track'] = track.name + if isinstance(track, Track): + params['artist'] = track.artist.name + params['track'] = track.name + else: + if artist is None: + track = self._api.search_track(track)[0] + params['artist'] = track.artist.name + params['track'] = track.name + else: + params['artist'] = isinstance(artist, Artist) and artist.name or artist + params['track'] = track self._api._post_data(params) + self._data = None @staticmethod def _hash_func(*args, **kwds): @@ -673,7 +762,25 @@ class User(LastfmBase, Cacheable, Shoutable): @LastfmBase.cached_property def albums(self): return self.get_albums() - + + @LastfmBase.autheticate + def add_album(self, album, artist = None): + params = {'method': 'library.addAlbum'} + if isinstance(album, Album): + params['artist'] = album.artist.name + params['album'] = album.name + else: + if artist is None: + album = self._api.search_album(album)[0] + params['artist'] = album.artist.name + params['album'] = album.name + else: + params['artist'] = isinstance(artist, Artist) and artist.name or artist + params['album'] = album + + self._api._post_data(params) + self._albums = None + def get_artists(self, limit = None): params = self._default_params({'method': 'library.getArtists'}) @@ -720,6 +827,16 @@ class User(LastfmBase, Cacheable, Shoutable): def artists(self): return self.get_artists() + @LastfmBase.autheticate + def add_artist(self, artist): + params = {'method': 'library.addArtist'} + if isinstance(artist, Artist): + params['artist'] = artist.name + else: + params['artist'] = artist + self._api._post_data(params) + self._artists = None + def get_tracks(self, limit = None): params = self._default_params({'method': 'library.getTracks'}) @@ -774,6 +891,23 @@ class User(LastfmBase, Cacheable, Shoutable): def tracks(self): return self.get_tracks() + @LastfmBase.autheticate + def add_track(self, track, artist = None): + params = {'method': 'library.addTrack'} + if isinstance(track, Track): + params['artist'] = track.artist.name + params['track'] = track.name + else: + if artist is None: + track = self._api.search_track(track)[0] + params['artist'] = track.artist.name + params['track'] = track.name + else: + params['artist'] = isinstance(artist, Artist) and artist.name or artist + params['track'] = track + self._api._post_data(params) + self._tracks = None + def _default_params(self, extra_params = {}): if not self.user.name: raise InvalidParametersError("user has to be provided.") diff --git a/lastfm/weeklychart.py b/lastfm/weeklychart.py index f1b17d4..662fb3c 100644 --- a/lastfm/weeklychart.py +++ b/lastfm/weeklychart.py @@ -170,19 +170,19 @@ class WeeklyArtistChart(WeeklyChart): start = datetime.utcfromtimestamp(int(data.attrib['from'])), end = datetime.utcfromtimestamp(int(data.attrib['to'])), ) + count_attribute = data.find('artist').findtext('playcount') and 'playcount' or 'weight' + def get_count_attribute(artist): + return {count_attribute: int(eval(artist.findtext(count_attribute)))} + def get_count_attribute_sum(artists): + return {count_attribute: reduce(lambda x,y:(x + int(eval(y.findtext(count_attribute)))), artists, 0)} + return WeeklyArtistChart( subject = subject, start = datetime.utcfromtimestamp(int(data.attrib['from'])), end = datetime.utcfromtimestamp(int(data.attrib['to'])), stats = Stats( subject = subject, - playcount = reduce( - lambda x,y:( - x + int(y.findtext('playcount')) - ), - data.findall('artist'), - 0 - ) + **get_count_attribute_sum(data.findall('artist')) ), artists = [ Artist( @@ -193,7 +193,7 @@ class WeeklyArtistChart(WeeklyChart): stats = Stats( subject = a.findtext('name'), rank = int(a.attrib['rank']), - playcount = int(a.findtext('playcount')), + **get_count_attribute(a) ), url = a.findtext('url'), )