]> kaliko git repositories - mpd-sima.git/blob - sima/lib/simafm.py
Use requests in lastfm module
[mpd-sima.git] / sima / lib / simafm.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 LFM
33 from sima.lib.meta import Artist
34 from sima.utils.utils import getws, Throttle, Cache, purge_cache
35 if len(LFM.get('apikey')) == 43:  # simple hack allowing imp.reload
36     getws(LFM)
37
38 # Some definitions
39 WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
40 SOCKET_TIMEOUT = 4
41
42
43 class WSError(Exception):
44     pass
45
46 class WSNotFound(WSError):
47     pass
48
49 class WSTimeout(WSError):
50     pass
51
52 class WSHTTPError(WSError):
53     pass
54
55
56
57 class SimaFM():
58     """
59     """
60     root_url = 'http://{host}/{version}/'.format(**LFM)
61     cache = {}
62     timestamp = datetime.utcnow()
63     #ratelimit = None
64
65     def __init__(self, cache=True):
66         self.artist = None
67         self._url = self.__class__.root_url
68         self.current_element = None
69         self.caching = cache
70         purge_cache(self.__class__)
71
72     def _fetch(self, payload):
73         """Use cached elements or proceed http request"""
74         url = Request('GET', self._url, params=payload,).prepare().url
75         if url in SimaFM.cache:
76             self.current_element = SimaFM.cache.get(url).elem
77             print('is cached')
78             return
79         try:
80             self._fetch_ech(payload)
81         except Timeout:
82             raise WSTimeout('Failed to reach server within {0}s'.format(
83                                SOCKET_TIMEOUT))
84         except ConnectionError as err:
85             raise WSError(err)
86
87     @Throttle(WAIT_BETWEEN_REQUESTS)
88     def _fetch_ech(self, payload):
89         """fetch from web service"""
90         req = get(self._url, 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 WSHTTPError(req.status_code)
95         self.current_element = req.json()
96         self._controls_answer()
97         if self.caching:
98             SimaFM.cache.update({req.url:
99                                  Cache(self.current_element)})
100
101     def _controls_answer(self):
102         """Controls answer.
103         """
104         if 'error' in self.current_element:
105             code = self.current_element.get('error')
106             mess = self.current_element.get('message')
107             if code == 6:
108                 raise WSNotFound('{0}: "{1}"'.format(mess, self.artist))
109             raise WSError(mess)
110         return True
111
112     def _forge_payload(self, artist, method='similar', track=None):
113         """
114         """
115         payloads = dict({'similar': {'method':'artist.getsimilar',},
116                         'top': {'method':'artist.gettoptracks',},
117                         'track': {'method':'track.getsimilar',},
118                         'info': {'method':'artist.getinfo',},
119                         })
120         payload = payloads.get(method)
121         payload.update(api_key=LFM.get('apikey'), format='json')
122         if not isinstance(artist, Artist):
123             raise TypeError('"{0!r}" not an Artist object'.format(artist))
124         self.artist = artist
125         if artist.mbid:
126             payload.update(mbid='{0}'.format(artist.mbid))
127         else:
128            payload.update(artist=artist.name)
129         payload.update(results=100)
130         if method == 'track':
131             payload.update(track=track)
132         return payload
133
134     def get_similar(self, artist=None):
135         """
136         """
137         payload = self._forge_payload(artist)
138         # Construct URL
139         self._fetch(payload)
140         for art in self.current_element.get('similarartists').get('artist'):
141             match = 100 * float(art.get('match'))
142             yield Artist(mbid=art.get('mbid', None),
143                          name=art.get('name')), match
144
145
146 # VIM MODLINE
147 # vim: ai ts=4 sw=4 sts=4 expandtab