1 # -*- coding: utf-8 -*-
3 # Copyright (c) 2014 Jack Kaliko <kaliko@azylum.org>
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.
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.
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/>.
21 Consume EchoNest web service
25 __author__ = 'Jack Kaliko'
30 from sima.lib.meta import Artist
31 from sima.lib.track import Track
32 from sima.lib.http import HttpClient
33 from sima.utils.utils import WSError, WSNotFound
34 from sima.utils.utils import getws
35 if len(ECH.get('apikey')) == 23: # simple hack allowing imp.reload
39 def get_mbid(obj, foreign='foreign_ids'):
41 for frgnid in obj.get(foreign):
42 if frgnid.get('catalog') == 'musicbrainz':
43 mbid = frgnid.get('foreign_id').split(':')[2]
48 """EchoNest http client
50 root_url = 'http://{host}/api/{version}'.format(**ECH)
60 self.http = HttpClient(cache=self.cache, stats=self.stats)
62 def _controls_answer(self, ans):
65 status = ans.get('response').get('status')
66 code = status.get('code')
70 raise WSNotFound('Artist not found')
71 raise WSError(status.get('message'))
73 def _forge_payload(self, artist, top=False):
76 payload = {'api_key': ECH.get('apikey')}
77 if not isinstance(artist, Artist):
78 raise TypeError('"{0!r}" not an Artist object'.format(artist))
81 id='musicbrainz:artist:{0}'.format(artist.mbid))
83 payload.update(name=artist.name)
84 payload.update(bucket='id:musicbrainz')
85 payload.update(results=100)
88 aid = payload.pop('id')
89 payload.update(artist_id=aid)
91 name = payload.pop('name')
92 payload.update(artist=name)
93 payload.update(results=100)
94 payload.update(sort='song_hotttnesss-desc')
95 # > hashing the URL into a cache key
96 # return a sorted list of 2-tuple to have consistent cache
97 return sorted(payload.items(), key=lambda param: param[0])
99 def get_similar(self, artist=None):
100 """Fetch similar artists
102 payload = self._forge_payload(artist)
104 ressource = '{0}/artist/similar'.format(SimaEch.root_url)
105 ans = self.http(ressource, payload)
106 self._controls_answer(ans.json())
107 for art in ans.json().get('response').get('artists'):
109 yield Artist(mbid=mbid, name=art.get('name'))
111 def get_toptrack(self, artist=None):
112 """Fetch artist top tracks
114 payload = self._forge_payload(artist, top=True)
116 ressource = '{0}/song/search'.format(SimaEch.root_url)
117 ans = self.http(ressource, payload)
118 self._controls_answer(ans.json())
121 'artist': artist.name,
122 'musicbrainz_artistid': artist.mbid,
124 for song in ans.json().get('response').get('songs'):
125 title = song.get('title')
126 if not art.get('musicbrainz_artistid'):
127 art['musicbrainz_artistid'] = get_mbid(song, 'artist_foreign_ids')
128 if title not in titles:
130 yield Track(title=title, **art)
134 # vim: ai ts=4 sw=4 sts=4 expandtab