refactoring + encapsulation

This commit is contained in:
Uriziel 2012-07-10 18:18:34 +02:00
parent 18681538e2
commit f8672bdd6d
5 changed files with 188 additions and 150 deletions

View File

@ -1,7 +1,4 @@
#coding:utf8 #coding:utf8
import thread
import sys
from twisted.internet import reactor from twisted.internet import reactor
from syncplay import client from syncplay import client
from syncplay.players import mpc_using_api from syncplay.players import mpc_using_api
@ -9,17 +6,16 @@ from syncplay.players import mpc_using_api
from syncplay import utils from syncplay import utils
def prepare_args(args): def prepareArguments():
if (args.mpc_path == None): args = utils.MPCConfigurationGetter()
sys.exit("You must supply mpc-path on first run") args.prepareClientConfiguration()
args.args.extend(['/open', '/new']) return args.getClientConfiguration()
if __name__ == '__main__': if __name__ == '__main__':
manager = None manager = None
try: try:
args = utils.get_configuration() args = prepareArguments()
prepare_args(args) manager = client.Manager(args.host, args.port, args.name, lambda m: mpc_using_api.run_mpc(m, args.mpc_path, args.file, args._args))
manager = client.Manager(args.host, args.port, args.name, lambda m: mpc_using_api.run_mpc(m, args.mpc_path, args.file, args.args))
manager.start() manager.start()
finally: finally:
if(manager): manager.stop() if(manager): manager.stop()

View File

@ -1,15 +1,17 @@
#coding:utf8 #coding:utf8
import thread
import sys
from twisted.internet import reactor from twisted.internet import reactor
from syncplay import client from syncplay import client
from syncplay.players import mplayer from syncplay.players import mplayer
from syncplay import utils from syncplay import utils
def prepareArguments():
args = utils.ConfigurationGetter()
args.prepareClientConfiguration()
return args.getClientConfiguration()
if __name__ == '__main__': if __name__ == '__main__':
args = utils.get_configuration() args = prepareArguments()
args.args.extend(('-slave', '-msglevel', 'all=1:global=4')) args.args.extend(('-slave', '-msglevel', 'all=1:global=4'))
if(args.file): args.args.extend((args.file,)) if(args.file): args.args.extend((args.file,))
manager = client.Manager(args.host, args.port, args.name, lambda m: mplayer.run_mplayer(m, 'mplayer', args.args)) manager = client.Manager(args.host, args.port, args.name, lambda m: mplayer.run_mplayer(m, 'mplayer', args.args))

View File

