# -*- coding: utf-8 -*-
-
-# Copyright (c) 2014 Jack Kaliko <kaliko@azylum.org>
+# Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Jack Kaliko <kaliko@azylum.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
#
"""
-Consume EchoNest web service
+Consume Last.fm web service
"""
-__version__ = '0.0.1'
+__version__ = '0.5.0'
__author__ = 'Jack Kaliko'
from sima import LFM
from sima.lib.meta import Artist
+from sima.utils.utils import WSError, WSNotFound, WSTimeout, WSHTTPError
from sima.utils.utils import getws, Throttle, Cache, purge_cache
if len(LFM.get('apikey')) == 43: # simple hack allowing imp.reload
getws(LFM)
# Some definitions
WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
-SOCKET_TIMEOUT = 4
-
-
-class WSError(Exception):
- pass
-
-class WSNotFound(WSError):
- pass
-
-class WSTimeout(WSError):
- pass
+SOCKET_TIMEOUT = 6
-class WSHTTPError(WSError):
- pass
-
-
-class SimaFM():
- """
+class SimaFM:
+ """Last.fm http client
"""
root_url = 'http://{host}/{version}/'.format(**LFM)
cache = {}
timestamp = datetime.utcnow()
- #ratelimit = None
+ name = 'Last.fm'
+ ratelimit = None
def __init__(self, cache=True):
self.artist = None
self.current_element = SimaFM.cache.get(url).elem
return
try:
- self._fetch_ech(payload)
+ self._fetch_ws(payload)
except Timeout:
raise WSTimeout('Failed to reach server within {0}s'.format(
SOCKET_TIMEOUT))
raise WSError(err)
@Throttle(WAIT_BETWEEN_REQUESTS)
- def _fetch_ech(self, payload):
+ def _fetch_ws(self, payload):
"""fetch from web service"""
req = get(self._url, params=payload,
timeout=SOCKET_TIMEOUT)
#self.__class__.ratelimit = req.headers.get('x-ratelimit-remaining', None)
if req.status_code is not 200:
- raise WSHTTPError(req.status_code)
+ raise WSHTTPError('{0.status_code}: {0.reason}'.format(req))
self.current_element = req.json()
self._controls_answer()
if self.caching:
return True
def _forge_payload(self, artist, method='similar', track=None):
- """
+ """Build payload
"""
payloads = dict({'similar': {'method':'artist.getsimilar',},
'top': {'method':'artist.gettoptracks',},
if artist.mbid:
payload.update(mbid='{0}'.format(artist.mbid))
else:
- payload.update(artist=artist.name)
+ payload.update(artist=artist.name,
+ autocorrect=1)
payload.update(results=100)
if method == 'track':
payload.update(track=track)
return payload
def get_similar(self, artist=None):
- """
+ """Fetch similar artists
"""
payload = self._forge_payload(artist)
# Construct URL
self._fetch(payload)
+ # Artist might be found be return no 'artist' list…
+ # cf. "Mulatu Astatqe" vs. "Mulatu Astatqé" with autocorrect=0
+ # json format is broken IMHO, xml is more consistent IIRC
+ # Here what we got:
+ # >>> {"similarartists":{"#text":"\n","artist":"Mulatu Astatqe"}}
+ # autocorrect=1 should fix it, checking anyway.
+ simarts = self.current_element.get('similarartists').get('artist')
+ if not isinstance(simarts, list):
+ raise WSError('Artist found but no similarities returned')
for art in self.current_element.get('similarartists').get('artist'):
- match = 100 * float(art.get('match'))
- yield Artist(mbid=art.get('mbid', None),
- name=art.get('name')), match
+ yield Artist(name=art.get('name'), mbid=art.get('mbid', None))
# VIM MODLINE