try:
from mpdunicode import *
from mpdunicode import MPDClient as MPDClientBase
except ImportError:
from mpd import *
from mpd import MPDClient as MPDClientBase
import threading
import Queue
import sys
class MPDClient():
'''This proxy class wraps round the mpd(unicode) module. It supplies a
threaded interface to the mpd server on top of the normal methods in the
mpd module.
If an exception is raised in the spawned tread this exception is given as
the first argument to the callback function.
'''
def __init__(self):
self.connection = MPDThread(None, None, None, None)
def __getattr__(self, command):
if hasattr(self.connection, command):
if self.connected():
return lambda *args: self.connection.doBlocking(command, args)
else:
raise ConnectionError('Not connected.')
else:
raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, command))
def send(self, command, args=(), callback=None, callbackArgs=None):
'''Put the command on the command queue.
If supplied the callback argument is called with the results as its
first argument and additional arguments from the tuple given in
callbackArgs.
'''
self.connection.send(command, args, callback, callbackArgs)
def connect(self, server, port, callback=None, callbackArgs=None):
'''Spawn a new connection thread connected to server on port.
When a connection is established or an error occurred the function
given as callback is called.
'''
self.connection.abort = True
self.connection = MPDThread(server, port, callback, callbackArgs)
def connected(self):
'''Returns True when the connection thread is running and has
established a connection.
'''
return self.connection.is_alive() and not self.connection.connecting
def disconnect(self):
'''Closes the current connection and exits the thread.
If a command is currently being executed the thread will exit after all
work for that command is done. It will discard any subsequent commands
in the queue.
'''
self.connection.abort = True
self.connection.send('close')
class MPDThread(MPDClientBase, threading.Thread):
'''This class represents the interface thread to the mpd server.
'''
_idle = False
def __init__(self, server, port, callback, callbackArgs):
threading.Thread.__init__(self)
self.daemon = True
self.queue = Queue.Queue()
self._lock = threading.RLock()
super(MPDThread, self).__init__()
self.connecting = False
self.exit = False
self.abort = False
if server != None:
self.connecting = True
self.send('connect', (server, port), callback, callbackArgs)
self.start()
def send(self, command, args=(), callback=None, callbackArgs=None):
'''Put the command on the command queue.
If supplied the callback argument is called with the results as its
first argument and additional arguments from the tuple given in
callbackArgs.
'''
self.queue.put((command, args, callback, callbackArgs))
if command != 'noidle' and self._idle:
self._writecommand('noidle', [])
def run(self):
while True:
command, args, callback, callbackArgs = self.queue.get()
print 'debug: got ', command, ' with arguments ', args, 'from queue.'
try:
value = self.__do(command, args)
except CommandError, e:
print 'debug: MPD thread - CommandError: ', e, '\n', sys.exc_info()
value = sys.exc_info()[1]
except Exception, e:
print 'debug: MPD thread - Exception: ', e, '\n', sys.exc_info()
value = sys.exc_info()[1]
self.exit = True
finally:
self.queue.task_done()
if command == 'connect':
self.connecting = False
if self.abort:
self.exit = True
elif callback == None:
pass
elif callbackArgs == None:
callback(value)
else:
callback(value, *callbackArgs)
if self.exit:
try:
self.__do('close', ())
self.__do('disconnect', ())
except:
pass
while not self.queue.empty():
self.queue.get()
self.queue.task_done()
sys.exit(1)
def doBlocking(self, command, args):
'''Sends the command to the mpd server and blocks until the results are
available.
'''
print 'debug: blocking on', command, ' with arguments ', args
if self.connecting:
raise ConnectionError('Not connected yet.')
if command != 'noidle' and self._idle:
self._writecommand('noidle', [])
self.queue.join()
return self.__do(command, args)
def __do(self, command, args):
try:
function = self.__getattribute__(command)
except AttributeError:
function = self.__getattr__(command)
return function(*args)
def _docommand(self, command, args, retval):
if command not in ('idle', 'noidle') and self._idle:
self._writecommand('noidle', [])
with self._lock:
return super(MPDThread, self)._docommand(command, args, retval)
def _writecommand(self, command, args=[]):
if command.startswith('command_list') and self._idle:
self._writecommand('noidle', [])
with self._lock:
super(MPDThread, self)._writecommand(command, args)
else:
super(MPDThread, self)._writecommand(command, args)