]> kaliko git repositories - mpd-sima.git/blob - sima/lib/simaecho.py
Converging webservices plugins
[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 import logging
29
30 from datetime import datetime, timedelta
31 from time import sleep
32
33 from requests import get, Request, Timeout, ConnectionError
34
35 from sima import ECH
36 from sima.lib.meta import Artist
37 from sima.utils.utils import getws, Throttle, Cache, purge_cache
38 if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
39     getws(ECH)
40
41 # Some definitions
42 WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
43 SOCKET_TIMEOUT = 4
44
45
46 class EchoError(Exception):
47     pass
48
49 class EchoNotFound(EchoError):
50     pass
51
52 class EchoTimeout(EchoError):
53     pass
54
55 class EchoHTTPError(EchoError):
56     pass
57
58 class SimaEch():
59     """
60     """
61     root_url = 'http://{host}/api/{version}'.format(**ECH)
62     cache = {}
63     timestamp = datetime.utcnow()
64     ratelimit = None
65
66     def __init__(self, cache=True):
67         self.artist = None
68         self._ressource = None
69         self.current_element = None
70         self.caching = cache
71         purge_cache(self.__class__)
72
73     def _fetch(self, payload):
74         """Use cached elements or proceed http request"""
75         url = Request('GET', self._ressource, params=payload,).prepare().url
76         if url in SimaEch.cache:
77             self.current_element = SimaEch.cache.get(url).elem
78             return
79         try:
80             self._fetch_ech(payload)
81         except Timeout:
82             raise EchoTimeout('Failed to reach server within {0}s'.format(
83                                SOCKET_TIMEOUT))
84         except ConnectionError as err:
85             raise EchoError(err)
86
87     @Throttle(WAIT_BETWEEN_REQUESTS)
88     def _fetch_ech(self, payload):
89         """fetch from web service"""
90         req = get(self._ressource, params=payload,
91                             timeout=SOCKET_TIMEOUT)
92         self.__class__.ratelimit = req.headers.get('x-ratelimit-remaining', None)
93         if req.status_code is not 200:
94             raise EchoHTTPError(req.status_code)
95         self.current_element = req.json()
96         self._controls_answer()
97         if self.caching:
98             SimaEch.cache.update({req.url:
99                                  Cache(self.current_element)})
100
101     def _controls_answer(self):
102         """Controls answer.
103         """
104         status = self.current_element.get('response').get('status')
105         code = status.get('code')
106         if code is 0:
107             return True
108         if code is 5:
109             raise EchoNotFound('Artist not found: "{0}"'.format(self.artist))
110         raise EchoError(status.get('message'))
111
112     def _forge_payload(self, artist):
113         """
114         """
115         payload = {'api_key': ECH.get('apikey')}
116         if not isinstance(artist, Artist):
117             raise TypeError('"{0!r}" not an Artist object'.format(artist))
118         self.artist = artist
119         if artist.mbid:
120             payload.update(
121                     id='musicbrainz:artist:{0}'.format(artist.mbid))
122         else:
123            payload.update(name=artist.name)
124         payload.update(bucket='id:musicbrainz')
125         payload.update(results=100)
126         return payload
127
128     def get_similar(self, artist=None):
129         """
130         """
131         payload = self._forge_payload(artist)
132         # Construct URL
133         self._ressource = '{0}/artist/similar'.format(SimaEch.root_url)
134         self._fetch(payload)
135         for art in self.current_element.get('response').get('artists'):
136             artist = {}
137             mbid = None
138             if 'foreign_ids' in art:
139                 for frgnid in art.get('foreign_ids'):
140                     if frgnid.get('catalog') == 'musicbrainz':
141                         mbid = frgnid.get('foreign_id'
142                                           ).lstrip('musicbrainz:artist:')
143             yield Artist(mbid=mbid, name=art.get('name'))
144
145
146 # VIM MODLINE
147 # vim: ai ts=4 sw=4 sts=4 expandtab