Added server password

Added server argument parser
Added isolated rooms
Added more verbose server settings
Added configurable default room for client and server
This commit is contained in:
Uriziel 2012-09-18 13:08:48 +02:00
parent a636fd4176
commit 698fba9054
6 changed files with 106 additions and 46 deletions

View File

@ -2,7 +2,10 @@
from twisted.internet import reactor
from syncplay.server import SyncFactory
reactor.listenTCP(8999, SyncFactory())
from syncplay.server import SyncFactory
from syncplay import utils
argsGetter = utils.ServerConfigurationGetter()
args = argsGetter.getConfiguration()
reactor.listenTCP(8999, SyncFactory(args.password, args.banlist, args.isolate_rooms))
reactor.run()

View File

@ -1,3 +1,3 @@
version = '0.2.0'
version = '0.3.0'
milestone = 'Clara'
projectURL = ''
projectURL = 'http://droptable.co.cc'

View File

@ -7,6 +7,7 @@ from twisted.internet.protocol import ClientFactory
import time
import itertools
import syncplay
import hashlib
class SyncClientProtocol(CommandProtocol):
def __init__(self, syncplayClient):
@ -15,7 +16,7 @@ class SyncClientProtocol(CommandProtocol):
self.sender = self._MessagesSender(self)
def connectionMade(self):
self.sendMessage('iam', self.syncplayClient.users.currentUser.name)
self.sendMessage('iam', self.syncplayClient.users.currentUser.name, self.syncplayClient.users.currentUser.room, self.syncplayClient.serverPassword)
self.syncplayClient.initProtocol(self)
def connectionLost(self, reason):
@ -98,9 +99,10 @@ class SyncClientProtocol(CommandProtocol):
@argumentCount(2)
def error(self, args):
self.__protocol.dropWithError(args[1])
self.__syncplayClient.ui.showMessage("Mismatch between client and server versions detected")
self.__syncplayClient.ui.showMessage("Your version is %s against server's %s" % (syncplay.version, args[0]))
self.__syncplayClient.ui.showMessage("Please use latest version of client and server")
if(syncplay.version <> args[0]):
self.__syncplayClient.ui.showMessage("Mismatch between client and server versions detected")
self.__syncplayClient.ui.showMessage("Your version is %s against server's %s" % (syncplay.version, args[0]))
self.__syncplayClient.ui.showMessage("Please use latest version of client and server")
@argumentCount(3)
def playing(self, args):
@ -205,10 +207,15 @@ class SyncClientFactory(ClientFactory):
self.retry = False
class SyncplayClient(object):
def __init__(self, name, make_player, ui, debug):
def __init__(self, name, make_player, ui, debug, room, password = None):
self.users = self.UserList()
self.users.currentUser.name = name
self.users.currentUser.room = 'default'
if(room == None):
room = 'default'
self.users.currentUser.room = room
if(password):
password = hashlib.md5(password).hexdigest()
self.serverPassword = password
self.ui = self.UiManager(self, ui, debug)
self.protocol_factory = None
self.protocol = None

View File

