implemented new methods and check for authenticated method calls.

master
Abhinav Sarkar 2008-12-31 20:09:06 +00:00
parent 2565dbb9a1
commit 55680ba9c8
10 changed files with 525 additions and 52 deletions

View File

@ -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

View File

@ -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()

View File

@ -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
)

View File

@ -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

View File

@ -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
}

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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.")

View File

@ -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'),
)