-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathfreesound_api.py
423 lines (353 loc) · 13.9 KB
/
freesound_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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
"""
A python client for the Freesound API.
Find the API documentation at http://www.freesound.org/docs/api/.
Apply for an API key at http://www.freesound.org/api/apply/.
The client automatically maps function arguments to http parameters of the API. JSON results are converted to python objects. The main object types (Sound, User, Pack) are augmented with the corresponding API calls.
Note that POST resources are not supported. Downloading full quality sounds requires Oauth2 authentication (see http://freesound.org/docs/api/authentication.html). Oauth2 authentication is supported, but you are expected to implement the workflow.
"""
import re
import json
import ssl
from os.path import join
from collections import namedtuple
import requests
from urllib.request import urlopen, Request
from urllib.parse import urlparse, urlencode, quote, parse_qs
from urllib.error import HTTPError
class URIS():
HOST = 'freesound.org'
BASE = 'https://'+HOST+'/apiv2'
TEXT_SEARCH = '/search/text/'
CONTENT_SEARCH= '/search/content/'
COMBINED_SEARCH = '/search/combined/'
SOUND = '/sounds/<sound_id>/'
SOUND_ANALYSIS = '/sounds/<sound_id>/analysis/'
SIMILAR_SOUNDS = '/sounds/<sound_id>/similar/'
COMMENTS = '/sounds/<sound_id>/comments/'
DOWNLOAD = '/sounds/<sound_id>/download/'
UPLOAD = '/sounds/upload/'
DESCRIBE = '/sounds/<sound_id>/describe/'
PENDING = '/sounds/pending_uploads/'
BOOKMARK = '/sounds/<sound_id>/bookmark/'
RATE = '/sounds/<sound_id>/rate/'
COMMENT = '/sounds/<sound_id>/comment/'
AUTHORIZE = '/oauth2/authorize/'
LOGOUT = '/api-auth/logout/'
LOGOUT_AUTHORIZE = '/oauth2/logout_and_authorize/'
ME = '/me/'
USER = '/users/<username>/'
USER_SOUNDS = '/users/<username>/sounds/'
USER_PACKS = '/users/<username>/packs/'
USER_BOOKMARK_CATEGORIES = '/users/<username>/bookmark_categories/'
USER_BOOKMARK_CATEGORY_SOUNDS = '/users/<username>/bookmark_categories/<category_id>/sounds/'
PACK = '/packs/<pack_id>/'
PACK_SOUNDS = '/packs/<pack_id>/sounds/'
PACK_DOWNLOAD = '/packs/<pack_id>/download/'
@classmethod
def uri(cls, uri, *args):
for a in args:
uri = re.sub('<[\w_]+>', quote(str(a)), uri, 1)
return cls.BASE+uri
class FreesoundClient():
"""
Start here, create a FreesoundClient and set an authentication token
using set_token
>>> c = FreesoundClient()
>>> c.set_token("<your_api_key>")
"""
client_secret = ""
client_id = ""
token = ""
header =""
def check_access(self):
uri = URIS.uri(URIS.SOUND,6)
request = FSRequest.request(uri, {}, self, Sound)
if hasattr(request, 'code'):
return False
else:
return True
def get_sound(self, sound_id):
"""
Get a sound object by id
http://freesound.org/docs/api/resources_apiv2.html#sound-resources
>>> sound = c.get_sound(6)
"""
uri = URIS.uri(URIS.SOUND,sound_id)
return FSRequest.request(uri, {}, self, Sound)
def text_search(self, **params):
"""
Search sounds using a text query and/or filter. Returns an iterable
Pager object. The fields parameter allows you to specify the information
you want in the results list
http://freesound.org/docs/api/resources_apiv2.html#text-search
>>> sounds = c.text_search(query="dubstep", filter="tag:loop", fields="id,name,url")
>>> for snd in sounds: print snd.name
"""
uri = URIS.uri(URIS.TEXT_SEARCH)
return FSRequest.request(uri, params, self, Pager)
def content_based_search(self, **params):
"""
Search sounds using a content-based descriptor target and/or filter
See essentia_example.py for an example using essentia
http://freesound.org/docs/api/resources_apiv2.html#content-search
>>> sounds = c.content_based_search(target="lowlevel.pitch.mean:220",descriptors_filter="lowlevel.pitch_instantaneous_confidence.mean:[0.8 TO 1]",fields="id,name,url")
>>> for snd in sounds: print snd.name
"""
uri = URIS.uri(URIS.CONTENT_SEARCH)
return FSRequest.request(uri, params, self, Pager)
def combined_search(self, **params):
"""
Combine both text and content-based queries.
http://freesound.org/docs/api/resources_apiv2.html#combined-search
>>> sounds = c.combined_search(target="lowlevel.pitch.mean:220", filter="single-note")
"""
uri = URIS.uri(URIS.COMBINED_SEARCH)
return FSRequest.request(uri,params,self,CombinedSearchPager)
def get_user(self,username):
"""
Get a user object by username
http://freesound.org/docs/api/resources_apiv2.html#combined-search
>>> u=c.get_user("xserra")
"""
uri = URIS.uri(URIS.USER, username)
return FSRequest.request(uri,{},self,User)
def get_pack(self,pack_id):
"""
Get a user object by username
http://freesound.org/docs/api/resources_apiv2.html#combined-search
>>> p = c.get_pack(3416)
"""
uri = URIS.uri(URIS.PACK, pack_id)
return FSRequest.request(uri,{},self,Pack)
def set_token(self, token, auth_type="token"):
"""
Set your API key or Oauth2 token
http://freesound.org/docs/api/authentication.html
http://freesound.org/docs/api/resources_apiv2.html#combined-search
>>> c.set_token("<your_api_key>")
"""
self.token = token#TODO
self.header = 'Bearer '+token if auth_type=='oauth' else 'Token '+token
class FreesoundObject:
"""
Base object, automatically populated from parsed json dictionary
"""
def __init__(self,json_dict, client):
self.client=client
def replace_dashes(d):
for k, v in list(d.items()):
if "-" in k:
d[k.replace("-","_")] = d[k]
del d[k]
if isinstance(v, dict):replace_dashes(v)
replace_dashes(json_dict)
self.__dict__.update(json_dict)
for k, v in json_dict.items():
if isinstance(v, dict):
self.__dict__[k] = FreesoundObject(v, client)
class FreesoundException(Exception):
"""
Freesound API exception
"""
def __init__(self, http_code, detail):
self.code = http_code
self.detail = detail
def __str__(self):
return '<FreesoundException: code=%s, detail="%s">' % \
(self.code, self.detail)
class FSRequest:
"""
Makes requests to the freesound API. Should not be used directly.
"""
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
@classmethod
def request(cls, uri, params={}, client=None, wrapper=FreesoundObject,
method='GET', data=False):
p = params if params else {}
url = '%s?%s' % (uri, urlencode(p)) if params else uri
d = urllib.urlencode(data) if data else None
headers = {'Authorization':client.header}
req = Request(url,d,headers)
try:
f = urlopen(req, context=cls.ctx)
except HTTPError as e:
resp = e.read()
if e.code >= 200 and e.code < 300:
return resp
else:
return FreesoundException(e.code,
json.loads(resp.decode("utf-8")))
resp = f.read()
f.close()
result = None
try:
result = json.loads(resp.decode("utf-8"))
except:
raise FreesoundException(0,"Couldn't parse response")
if wrapper:
return wrapper(result,client)
return result
@classmethod
def retrieve(cls, url, client, path):
r = requests.get(url)
with open (path, 'wb') as audiofile:
audiofile.write(r.content)
return path
class Pager(FreesoundObject):
"""
Paginates search results. Can be used in for loops to iterate its results array.
"""
def __getitem__(self, key):
return Sound(self.results[key], self.client)
def next_page(self):
"""
Get a Pager with the next results page.
"""
return FSRequest.request(self.next, {}, self.client, Pager)
def previous_page(self):
"""
Get a Pager with the previous results page.
"""
return FSRequest.request(self.previous, {}, self.client, Pager)
def get_page(self, n):
url = self.next
uri = urlparse(url)
for index,item in enumerate(uri):
if ('query' in item):
urid=index
query = uri[urid].split("&")
for index,item in enumerate(query):
if ("page" in item):
query[index] = 'page=' + str(n)
url = uri[0] + '://' + uri[1] + uri[2] + '?' + "&".join(query)
return FSRequest.request(url, {}, self.client, Pager)
class GenericPager(Pager):
"""
Paginates results for objects different than Sound.
"""
def __getitem__(self, key):
return FreesoundObject(self.results[key],self.client)
class CombinedSearchPager(FreesoundObject):
"""
Combined search uses a different pagination style.
The total amount of results is not available, and the size of the page is not guaranteed.
Use :py:meth:`~freesound.CombinedSearchPager.more` to get more results if available.
"""
def __getitem__(self, key):
return Sound(self.results[key], None)
def more(self):
"""
Get more results
"""
return FSRequest.request(self.more, {}, self.client, CombinedSearchPager)
class Sound(FreesoundObject):
"""
Freesound Sound resources
>>> sound = c.get_sound(6)
"""
def retrieve(self, directory, name=False):
"""
Download the original sound file (requires Oauth2 authentication).
http://freesound.org/docs/api/resources_apiv2.html#download-sound-oauth2-required
>>> sound.retrieve("/tmp")
"""
path = join(directory, name if name else self.name)
uri = URIS.uri(URIS.DOWNLOAD, self.id)
return FSRequest.retrieve(uri, self.client,path)
def retrieve_preview(self, directory, name=False, quality=False):
"""
Download the high quality mp3 preview.
>>> sound.retrieve_preview("/tmp")
"""
if (not quality):
path = join(directory, name if name else str(self.previews.preview_lq_mp3.split("/")[-1]))
return FSRequest.retrieve(self.previews.preview_lq_mp3, self.client,path)
else:
path = join(directory, name if name else str(self.previews.preview_hq_mp3.split("/")[-1]))
return FSRequest.retrieve(self.previews.preview_hq_mp3, self.client,path)
def get_analysis(self, descriptors=None):
"""
Get content-based descriptors.
http://freesound.org/docs/api/resources_apiv2.html#sound-analysis
>>> a = sound.get_analysis(descriptors="lowlevel.pitch.mean")
>>> print(a.lowlevel.pitch.mean)
"""
uri = URIS.uri(URIS.SOUND_ANALYSIS,self.id)
params = {}
if descriptors:
params['descriptors']=descriptors
return FSRequest.request(uri, params,self.client,FreesoundObject)
def get_similar(self):
"""
Get similar sounds based on content-based descriptors.
http://freesound.org/docs/api/resources_apiv2.html#similar-sounds
>>> s = sound.get_similar()
"""
uri = URIS.uri(URIS.SIMILAR_SOUNDS,self.id)
return FSRequest.request(uri, {},self.client, Pager)
def get_comments(self):
"""
Get user comments.
http://freesound.org/docs/api/resources_apiv2.html#sound-comments
>>> comments = sound.get_comments()
"""
uri = URIS.uri(URIS.COMMENTS,self.id)
return FSRequest.request(uri, {}, self.client, GenericPager)
def __repr__(self):
return '<Sound: id="%s", name="%s">' % \
(self.id, self.name)
class User(FreesoundObject):
"""
Freesound User resources.
>>> u=c.get_user("xserra")
"""
def get_sounds(self):
"""
Get user sounds.
http://freesound.org/docs/api/resources_apiv2.html#user-sounds
>>> u.get_sounds()
"""
uri = URIS.uri(URIS.USER_SOUNDS,self.username)
return FSRequest.request(uri, {}, self.client, Pager)
def get_packs(self):
"""
Get user packs.
http://freesound.org/docs/api/resources_apiv2.html#user-packs
>>> u.get_packs()
"""
uri = URIS.uri(URIS.USER_PACKS,self.username)
return FSRequest.request(uri, {}, self.client, GenericPager)
def get_bookmark_categories(self):
"""
Get user bookmark categories.
http://freesound.org/docs/api/resources_apiv2.html#user-bookmark-categories
>>> u.get_bookmark_categories()
"""
uri = URIS.uri(URIS.USER_BOOKMARK_CATEGORIES,self.username)
return FSRequest.request(uri, {}, self.client, GenericPager)
def get_bookmark_category_sounds(self, category_id):
"""
Get user bookmarks.
http://freesound.org/docs/api/resources_apiv2.html#user-bookmark-category-sounds
>>> p=u.get_bookmark_category_sounds(0)
"""
uri = URIS.uri(URIS.USER_BOOKMARK_CATEGORY_SOUNDS,self.username,category_id)
return FSRequest.request(uri, {}, self.client, Pager)
def __repr__(self): return '<User: "%s">' % ( self.username)
class Pack(FreesoundObject):
"""
Freesound Pack resources.
>>> p = c.get_pack(3416)
"""
def get_sounds(self):
"""
Get pack sounds
http://freesound.org/docs/api/resources_apiv2.html#pack-sounds
>>> sounds = p.get_sounds()
"""
uri = URIS.uri(URIS.PACK_SOUNDS,self.id)
return FSRequest.request(uri, {}, self.client, Pager)
def __repr__(self):
return '<Pack: name="%s">' % ( self.name)