* Track.getAlbum doesn't crash when the album could not be determined.
 * Most of User functions now exist in the new AuthenticatedUser due to a web services limitation.
 * Track.getImage is removed, it's the same as Track.getAlbum().getImage().
This commit is contained in:
Amr Hassan 2008-11-22 03:32:58 +00:00
parent 903c9b1622
commit 27947fbb9d
2 changed files with 140 additions and 127 deletions

View file

@ -1,3 +1,8 @@
0.2.18
* Track.getAlbum doesn't crash when the album could not be determined.
* Most of User functions now exist in the new AuthenticatedUser due to a web services limitation.
* Track.getImage is removed, it's the same as Track.getAlbum().getImage().
0.2.17 0.2.17
* All the getTopTags and getTopTagsWithCounts return an empty sequence if failed instead of None. * All the getTopTags and getTopTagsWithCounts return an empty sequence if failed instead of None.
* toStr() is now less crashy. * toStr() is now less crashy.
@ -26,7 +31,7 @@
0.2b12 0.2b12
* fixed: some unicode problems. * fixed: some unicode problems.
* fix: a typo in the function name Exceptionable.clear_errors() * fix: a typo in the function name Exceptionable.clear_errors()
* fixed: User.fetchPlaylist (#3) * fixed: User.fetchPlaylist (issue #3)
* api change: User.getPlaylistsIDs is now User.getPlaylistsData, and User.getPlaylistsIDs returns a list of IDs now. * api change: User.getPlaylistsIDs is now User.getPlaylistsData, and User.getPlaylistsIDs returns a list of IDs now.
0.2b10 0.2b10

252
pylast.py
View file

@ -21,7 +21,7 @@
# http://code.google.com/p/pylast/ # http://code.google.com/p/pylast/
__name__ = 'pyLast' __name__ = 'pyLast'
__version__ = '0.2.17' __version__ = '0.2.18'
__doc__ = 'A Python interface to the Last.fm API.' __doc__ = 'A Python interface to the Last.fm API.'
__author__ = 'Amr Hassan' __author__ = 'Amr Hassan'
__email__ = 'amr.hassan@gmail.com' __email__ = 'amr.hassan@gmail.com'
@ -66,6 +66,7 @@ PERIOD_12MONTHS = '12month'
IMAGE_SMALL = 0 IMAGE_SMALL = 0
IMAGE_MEDIUM = 1 IMAGE_MEDIUM = 1
IMAGE_LARGE = 2 IMAGE_LARGE = 2
IMAGE_EXTRA_LARGE = 3
DOMAIN_ENGLISH = 'www.last.fm' DOMAIN_ENGLISH = 'www.last.fm'
DOMAIN_GERMAN = 'www.lastfm.de' DOMAIN_GERMAN = 'www.lastfm.de'
@ -94,20 +95,20 @@ def _status2str(lastfm_status):
statuses = { statuses = {
STATUS_OK: 'OK', STATUS_OK: 'OK',
STATUS_FAILED: 'Failed', STATUS_FAILED: 'Failed',
STATUS_INVALID_METHOD: 'Invalid Method - No method with that name in this package', STATUS_INVALID_METHOD: 'Invalid Method - No method with that name in this package.',
STATUS_TOKEN_ERROR: 'Token Error - There was an error granting the Request token', STATUS_TOKEN_ERROR: 'Token Error - There was an error granting the Request token.',
STATUS_INVALID_SERVICE: 'Invalid Service - This service does not exist', STATUS_INVALID_SERVICE: 'Invalid Service - This service does not exist.',
STATUS_AUTH_FAILED: 'Authentication Failed - You do not have permissions to access the service', STATUS_AUTH_FAILED: 'Authentication Failed - You do not have permissions to access the service.',
STATUS_INVALID_FORMAT: "Invalid Format - This service doesn't exist in that format", STATUS_INVALID_FORMAT: "Invalid Format - This service doesn't exist in that format.",
STATUS_INVALID_PARAMS: 'Invalid Parameters - Your Request is missing a required parameter', STATUS_INVALID_PARAMS: 'Invalid Parameters - Your Request is missing a required parameter.',
STATUS_INVALID_RESOURCE: 'Invalid Resource Specified', STATUS_INVALID_RESOURCE: 'Invalid Resource Specified.',
STATUS_INVALID_SK: 'Invalid Session Key - Please re-authenticate', STATUS_INVALID_SK: 'Invalid Session Key - Please re-authenticate.',
STATUS_INVALID_API_KEY: 'Invalid API Key - You must be granted a valid key by last.fm', STATUS_INVALID_API_KEY: 'Invalid API Key - You must be granted a valid key by last.fm.',
STATUS_OFFLINE: 'Service Offline - This service is temporarily offline. Try again later.', STATUS_OFFLINE: 'Service Offline - This service is temporarily offline. Try again later.',
STATUS_SUBSCRIBERS_ONLY: 'Subscribers Only - This service is only available to paid last.fm subscribers', STATUS_SUBSCRIBERS_ONLY: 'Subscribers Only - This service is only available to paid last.fm subscribers.',
STATUS_TOKEN_UNAUTHORIZED: 'Unauthorized Token - This token has not been authorized', STATUS_TOKEN_UNAUTHORIZED: 'Unauthorized Token - This token has not been authorized.',
STATUS_TOKEN_EXPIRED: 'Token Expired -This token has expired', STATUS_TOKEN_EXPIRED: 'Token Expired -This token has expired.',
STATUS_INVALID_SIGNATURE: 'Invalid method signature supplied', STATUS_INVALID_SIGNATURE: 'Invalid method signature supplied.',
} }
return statuses[int(lastfm_status)] return statuses[int(lastfm_status)]
@ -318,7 +319,7 @@ class SessionGenerator(_Asynchronizer, _Exceptionable):
"""Steps of authorization: """Steps of authorization:
1. Retrieve token: token = getToken() 1. Retrieve token: token = getToken()
2. Authorize this token by openning the web page at the URL returned by getAuthURL(token) 2. Authorize this token by openning the web page at the URL returned by getAuthURL(token)
3. Call getSessionData(token) to collect the session parameters. 3. Call getSessionKey(token) to collect the session parameters.
A session key's lifetime is infinie, unless the user provokes the rights of the given API Key. A session key's lifetime is infinie, unless the user provokes the rights of the given API Key.
""" """
@ -336,7 +337,6 @@ class SessionGenerator(_Asynchronizer, _Exceptionable):
The token then has to be authorized from getAuthURL before creating session. The token then has to be authorized from getAuthURL before creating session.
""" """
doc = _Request(self, 'auth.getToken', self.api_key, dict(), True, self.secret).execute() doc = _Request(self, 'auth.getToken', self.api_key, dict(), True, self.secret).execute()
if not doc: if not doc:
@ -356,8 +356,13 @@ class SessionGenerator(_Asynchronizer, _Exceptionable):
def getSessionData(self, token): def getSessionData(self, token):
"""Retrieves session data for the authorized token. """Retrieves session data for the authorized token.
getSessionData(token) --> {'name': str, 'key': str, 'subscriber': bool} getSessionData(token) --> {'name': str, 'key': str, 'subscriber': bool}
[DEPRECATED]
Use SessionGenerator.getSessionKey and AuthenticatedUser instead.
""" """
warn_deprecated("SessionGenerator.getSessionData", "SessionGenerator.getSessionKey and AuthenticatedUser")
params = {'token': token} params = {'token': token}
doc = _Request(self, 'auth.getSession', self.api_key, params, True, self.secret).execute() doc = _Request(self, 'auth.getSession', self.api_key, params, True, self.secret).execute()
@ -375,6 +380,18 @@ class SessionGenerator(_Asynchronizer, _Exceptionable):
return data return data
def getSessionKey(self, token):
"""Retrieves the authorized session key.
"""
params = {'token': token}
doc = _Request(self, 'auth.getSession', self.api_key, params, True, self.secret).execute()
if not doc:
return None
return doc.getElementsByTagName('key')[0].firstChild.data
class _BaseObject(_Asynchronizer, _Exceptionable): class _BaseObject(_Asynchronizer, _Exceptionable):
"""An abstract webservices object.""" """An abstract webservices object."""
@ -642,9 +659,10 @@ class Album(_BaseObject, _Cacheable, _Taggable):
return self._getCachedInfo('release_date') return self._getCachedInfo('release_date')
def getImage(self, size = IMAGE_LARGE): def getImage(self, size = IMAGE_EXTRA_LARGE):
"""Returns the associated image URL. """Returns the associated image URL.
* size: The image size. Possible values: * size: The image size. Possible values:
o IMAGE_EXTRA_LARGE
o IMAGE_LARGE o IMAGE_LARGE
o IMAGE_MEDIUM o IMAGE_MEDIUM
o IMAGE_SMALL o IMAGE_SMALL
@ -682,7 +700,6 @@ class Album(_BaseObject, _Cacheable, _Taggable):
return l return l
def fetchPlaylist(self): def fetchPlaylist(self):
"""Returns the list of Tracks on this album. """ """Returns the list of Tracks on this album. """
@ -834,24 +851,8 @@ class Track(_BaseObject, _Cacheable, _Taggable):
def getAlbum(self): def getAlbum(self):
"""Returns the album object of this track.""" """Returns the album object of this track."""
return Album(self.getArtistName(), self.getAlbumName(), *self.auth_data) if self.getAlbumName():
return Album(self.getArtistName(), self.getAlbumName(), *self.auth_data)
def getImage(self, size = IMAGE_LARGE, if_na_get_artist_image = False):
"""Returns the associated image URL.
* size: The image size. Possible values:
o IMAGE_LARGE
o IMAGE_MEDIUM
o IMAGE_SMALL
* if_na_get_artist_image: If set to True, it will return the artist's image if the track has none.
"""
url = self._getCachedInfo('images', size)
if if_na_get_artist_image:
if not url or url.startswith('http://cdn.last.fm/depth/catalogue'):
url = self.getArtist().getImage(size)
return url
def addToPlaylist(self, playlist_id): def addToPlaylist(self, playlist_id):
"""Adds this track to a user playlist. """Adds this track to a user playlist.
@ -1904,7 +1905,7 @@ class Library(_BaseObject):
doc = _Request(self, 'library.getAlbums', self.api_key, params).execute() doc = _Request(self, 'library.getAlbums', self.api_key, params).execute()
if not doc: if not doc:
return None return []
albums = doc.getElementsByTagName('album') albums = doc.getElementsByTagName('album')
list = [] list = []
@ -1937,7 +1938,7 @@ class Library(_BaseObject):
doc = _Request(self, 'library.getArtists', self.api_key, params).execute() doc = _Request(self, 'library.getArtists', self.api_key, params).execute()
if not doc: if not doc:
return None return []
artists = doc.getElementsByTagName('artist') artists = doc.getElementsByTagName('artist')
list = [] list = []
@ -1966,7 +1967,7 @@ class Library(_BaseObject):
doc = _Request(self, 'library.getTracks', self.api_key, params).execute() doc = _Request(self, 'library.getTracks', self.api_key, params).execute()
if not doc: if not doc:
return None return []
tracks = doc.getElementsByTagName('track') tracks = doc.getElementsByTagName('track')
list = [] list = []
@ -2276,99 +2277,21 @@ class Tag(_BaseObject):
return self.getName().encode('utf-8') return self.getName().encode('utf-8')
class User(_BaseObject, _Cacheable): class User(_BaseObject):
"""A Last.fm user.""" """A Last.fm user."""
def __init__(self, user_name, api_key, api_secret, session_key): def __init__(self, user_name, api_key, api_secret, session_key):
_BaseObject.__init__(self, api_key, api_secret, session_key) _BaseObject.__init__(self, api_key, api_secret, session_key)
_Cacheable.__init__(self)
self.name = user_name self.name = user_name
self._cached_info = None
def _getParams(self): def _getParams(self):
return {'sk': self.session_key, 'user': self.name} return {'sk': self.session_key, "user": self.getName()}
def _getInfo(self): def getName(self):
"""Returns a dictionary with various metadata values.""" """Returns the nuser name."""
params = self._getParams() return self.name
doc = _Request(self, 'user.getInfo', self.api_key, params, True, self.secret).execute()
if not doc:
return None
data = {}
data['name'] = self._extract(doc, 'name')
data['image'] = self._extract(doc, 'image')
data['language'] = self._extract(doc, 'lang')
data['country'] = self._extract(doc, 'country')
data['age'] = self._extract(doc, 'age')
data['gender'] = self._extract(doc, 'gender')
data['subscriber'] = self._extract(doc, 'subscriber')
data['play_count'] = self._extract(doc, 'playcount')
return data
def getName(self, from_server = False):
"""Returns the user name.
* from_server: If set to True, the value will be retrieved from the server.
"""
if from_server:
return self._getCachedInfo('name')
else:
return self.name
def getImage(self):
"""Returns the user's avatar."""
return self._getCachedInfo('image')
def getLanguage(self):
"""Returns the language code of the language used by the user."""
return self._getCachedInfo('language')
def getCountryName(self):
"""Returns the name of the country of the user."""
return self._getCachedInfo('country')
def getAge(self):
"""Returns the user's age."""
return self._getCachedInfo('age')
def getGender(self):
"""Returns the user's gender. Either USER_MALE or USER_FEMALE."""
value = self._getCachedInfo('gender')
if value == 'm':
return USER_MALE
elif value == 'f':
return USER_FEMALE
return None
def isSubscriber(self):
"""Returns whether the user is a subscriber or not. True or False."""
value = self._getCachedInfo('subscriber')
if value == '1':
return True
elif value == '0':
return False
return None
def getPlayCount(self):
"""Returns the user's playcount so far."""
return int(self._getCachedInfo('play_count'))
def getEvents(self): def getEvents(self):
"""Returns all the upcoming events for this user. """ """Returns all the upcoming events for this user. """
@ -2899,6 +2822,91 @@ class User(_BaseObject, _Cacheable):
return self.getName().encode('utf-8') return self.getName().encode('utf-8')
class AuthenticatedUser(User, _Cacheable):
def __init__(self, api_key, api_secret, session_key):
User.__init__("", api_key, api_secret, session_key);
_Cacheable.__init__(self)
self._cached_info = None
def _getParams(self):
return {'sk': self.session_key}
def _getInfo(self):
"""Returns a dictionary with various metadata values."""
params = self._getParams()
doc = _Request(self, 'user.getInfo', self.api_key, params, True, self.secret).execute()
if not doc:
return None
data = {}
data['name'] = self._extract(doc, 'name')
data['image'] = self._extract(doc, 'image')
data['language'] = self._extract(doc, 'lang')
data['country'] = self._extract(doc, 'country')
data['age'] = self._extract(doc, 'age')
data['gender'] = self._extract(doc, 'gender')
data['subscriber'] = self._extract(doc, 'subscriber')
data['play_count'] = self._extract(doc, 'playcount')
return data
def getName(self):
"""Returns the user name."""
return self._getCachedInfo('name')
def getImage(self):
"""Returns the user's avatar."""
return self._getCachedInfo('image')
def getLanguage(self):
"""Returns the language code of the language used by the user."""
return self._getCachedInfo('language')
def getCountryName(self):
"""Returns the name of the country of the user."""
return self._getCachedInfo('country')
def getAge(self):
"""Returns the user's age."""
return self._getCachedInfo('age')
def getGender(self):
"""Returns the user's gender. Either USER_MALE or USER_FEMALE."""
value = self._getCachedInfo('gender')
if value == 'm':
return USER_MALE
elif value == 'f':
return USER_FEMALE
return None
def isSubscriber(self):
"""Returns whether the user is a subscriber or not. True or False."""
value = self._getCachedInfo('subscriber')
if value == '1':
return True
elif value == '0':
return False
return None
def getPlayCount(self):
"""Returns the user's playcount so far."""
return int(self._getCachedInfo('play_count'))
class _Search(_BaseObject): class _Search(_BaseObject):
"""An abstract class. Use one of its derivatives.""" """An abstract class. Use one of its derivatives."""