Author: duncan
Date: Fri Mar 7 16:38:06 2008
New Revision: 10476
Log:
Some fixes for music player daemon to shutdown gracefully
Some corrections for the 'Freevo' class and method spacings
Modified:
branches/rel-1-7/freevo/src/audio/plugins/mpd_playlist.py
branches/rel-1-7/freevo/src/audio/plugins/mpd_status.py
branches/rel-1-7/freevo/src/audio/plugins/mpdclient2.py
branches/rel-1/freevo/src/audio/plugins/album_tree.py
branches/rel-1/freevo/src/audio/plugins/mpd_playlist.py
branches/rel-1/freevo/src/audio/plugins/mpd_status.py
branches/rel-1/freevo/src/audio/plugins/mpdclient2.py
Modified: branches/rel-1-7/freevo/src/audio/plugins/mpd_playlist.py
==============================================================================
--- branches/rel-1-7/freevo/src/audio/plugins/mpd_playlist.py (original)
+++ branches/rel-1-7/freevo/src/audio/plugins/mpd_playlist.py Fri Mar 7
16:38:06 2008
@@ -100,6 +100,7 @@
def shutdown(self):
"""close the connection to the mpd server"""
+ self.conn.join()
try:
# this always throws EOFError, even though there isn't really an
error
self.conn.close()
Modified: branches/rel-1-7/freevo/src/audio/plugins/mpd_status.py
==============================================================================
--- branches/rel-1-7/freevo/src/audio/plugins/mpd_status.py (original)
+++ branches/rel-1-7/freevo/src/audio/plugins/mpd_status.py Fri Mar 7
16:38:06 2008
@@ -72,7 +72,6 @@
__maintainer_email__ = __author_email__
__version__ = '2'
-
def __init__(self):
"""Initilise the plugin"""
if not config.MPD_MUSIC_BASE_PATH:
@@ -115,6 +114,7 @@
def shutdown(self):
"""close the connection to the MPD server"""
+ self.conn.join()
try:
self.conn.close()
except EOFError:
Modified: branches/rel-1-7/freevo/src/audio/plugins/mpdclient2.py
==============================================================================
--- branches/rel-1-7/freevo/src/audio/plugins/mpdclient2.py (original)
+++ branches/rel-1-7/freevo/src/audio/plugins/mpdclient2.py Fri Mar 7
16:38:06 2008
@@ -11,7 +11,7 @@
import socket
from time import sleep
-import thread
+from threading import Thread, Event, Lock
# a line is either:
#
@@ -22,6 +22,7 @@
class socket_talker(object):
def __init__(self, host, port):
+ _debug_('socket_talker.__init__(host, port)', 2)
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -31,9 +32,10 @@
self.ack = ''
self.done = True
- # this SUCKS
+ # this SUCKS
def get_line(self):
+ _debug_('get_line()', 2)
if not self.current_line:
self.current_line = self.file.readline().rstrip("\n")
if not self.current_line:
@@ -42,7 +44,9 @@
self.done = True
return self.current_line
+
def putline(self, line):
+ _debug_('putline(line)', 2)
self.file.write("%s\n" % line)
try:
self.file.flush()
@@ -50,7 +54,9 @@
pass
self.done = False
+
def get_pair(self):
+ _debug_('get_pair()', 2)
line = self.get_line()
self.ack = ''
@@ -152,21 +158,25 @@
}
def is_command(cmd):
+ _debug_('is_command(cmd)', 2)
return cmd in [ k[0] for k in commands.keys() ]
def escape(text):
+ _debug_('escape(text)', 2)
# join/split is faster than replace
text = '\\\\'.join(text.split('\\')) # \ -> \\
text = '\\"'.join(text.split('"')) # " -> \"
return text
def get_command(cmd, args):
+ _debug_('get_command(cmd, args)', 2)
try:
return commands[(cmd, len(args))]
except KeyError:
raise RuntimeError("no such command: %s (%d args)" % (cmd, len(args)))
def send_command(talker, cmd, args):
+ _debug_('send_command(talker, cmd, args)', 2)
args = list(args[:])
for i, arg in enumerate(args):
if not isinstance(arg, int):
@@ -175,15 +185,21 @@
talker.putline(format % tuple([cmd] + list(args)))
class sender_n_fetcher(object):
+ """ """
def __init__(self, sender, fetcher):
+ _debug_('sender_n_fetcher.__init__(sender, fetcher)', 2)
self.sender = sender
self.fetcher = fetcher
self.iterate = False
+
def __getattr__(self, cmd):
+ _debug_('sender_n_fetcher.__getattr__(cmd)', 2)
return lambda *args: self.send_n_fetch(cmd, args)
+
def send_n_fetch(self, cmd, args):
+ _debug_('send_n_fetch(cmd, args)', 2)
getattr(self.sender, cmd)(*args)
junk, howmany, type, keywords = get_command(cmd, args)
@@ -202,9 +218,11 @@
self.fetcher.clear()
return result
+
# stupid hack because you apparently can't return non-None and yield
# within the same function
def yield_then_clear(it):
+ _debug_('yield_then_clear(it)', 2)
for x in it:
yield x
self.fetcher.clear()
@@ -212,23 +230,36 @@
class command_sender(object):
+ """ """
def __init__(self, talker):
+ _debug_('command_sender.__init__(talker)', 2)
self.talker = talker
+
+
def __getattr__(self, cmd):
+ _debug_('command_sender.__getattr__(cmd)', 2)
return lambda *args: send_command(self.talker, cmd, args)
+
+
class response_fetcher(object):
+ """ """
def __init__(self, talker):
+ _debug_('response_fetcher.__init__(talker)', 2)
self.talker = talker
self.converters = {}
+
def clear(self):
+ _debug_('clear()', 2)
while not self.talker.done:
self.talker.current_line = ''
self.talker.get_line()
self.talker.current_line = ''
+
def one_object(self, keywords, type):
+ _debug_('one_object(keywords, type)', 2)
# if type isn't empty, then the object's type is set to it. otherwise
# the type is set to the key of the first key/val pair.
@@ -261,7 +292,9 @@
return entity
+
def all_objects(self, keywords, type):
+ _debug_('all_objects(keywords, type)', 2)
while 1:
obj = self.one_object(keywords, type)
if not obj:
@@ -270,14 +303,23 @@
if self.talker.done:
raise StopIteration
+
def convert(self, cmd, key, val):
+ _debug_('convert(cmd, key, val)', 2)
# if there's a converter, convert it, otherwise return it the same
return self.converters.get(cmd, {}).get(key, lambda x: x)(val)
+
+
class dictobj(dict):
+ """ """
def __getattr__(self, attr):
+ _debug_('dictobj.__getattr__(attr)', 2)
return self[attr]
+
+
def __repr__(self):
+ _debug_('dictobj.__repr__()', 2)
# <mpdclient2.dictobj at 0x12345678 ..
# {
# key: val,
@@ -288,8 +330,12 @@
',\n '.join([ '%s: %s' % (k, v) for k, v in self.items() ])
+
'\n }>')
+
+
class mpd_connection(object):
+ """ """
def __init__(self, host, port):
+ _debug_('mpd_connection.__init__(host, port)', 2)
self.talker = socket_talker(host, port)
self.send = command_sender(self.talker)
self.fetch = response_fetcher(self.talker)
@@ -297,25 +343,31 @@
self._hello()
+
def _hello(self):
+ _debug_('_hello()', 2)
line = self.talker.get_line()
if not line.startswith("OK MPD "):
raise RuntimeError("this ain't mpd")
self.mpd_version = line[len("OK MPD "):].strip()
self.talker.current_line = ''
+
# conn.foo() is equivalent to conn.do.foo(), but nicer
def __getattr__(self, attr):
+ _debug_('mpd_connection.__getattr__(attr)', 2)
if is_command(attr):
return getattr(self.do, attr)
raise AttributeError(attr)
def parse_host(host):
+ _debug_('parse_host(host)', 2)
if '@' in host:
return host.split('@', 1)
return '', host
def connect(**kw):
+ _debug_('connect(**kw)', 2)
import os
port = int(os.environ.get('MPD_PORT', 6600))
@@ -339,41 +391,71 @@
#
-# # Thread safe extenstion added by Graham Billiau <[EMAIL PROTECTED]>
+# Thread safe extenstion added by Graham Billiau <[EMAIL PROTECTED]>
#
-
-# TODO:
-# sometimes this hangs, probably a problem with the locking
-
-def MPD_Ping_Thread(conn):
+class MPD_Ping_Thread(Thread):
"""This should be run as a thread
It constantly loops, sending keepalive packets to the mpd server
it exits cleanly after the connection is closed"""
- while True:
- sleep(10)
- try:
- if not conn:
- return
- conn.ping()
- except socket.error:
- return
+ def __init__(self, conn):
+ _debug_('__init__(conn=%r)' % (conn,), 2)
+ self.conn = conn
+ self._stopevent = Event()
+ self._sleepperiod = 10.0
+ Thread.__init__(self, name="MPD_Ping_Thread")
+
+
+ def join(self, timeout=None):
+ _debug_('join(timeout=None)', 2)
+ """
+ Stop the thread
+ """
+ self._stopevent.set()
+ Thread.join(self, timeout)
+
+
+ def run(self):
+ _debug_('run()', 2)
+ while not self._stopevent.isSet():
+ try:
+ self.conn.ping()
+ except socket.error:
+ break
+ self._stopevent.wait(self._sleepperiod)
+
+
class Thread_MPD_Connection:
"""This is a wrapper around the mpdclient2 library to make it thread
safe"""
- #conn
- def __init__ (self, host, port, keepalive=False, pword = None):
+ def __init__(self, host, port, keepalive=False, pword=None):
"""create the connection and locks,
- if keepalive is True the connection will not time out and must be
explcitly closed"""
+ if keepalive is True the connection will not time out and must be
explcitly closed
+ """
+ _debug_('Thread_MPD_Connection.__init__(host=%r, port=%r,
keepalive=%r, pword=%r)' %
+ (host, port, keepalive, pword), 1)
self.conn = mpd_connection(host, port)
- if (pword is not None):
+ if pword is not None:
self.conn.password(pword)
- self.lock = thread.allocate_lock()
- if (keepalive == True):
- thread.start_new_thread(MPD_Ping_Thread, (self, ))
+ self.lock = Lock()
+ if keepalive:
+ self.ping_thread = MPD_Ping_Thread(self)
+ if self.ping_thread:
+ self.ping_thread.start()
+
+
+ def join(self):
+ """ stop the ping thread """
+ _debug_('Thread_MPD_Connection.join()', 2)
+ if self.ping_thread:
+ self.ping_thread.join()
+
def __getattr__(self, attr):
"""pass the request on to the connection object, while ensuring no
conflicts"""
+ _debug_('__getattr__(attr)', 2)
self.lock.acquire()
- funct = self.conn.__getattr__(attr)
- self.lock.release()
+ try:
+ funct = self.conn.__getattr__(attr)
+ finally:
+ self.lock.release()
return funct
Modified: branches/rel-1/freevo/src/audio/plugins/album_tree.py
==============================================================================
--- branches/rel-1/freevo/src/audio/plugins/album_tree.py (original)
+++ branches/rel-1/freevo/src/audio/plugins/album_tree.py Fri Mar 7
16:38:06 2008
@@ -42,6 +42,7 @@
from audio import audioitem
from gui import ProgressBox
+
class treeSpec(object):
"""
see: PluginInterface() below for freevo plugin doc.
@@ -59,6 +60,7 @@
self.alt_grouping = alt_grouping
self.cursor = cursor
+
def get_query(self, data):
"""
builds query
@@ -94,12 +96,15 @@
return query
+
def execute(self, data):
self.cursor.execute(self.get_query(data))
return list(self.cursor)
#should return an iterator/generator instead of a list?
#dont confuse others/need count for progress -->return list
+
+
class PluginInterface(plugin.MainMenuPlugin):
"""
Plugin to browse songs in a tree-like way.
@@ -188,6 +193,7 @@
else:
self.load_spec(config.AUDIO_ALBUM_TREE_SPEC)
+
def shutdown(self):
"""
shut down the sqlite database
@@ -195,6 +201,7 @@
_debug_('shutdown', 2)
db.close()
+
def load_spec(self, spec_list):
"""
load definitions from config
@@ -207,6 +214,7 @@
tree.alt_grouping = specdef['alt_grouping']
self.album_tree_list.append(tree)
+
def load_demo(self):
"""
load predefined testing layout
@@ -241,15 +249,16 @@
def items(self, parent):
return [ self.show_item ]
+
def actions(self):
#todo: add random 10 etc..
return []
+
def onchoose_main(self, arg=None, menuw=None):
"""
main menu
"""
- #
items = []
for tree in self.album_tree_list:
items.append(menu.MenuItem(tree.name, action=self.onchoose_node,
arg=[tree, []]))
@@ -260,6 +269,7 @@
menuw.pushmenu(myobjectmenu)
menuw.refresh()
+
def onchoose_node(self, arg=None, menuw=None):
"""
browse through a tree specification
@@ -290,6 +300,7 @@
menuw.pushmenu(myobjectmenu)
menuw.refresh()
+
def onchoose_last_node(self, tree, data, menuw):
"""
last node in tree generates a playlist.
Modified: branches/rel-1/freevo/src/audio/plugins/mpd_playlist.py
==============================================================================
--- branches/rel-1/freevo/src/audio/plugins/mpd_playlist.py (original)
+++ branches/rel-1/freevo/src/audio/plugins/mpd_playlist.py Fri Mar 7
16:38:06 2008
@@ -100,6 +100,7 @@
def shutdown(self):
"""close the connection to the mpd server"""
+ self.conn.join()
try:
# this always throws EOFError, even though there isn't really an
error
self.conn.close()
Modified: branches/rel-1/freevo/src/audio/plugins/mpd_status.py
==============================================================================
--- branches/rel-1/freevo/src/audio/plugins/mpd_status.py (original)
+++ branches/rel-1/freevo/src/audio/plugins/mpd_status.py Fri Mar 7
16:38:06 2008
@@ -72,7 +72,6 @@
__maintainer_email__ = __author_email__
__version__ = '2'
-
def __init__(self):
"""Initilise the plugin"""
if not config.MPD_MUSIC_BASE_PATH:
@@ -115,6 +114,7 @@
def shutdown(self):
"""close the connection to the MPD server"""
+ self.conn.join()
try:
self.conn.close()
except EOFError:
Modified: branches/rel-1/freevo/src/audio/plugins/mpdclient2.py
==============================================================================
--- branches/rel-1/freevo/src/audio/plugins/mpdclient2.py (original)
+++ branches/rel-1/freevo/src/audio/plugins/mpdclient2.py Fri Mar 7
16:38:06 2008
@@ -11,7 +11,7 @@
import socket
from time import sleep
-import thread
+from threading import Thread, Event, Lock
# a line is either:
#
@@ -22,6 +22,7 @@
class socket_talker(object):
def __init__(self, host, port):
+ _debug_('socket_talker.__init__(host, port)', 2)
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -31,9 +32,10 @@
self.ack = ''
self.done = True
- # this SUCKS
+ # this SUCKS
def get_line(self):
+ _debug_('get_line()', 2)
if not self.current_line:
self.current_line = self.file.readline().rstrip("\n")
if not self.current_line:
@@ -42,7 +44,9 @@
self.done = True
return self.current_line
+
def putline(self, line):
+ _debug_('putline(line)', 2)
self.file.write("%s\n" % line)
try:
self.file.flush()
@@ -50,7 +54,9 @@
pass
self.done = False
+
def get_pair(self):
+ _debug_('get_pair()', 2)
line = self.get_line()
self.ack = ''
@@ -152,21 +158,25 @@
}
def is_command(cmd):
+ _debug_('is_command(cmd)', 2)
return cmd in [ k[0] for k in commands.keys() ]
def escape(text):
+ _debug_('escape(text)', 2)
# join/split is faster than replace
text = '\\\\'.join(text.split('\\')) # \ -> \\
text = '\\"'.join(text.split('"')) # " -> \"
return text
def get_command(cmd, args):
+ _debug_('get_command(cmd, args)', 2)
try:
return commands[(cmd, len(args))]
except KeyError:
raise RuntimeError("no such command: %s (%d args)" % (cmd, len(args)))
def send_command(talker, cmd, args):
+ _debug_('send_command(talker, cmd, args)', 2)
args = list(args[:])
for i, arg in enumerate(args):
if not isinstance(arg, int):
@@ -175,15 +185,21 @@
talker.putline(format % tuple([cmd] + list(args)))
class sender_n_fetcher(object):
+ """ """
def __init__(self, sender, fetcher):
+ _debug_('sender_n_fetcher.__init__(sender, fetcher)', 2)
self.sender = sender
self.fetcher = fetcher
self.iterate = False
+
def __getattr__(self, cmd):
+ _debug_('sender_n_fetcher.__getattr__(cmd)', 2)
return lambda *args: self.send_n_fetch(cmd, args)
+
def send_n_fetch(self, cmd, args):
+ _debug_('send_n_fetch(cmd, args)', 2)
getattr(self.sender, cmd)(*args)
junk, howmany, type, keywords = get_command(cmd, args)
@@ -202,9 +218,11 @@
self.fetcher.clear()
return result
+
# stupid hack because you apparently can't return non-None and yield
# within the same function
def yield_then_clear(it):
+ _debug_('yield_then_clear(it)', 2)
for x in it:
yield x
self.fetcher.clear()
@@ -212,23 +230,36 @@
class command_sender(object):
+ """ """
def __init__(self, talker):
+ _debug_('command_sender.__init__(talker)', 2)
self.talker = talker
+
+
def __getattr__(self, cmd):
+ _debug_('command_sender.__getattr__(cmd)', 2)
return lambda *args: send_command(self.talker, cmd, args)
+
+
class response_fetcher(object):
+ """ """
def __init__(self, talker):
+ _debug_('response_fetcher.__init__(talker)', 2)
self.talker = talker
self.converters = {}
+
def clear(self):
+ _debug_('clear()', 2)
while not self.talker.done:
self.talker.current_line = ''
self.talker.get_line()
self.talker.current_line = ''
+
def one_object(self, keywords, type):
+ _debug_('one_object(keywords, type)', 2)
# if type isn't empty, then the object's type is set to it. otherwise
# the type is set to the key of the first key/val pair.
@@ -261,7 +292,9 @@
return entity
+
def all_objects(self, keywords, type):
+ _debug_('all_objects(keywords, type)', 2)
while 1:
obj = self.one_object(keywords, type)
if not obj:
@@ -270,14 +303,23 @@
if self.talker.done:
raise StopIteration
+
def convert(self, cmd, key, val):
+ _debug_('convert(cmd, key, val)', 2)
# if there's a converter, convert it, otherwise return it the same
return self.converters.get(cmd, {}).get(key, lambda x: x)(val)
+
+
class dictobj(dict):
+ """ """
def __getattr__(self, attr):
+ _debug_('dictobj.__getattr__(attr)', 2)
return self[attr]
+
+
def __repr__(self):
+ _debug_('dictobj.__repr__()', 2)
# <mpdclient2.dictobj at 0x12345678 ..
# {
# key: val,
@@ -288,8 +330,12 @@
',\n '.join([ '%s: %s' % (k, v) for k, v in self.items() ])
+
'\n }>')
+
+
class mpd_connection(object):
+ """ """
def __init__(self, host, port):
+ _debug_('mpd_connection.__init__(host, port)', 2)
self.talker = socket_talker(host, port)
self.send = command_sender(self.talker)
self.fetch = response_fetcher(self.talker)
@@ -297,25 +343,31 @@
self._hello()
+
def _hello(self):
+ _debug_('_hello()', 2)
line = self.talker.get_line()
if not line.startswith("OK MPD "):
raise RuntimeError("this ain't mpd")
self.mpd_version = line[len("OK MPD "):].strip()
self.talker.current_line = ''
+
# conn.foo() is equivalent to conn.do.foo(), but nicer
def __getattr__(self, attr):
+ _debug_('mpd_connection.__getattr__(attr)', 2)
if is_command(attr):
return getattr(self.do, attr)
raise AttributeError(attr)
def parse_host(host):
+ _debug_('parse_host(host)', 2)
if '@' in host:
return host.split('@', 1)
return '', host
def connect(**kw):
+ _debug_('connect(**kw)', 2)
import os
port = int(os.environ.get('MPD_PORT', 6600))
@@ -339,41 +391,71 @@
#
-# # Thread safe extenstion added by Graham Billiau <[EMAIL PROTECTED]>
+# Thread safe extenstion added by Graham Billiau <[EMAIL PROTECTED]>
#
-
-# TODO:
-# sometimes this hangs, probably a problem with the locking
-
-def MPD_Ping_Thread(conn):
+class MPD_Ping_Thread(Thread):
"""This should be run as a thread
It constantly loops, sending keepalive packets to the mpd server
it exits cleanly after the connection is closed"""
- while True:
- sleep(10)
- try:
- if not conn:
- return
- conn.ping()
- except socket.error:
- return
+ def __init__(self, conn):
+ _debug_('__init__(conn=%r)' % (conn,), 2)
+ self.conn = conn
+ self._stopevent = Event()
+ self._sleepperiod = 10.0
+ Thread.__init__(self, name="MPD_Ping_Thread")
+
+
+ def join(self, timeout=None):
+ _debug_('join(timeout=None)', 2)
+ """
+ Stop the thread
+ """
+ self._stopevent.set()
+ Thread.join(self, timeout)
+
+
+ def run(self):
+ _debug_('run()', 2)
+ while not self._stopevent.isSet():
+ try:
+ self.conn.ping()
+ except socket.error:
+ break
+ self._stopevent.wait(self._sleepperiod)
+
+
class Thread_MPD_Connection:
"""This is a wrapper around the mpdclient2 library to make it thread
safe"""
- #conn
- def __init__ (self, host, port, keepalive=False, pword = None):
+ def __init__(self, host, port, keepalive=False, pword=None):
"""create the connection and locks,
- if keepalive is True the connection will not time out and must be
explcitly closed"""
+ if keepalive is True the connection will not time out and must be
explcitly closed
+ """
+ _debug_('Thread_MPD_Connection.__init__(host=%r, port=%r,
keepalive=%r, pword=%r)' %
+ (host, port, keepalive, pword), 1)
self.conn = mpd_connection(host, port)
- if (pword is not None):
+ if pword is not None:
self.conn.password(pword)
- self.lock = thread.allocate_lock()
- if (keepalive == True):
- thread.start_new_thread(MPD_Ping_Thread, (self, ))
+ self.lock = Lock()
+ if keepalive:
+ self.ping_thread = MPD_Ping_Thread(self)
+ if self.ping_thread:
+ self.ping_thread.start()
+
+
+ def join(self):
+ """ stop the ping thread """
+ _debug_('Thread_MPD_Connection.join()', 2)
+ if self.ping_thread:
+ self.ping_thread.join()
+
def __getattr__(self, attr):
"""pass the request on to the connection object, while ensuring no
conflicts"""
+ _debug_('__getattr__(attr)', 2)
self.lock.acquire()
- funct = self.conn.__getattr__(attr)
- self.lock.release()
+ try:
+ funct = self.conn.__getattr__(attr)
+ finally:
+ self.lock.release()
return funct
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog