Code a bit more readable, protocol standarized a bit, added better name

handling on server
This commit is contained in:
Uriziel 2012-06-23 15:30:16 +02:00
parent c5a33875b3
commit f6e6cad1a6
7 changed files with 120 additions and 96 deletions

View File

@ -1,53 +0,0 @@
import sys, os
import ConfigParser
import argparse
def stdin_thread(manag):
try:
fd = sys.stdin.fileno()
while True:
data = os.read(fd, 1024)
if not data:
break
manag.execute_command(data.rstrip('\n\r'))
except:
pass
def get_configuration():
parser = argparse.ArgumentParser(description='Synchronize multiple players over the web.',
epilog='If no options supplied config values will be used')
parser.add_argument('host', metavar='host', type=str, nargs='?', help='server\'s address')
parser.add_argument('name', metavar='name', type=str, nargs='?', help='desired username')
parser.add_argument('args', metavar='opts', type=str, nargs='*', help='player options, if you need to pass options starting with - prepend them with single \'--\' argument')
args = parser.parse_args()
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(os.path.join(os.path.dirname(__file__), 'syncplay.ini'))
try:
if(args.host == None):
host = config.get('sync', 'host')
else:
host = args.host
if(args.name == None):
name = config.get('sync', 'name')
else:
name = args.name
except ConfigParser.NoSectionError:
sys.exit("Host or username not specified")
with open('syncplay.ini', 'wb') as configfile:
try:
config.set('sync', 'host' ,host)
config.set('sync', 'name' ,name)
except ConfigParser.NoSectionError:
config.add_section('sync')
config.set('sync', 'host' ,host)
config.set('sync', 'name' ,name)
config.write(configfile)
if ':' in host:
host, port = host.split(':', 1)
port = int(port)
else:
port = 8999
return host,port,name,args.args

View File

@ -1,16 +1,14 @@
#coding:utf8 #coding:utf8
import thread 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 from syncplay.players import mpc
from syncplay import utils
import common_functions
if __name__ == '__main__': if __name__ == '__main__':
host, port, name, args = common_functions.get_configuration() host, port, name, args = utils.get_configuration()
manager = client.Manager(host, port, name, lambda m: mpc.run_mpc(m)) manager = client.Manager(host, port, name, lambda m: mpc.run_mpc(m))
thread.start_new_thread(common_functions.stdin_thread, (manager,)) thread.start_new_thread(utils.stdin_thread, (manager,))
manager.start() manager.start()

View File

@ -6,13 +6,12 @@ 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
import common_functions
if __name__ == '__main__': if __name__ == '__main__':
host, port, name, args = common_functions.get_configuration() host, port, name, args = utils.get_configuration()
args.extend(('-slave', '-msglevel', 'all=1:global=4')) args.extend(('-slave', '-msglevel', 'all=1:global=4'))
manager = client.Manager(host, port, name, lambda m: mplayer.run_mplayer(m, 'mplayer', args)) manager = client.Manager(host, port, name, lambda m: mplayer.run_mplayer(m, 'mplayer', args))
thread.start_new_thread(common_functions.stdin_thread, (manager,)) thread.start_new_thread(utils.stdin_thread, (manager,))
manager.start() manager.start()

View File

