]> kaliko git repositories - mpd-sima.git/blob - sima/lib/simaecho.py
14623031df6ab1f46d0d991572975171f0908812
[mpd-sima.git] / sima / lib / simaecho.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 Jack Kaliko <kaliko@azylum.org>
4 #
5 #   This program is free software: you can redistribute it and/or modify
6 #   it under the terms of the GNU General Public License as published by
7 #   the Free Software Foundation, either version 3 of the License, or
8 #   (at your option) any later version.
9 #
10 #   This program is distributed in the hope that it will be useful,
11 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #   GNU General Public License for more details.
14 #
15 #   You should have received a copy of the GNU General Public License
16 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 #
19
20 """
21 Consume EchoNest web service
22 """
23
24 __version__ = '0.0.1'
25 __author__ = 'Jack Kaliko'
26
27
28 from datetime import datetime, timedelta
29
30 from requests import get, Request, Timeout, ConnectionError
31
32 from sima import ECH
33 from sima.lib.meta import Artist
34 from sima.utils.utils import WSError, WSNotFound, WSTimeout, WSHTTPError
35 from sima.utils.utils import getws, Throttle, Cache, purge_cache
36 if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
37     getws(ECH)
38
39 # Some definitions
40 WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
41 SOCKET_TIMEOUT = 4
42
43
44 class SimaEch:
45     """EchoNest http client
46     """
47     root_url = 'http://{host}/api/{version}'.format(**ECH)
48     cache = {}
49     timestamp = datetime.utcnow()
50     ratelimit = None
51     name = 'EchoNest'
52
53     def __init__(self, cache=True):
54         self.artist = None
55         self._ressource = None
56         self.current_element = None
57         self.caching = cache
58         purge_cache(self.__class__)
59
60     def _fetch(self, payload):
61         """Use cached elements or proceed http request"""
62         url = Request('GET', self._ressource, params=payload,).prepare().url
63         if url in SimaEch.cache:
64             self.current_element = SimaEch.cache.get(url).elem
65             return
66         try:
67             self._fetch_ws(payload)
68         except Timeout:
69             raise WSTimeout('Failed to reach server within {0}s'.format(
70                                SOCKET_TIMEOUT))
71         except ConnectionError as err:
72             raise WSError(err)
73
74     @Throttle(WAIT_BETWEEN_REQUESTS)
75     def _fetch_ws(self, payload):
76         """fetch from web service"""
77         req = get(self._ressource, params=payload,
78                             timeout=SOCKET_TIMEOUT)
79         self.__class__.ratelimit = req.headers.get('x-ratelimit-remaining', None)
80         if req.status_code is not 200:
81             raise WSHTTPError(req.status_code)
82         self.current_element = req.json()
83         self._controls_answer()
84         if self.caching:
85             SimaEch.cache.update({req.url:
86                                  Cache(self.current_element)})
87
88     def _controls_answer(self):
89         """Controls answer.
90         """
91         status = self.current_element.get('response').get('status')
92         code = status.get('code')
93         if code is 0:
94             return True
95         if code is 5:
96             raise WSNotFound('Artist not found: "{0}"'.format(self.artist))
97         raise WSError(status.get('message'))
98
99     def _forge_payload(self, artist):
100         """Build payload
101         """
102         payload = {'api_key': ECH.get('apikey')}
103         if not isinstance(artist, Artist):
104             raise TypeError('"{0!r}" not an Artist object'.format(artist))
105         self.artist = artist
106         if artist.mbid:
107             payload.update(
108                     id='musicbrainz:artist:{0}'.format(artist.mbid))
109         else:
110             payload.update(name=artist.name)
111         payload.update(bucket='id:musicbrainz')
112         payload.update(results=100)
113         return payload
114
115     def get_similar(self, artist=None):
116         """Fetch similar artists
117         """
118         payload = self._forge_payload(artist)
119         # Construct URL
120         self._ressource = '{0}/artist/similar'.format(SimaEch.root_url)
121         self._fetch(payload)
122         for art in self.current_element.get('response').get('artists'):
123             artist = {}
124             mbid = None
125             if 'foreign_ids' in art:
126                 for frgnid in art.get('foreign_ids'):
127                     if frgnid.get('catalog') == 'musicbrainz':
128                         mbid = frgnid.get('foreign_id'
129                                           ).lstrip('musicbrainz:artist:')
130             yield Artist(mbid=mbid, name=art.get('name'))
131
132
133 # VIM MODLINE
134 # vim: ai ts=4 sw=4 sts=4 expandtab