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 twisted.internet import reactor
|
||||||
|
|
||||||
from syncplay.server import SyncFactory
|
from syncplay.server import SyncFactory
|
||||||
|
from syncplay import utils
|
||||||
reactor.listenTCP(8999, SyncFactory())
|
|
||||||
|
argsGetter = utils.ServerConfigurationGetter()
|
||||||
|
args = argsGetter.getConfiguration()
|
||||||
|
reactor.listenTCP(8999, SyncFactory(args.password, args.banlist, args.isolate_rooms))
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
version = '0.2.0'
|
version = '0.3.0'
|
||||||
milestone = 'Clara'
|
milestone = 'Clara'
|
||||||
projectURL = ''
|
projectURL = 'http://droptable.co.cc'
|
||||||
@ -7,6 +7,7 @@ from twisted.internet.protocol import ClientFactory
|
|||||||
import time
|
import time
|
||||||
import itertools
|
import itertools
|
||||||
import syncplay
|
import syncplay
|
||||||
|
import hashlib
|
||||||
|
|
||||||
class SyncClientProtocol(CommandProtocol):
|
class SyncClientProtocol(CommandProtocol):
|
||||||
def __init__(self, syncplayClient):
|
def __init__(self, syncplayClient):
|
||||||
@ -15,7 +16,7 @@ class SyncClientProtocol(CommandProtocol):
|
|||||||
self.sender = self._MessagesSender(self)
|
self.sender = self._MessagesSender(self)
|
||||||
|
|
||||||
def connectionMade(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)
|
self.syncplayClient.initProtocol(self)
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
@ -98,9 +99,10 @@ class SyncClientProtocol(CommandProtocol):
|
|||||||
@argumentCount(2)
|
@argumentCount(2)
|
||||||
def error(self, args):
|
def error(self, args):
|
||||||
self.__protocol.dropWithError(args[1])
|
self.__protocol.dropWithError(args[1])
|
||||||
self.__syncplayClient.ui.showMessage("Mismatch between client and server versions detected")
|
if(syncplay.version <> args[0]):
|
||||||
self.__syncplayClient.ui.showMessage("Your version is %s against server's %s" % (syncplay.version, args[0]))
|
self.__syncplayClient.ui.showMessage("Mismatch between client and server versions detected")
|
||||||
self.__syncplayClient.ui.showMessage("Please use latest version of client and server")
|
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)
|
@argumentCount(3)
|
||||||
def playing(self, args):
|
def playing(self, args):
|
||||||
@ -205,10 +207,15 @@ class SyncClientFactory(ClientFactory):
|
|||||||
self.retry = False
|
self.retry = False
|
||||||
|
|
||||||
class SyncplayClient(object):
|
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 = self.UserList()
|
||||||
self.users.currentUser.name = name
|
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.ui = self.UiManager(self, ui, debug)
|
||||||
self.protocol_factory = None
|
self.protocol_factory = None
|
||||||
self.protocol = None
|
self.protocol = None
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import re
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
import hashlib
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from twisted.internet.protocol import Factory
|
from twisted.internet.protocol import Factory
|
||||||
|
|
||||||
@ -36,6 +36,10 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
self.factory = factory
|
self.factory = factory
|
||||||
self.state = 'init'
|
self.state = 'init'
|
||||||
|
|
||||||
|
def dropWithError(self, error):
|
||||||
|
CommandProtocol.dropWithError(self, error)
|
||||||
|
print "Client drop: %s -- %s" % (self.transport.getPeer().host, error)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash('|'.join((
|
return hash('|'.join((
|
||||||
self.transport.getPeer().host,
|
self.transport.getPeer().host,
|
||||||
@ -63,13 +67,25 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@state('init')
|
@state('init')
|
||||||
@argumentCount(1)
|
@argumentCount(2, 3)
|
||||||
def iam(self, args):
|
def iam(self, args):
|
||||||
name = re.sub('[^\w]','',args[0])
|
name = re.sub('[^\w]','',args.pop(0))
|
||||||
if not name:
|
if not name:
|
||||||
self.dropWithError('Invalid nickname')
|
self.dropWithError('Invalid nickname')
|
||||||
return
|
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')
|
self.__protocol.change_state('connected')
|
||||||
|
|
||||||
|
|
||||||
@ -203,7 +219,7 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
|
|
||||||
|
|
||||||
class WatcherInfo(object):
|
class WatcherInfo(object):
|
||||||
def __init__(self, watcher_proto, name):
|
def __init__(self, watcher_proto, name, room):
|
||||||
self.watcher_proto = watcher_proto
|
self.watcher_proto = watcher_proto
|
||||||
self.name = name
|
self.name = name
|
||||||
self.active = True
|
self.active = True
|
||||||
@ -215,7 +231,7 @@ class WatcherInfo(object):
|
|||||||
self.last_update = None
|
self.last_update = None
|
||||||
self.last_update_sent = None
|
self.last_update_sent = None
|
||||||
|
|
||||||
self.room = 'default'
|
self.room = room
|
||||||
self.ping = None
|
self.ping = None
|
||||||
self.time_offset = 0
|
self.time_offset = 0
|
||||||
self.time_offset_data = []
|
self.time_offset_data = []
|
||||||
@ -225,26 +241,32 @@ class WatcherInfo(object):
|
|||||||
self.counter = 0
|
self.counter = 0
|
||||||
|
|
||||||
class SyncFactory(Factory):
|
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.watchers = dict()
|
||||||
self.paused = {}
|
self.paused = {}
|
||||||
self.paused['default'] = True
|
|
||||||
self.pause_change_time = None
|
self.pause_change_time = None
|
||||||
self.pause_change_by = 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.min_pause_lock = min_pause_lock
|
||||||
self.update_time_limit = update_time_limit
|
self.update_time_limit = update_time_limit
|
||||||
|
|
||||||
def buildProtocol(self, addr):
|
def buildProtocol(self, addr):
|
||||||
return SyncServerProtocol(self)
|
return SyncServerProtocol(self)
|
||||||
|
|
||||||
def add_watcher(self, watcher_proto, name):
|
def add_watcher(self, watcher_proto, name, room):
|
||||||
allnames = []
|
allnames = []
|
||||||
for watcher in self.watchers.itervalues():
|
for watcher in self.watchers.itervalues():
|
||||||
allnames.append(watcher.name.lower())
|
allnames.append(watcher.name.lower())
|
||||||
while name.lower() in allnames:
|
while name.lower() in allnames:
|
||||||
name += '_'
|
name += '_'
|
||||||
watcher = WatcherInfo(watcher_proto, name)
|
if(not self.paused.has_key(room)):
|
||||||
|
self.paused[room] = True
|
||||||
|
watcher = WatcherInfo(watcher_proto, name, room)
|
||||||
if self.watchers:
|
if self.watchers:
|
||||||
watcher.max_position = min(w.max_position for w in self.watchers.itervalues())
|
watcher.max_position = min(w.max_position for w in self.watchers.itervalues())
|
||||||
self.watchers[watcher_proto] = watcher
|
self.watchers[watcher_proto] = watcher
|
||||||
@ -267,10 +289,7 @@ class SyncFactory(Factory):
|
|||||||
def remove_room_if_empty(self, room):
|
def remove_room_if_empty(self, room):
|
||||||
room_user_count = sum(1 if watcher.room == room else 0 for watcher in self.watchers.itervalues())
|
room_user_count = sum(1 if watcher.room == room else 0 for watcher in self.watchers.itervalues())
|
||||||
if not room_user_count:
|
if not room_user_count:
|
||||||
if room == 'default':
|
self.paused.pop(room)
|
||||||
self.paused['default'] = True
|
|
||||||
else:
|
|
||||||
self.paused.pop(room)
|
|
||||||
|
|
||||||
|
|
||||||
def update_state(self, watcher_proto, counter, ctime, paused, position):
|
def update_state(self, watcher_proto, counter, ctime, paused, position):
|
||||||
@ -424,6 +443,8 @@ class SyncFactory(Factory):
|
|||||||
what(receiver)
|
what(receiver)
|
||||||
|
|
||||||
def broadcast(self, sender, what):
|
def broadcast(self, sender, what):
|
||||||
|
if(self.isolate_rooms):
|
||||||
|
self.broadcast_room(sender, what)
|
||||||
for receiver in self.watchers.itervalues():
|
for receiver in self.watchers.itervalues():
|
||||||
#if receiver != sender:
|
#if receiver != sender:
|
||||||
what(receiver)
|
what(receiver)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import argparse
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import itertools
|
||||||
|
|
||||||
class ArgumentParser():
|
class ArgumentParser():
|
||||||
RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)")
|
RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)")
|
||||||
@ -51,6 +52,7 @@ class ArgumentParser():
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def joinArguments(args):
|
def joinArguments(args):
|
||||||
|
args = list(itertools.ifilterfalse(lambda x: None == x, args))
|
||||||
return ' '.join(ArgumentParser.quoteArgument(arg) for arg in args)
|
return ' '.join(ArgumentParser.quoteArgument(arg) for arg in args)
|
||||||
|
|
||||||
def find_exec_path(name):
|
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('--name', metavar='username', type=str, help='desired username')
|
||||||
self._parser.add_argument('-d','--debug', action='store_true', help='debug mode')
|
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('--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('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')
|
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._splitPortAndHost()
|
||||||
self._config.set(section_name, 'host', self._args.host)
|
self._config.set(section_name, 'host', self._args.host)
|
||||||
self._config.set(section_name, 'name', self._args.name)
|
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):
|
def _readMissingValuesFromConfigFile(self):
|
||||||
self._openConfigFile()
|
self._openConfigFile()
|
||||||
section_name = self._getSectionName()
|
section_name = self._getSectionName()
|
||||||
@ -137,15 +149,20 @@ class ConfigurationGetter(object):
|
|||||||
self._valuesToReadFromConfig(section_name)
|
self._valuesToReadFromConfig(section_name)
|
||||||
except ConfigParser.NoSectionError:
|
except ConfigParser.NoSectionError:
|
||||||
pass
|
pass
|
||||||
except ConfigParser.NoOptionError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _valuesToReadFromConfig(self, section_name):
|
def _valuesToReadFromConfig(self, section_name):
|
||||||
if (self._args.host == None):
|
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):
|
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):
|
def _splitPortAndHost(self):
|
||||||
if(self._args.host):
|
if(self._args.host):
|
||||||
if ':' in self._args.host:
|
if ':' in self._args.host:
|
||||||
@ -154,16 +171,15 @@ class ConfigurationGetter(object):
|
|||||||
else:
|
else:
|
||||||
self._args.port = 8999
|
self._args.port = 8999
|
||||||
|
|
||||||
def prepareClientConfiguration(self):
|
def getConfiguration(self):
|
||||||
self._findWorkingDirectory()
|
self._findWorkingDirectory()
|
||||||
self._prepareArgParser()
|
self._prepareArgParser()
|
||||||
self._args = self._parser.parse_args()
|
self._args = self._parser.parse_args()
|
||||||
self._readMissingValuesFromConfigFile()
|
self._readMissingValuesFromConfigFile()
|
||||||
self.saveValuesIntoConfigFile()
|
self.saveValuesIntoConfigFile()
|
||||||
self._splitPortAndHost()
|
self._splitPortAndHost()
|
||||||
|
|
||||||
def getClientConfiguration(self):
|
|
||||||
return self._args
|
return self._args
|
||||||
|
|
||||||
|
|
||||||
class MPCConfigurationGetter(ConfigurationGetter):
|
class MPCConfigurationGetter(ConfigurationGetter):
|
||||||
def _prepareArgParser(self):
|
def _prepareArgParser(self):
|
||||||
@ -177,14 +193,28 @@ class MPCConfigurationGetter(ConfigurationGetter):
|
|||||||
def _valuesToReadFromConfig(self, section_name):
|
def _valuesToReadFromConfig(self, section_name):
|
||||||
ConfigurationGetter._valuesToReadFromConfig(self, section_name)
|
ConfigurationGetter._valuesToReadFromConfig(self, section_name)
|
||||||
if (self._args.mpc_path == None):
|
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):
|
def __addSpecialMPCFlags(self):
|
||||||
return self._args._args.extend(['/open', '/new'])
|
self._args._args.extend(['/open', '/new'])
|
||||||
|
|
||||||
def prepareClientConfiguration(self):
|
def getConfiguration(self):
|
||||||
ConfigurationGetter.prepareClientConfiguration(self)
|
ConfigurationGetter.getConfiguration(self)
|
||||||
self.__addSpecialMPCFlags()
|
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._promptForMissingArguments()
|
||||||
self.args._args.extend(('-slave', '-msglevel', 'all=1:global=4'))
|
self.args._args.extend(('-slave', '-msglevel', 'all=1:global=4'))
|
||||||
if(self.args.file): self.args._args.extend((self.args.file,))
|
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)
|
self.interface.addClient(syncplayClient)
|
||||||
syncplayClient.start(self.args.host, self.args.port)
|
syncplayClient.start(self.args.host, self.args.port)
|
||||||
def _prepareArguments(self):
|
def _prepareArguments(self):
|
||||||
self.argsGetter = utils.ConfigurationGetter()
|
self.argsGetter = utils.ConfigurationGetter()
|
||||||
self.argsGetter.prepareClientConfiguration()
|
self.args = self.argsGetter.getConfiguration()
|
||||||
self.args = self.argsGetter.getClientConfiguration()
|
|
||||||
|
|
||||||
def _promptForMissingArguments(self):
|
def _promptForMissingArguments(self):
|
||||||
if (self.args.host == None):
|
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):
|
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()
|
self.argsGetter.saveValuesIntoConfigFile()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user