@ -8,7 +8,7 @@ from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory from twisted.internet.protocol import ClientFactory
from syncplay import utils from syncplay import utils
from .network_utils import ( from .network_utils import (
arg_count, argumentCount,
CommandProtocol, CommandProtocol,
) )
from .utils import format_time from .utils import format_time
@ -30,7 +30,7 @@ class SyncClientProtocol(CommandProtocol):
self.manager.stop() self.manager.stop()
CommandProtocol.handle_error(self, args) CommandProtocol.handle_error(self, args)
@arg_count(1) @argumentCount(1)
def handle_init_hello(self, args): def handle_init_hello(self, args):
message ='Connected as ' + args[0] message ='Connected as ' + args[0]
print message print message
@ -39,7 +39,7 @@ class SyncClientProtocol(CommandProtocol):
self.send_list() self.send_list()
self.manager.schedule_send_status() self.manager.schedule_send_status()
@arg_count(2, 3) @argumentCount(2, 3)
def handle_connected_present(self, args): def handle_connected_present(self, args):
if len(args) == 3: if len(args) == 3:
who, where, what = args who, where, what = args
@ -54,7 +54,7 @@ class SyncClientProtocol(CommandProtocol):
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self.manager.player): self.manager.player.display_message(message)
@arg_count(4, 5) @argumentCount(4, 5)
def handle_connected_state(self, args): def handle_connected_state(self, args):
args = self.__parseState(args) args = self.__parseState(args)
if not args: if not args:
@ -65,7 +65,7 @@ class SyncClientProtocol(CommandProtocol):
self.manager.update_global_state(counter, ctime, paused, position, name) self.manager.update_global_state(counter, ctime, paused, position, name)
@arg_count(3) @argumentCount(3)
def handle_connected_seek(self, args): def handle_connected_seek(self, args):
ctime, position, who = args ctime, position, who = args
try: try:
@ -79,30 +79,30 @@ class SyncClientProtocol(CommandProtocol):
self.manager.seek(ctime, position, who) self.manager.seek(ctime, position, who)
@arg_count(1) @argumentCount(1)
def handle_connected_ping(self, args): def handle_connected_ping(self, args):
self.send_message('pong', args[0], int(time.time()*100000)) self.send_message('pong', args[0], int(time.time()*100000))
@arg_count(3) @argumentCount(3)
def handle_connected_playing(self, args): def handle_connected_playing(self, args):
who, where, what = args who, where, what = args
message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where) message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where)
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self.manager.player): self.manager.player.display_message(message)
@arg_count(1) @argumentCount(1)
def handle_connected_joined(self, args): def handle_connected_joined(self, args):
message = '%s joined' % args[0] message = '%s joined' % args[0]
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self.manager.player): self.manager.player.display_message(message)
@arg_count(2) @argumentCount(2)
def handle_connected_room(self, args): def handle_connected_room(self, args):
message = '%s entered the room: \'%s\'' % (args[0], args[1]) message = '%s entered the room: \'%s\'' % (args[0], args[1])
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self.manager.player): self.manager.player.display_message(message)
@arg_count(1) @argumentCount(1)
def handle_connected_left(self, args): def handle_connected_left(self, args):
message = '%s left' % args[0] message = '%s left' % args[0]
print message print message

View File

@ -18,13 +18,12 @@ from zope.interface import implements
from .utils import ArgumentParser from .utils import ArgumentParser
def argumentCount(minimum, maximum=None):
def arg_count(minimum, maximum=None):
def decorator(f): def decorator(f):
@wraps(f) @wraps(f)
def wrapper(self, args): def wrapper(self, args):
if ((len(args) != minimum) if maximum is None else not (minimum <= len(args) <= maximum)): if ((len(args) != minimum) if maximum is None else not (minimum <= len(args) <= maximum)):
self.drop_with_error('Invalid arguments') self.dropWithError('Invalid arguments')
return return
return f(self, args) return f(self, args)
return wrapper return wrapper

View File

