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