python-lastfm/lastfm/api.py

231 lines
7.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python
__author__ = "Abhinav Sarkar"
__version__ = "0.1"
__license__ = "GNU Lesser General Public License"
class Api(object):
"""The class representing the last.fm web services API."""
DEFAULT_CACHE_TIMEOUT = 3600 # cache for 1 hour
API_ROOT_URL = "http://ws.audioscrobbler.com/2.0/"
def __init__(self,
apiKey = '23caa86333d2cb2055fa82129802780a',
input_encoding=None,
request_headers=None):
self.__apiKey = apiKey
self._cache = FileCache()
self._urllib = urllib2
self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT
self._InitializeRequestHeaders(request_headers)
self._InitializeUserAgent()
self._input_encoding = input_encoding
def getApiKey(self):
return self.__apiKey
def setCache(self, cache):
'''Override the default cache. Set to None to prevent caching.
Args:
cache: an instance that supports the same API as the audioscrobblerws.FileCache
'''
self._cache = cache
def setUrllib(self, urllib):
'''Override the default urllib implementation.
Args:
urllib: an instance that supports the same API as the urllib2 module
'''
self._urllib = urllib
def setCacheTimeout(self, cache_timeout):
'''Override the default cache timeout.
Args:
cache_timeout: time, in seconds, that responses should be reused.
'''
self._cache_timeout = cache_timeout
def setUserAgent(self, user_agent):
'''Override the default user agent
Args:
user_agent: a string that should be send to the server as the User-agent
'''
self._request_headers['User-Agent'] = user_agent
def _BuildUrl(self, url, path_elements=None, extra_params=None):
# Break url into consituent parts
(scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
path = path.replace(' ', '+')
# Add any additional path elements to the path
if path_elements:
# Filter out the path elements that have a value of None
p = [i for i in path_elements if i]
if not path.endswith('/'):
path += '/'
path += '/'.join(p)
# Add any additional query parameters to the query string
if extra_params and len(extra_params) > 0:
extra_query = self._EncodeParameters(extra_params)
# Add it to the existing query
if query:
query += '&' + extra_query
else:
query = extra_query
# Return the rebuilt URL
return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
def _InitializeRequestHeaders(self, request_headers):
if request_headers:
self._request_headers = request_headers
else:
self._request_headers = {}
def _InitializeUserAgent(self):
user_agent = 'Python-urllib/%s (python-audioscrobblerws/%s)' % \
(self._urllib.__version__, __version__)
self.setUserAgent(user_agent)
def _GetOpener(self, url):
opener = self._urllib.build_opener()
opener.addheaders = self._request_headers.items()
return opener
def _Encode(self, s):
if self._input_encoding:
return unicode(s, self._input_encoding).encode('utf-8')
else:
return unicode(s).encode('utf-8')
def _EncodeParameters(self, parameters):
'''Return a string in key=value&key=value form
Values of None are not included in the output string.
Args:
parameters:
A dict of (key, value) tuples, where value is encoded as
specified by self._encoding
Returns:
A URL-encoded string in "key=value&key=value" form
'''
if parameters is None:
return None
else:
return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in parameters.items() if v is not None]))
def getAlbum(self,
artist = None,
album = None,
mbid = None):
return Album.getInfo(self, artist, album, mbid)
def getArtist(self,
artist = None,
mbid = None):
return Artist.getInfo(self, artist, mbid)
2008-07-10 19:34:42 +05:30
def searchArtist(self,
artist,
limit = None,
page = None):
return Artist.search(self, artist, limit, page)
def getEvent(self, event):
return Event.getInfo(self, event)
2008-07-10 19:34:42 +05:30
def getLocation(self, name):
return Location(self, name)
def getCountry(self, name):
return Country(self, name)
def getGroup(self, name):
return Group(self, name)
2008-07-10 19:34:42 +05:30
def _fetchUrl(self,
url,
parameters = None,
no_cache = False):
'''Fetch a URL, optionally caching for a specified time.
Args:
url: The URL to retrieve
parameters: A dict of key/value pairs that should added to
the query string. [OPTIONAL]
no_cache: If true, overrides the cache on the current request
Returns:
A string containing the body of the response.
'''
# Add key/value parameters to the query string of the url
url = self._BuildUrl(url, extra_params=parameters)
# Get a url opener that can handle basic auth
opener = self._GetOpener(url)
# Open and return the URL immediately if we're not going to cache
if no_cache or not self._cache or not self._cache_timeout:
try:
url_data = opener.open(url).read()
except urllib2.HTTPError, e:
url_data = e.read()
else:
# Unique keys are a combination of the url and the username
key = url.encode('utf-8')
# See if it has been cached before
last_cached = self._cache.GetCachedTime(key)
# If the cached version is outdated then fetch another and store it
if not last_cached or time.time() >= last_cached + self._cache_timeout:
try:
url_data = opener.open(url).read()
except urllib2.HTTPError, e:
url_data = e.read()
self._cache.Set(key, url_data)
else:
url_data = self._cache.Get(key)
# Always return the latest version
return url_data
def fetchData(self, params):
params.update({'api_key': self.__apiKey})
2008-07-10 19:34:42 +05:30
xml = self._fetchUrl(Api.API_ROOT_URL, params)
data = ElementTree.XML(xml)
if data.get('status') != "ok":
raise LastfmError("Error code: %s (%s)" % (data.find("error").get('code'), data.findtext('error')))
return data
import urllib
import urllib2
import urlparse
import time
from datetime import datetime
import sys
if sys.version.startswith('2.5'):
import xml.etree.cElementTree as ElementTree
else:
import cElementTree as ElementTree
2008-07-10 19:34:42 +05:30
from error import LastfmError
from filecache import FileCache
from album import Album
from artist import Artist
from event import Event
2008-07-10 19:34:42 +05:30
from geo import Location, Country
from group import Group
#from tag import Tag
#from track import Track
#from user import User