diff --git a/common_functions.py b/common_functions.py deleted file mode 100644 index 566ce7b..0000000 --- a/common_functions.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/sync_mpc.py b/sync_mpc.py index cc54234..8ad8696 100755 --- a/sync_mpc.py +++ b/sync_mpc.py @@ -1,16 +1,14 @@ #coding:utf8 import thread -import sys from twisted.internet import reactor from syncplay import client from syncplay.players import mpc - -import common_functions +from syncplay import utils 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)) - thread.start_new_thread(common_functions.stdin_thread, (manager,)) + thread.start_new_thread(utils.stdin_thread, (manager,)) manager.start() diff --git a/sync_mplayer.py b/sync_mplayer.py index f4ca0d4..e61ef69 100755 --- a/sync_mplayer.py +++ b/sync_mplayer.py @@ -6,13 +6,12 @@ from twisted.internet import reactor from syncplay import client from syncplay.players import mplayer - -import common_functions +from syncplay import utils 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')) 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() diff --git a/syncplay/client.py b/syncplay/client.py index 11ad84c..106b08c 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -20,7 +20,7 @@ class SyncClientProtocol(CommandProtocol): def __init__(self, manager): CommandProtocol.__init__(self) self.manager = manager - + def connectionMade(self): self.send_message('iam', self.manager.name) self.manager.init_protocol(self) @@ -35,10 +35,13 @@ class SyncClientProtocol(CommandProtocol): @arg_count(1) def handle_init_hello(self, args): print 'Connected as', args[0] + self.manager.name = args[0] self.change_state('connected') + self.send_list() + self.manager.schedule_send_status() @arg_count(1, 2) - def handle_init_present(self, args): + def handle_connected_present(self, args): if len(args) == 2: who, what = args else: @@ -90,6 +93,8 @@ class SyncClientProtocol(CommandProtocol): def handle_connected_left(self, args): print '%s left' % args[0] + def send_list(self): + self.send_message('list') def send_state(self, counter, ctime, paused, position): 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( init = dict( - present = 'handle_init_present', hello = 'handle_init_hello', ), connected = dict( + list = 'handle_connected_list', + present = 'handle_connected_present', state = 'handle_connected_state', seek = 'handle_connected_seek', ping = 'handle_connected_ping', @@ -225,8 +231,6 @@ class Manager(object): def init_protocol(self, protocol): self.protocol = protocol - self.schedule_send_status() - self.send_filename() if self.make_player: self.make_player(self) self.make_player = None @@ -278,7 +282,7 @@ class Manager(object): if self.protocol and 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 if seek_type == 's': @@ -302,9 +306,16 @@ class Manager(object): def execute_command(self, data): RE_SEEK = re.compile("^(s[+s]?) ?(-?\d+)?([^0-9](\d+))?$") + RE_ROOM = re.compile("^room( (\w+))?") matched_seek = RE_SEEK.match(data) + matched_room = RE_ROOM.match(data) 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": self.counter += 1 tmp_pos = self.player_position @@ -328,12 +339,12 @@ class Manager(object): if old_paused != paused and self.global_paused != paused: self.send_status(True) if paused: - print "You have paused" + print '%s paused' % self.name if(diff > 0): self.player.set_position(self.get_global_position()) self.ask_player() else: - print "You have resumed" + print '%s resumed' % self.name if not (self.global_paused or self.seek_sent_wait): if (0.4 if self.player_speed_fix else 0.6) <= diff <= 4: diff --git a/syncplay/network_utils.py b/syncplay/network_utils.py index 08da0f7..db73ec9 100644 --- a/syncplay/network_utils.py +++ b/syncplay/network_utils.py @@ -43,13 +43,14 @@ class CommandProtocol(LineReceiver): line = line.strip() if not line: return - #print '>>>', line + args = split_args(line) if not args: self.drop_with_error('Malformed line') return command = args.pop(0) - + if command not in ['ping', 'pong']: + print '>>>', line if command == 'error': self.handle_error(args) return @@ -61,7 +62,6 @@ class CommandProtocol(LineReceiver): if not handler: self.drop_with_error('Unknown command: `%s`' % command) return # TODO log it too - handler(args) def handle_error(self, args): @@ -74,7 +74,8 @@ class CommandProtocol(LineReceiver): def send_message(self, *args): line = join_args(args) - #print '<<<', line + if args[0] not in ['ping', 'pong']: + print '<<<', line self.sendLine(line) def drop(self): @@ -143,7 +144,7 @@ class BodyGetter(Protocol): if self.length > 0: data = data[:self.length] 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): self.callback(self.body.getvalue()) diff --git a/syncplay/server.py b/syncplay/server.py index 9ebef26..da59edc 100644 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -17,7 +17,6 @@ from .utils import parse_state random.seed() CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' -RE_NAME = re.compile('^(.*?)(\d*)$') def random_chars(): return ''.join(random.choice(CHARS) for _ in xrange(10)) @@ -35,13 +34,18 @@ class SyncServerProtocol(CommandProtocol): if not len(args) == 1: self.drop_with_error('Invalid arguments') return - name = args[0].strip() - if not args: + name = re.sub('[^\w]','',args[0]) + if not name: self.drop_with_error('Invalid nickname') return - self.factory.add_watcher(self, args[0]) + self.factory.add_watcher(self, name) 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) def handle_connected_state(self, args): args = parse_state(args) @@ -52,7 +56,12 @@ class SyncServerProtocol(CommandProtocol): counter, ctime, paused, position, _ = args 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) def handle_connected_seek(self, args): counter, ctime, position = args @@ -130,6 +139,8 @@ class SyncServerProtocol(CommandProtocol): iam = 'handle_init_iam', ), connected = dict( + room = 'handle_connected_room', + list = 'handle_connected_list', state = 'handle_connected_state', seek = 'handle_connected_seek', pong = 'handle_connected_pong', @@ -152,6 +163,7 @@ class WatcherInfo(object): self.last_update = None self.last_update_sent = None + self.room = 'default' self.ping = None self.time_offset = 0 self.time_offset_data = [] @@ -176,25 +188,16 @@ class SyncFactory(Factory): return SyncServerProtocol(self) 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: - m = RE_NAME.match(name) - name, number = m.group(1), m.group(2) - if number: - number = str(int(number)+1) - else: - number = '1' - name += number - + name += '_' + watcher = WatcherInfo(watcher_proto, name) if self.watchers: watcher.max_position = min(w.max_position for w in self.watchers.itervalues()) 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) self.send_state_to(watcher) self.send_ping_to(watcher) @@ -363,4 +366,15 @@ class SyncFactory(Factory): for receiver in self.watchers.itervalues(): if receiver != watcher: 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) diff --git a/syncplay/utils.py b/syncplay/utils.py index c804929..c40e13d 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -85,3 +85,57 @@ def format_time(value): hours, minutes = divmod(minutes, 60) 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