Added 'helldon' to transparently port the thing to the desktop :P
[jamaendo] / jamaendo / api.py
index f7cbc0e..7792c33 100644 (file)
@@ -24,6 +24,7 @@
 # Image / cover downloads.. and more?
 import urllib, threading, os, time, simplejson, re
 import logging, hashlib
 # Image / cover downloads.. and more?
 import urllib, threading, os, time, simplejson, re
 import logging, hashlib
+import pycurl, StringIO
 
 _CACHEDIR = None
 _COVERDIR = None
 
 _CACHEDIR = None
 _COVERDIR = None
@@ -52,6 +53,19 @@ _TRACK_FIELDS = ['id', 'name', 'album_image', 'artist_id', 'artist_name', 'album
 _RADIO_FIELDS = ['id', 'name', 'idstr', 'image']
 _TAG_FIELDS = ['id', 'name']
 
 _RADIO_FIELDS = ['id', 'name', 'idstr', 'image']
 _TAG_FIELDS = ['id', 'name']
 
+def curlGET(url):
+    c = pycurl.Curl()
+    s = StringIO.StringIO()
+    c.setopt(pycurl.FOLLOWLOCATION, 1)
+    c.setopt(pycurl.URL, url)
+    c.setopt(pycurl.WRITEFUNCTION, s.write)
+    try:
+        c.perform()
+    finally:
+        c.close()
+    s.seek(0)
+    return s.read()
+
 class LazyQuery(object):
     def set_from_json(self, json):
         for key, value in json.iteritems():
 class LazyQuery(object):
     def set_from_json(self, json):
         for key, value in json.iteritems():
@@ -115,6 +129,9 @@ class Artist(LazyQuery):
     def _set_from(self, other):
         return self._set_from_impl(other, 'name', 'image', 'albums')
 
     def _set_from(self, other):
         return self._set_from_impl(other, 'name', 'image', 'albums')
 
+    def get_data(self):
+        return {'name':self.name, 'image':self.image}
+
 class Album(LazyQuery):
     def __init__(self, ID, json=None):
         self.ID = int(ID)
 class Album(LazyQuery):
     def __init__(self, ID, json=None):
         self.ID = int(ID)
@@ -137,6 +154,12 @@ class Album(LazyQuery):
     def _set_from(self, other):
         return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
 
     def _set_from(self, other):
         return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
 
+    def get_data(self):
+        return {'name':self.name, 'image':self.image,
+                'artist_name':self.artist_name,
+                'artist_id':self.artist_id,
+                'license_url':self.license_url}
+
 class Track(LazyQuery):
     def __init__(self, ID, json=None):
         self.ID = int(ID)
 class Track(LazyQuery):
     def __init__(self, ID, json=None):
         self.ID = int(ID)
@@ -157,6 +180,16 @@ class Track(LazyQuery):
     def ogg_url(self):
        return _OGGURL%(self.ID)
 
     def ogg_url(self):
        return _OGGURL%(self.ID)
 
+    def get_data(self):
+        return {'name':self.name,
+                'artist_id':self.artist_id,
+                'artist_name':self.artist_name,
+                'album_image':self.album_image,
+                'album_name':self.album_name,
+                'album_id':self.album_id,
+                'numalbum':self.numalbum,
+                'duration':self.duration}
+
     def _needs_load(self):
         return self._needs_load_impl('name', 'artist_name', 'artist_id', 'album_name', 'album_id', 'numalbum', 'duration')
 
     def _needs_load(self):
         return self._needs_load_impl('name', 'artist_name', 'artist_id', 'album_name', 'album_id', 'numalbum', 'duration')
 
@@ -225,9 +258,7 @@ class Query(object):
         log.info("%s", url)
         Query._ratelimit()
         try:
         log.info("%s", url)
         Query._ratelimit()
         try:
-            f = urllib.urlopen(url)
-            ret = simplejson.load(f)
-            f.close()
+            ret = simplejson.loads(curlGET(url))
         except Exception, e:
             return None
         return ret
         except Exception, e:
             return None
         return ret
@@ -245,13 +276,32 @@ class CoverFetcher(threading.Thread):
         self.cond = threading.Condition()
         self.work = []
 
         self.cond = threading.Condition()
         self.work = []
 
+    def _retrieve(self, url, fname):
+        BROKEN = 'http://imgjam.com/radios/default/default.100.png'
+        if url == BROKEN:
+            return None
+        f = open(fname, 'wb')
+        c = pycurl.Curl()
+        c.setopt(pycurl.FOLLOWLOCATION, 1)
+        c.setopt(pycurl.URL, str(url))
+        c.setopt(pycurl.WRITEFUNCTION, f.write)
+        try:
+            c.perform()
+        except:
+            fname = None
+        finally:
+            c.close()
+            f.close()
+        log.debug("Coverfetch: %s -> %s", url, fname)
+        return fname
+
     def _fetch_cover(self, albumid, size):
         try:
             coverdir = _COVERDIR if _COVERDIR else '/tmp'
             to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
             if not os.path.isfile(to):
                 url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
     def _fetch_cover(self, albumid, size):
         try:
             coverdir = _COVERDIR if _COVERDIR else '/tmp'
             to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
             if not os.path.isfile(to):
                 url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
-                urllib.urlretrieve(url, to)
+                to = self._retrieve(url, to)
             return to
         except Exception, e:
             return None
             return to
         except Exception, e:
             return None
@@ -262,7 +312,7 @@ class CoverFetcher(threading.Thread):
             coverdir = _COVERDIR if _COVERDIR else '/tmp'
             to = os.path.join(coverdir, h+'.jpg')
             if not os.path.isfile(to):
             coverdir = _COVERDIR if _COVERDIR else '/tmp'
             to = os.path.join(coverdir, h+'.jpg')
             if not os.path.isfile(to):
-                urllib.urlretrieve(url, to)
+                to = self._retrieve(url, to)
             return to
         except Exception, e:
             return None
             return to
         except Exception, e:
             return None
@@ -276,7 +326,7 @@ class CoverFetcher(threading.Thread):
     def request_images(self, urls, cb):
         """cb([(url, image)])"""
         self.cond.acquire()
     def request_images(self, urls, cb):
         """cb([(url, image)])"""
         self.cond.acquire()
-        self.work.insert(0, ('images', urls, cb))
+        self.work = [('image', url, cb) for url in urls] + self.work
         self.cond.notify()
         self.cond.release()
 
         self.cond.notify()
         self.cond.release()
 
@@ -294,22 +344,20 @@ class CoverFetcher(threading.Thread):
 
             multi = len(work) > 1
             for job in work:
 
             multi = len(work) > 1
             for job in work:
-                if job[0] == 'images':
-                    self.process_images(job[1], job[2])
+                if job[0] == 'image':
+                    self.process_image(job[1], job[2])
                 else:
                     self.process_cover(*job)
                 else:
                     self.process_cover(*job)
-                if multi:
-                    time.sleep(1.0)
 
     def process_cover(self, albumid, size, cb):
         cover = self._fetch_cover(albumid, size)
         if cover:
             cb(albumid, size, cover)
 
 
     def process_cover(self, albumid, size, cb):
         cover = self._fetch_cover(albumid, size)
         if cover:
             cb(albumid, size, cover)
 
-    def process_images(self, urls, cb):
-        results = [(url, image) for url, image in ((url, self._fetch_image(url)) for url in urls) if image is not None]
-        if results:
-            cb(results)
+    def process_image(self, url, cb):
+        image = self._fetch_image(url)
+        if image:
+            cb([(url, image)])
 
 class CoverCache(object):
     """
 
 class CoverCache(object):
     """
@@ -644,7 +692,7 @@ def get_albums(artist_id):
     a = q.execute()
     if not a:
         raise JamendoAPIException(str(q))
     a = q.execute()
     if not a:
         raise JamendoAPIException(str(q))
-    _update_cache(_artists, a)
+    _update_cache(_albums, a)
     return a
 
 def get_album(album_id):
     return a
 
 def get_album(album_id):