The first few lines of the script explain the structure and the mechanism.
The problem that I'm facing is that the execution is getting stuck at line 53. Once the Downloader acts on the first request it generates the api correctly however on reaching http_object.request(audioscrobbler_api)
it gets stuck.
The script was coded and tested on another system and it yielded the correct result.
I can confirm that the httplib2 package is not broken as it functions properly while methods of that library (including request
) are called from other scripts.
What is causing the script to get stuck ?
Script:
#
# Album artwork downloading module for Encore Music Player application.
# Loosely based on the producer-consumer model devised by E W Djikstra.
#
# The Downloader class (implemented as a daemon thread) acts as the consumer
# in the system where it reads requests from the buffer and tries to fetch the
# artwork from ws.audioscrobbler.com (LastFM's web service portal).
#
# Requester class, the producer, is a standard thread class that places the request
# in the buffer when started.
#
# DBusRequester class provides an interface to the script and is made available on
# the session bus of the DBus daemon under the name of 'com.encore.AlbumArtDownloader'
# which enables the core music player to request downloads.
#
import threading, urllib, httplib2, md5, libxml2, os, dbus, dbus.service, signal
from collections import deque
from gi.repository import GObject
from dbus.mainloop.glib import DBusGMainLoop
requests = deque()
mutex = threading.Lock()
count = threading.Semaphore(0)
DBusGMainLoop(set_as_default = True)
class Downloader(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
print "=> Downloader waiting for requests"
count.acquire() # wait for new request if buffer is empty
mutex.acquire() # enter critical section
request = requests.popleft()
mutex.release() # leave critical section
(p, q) = request
try:
print "=> Generating api for %s by %s" % (p,q)
params = urllib.urlencode({'method': 'album.getinfo', 'api_key': 'XXX', 'artist': p, 'album': q})
audioscrobbler_api = "http://ws.audioscrobbler.com/2.0/?%s" % params
print "=> Generated URL %s" % (audioscrobbler_api)
http_object = httplib2.Http()
print "=> Requesting response"
resp, content = http_object.request(audioscrobbler_api)
print "=> Received response"
if not resp.status == 200:
print "Unable to fetch artwork for %s by %s" % (q, p)
continue # proceed to the next item in queue if request fails
doc = libxml2.parseDoc(content)
ctxt = doc.xpathNewContext()
res = ctxt.xpathEval("//image[@size='medium']") # grab the element containing the link to a medium sized artwork
if len(res) < 1:
continue # proceed to the next item in queue if the required image node is not found
image_uri = res[0].content # extract uri from node
wget_status = os.system("wget %s -q --tries 3 -O temp" % (image_uri))
if not wget_status == 0:
continue # proceed to the next item in queue if download fails
artwork_name = "%s.png" % (md5.md5("%s + %s" % (p, q)).hexdigest())
os.system("convert temp -resize 64x64 %s" % artwork_name)
except:
pass # handle http request error
class Requester(threading.Thread):
def __init__(self, request):
self.request = request
threading.Thread.__init__(self)
def run(self):
mutex.acquire() # enter critical section
if not self.request in requests:
requests.append(self.request)
count.release() # signal downloader
mutex.release() # leave critical section
class DBusRequester(dbus.service.Object):
def __init__(self):
bus_name = dbus.service.BusName('com.encore.AlbumArtDownloader', bus=dbus.SessionBus())
dbus.service.Object.__init__(self, bus_name, '/com/encore/AlbumArtDownloader')
@dbus.service.method('com.encore.AlbumArtDownloader')
def queue_request(self, artist_name, album_name):
request = (artist_name, album_name)
requester = Requester(request)
requester.start()
def sigint_handler(signum, frame):
"""Exit gracefully on receiving SIGINT."""
loop.quit()
signal.signal(signal.SIGINT, sigint_handler)
downloader_daemon = Downloader()
downloader_daemon.daemon = True
downloader_daemon.start()
requester_service = DBusRequester()
loop = GObject.MainLoop()
loop.run()
On doing a Ctrl-C
=> Downloader waiting for requests
=> Generating api for paul van dyk by evolution
=> Generated URL http://ws.audioscrobbler.com/2.0/?album=evolution&api_key=XXXXXXXXXXXXXXXXXXXX&method=album.getinfo&artist=paul+van+dyk
=> Requesting response
^C
Thanks !!
The problem was caused by Python's Global Interpreter Lock (GIL).
fixes the problem.