-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.py
121 lines (84 loc) · 3.35 KB
/
api.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""Talks to AcoustID server, answers questions about your tracks.
"""
from __future__ import unicode_literals
import logging
import urlparse
import requests
from . import consts, exceptions
log = logging.getLogger('acoustid-api')
class AcoustID(object):
"""Simple API wrapper for AcoustID.
For more information about the API,
check out http://acoustid.org/webservice.
"""
LOOKUP_PATH = '/v2/lookup'
def __init__(self, client_key, host=None, format='json'):
self.client_key = client_key
self.host = host or consts.DEFAULT_HOST
if not format in consts.FORMATS:
raise exceptions.UnknownFormat('"format" must be one of {%s}' %
', '.join(consts.FORMATS))
self.format = format
self._session = None
def _dispatch(self, method, path, params=None, data=None,
return_key='results'):
"Internal request processor"
if not self._session:
self._session = requests.Session()
url = urlparse.urljoin(self.host, path)
req = requests.Request(method, url, params=params, data=data)
req = req.prepare()
log.debug(req.url)
resp = self._session.send(req)
resp_body = resp.json()
if resp_body['status'][0] == 'e':
self._handle_error(resp_body['error'])
# send back the results
return resp_body[return_key]
def _handle_error(self, error):
"Spins a raw error code into a useful exception"
msg = error.get('message', 'Unknown error')
if error['code'] in consts.ERRORS:
raise consts.ERRORS[error['code']](msg)
# what returned was unexpected
raise exceptions.ApiRequestError(msg)
def _get_params(self, extra=None):
params = {
'format': self.format,
'client': self.client_key,
}
if extra is not None:
params.update(extra)
# flatten "meta" list
if ('meta' in params
and isinstance(params['meta'], (list, set, tuple))):
params['meta'] = ' '.join(params['meta'])
return params
def track(self, track_id, meta=None):
"Returns results for lookup via single track id"
params = self._get_params(extra={
'trackid': track_id,
'meta': meta,
})
return self._dispatch('GET', self.LOOKUP_PATH, params=params)
def fingerprint(self, fingerprint, duration, meta=None):
"Returns results for fingerprint-based lookup"
if duration <= 0 or duration > 0x7fff:
raise exceptions.InvalidDuration('Duration %s is invalid' %
duration)
params = self._get_params(extra={
'fingerprint': fingerprint,
'duration': duration,
'meta': meta,
})
return self._dispatch('GET', self.LOOKUP_PATH, params=params)
def acoustids_for_mbid(self, mbid=None):
"Returns AcoustIDs for a single MusicBrainz id"
# batch implementation will be availble when
# multi-mbid responses aren't 500'ing
params = self._get_params(extra={
'batch': 0,
'mbid': mbid,
})
return self._dispatch('GET', '/v2/track/list_by_mbid', params=params,
return_key='tracks')