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().
This commit is contained in:
parent
903c9b1622
commit
27947fbb9d
|
@ -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
|
||||||
|
|
248
pylast.py
248
pylast.py
|
@ -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,25 +851,9 @@ 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."""
|
||||||
|
|
||||||
|
if self.getAlbumName():
|
||||||
return Album(self.getArtistName(), self.getAlbumName(), *self.auth_data)
|
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.
|
||||||
* playlist_id: The unique playlist ID.
|
* playlist_id: The unique playlist ID.
|
||||||
|
@ -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,100 +2277,22 @@ 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()
|
|
||||||
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
|
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."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue