]> kaliko git repositories - mpd-sima.git/blobdiff - sima/lib/simaecho.py
Fixed blacklisting in track mode
[mpd-sima.git] / sima / lib / simaecho.py
index fbb60b07fb973305beec2aa2a5a168ca7db8f0ca..bbfc11416d8fafd8a4f7eaa4b1e064af924848d5 100644 (file)
@@ -25,16 +25,15 @@ __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
+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)
 
 if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
     getws(ECH)
 
@@ -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:
@@ -148,11 +94,11 @@ class SimaEch():
         if code is 0:
             return True
         if code is 5:
         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(results=100)
         payload.update(bucket='id:musicbrainz')
         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