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,74 +1,58 @@
#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): class InvalidArgumentException(Exception):
pass pass
def quote_arg(arg): @staticmethod
if isinstance(arg, unicode): def quoteArgument(arg):
arg = arg.encode('utf8') if isinstance(arg, unicode):
elif not isinstance(arg, str): arg = arg.encode('utf8')
arg = str(arg) elif not isinstance(arg, str):
arg = str(arg)
if not arg or RE_NEED_QUOTING.search(arg): if not arg or ArgumentParser.RE_NEED_QUOTING.search(arg):
return "'%s'" % RE_QUOTABLE.sub(r'\\\g<0>', arg) return "'%s'" % ArgumentParser.RE_QUOTABLE.sub(r'\\\g<0>', arg)
return arg return arg
def unqote_arg(arg): @staticmethod
if arg.startswith("'") and len(arg) > 1: def unqoteArgument(arg):
arg = RE_UNQUOTABLE.sub(r'\1', arg[1:-1]) if arg.startswith("'") and len(arg) > 1:
return arg.decode('utf8', 'replace') arg = ArgumentParser.RE_UNQUOTABLE.sub(r'\1', arg[1:-1])
return arg.decode('utf8', 'replace')
def _split_args(args): @staticmethod
pos = 0 def __splitArguments(args):
while pos < len(args): pos = 0
match = RE_ARG.match(args, pos) while pos < len(args):
if not match: match = ArgumentParser.RE_ARG.match(args, pos)
raise InvalidArgumentException() if not match:
pos = match.end() raise ArgumentParser.InvalidArgumentException()
yield unqote_arg(match.group(1)) pos = match.end()
yield ArgumentParser.unqoteArgument(match.group(1))
def split_args(args): @staticmethod
try: def splitArguments(args):
return list(_split_args(args)) try:
except InvalidArgumentException: return list(ArgumentParser.__splitArguments(args))
return None except ArgumentParser.InvalidArgumentException:
return None
def join_args(args): @staticmethod
return ' '.join(quote_arg(arg) for arg in args) def joinArguments(args):
return ' '.join(ArgumentParser.quoteArgument(arg) for arg in args)
def parse_state(args):
if len(args) == 4:
counter, ctime, state, position = args
who_changed_state = None
elif len(args) == 5:
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):
@ -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')
parser.add_argument('--host', metavar='hostname', type=str, help='server\'s address')
parser.add_argument('--name', metavar='username', type=str, help='desired username')
parser.add_argument('-m', '--mpc-path', metavar='path', type=str, help='path to mpc-hc.exe (only for sync_mpc_api client)')
parser.add_argument('-d','--debug', action='store_true', help='debug mode')
parser.add_argument('-n','--no-store', action='store_true', help='don\'t store values in syncplay.ini')
parser.add_argument('file', metavar='file', type=str, nargs='?', help='file to play')
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): class ConfigurationGetter(object):
def __init__(self):
self._config = None
self._args = None
self._syncplayClient = None
self._workingDir = None
self._parser = None
def _findWorkingDirectory(self):
frozen = getattr(sys, 'frozen', '')
if not frozen:
self._workingDir = os.path.dirname(__file__)
elif frozen in ('dll', 'console_exe', 'windows_exe'):
self._workingDir = os.path.dirname(sys.executable)
else:
raise Exception('Working dir not found')
def _prepareArgParser(self):
self._parser = argparse.ArgumentParser(description='Syncplay',
epilog='If no options supplied _config values will be used')
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()
with open(os.path.join(working_path, 'syncplay.ini'), 'wb') as configfile:
if(not config.has_section(section_name)):
config.add_section(section_name)
config.set(section_name, 'host', args.host)
config.set(section_name, 'name', args.name)
config.set(section_name, 'mpc_path', args.mpc_path)
config.write(configfile)
if ':' in args.host:
args.host, port = args.host.split(':', 1)
args.port = int(port)
else:
args.port = 8999
return args