1 import urllib, threading, os, gzip, time, json, re
2 _DUMP_URL = '''http://img.jamendo.com/data/dbdump_artistalbumtrack.xml.gz'''
3 _DUMP = os.path.expanduser('''~/.cache/jamaendo/dbdump.xml.gz''')
6 os.makedirs(os.path.dirname(_DUMP))
11 return os.path.isfile(_DUMP)
13 def _file_is_old(fil, old_age):
14 return os.path.getmtime(fil) < (time.time() - old_age)
17 return not has_dump() or _file_is_old(_DUMP, 60*60*24) # 1 day
19 def refresh_dump(complete_callback, progress_callback=None, force=False):
20 if force or _dump_is_old():
21 downloader = Downloader(complete_callback, progress_callback)
26 class Downloader(threading.Thread):
27 def __init__(self, complete_callback, progress_callback):
28 threading.Thread.__init__(self)
29 self.complete_callback = complete_callback
30 self.progress_callback = progress_callback
32 def actual_callback(self, numblocks, blocksize, filesize):
33 if self.progress_callback:
35 percent = min((numblocks*blocksize*100)/filesize, 100)
38 self.progress_callback(percent)
41 urllib.urlretrieve(_DUMP_URL, _DUMP, self.actual_callback)
42 self.complete_callback()
44 def fast_iter(context, func):
45 for event, elem in context:
48 while elem.getprevious() is not None:
49 del elem.getparent()[0]
52 from lxml import etree
57 if isinstance(v, basestring):
58 return v.encode('utf-8')
61 return "{%s}" % (", ".join("%s=%s"%(k.encode('utf-8'), printable(v)) \
62 for k,v in self.__dict__.iteritems() if not k.startswith('_')))
69 self.fil = gzip.open(_DUMP)
74 def make_artist_obj(self, element):
75 if element.text is not None and element.text != "":
80 if child.tag in ['name', 'id', 'image']:
81 ret[child.tag] = child.text
84 def make_album_obj(self, element):
85 if element.text is not None and element.text != "":
90 if child.tag in ['name', 'id', 'image']:
92 ret[child.tag] = child.text
95 if child.tag == 'Tracks':
99 for trackinfo in track:
100 if trackinfo.tag in ['name', 'id', 'numalbum']:
101 trackd[trackinfo.tag] = trackinfo.text
102 tracks.append(trackd)
103 ret['tracks'] = tracks
106 def artist_walker(self, name_match):
107 for event, element in etree.iterparse(self.fil, tag="artist"):
108 name = element.xpath('./name')[0].text.lower()
109 if name and name.find(name_match) > -1:
110 yield self.make_artist_obj(element)
112 while element.getprevious() is not None:
113 del element.getparent()[0]
116 def album_walker(self, name_match):
117 for event, element in etree.iterparse(self.fil, tag="album"):
118 name = element.xpath('./name')[0].text
119 if name and name.lower().find(name_match) > -1:
120 yield self.make_album_obj(element)
122 while element.getprevious() is not None:
123 del element.getparent()[0]
126 def artistid_walker(self, artistids):
127 for event, element in etree.iterparse(self.fil, tag="artist"):
128 _id = element.xpath('./id')[0].text
129 if _id and int(_id) in artistids:
130 yield self.make_artist_obj(element)
132 while element.getprevious() is not None:
133 del element.getparent()[0]
136 def albumid_walker(self, albumids):
137 for event, element in etree.iterparse(self.fil, tag="album"):
138 _id = element.xpath('./id')[0].text
139 if _id and int(_id) in albumids:
140 yield self.make_album_obj(element)
142 while element.getprevious() is not None:
143 del element.getparent()[0]
146 def search_artists(self, substr):
147 substr = substr.lower()
148 return (artist for artist in self.artist_walker(substr))
150 def search_albums(self, substr):
151 substr = substr.lower()
152 return (album for album in self.album_walker(substr))
154 def get_album(self, artistid):
155 return (artist for artist in self.artistid_walker(artistid))
157 def get_album(self, albumid):
158 return (album for album in self.albumid_walker(albumid))
160 _GET2 = '''http://api.jamendo.com/get2/'''
163 last_query = time.time()
165 def __init__(self, order, select=['id', 'name', 'image', 'artist_name'], request='album', track=None, n=8):
166 if request == 'track':
167 self.url = "%s%s/%s/json/%s?n=%s&order=%s" % (_GET2, '+'.join(select), request, '+'.join(track), n, order)
169 self.url = "%s%s/%s/json/?n=%s&order=%s" % (_GET2, '+'.join(select), request, n, order)
172 """ratelimited query"""
174 if now - self.last_query < 1.0:
175 time.sleep(1.0 - (now - self.last_query))
176 self.last_query = now
177 f = urllib.urlopen(self.url)
182 class Queries(object):
183 albums_this_week = Query(order='ratingweek_desc')
184 albums_all_time = Query(order='ratingtotal_desc')
185 albums_this_month = Query(order='ratingmonth_desc')
186 albums_today = Query(order='ratingday_desc')
187 playlists_all_time = Query(select=['id','name', 'user_idstr'], request='playlist', order='ratingtotal_desc')
188 tracks_this_month = Query(select=['id', 'name', 'url', 'stream', 'album_name', 'album_url', 'album_id', 'artist_id', 'artist_name'],
190 track=['track_album', 'album_artist'],
191 order='ratingmonth_desc')
193 def get_cover(albumid, size=200):
194 to = '~/.cache/jamaendo/cover-%d-%d.jpg'%(albumid, size)
195 if not os.path.isfile(to):
196 url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
197 urllib.urlretrieve(url, to)
200 def get_ogg_url(trackid):
201 return _GET2+ 'stream/track/redirect/?id=%d&streamencoding=ogg2'%(trackid)
203 def get_mp3_url(trackid):
204 return _GET2+ 'stream/track/redirect/?id=%d&streamencoding=mp31'%(trackid)