@ -4,7 +4,7 @@ import re
import time
import random
from functools import wraps
import hashlib
from twisted.internet import reactor
from twisted.internet.protocol import Factory
@ -36,6 +36,10 @@ class SyncServerProtocol(CommandProtocol):
self.factory = factory
self.state = 'init'
def dropWithError(self, error):
CommandProtocol.dropWithError(self, error)
print "Client drop: %s -- %s" % (self.transport.getPeer().host, error)
def __hash__(self):
return hash('|'.join((
self.transport.getPeer().host,
@ -63,13 +67,25 @@ class SyncServerProtocol(CommandProtocol):
return True
@state('init')
@argumentCount(1)
@argumentCount(2, 3)
def iam(self, args):
name = re.sub('[^\w]','',args[0])
name = re.sub('[^\w]','',args.pop(0))
if not name:
self.dropWithError('Invalid nickname')
return
self.factory.add_watcher(self.__protocol, name)
room = re.sub('[^\w]','',args.pop(0))
if not room:
self.dropWithError('Invalid room')
return
if(self.factory.password):
if(len(args)):
password = args.pop(0)
if(self.factory.password <> password):
self.dropWithError('Wrong server password specified')
else:
self.dropWithError('No password specified')
return
self.factory.add_watcher(self.__protocol, name, room)
self.__protocol.change_state('connected')
@ -203,7 +219,7 @@ class SyncServerProtocol(CommandProtocol):
class WatcherInfo(object):
def __init__(self, watcher_proto, name):
def __init__(self, watcher_proto, name, room):
self.watcher_proto = watcher_proto
self.name = name
self.active = True
@ -215,7 +231,7 @@ class WatcherInfo(object):
self.last_update = None
self.last_update_sent = None
self.room = 'default'
self.room = room
self.ping = None
self.time_offset = 0
self.time_offset_data = []
@ -225,26 +241,32 @@ class WatcherInfo(object):
self.counter = 0
class SyncFactory(Factory):
def __init__(self, min_pause_lock = 2 , update_time_limit = 1):
def __init__(self, password = '', banlist = None , isolate_rooms = False, min_pause_lock = 2 , update_time_limit = 1):
self.watchers = dict()
self.paused = {}
self.paused['default'] = True
self.pause_change_time = None
self.pause_change_by = None
if(password):
password = hashlib.md5(password).hexdigest()
self.password = password
self.banlsit = banlist
self.isolate_rooms = isolate_rooms
self.min_pause_lock = min_pause_lock
self.update_time_limit = update_time_limit
def buildProtocol(self, addr):
return SyncServerProtocol(self)
def add_watcher(self, watcher_proto, name):
def add_watcher(self, watcher_proto, name, room):
allnames = []
for watcher in self.watchers.itervalues():
allnames.append(watcher.name.lower())
while name.lower() in allnames:
name += '_'
watcher = WatcherInfo(watcher_proto, name)
name += '_'
if(not self.paused.has_key(room)):
self.paused[room] = True
watcher = WatcherInfo(watcher_proto, name, room)
if self.watchers:
watcher.max_position = min(w.max_position for w in self.watchers.itervalues())
self.watchers[watcher_proto] = watcher
@ -267,10 +289,7 @@ class SyncFactory(Factory):
def remove_room_if_empty(self, room):
room_user_count = sum(1 if watcher.room == room else 0 for watcher in self.watchers.itervalues())
if not room_user_count:
if room == 'default':
self.paused['default'] = True
else:
self.paused.pop(room)
self.paused.pop(room)
def update_state(self, watcher_proto, counter, ctime, paused, position):
@ -424,6 +443,8 @@ class SyncFactory(Factory):
what(receiver)
def broadcast(self, sender, what):
if(self.isolate_rooms):
self.broadcast_room(sender, what)
for receiver in self.watchers.itervalues():
#if receiver != sender:
what(receiver)

View File

@ -5,6 +5,7 @@ import argparse
import os
import re
import sys
import itertools
class ArgumentParser():
RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)")
@ -51,6 +52,7 @@ class ArgumentParser():
@staticmethod
def joinArguments(args):
args = list(itertools.ifilterfalse(lambda x: None == x, args))
return ' '.join(ArgumentParser.quoteArgument(arg) for arg in args)
def find_exec_path(name):
@ -104,6 +106,8 @@ class ConfigurationGetter(object):
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('--room', metavar='room', type=str, nargs='?', help='default room')
self._parser.add_argument('--password', metavar='password', type=str, nargs='?', help='server password')
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')
@ -129,7 +133,15 @@ class ConfigurationGetter(object):
self._splitPortAndHost()
self._config.set(section_name, 'host', self._args.host)
self._config.set(section_name, 'name', self._args.name)
self._config.set(section_name, 'room', self._args.room)
self._config.set(section_name, 'password', self._args.password)
def _readConfigValue(self, section_name, name):
try:
return self._config.get(section_name, name)
except ConfigParser.NoOptionError:
return None
def _readMissingValuesFromConfigFile(self):
self._openConfigFile()
section_name = self._getSectionName()
@ -137,15 +149,20 @@ class ConfigurationGetter(object):
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')
self._args.host = self._readConfigValue(section_name, 'host')
if (self._args.name == None):
self._args.name = self._config.get(section_name, 'name')
self._args.name = self._readConfigValue(section_name, 'name')
if (self._args.room == None):
self._args.room = self._readConfigValue(section_name, 'room')
if (self._args.password == None):
self._args.password = self._readConfigValue(section_name, 'password')
def _splitPortAndHost(self):
if(self._args.host):
if ':' in self._args.host:
@ -154,16 +171,15 @@ class ConfigurationGetter(object):
else:
self._args.port = 8999
def prepareClientConfiguration(self):
def getConfiguration(self):
self._findWorkingDirectory()
self._prepareArgParser()
self._args = self._parser.parse_args()
self._readMissingValuesFromConfigFile()
self.saveValuesIntoConfigFile()
self._splitPortAndHost()
def getClientConfiguration(self):
return self._args
class MPCConfigurationGetter(ConfigurationGetter):
def _prepareArgParser(self):
@ -177,14 +193,28 @@ class MPCConfigurationGetter(ConfigurationGetter):
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')
self._args.mpc_path = self._readConfigValue(section_name, 'mpc_path')
def __addSpecialMPCFlags(self):
return self._args._args.extend(['/open', '/new'])
self._args._args.extend(['/open', '/new'])
def prepareClientConfiguration(self):
ConfigurationGetter.prepareClientConfiguration(self)
def getConfiguration(self):
ConfigurationGetter.getConfiguration(self)
self.__addSpecialMPCFlags()
return self._args
class ServerConfigurationGetter(ConfigurationGetter):
def getConfiguration(self):
self._prepareArgParser()
self._args = self._parser.parse_args()
return self._args
def _prepareArgParser(self):
self._parser = argparse.ArgumentParser(description='Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network. Server instance',
epilog='If no options supplied _config values will be used')
self._parser.add_argument('--password', metavar='password', type=str, nargs='?', help='server password')
self._parser.add_argument('--banlist', metavar='banlist', type=str, nargs='?', help='server banlist file')
self._parser.add_argument('--isolate-rooms', action='store_true', help='should rooms be isolated?')

View File

@ -12,19 +12,18 @@ class SyncplayMplayer:
self._promptForMissingArguments()
self.args._args.extend(('-slave', '-msglevel', 'all=1:global=4'))
if(self.args.file): self.args._args.extend((self.args.file,))
syncplayClient = client.SyncplayClient(self.args.name, lambda m: mplayer.run_mplayer(m, 'mplayer', self.args._args), self.interface, self.args.debug)
syncplayClient = client.SyncplayClient(self.args.name, lambda m: mplayer.run_mplayer(m, 'mplayer', self.args._args), self.interface, self.args.debug, self.args.room, self.args.password)
self.interface.addClient(syncplayClient)
syncplayClient.start(self.args.host, self.args.port)
def _prepareArguments(self):
self.argsGetter = utils.ConfigurationGetter()
self.argsGetter.prepareClientConfiguration()
self.args = self.argsGetter.getClientConfiguration()
self.args = self.argsGetter.getConfiguration()
def _promptForMissingArguments(self):
if (self.args.host == None):
self.args.host = self.interface.promptFor(promptName = "Hostname", message = "You must supply hostname on the first run, it's easier trough command line arguments.")
self.args.host = self.interface.promptFor(promptName = "Hostname", message = "You must supply hostname on the first run, it's easier through command line arguments.")
if (self.args.name == None):
self.args.name = self.interface.promptFor(promptName = "Username", message = "You must supply username on the first run, it's easier trough command line arguments.")
self.args.name = self.interface.promptFor(promptName = "Username", message = "You must supply username on the first run, it's easier through command line arguments.")
self.argsGetter.saveValuesIntoConfigFile()
if __name__ == '__main__':