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:
parent
a636fd4176
commit
698fba9054
@ -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()
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
version = '0.2.0'
|
||||
version = '0.3.0'
|
||||
milestone = 'Clara'
|
||||
projectURL = ''
|
||||
projectURL = 'http://droptable.co.cc'
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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?')
|
||||
|
||||
|
||||
@ -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__':
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user