@ -1,75 +1,59 @@
#coding:utf8 #coding:utf8
import ConfigParser
import argparse
import os import os
import re import re
import sys
RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)") class ArgumentParser():
RE_NEED_QUOTING = re.compile(r"[\s'\\]") RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)")
RE_QUOTABLE = re.compile(r"['\\]") RE_NEED_QUOTING = re.compile(r"[\s'\\]")
RE_UNQUOTABLE = re.compile(r"\\(['\\])") RE_QUOTABLE = re.compile(r"['\\]")
RE_UNQUOTABLE = re.compile(r"\\(['\\])")
class InvalidArgumentException(Exception):
pass class InvalidArgumentException(Exception):
pass
def quote_arg(arg):
if isinstance(arg, unicode): @staticmethod
arg = arg.encode('utf8') def quoteArgument(arg):
elif not isinstance(arg, str): if isinstance(arg, unicode):
arg = str(arg) arg = arg.encode('utf8')
elif not isinstance(arg, str):
if not arg or RE_NEED_QUOTING.search(arg): arg = str(arg)
return "'%s'" % RE_QUOTABLE.sub(r'\\\g<0>', arg)
return arg if not arg or ArgumentParser.RE_NEED_QUOTING.search(arg):
return "'%s'" % ArgumentParser.RE_QUOTABLE.sub(r'\\\g<0>', arg)
def unqote_arg(arg): return arg
if arg.startswith("'") and len(arg) > 1:
arg = RE_UNQUOTABLE.sub(r'\1', arg[1:-1]) @staticmethod
return arg.decode('utf8', 'replace') def unqoteArgument(arg):
if arg.startswith("'") and len(arg) > 1:
def _split_args(args): arg = ArgumentParser.RE_UNQUOTABLE.sub(r'\1', arg[1:-1])
pos = 0 return arg.decode('utf8', 'replace')
while pos < len(args):
match = RE_ARG.match(args, pos) @staticmethod
if not match: def __splitArguments(args):
raise InvalidArgumentException() pos = 0
pos = match.end() while pos < len(args):
yield unqote_arg(match.group(1)) match = ArgumentParser.RE_ARG.match(args, pos)
if not match:
def split_args(args): raise ArgumentParser.InvalidArgumentException()
try: pos = match.end()
return list(_split_args(args)) yield ArgumentParser.unqoteArgument(match.group(1))
except InvalidArgumentException:
return None @staticmethod
def splitArguments(args):
def join_args(args): try:
return ' '.join(quote_arg(arg) for arg in args) return list(ArgumentParser.__splitArguments(args))
except ArgumentParser.InvalidArgumentException:
def parse_state(args): return None
if len(args) == 4:
counter, ctime, state, position = args @staticmethod
who_changed_state = None def joinArguments(args):
elif len(args) == 5: return ' '.join(ArgumentParser.quoteArgument(arg) for arg in args)
counter, ctime, state, position, who_changed_state = args
else:
return
if not state in ('paused', 'playing'):
return
paused = state == 'paused'
try:
counter = int(counter)
ctime = int(ctime)
position = int(position)
except ValueError:
return
ctime /= 1000.0
position /= 1000.0
return counter, ctime, paused, position, who_changed_state
def find_exec_path(name): def find_exec_path(name):
if os.access(name, os.X_OK): if os.access(name, os.X_OK):
return name return name
@ -86,22 +70,13 @@ def format_time(value):
return '%02d:%02d:%02d.%02d' % (hours, minutes, seconds, mseconds) return '%02d:%02d:%02d.%02d' % (hours, minutes, seconds, mseconds)
import sys
import ConfigParser
import argparse
def get_working_directory():
frozen = getattr(sys, 'frozen', '')
if not frozen:
# not frozen: in regular python interpreter
approot = os.path.dirname(os.path.dirname(__file__))
elif frozen in ('dll', 'console_exe', 'windows_exe'):
# py2exe:
approot = os.path.dirname(sys.executable)
else:
raise Exception('Working dir not found')
return approot
def stdin_thread(manager): def stdin_thread(manager):
try: try:
@ -114,45 +89,111 @@ def stdin_thread(manager):
except: except:
pass pass
def get_configuration():
parser = argparse.ArgumentParser(description='Syncplay',
epilog='If no options supplied config values will be used') class ConfigurationGetter(object):
parser.add_argument('--host', metavar='hostname', type=str, help='server\'s address') def __init__(self):
parser.add_argument('--name', metavar='username', type=str, help='desired username') self._config = None
parser.add_argument('-m', '--mpc-path', metavar='path', type=str, help='path to mpc-hc.exe (only for sync_mpc_api client)') self._args = None
parser.add_argument('-d','--debug', action='store_true', help='debug mode') self._syncplayClient = None
parser.add_argument('-n','--no-store', action='store_true', help='don\'t store values in syncplay.ini') self._workingDir = None
parser.add_argument('file', metavar='file', type=str, nargs='?', help='file to play') self._parser = None
parser.add_argument('args', metavar='options', type=str, nargs='*', help='player options, if you need to pass options starting with - prepend them with single \'--\' argument')
args = parser.parse_args()
working_path = get_working_directory()
config = ConfigParser.RawConfigParser()
config.read(os.path.join(working_path, 'syncplay.ini'))
section_name = 'sync' if not args.debug else 'debug'
try:
if(args.host == None): args.host = config.get(section_name, 'host')
if(args.name == None): args.name = config.get(section_name, 'name')
if(args.mpc_path == None): args.mpc_path = config.get(section_name, 'mpc_path')
except ConfigParser.NoSectionError:
pass
except ConfigParser.NoOptionError:
pass
if(args.host == None or args.name == None):
sys.exit("You must supply name and host on the first run")
if(not args.no_store):
with open(os.path.join(working_path, 'syncplay.ini'), 'wb') as configfile: def _findWorkingDirectory(self):
if(not config.has_section(section_name)): frozen = getattr(sys, 'frozen', '')
config.add_section(section_name) if not frozen:
config.set(section_name, 'host', args.host) self._workingDir = os.path.dirname(__file__)
config.set(section_name, 'name', args.name) elif frozen in ('dll', 'console_exe', 'windows_exe'):
config.set(section_name, 'mpc_path', args.mpc_path) self._workingDir = os.path.dirname(sys.executable)
config.write(configfile) else:
if ':' in args.host: raise Exception('Working dir not found')
args.host, port = args.host.split(':', 1)
args.port = int(port) def _prepareArgParser(self):
else: self._parser = argparse.ArgumentParser(description='Syncplay',
args.port = 8999 epilog='If no options supplied _config values will be used')
return args self._parser.add_argument('--no-gui', action='store_true', help='show no GUI')
self._parser.add_argument('--host', metavar='hostname', type=str, help='server\'s address')
self._parser.add_argument('--name', metavar='username', type=str, help='desired username')
self._parser.add_argument('-d','--debug', action='store_true', help='debug mode')
self._parser.add_argument('--no-store', action='store_true', help='don\'t store values in syncplay.ini')
self._parser.add_argument('file', metavar='file', type=str, nargs='?', help='file to play')
self._parser.add_argument('_args', metavar='options', type=str, nargs='*', help='player options, if you need to pass options starting with - prepend them with single \'--\' argument')
def _openConfigFile(self):
if(not self._config):
self._config = ConfigParser.RawConfigParser(allow_no_value=True)
self._config.read(os.path.join(self._workingDir, 'syncplay.ini'))
def _getSectionName(self):
return 'sync' if not self._args.debug else 'debug'
def _saveValuesIntoConfigFile(self):
self._openConfigFile()
section_name = self._getSectionName()
if(not self._args.no_store):
with open(os.path.join(self._workingDir, 'syncplay.ini'), 'wb') as configfile:
if(not self._config.has_section(section_name)):
self._config.add_section(section_name)
self._setUpValuesToSave(section_name)
self._config.write(configfile)
def _setUpValuesToSave(self, section_name):
self._config.set(section_name, 'host', self._args.host)
self._config.set(section_name, 'name', self._args.name)
def _readMissingValuesFromConfigFile(self):
self._openConfigFile()
section_name = self._getSectionName()
try:
self._valuesToReadFromConfig(section_name)
except ConfigParser.NoSectionError:
pass
except ConfigParser.NoOptionError:
pass
def _valuesToReadFromConfig(self, section_name):
if (self._args.host == None):
self._args.host = self._config.get(section_name, 'host')
if (self._args.name == None):
self._args.name = self._config.get(section_name, 'name')
def _splitPortAndHost(self):
if ':' in self._args.host:
self._args.host, port = self._args.host.split(':', 1)
self._args.port = int(port)
else:
self._args.port = 8999
def prepareClientConfiguration(self):
self._findWorkingDirectory()
self._prepareArgParser()
self._args = self._parser.parse_args()
self._readMissingValuesFromConfigFile()
self._splitPortAndHost()
def getClientConfiguration(self):
return self._args
class MPCConfigurationGetter(ConfigurationGetter):
def _prepareArgParser(self):
ConfigurationGetter._prepareArgParser(self)
self._parser.add_argument('--mpc-path', metavar='path', type=str, help='path to mpc-hc.exe (only for sync_mpc_api client)')
def _setUpValuesToSave(self, section_name):
ConfigurationGetter._setUpValuesToSave(self, section_name)
self._config.set(section_name, 'mpc_path', self._args.mpc_path)
def _valuesToReadFromConfig(self, section_name):
ConfigurationGetter._valuesToReadFromConfig(self, section_name)
if (self._args.mpc_path == None):
self._args.mpc_path = self._config.get(section_name, 'mpc_path')
def __addSpecialMPCFlags(self):
return self._args._args.extend(['/open', '/new'])
def prepareClientConfiguration(self):
ConfigurationGetter.prepareClientConfiguration(self)
self.__addSpecialMPCFlags()