@ -20,7 +20,7 @@ class SyncClientProtocol(CommandProtocol):
def __init__(self, manager): def __init__(self, manager):
CommandProtocol.__init__(self) CommandProtocol.__init__(self)
self.manager = manager self.manager = manager
def connectionMade(self): def connectionMade(self):
self.send_message('iam', self.manager.name) self.send_message('iam', self.manager.name)
self.manager.init_protocol(self) self.manager.init_protocol(self)
@ -35,10 +35,13 @@ class SyncClientProtocol(CommandProtocol):
@arg_count(1) @arg_count(1)
def handle_init_hello(self, args): def handle_init_hello(self, args):
print 'Connected as', args[0] print 'Connected as', args[0]
self.manager.name = args[0]
self.change_state('connected') self.change_state('connected')
self.send_list()
self.manager.schedule_send_status()
@arg_count(1, 2) @arg_count(1, 2)
def handle_init_present(self, args): def handle_connected_present(self, args):
if len(args) == 2: if len(args) == 2:
who, what = args who, what = args
else: else:
@ -90,6 +93,8 @@ class SyncClientProtocol(CommandProtocol):
def handle_connected_left(self, args): def handle_connected_left(self, args):
print '%s left' % args[0] print '%s left' % args[0]
def send_list(self):
self.send_message('list')
def send_state(self, counter, ctime, paused, position): def send_state(self, counter, ctime, paused, position):
self.send_message('state', counter, int(ctime*1000), ('paused' if paused else 'playing'), int(position*1000)) self.send_message('state', counter, int(ctime*1000), ('paused' if paused else 'playing'), int(position*1000))
@ -102,10 +107,11 @@ class SyncClientProtocol(CommandProtocol):
states = dict( states = dict(
init = dict( init = dict(
present = 'handle_init_present',
hello = 'handle_init_hello', hello = 'handle_init_hello',
), ),
connected = dict( connected = dict(
list = 'handle_connected_list',
present = 'handle_connected_present',
state = 'handle_connected_state', state = 'handle_connected_state',
seek = 'handle_connected_seek', seek = 'handle_connected_seek',
ping = 'handle_connected_ping', ping = 'handle_connected_ping',
@ -225,8 +231,6 @@ class Manager(object):
def init_protocol(self, protocol): def init_protocol(self, protocol):
self.protocol = protocol self.protocol = protocol
self.schedule_send_status()
self.send_filename()
if self.make_player: if self.make_player:
self.make_player(self) self.make_player(self)
self.make_player = None self.make_player = None
@ -278,7 +282,7 @@ class Manager(object):
if self.protocol and self.player_filename: if self.protocol and self.player_filename:
self.protocol.send_playing(self.player_filename) self.protocol.send_playing(self.player_filename)
def exectue_seek_cmd(self, seek_type, minutes, seconds): def __exectue_seek_cmd(self, seek_type, minutes, seconds):
self.player_position_before_last_seek = self.player_position self.player_position_before_last_seek = self.player_position
if seek_type == 's': if seek_type == 's':
@ -302,9 +306,16 @@ class Manager(object):
def execute_command(self, data): def execute_command(self, data):
RE_SEEK = re.compile("^(s[+s]?) ?(-?\d+)?([^0-9](\d+))?$") RE_SEEK = re.compile("^(s[+s]?) ?(-?\d+)?([^0-9](\d+))?$")
RE_ROOM = re.compile("^room( (\w+))?")
matched_seek = RE_SEEK.match(data) matched_seek = RE_SEEK.match(data)
matched_room = RE_ROOM.match(data)
if matched_seek : if matched_seek :
self.exectue_seek_cmd(matched_seek.group(1), matched_seek.group(2), matched_seek.group(4)) self.__exectue_seek_cmd(matched_seek.group(1), matched_seek.group(2), matched_seek.group(4))
elif matched_room:
room = matched_room.group(2)
if room == None:
room = 'default'
self.protocol.send_room(room)
elif data == "r": elif data == "r":
self.counter += 1 self.counter += 1
tmp_pos = self.player_position tmp_pos = self.player_position
@ -328,12 +339,12 @@ class Manager(object):
if old_paused != paused and self.global_paused != paused: if old_paused != paused and self.global_paused != paused:
self.send_status(True) self.send_status(True)
if paused: if paused:
print "You have paused" print '%s paused' % self.name
if(diff > 0): if(diff > 0):
self.player.set_position(self.get_global_position()) self.player.set_position(self.get_global_position())
self.ask_player() self.ask_player()
else: else:
print "You have resumed" print '%s resumed' % self.name
if not (self.global_paused or self.seek_sent_wait): if not (self.global_paused or self.seek_sent_wait):
if (0.4 if self.player_speed_fix else 0.6) <= diff <= 4: if (0.4 if self.player_speed_fix else 0.6) <= diff <= 4:

View File

@ -43,13 +43,14 @@ class CommandProtocol(LineReceiver):
line = line.strip() line = line.strip()
if not line: if not line:
return return
#print '>>>', line
args = split_args(line) args = split_args(line)
if not args: if not args:
self.drop_with_error('Malformed line') self.drop_with_error('Malformed line')
return return
command = args.pop(0) command = args.pop(0)
if command not in ['ping', 'pong']:
print '>>>', line
if command == 'error': if command == 'error':
self.handle_error(args) self.handle_error(args)
return return
@ -61,7 +62,6 @@ class CommandProtocol(LineReceiver):
if not handler: if not handler:
self.drop_with_error('Unknown command: `%s`' % command) self.drop_with_error('Unknown command: `%s`' % command)
return # TODO log it too return # TODO log it too
handler(args) handler(args)
def handle_error(self, args): def handle_error(self, args):
@ -74,7 +74,8 @@ class CommandProtocol(LineReceiver):
def send_message(self, *args): def send_message(self, *args):
line = join_args(args) line = join_args(args)
#print '<<<', line if args[0] not in ['ping', 'pong']:
print '<<<', line
self.sendLine(line) self.sendLine(line)
def drop(self): def drop(self):
@ -143,7 +144,7 @@ class BodyGetter(Protocol):
if self.length > 0: if self.length > 0:
data = data[:self.length] data = data[:self.length]
self.body.write(data) self.body.write(data)
self.length -= len(length) self.length -= self.body.len #TODO: need fixing! chuj wie jak to działa :D
def connectionLost(self, reason): def connectionLost(self, reason):
self.callback(self.body.getvalue()) self.callback(self.body.getvalue())

View File

@ -17,7 +17,6 @@ from .utils import parse_state
random.seed() random.seed()
CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
RE_NAME = re.compile('^(.*?)(\d*)$')
def random_chars(): def random_chars():
return ''.join(random.choice(CHARS) for _ in xrange(10)) return ''.join(random.choice(CHARS) for _ in xrange(10))
@ -35,13 +34,18 @@ class SyncServerProtocol(CommandProtocol):
if not len(args) == 1: if not len(args) == 1:
self.drop_with_error('Invalid arguments') self.drop_with_error('Invalid arguments')
return return
name = args[0].strip() name = re.sub('[^\w]','',args[0])
if not args: if not name:
self.drop_with_error('Invalid nickname') self.drop_with_error('Invalid nickname')
return return
self.factory.add_watcher(self, args[0]) self.factory.add_watcher(self, name)
self.change_state('connected') self.change_state('connected')
@arg_count(1)
def handle_connected_room(self, args):
watcher = self.factory.watchers.get(self)
watcher.room = args[0]
@arg_count(4) @arg_count(4)
def handle_connected_state(self, args): def handle_connected_state(self, args):
args = parse_state(args) args = parse_state(args)
@ -52,7 +56,12 @@ class SyncServerProtocol(CommandProtocol):
counter, ctime, paused, position, _ = args counter, ctime, paused, position, _ = args
self.factory.update_state(self, counter, ctime, paused, position) self.factory.update_state(self, counter, ctime, paused, position)
@arg_count(0)
def handle_connected_list(self, args):
watcher = self.factory.watchers.get(self)
self.factory.broadcast(watcher, lambda receiver: self.send_present(receiver.name, receiver.filename))
@arg_count(3) @arg_count(3)
def handle_connected_seek(self, args): def handle_connected_seek(self, args):
counter, ctime, position = args counter, ctime, position = args
@ -130,6 +139,8 @@ class SyncServerProtocol(CommandProtocol):
iam = 'handle_init_iam', iam = 'handle_init_iam',
), ),
connected = dict( connected = dict(
room = 'handle_connected_room',
list = 'handle_connected_list',
state = 'handle_connected_state', state = 'handle_connected_state',
seek = 'handle_connected_seek', seek = 'handle_connected_seek',
pong = 'handle_connected_pong', pong = 'handle_connected_pong',
@ -152,6 +163,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.ping = None self.ping = None
self.time_offset = 0 self.time_offset = 0
self.time_offset_data = [] self.time_offset_data = []
@ -176,25 +188,16 @@ class SyncFactory(Factory):
return SyncServerProtocol(self) return SyncServerProtocol(self)
def add_watcher(self, watcher_proto, name): def add_watcher(self, watcher_proto, name):
allnames = (watcher.name.lower() for watcher in self.watchers.itervalues()) allnames = []
for watcher in self.watchers.itervalues():
allnames.append(watcher.name.lower())
while name.lower() in allnames: while name.lower() in allnames:
m = RE_NAME.match(name) name += '_'
name, number = m.group(1), m.group(2)
if number:
number = str(int(number)+1)
else:
number = '1'
name += number
watcher = WatcherInfo(watcher_proto, name) watcher = WatcherInfo(watcher_proto, name)
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
for receiver in self.watchers.itervalues():
if receiver == watcher:
continue
receiver.watcher_proto.send_joined(name)
watcher_proto.send_present(receiver.name, receiver.filename)
watcher_proto.send_hello(name) watcher_proto.send_hello(name)
self.send_state_to(watcher) self.send_state_to(watcher)
self.send_ping_to(watcher) self.send_ping_to(watcher)
@ -363,4 +366,15 @@ class SyncFactory(Factory):
for receiver in self.watchers.itervalues(): for receiver in self.watchers.itervalues():
if receiver != watcher: if receiver != watcher:
receiver.watcher_proto.send_playing(watcher.name, filename) receiver.watcher_proto.send_playing(watcher.name, filename)
def broadcast_room(self, sender, what):
for receiver in self.watchers.itervalues():
if receiver.room == sender.room and receiver != sender:
print receiver.room, sender.room
what(receiver)
def broadcast(self, sender, what):
for receiver in self.watchers.itervalues():
if receiver != sender:
for receiver in self.watchers.itervalues():
what(receiver)

View File

@ -85,3 +85,57 @@ def format_time(value):
hours, minutes = divmod(minutes, 60) hours, minutes = divmod(minutes, 60)
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 stdin_thread(manager):
try:
fd = sys.stdin.fileno()
while True:
data = os.read(fd, 1024)
if not data:
break
manager.execute_command(data.rstrip('\n\r'))
except:
pass
def get_configuration():
parser = argparse.ArgumentParser(description='Synchronize multiple players over the web.',
epilog='If no options supplied config values will be used')
parser.add_argument('host', metavar='host', type=str, nargs='?', help='server\'s address')
parser.add_argument('name', metavar='name', type=str, nargs='?', help='desired username')
parser.add_argument('args', metavar='opts', type=str, nargs='*', help='player options, if you need to pass options starting with - prepend them with single \'--\' argument')
args = parser.parse_args()
config = ConfigParser.RawConfigParser(allow_no_value=True)
config.read(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'syncplay.ini'))
try:
if(args.host == None):
host = config.get('sync', 'host')
else:
host = args.host
if(args.name == None):
name = config.get('sync', 'name')
else:
name = args.name
except ConfigParser.NoSectionError:
sys.exit("Host or username not specified")
with open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'syncplay.ini'), 'wb') as configfile:
try:
config.set('sync', 'host' ,host)
config.set('sync', 'name' ,name)
except ConfigParser.NoSectionError:
config.add_section('sync')
config.set('sync', 'host' ,host)
config.set('sync', 'name' ,name)
config.write(configfile)
if ':' in host:
host, port = host.split(':', 1)
port = int(port)
else:
port = 8999
return host,port,name,args.args