]> kaliko git repositories - mpd-sima.git/blob - sima/lib/simafm.py
Add copyright headers & clean-up
[mpd-sima.git] / sima / lib / simafm.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009, 2010, 2011, 2012, 2013, 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.5.0'
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 WSError, WSNotFound, WSTimeout, WSHTTPError
35 from sima.utils.utils import getws, Throttle, Cache, purge_cache
36 if len(LFM.get('apikey')) == 43:  # simple hack allowing imp.reload
37     getws(LFM)
38
39 # Some definitions
40 WAIT_BETWEEN_REQUESTS = timedelta(0, 1)
41 SOCKET_TIMEOUT = 6
42
43
44 class SimaFM():
45     """
46     """
47     root_url = 'http://{host}/{version}/'.format(**LFM)
48     cache = {}
49     timestamp = datetime.utcnow()
50     name = 'Last.fm'
51     ratelimit = None
52
53     def __init__(self, cache=True):
54         self.artist = None
55         self._url = self.__class__.root_url
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._url, params=payload,).prepare().url
63         if url in SimaFM.cache:
64             self.current_element = SimaFM.cache.get(url).elem
65             return
66         try:
67             self._fetch_ech(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_ech(self, payload):
76         """fetch from web service"""
77         req = get(self._url, 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             SimaFM.cache.update({req.url:
86                                  Cache(self.current_element)})
87
88     def _controls_answer(self):
89         """Controls answer.
90         """
91         if 'error' in self.current_element:
92             code = self.current_element.get('error')
93             mess = self.current_element.get('message')
94             if code == 6:
95                 raise WSNotFound('{0}: "{1}"'.format(mess, self.artist))
96             raise WSError(mess)
97         return True
98
99     def _forge_payload(self, artist, method='similar', track=None):
100         """
101         """
102         payloads = dict({'similar': {'method':'artist.getsimilar',},
103                         'top': {'method':'artist.gettoptracks',},
104                         'track': {'method':'track.getsimilar',},
105                         'info': {'method':'artist.getinfo',},
106                         })
107         payload = payloads.get(method)
108         payload.update(api_key=LFM.get('apikey'), format='json')
109         if not isinstance(artist, Artist):
110             raise TypeError('"{0!r}" not an Artist object'.format(artist))
111         self.artist = artist
112         if artist.mbid:
113             payload.update(mbid='{0}'.format(artist.mbid))
114         else:
115            payload.update(artist=artist.name)
116         payload.update(results=100)
117         if method == 'track':
118             payload.update(track=track)
119         return payload
120
121     def get_similar(self, artist=None):
122         """
123         """
124         payload = self._forge_payload(artist)
125         # Construct URL
126         self._fetch(payload)
127         for art in self.current_element.get('similarartists').get('artist'):
128             yield Artist(name=art.get('name'), mbid=art.get('mbid', None))
129
130
131 # VIM MODLINE
132 # vim: ai ts=4 sw=4 sts=4 expandtab