]> kaliko git repositories - mpd-sima.git/blobdiff - sima/lib/simaecho.py
Fixed blacklisting in track mode
[mpd-sima.git] / sima / lib / simaecho.py
index 3c7528ff9de2064409ea76fba88fb7ec625477a9..bbfc11416d8fafd8a4f7eaa4b1e064af924848d5 100644 (file)
@@ -25,17 +25,16 @@ __version__ = '0.0.1'
 __author__ = 'Jack Kaliko'
 
 
 __author__ = 'Jack Kaliko'
 
 
-import logging
-
 from datetime import datetime, timedelta
 from datetime import datetime, timedelta
-from time import sleep
 
 from requests import get, Request, Timeout, ConnectionError
 
 from sima import ECH
 from sima.lib.meta import Artist
 
 from requests import get, Request, Timeout, ConnectionError
 
 from sima import ECH
 from sima.lib.meta import Artist
-from sima.utils.utils import getws
-if len(ECH.get('apikey')) == 23:
+from sima.lib.track import Track
+from sima.utils.utils import WSError, WSNotFound, WSTimeout, WSHTTPError
+from sima.utils.utils import getws, Throttle, Cache, purge_cache
+if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
     getws(ECH)
 
 # Some definitions
     getws(ECH)
 
 # Some definitions
@@ -43,74 +42,21 @@ WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
 SOCKET_TIMEOUT = 4
 
 
 SOCKET_TIMEOUT = 4
 
 
-class EchoError(Exception):
-    pass
-
-class EchoNotFound(EchoError):
-    pass
-
-class EchoTimeout(EchoError):
-    pass
-
-class EchoHTTPError(EchoError):
-    pass
-
-class Throttle():
-    def __init__(self, wait):
-        self.wait = wait
-        self.last_called = datetime.now()
-
-    def __call__(self, func):
-        def wrapper(*args, **kwargs):
-            while self.last_called + self.wait > datetime.now():
-                sleep(0.1)
-            result = func(*args, **kwargs)
-            self.last_called = datetime.now()
-            return result
-        return wrapper
-
-
-class Cache():
-    def __init__(self, elem, last=None):
-        self.elem = elem
-        self.requestdate = last
-        if not last:
-            self.requestdate = datetime.utcnow()
-
-    def created(self):
-        return self.requestdate
-
-    def get(self):
-        return self.elem
-
-
-def purge_cache(age=4):
-    now = datetime.utcnow()
-    if now.hour == SimaEch.timestamp.hour:
-        return
-    SimaEch.timestamp = datetime.utcnow()
-    cache = SimaEch.cache
-    delta = timedelta(hours=age)
-    for url in list(cache.keys()):
-        timestamp = cache.get(url).created()
-        if now - timestamp > delta:
-            cache.pop(url)
-
-
-class SimaEch():
-    """
+class SimaEch:
+    """EchoNest http client
     """
     root_url = 'http://{host}/api/{version}'.format(**ECH)
     cache = {}
     timestamp = datetime.utcnow()
     ratelimit = None
     """
     root_url = 'http://{host}/api/{version}'.format(**ECH)
     cache = {}
     timestamp = datetime.utcnow()
     ratelimit = None
+    name = 'EchoNest'
 
     def __init__(self, cache=True):
         self.artist = None
         self._ressource = None
         self.current_element = None
         self.caching = cache
 
     def __init__(self, cache=True):
         self.artist = None
         self._ressource = None
         self.current_element = None
         self.caching = cache
-        purge_cache()
+        purge_cache(self.__class__)
 
     def _fetch(self, payload):
         """Use cached elements or proceed http request"""
 
     def _fetch(self, payload):
         """Use cached elements or proceed http request"""
@@ -119,21 +65,21 @@ class SimaEch():
             self.current_element = SimaEch.cache.get(url).elem
             return
         try:
             self.current_element = SimaEch.cache.get(url).elem
             return
         try:
-            self._fetch_ech(payload)
+            self._fetch_ws(payload)
         except Timeout:
         except Timeout:
-            raise EchoTimeout('Failed to reach server within {0}s'.format(
+            raise WSTimeout('Failed to reach server within {0}s'.format(
                                SOCKET_TIMEOUT))
         except ConnectionError as err:
                                SOCKET_TIMEOUT))
         except ConnectionError as err:
-            raise EchoError(err)
+            raise WSError(err)
 
     @Throttle(WAIT_BETWEEN_REQUESTS)
 
     @Throttle(WAIT_BETWEEN_REQUESTS)
-    def _fetch_ech(self, payload):
+    def _fetch_ws(self, payload):
         """fetch from web service"""
         req = get(self._ressource, params=payload,
                             timeout=SOCKET_TIMEOUT)
         self.__class__.ratelimit = req.headers.get('x-ratelimit-remaining', None)
         if req.status_code is not 200:
         """fetch from web service"""
         req = get(self._ressource, params=payload,
                             timeout=SOCKET_TIMEOUT)
         self.__class__.ratelimit = req.headers.get('x-ratelimit-remaining', None)
         if req.status_code is not 200:
-            raise EchoHTTPError(req.status_code)
+            raise WSHTTPError('{0.status_code}: {0.reason}'.format(req))
         self.current_element = req.json()
         self._controls_answer()
         if self.caching:
         self.current_element = req.json()
         self._controls_answer()
         if self.caching:
@@ -141,18 +87,18 @@ class SimaEch():
                                  Cache(self.current_element)})
 
     def _controls_answer(self):
                                  Cache(self.current_element)})
 
     def _controls_answer(self):
-        """Controls last.fm answer.
+        """Controls answer.
         """
         status = self.current_element.get('response').get('status')
         code = status.get('code')
         if code is 0:
             return True
         if code is 5:
         """
         status = self.current_element.get('response').get('status')
         code = status.get('code')
         if code is 0:
             return True
         if code is 5:
-            raise EchoNotFound('Artist not found: "{0}"'.format(self.artist))
-        raise EchoError(status.get('message'))
+            raise WSNotFound('Artist not found: "{0}"'.format(self.artist))
+        raise WSError(status.get('message'))
 
 
-    def _forge_payload(self, artist):
-        """
+    def _forge_payload(self, artist, top=False):
+        """Build payload
         """
         payload = {'api_key': ECH.get('apikey')}
         if not isinstance(artist, Artist):
         """
         payload = {'api_key': ECH.get('apikey')}
         if not isinstance(artist, Artist):
@@ -162,13 +108,22 @@ class SimaEch():
             payload.update(
                     id='musicbrainz:artist:{0}'.format(artist.mbid))
         else:
             payload.update(
                     id='musicbrainz:artist:{0}'.format(artist.mbid))
         else:
-           payload.update(name=artist.name)
+            payload.update(name=artist.name)
         payload.update(bucket='id:musicbrainz')
         payload.update(bucket='id:musicbrainz')
-        payload.update(results=30)
+        payload.update(results=100)
+        if top:
+            if artist.mbid:
+                aid = payload.pop('id')
+                payload.update(artist_id=aid)
+            else:
+                name = payload.pop('name')
+                payload.update(artist=name)
+            payload.update(results=100)
+            payload.update(sort='song_hotttnesss-desc')
         return payload
 
     def get_similar(self, artist=None):
         return payload
 
     def get_similar(self, artist=None):
-        """
+        """Fetch similar artists
         """
         payload = self._forge_payload(artist)
         # Construct URL
         """
         payload = self._forge_payload(artist)
         # Construct URL
@@ -184,6 +139,24 @@ class SimaEch():
                                           ).lstrip('musicbrainz:artist:')
             yield Artist(mbid=mbid, name=art.get('name'))
 
                                           ).lstrip('musicbrainz:artist:')
             yield Artist(mbid=mbid, name=art.get('name'))
 
+    def get_toptrack(self, artist=None):
+        """Fetch artist top tracks
+        """
+        payload = self._forge_payload(artist, top=True)
+        # Construct URL
+        self._ressource = '{0}/song/search'.format(SimaEch.root_url)
+        self._fetch(payload)
+        titles = list()
+        artist = {
+                'artist': artist.name,
+                'musicbrainz_artistid': artist.mbid,
+                }
+        for song in self.current_element.get('response').get('songs'):
+            title = song.get('title')
+            if title not in titles:
+                titles.append(title)
+                yield Track(title=title, **artist)
+
 
 # VIM MODLINE
 # vim: ai ts=4 sw=4 sts=4 expandtab
 
 # VIM MODLINE
 # vim: ai ts=4 sw=4 sts=4 expandtab