Changes in protocol
* Escape characters and allow more than one string with spaces * Time is now in miliseconds * More proper error handling (though argument count checking need improvement)
This commit is contained in:
parent
2b472fa802
commit
671eb79c08
@ -22,21 +22,31 @@ class SyncClientProtocol(CommandProtocol):
|
|||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self.manager.protocol = None
|
self.manager.protocol = None
|
||||||
|
|
||||||
def handle_connected_state(self, arg):
|
def handle_error(self, args):
|
||||||
arg = parse_state(arg)
|
self.manager.stop()
|
||||||
if not arg:
|
CommandProtocol.handle_error(self, args)
|
||||||
|
|
||||||
|
def handle_connected_state(self, args):
|
||||||
|
args = parse_state(args)
|
||||||
|
if not args:
|
||||||
self.drop_with_error('Malformed state attributes')
|
self.drop_with_error('Malformed state attributes')
|
||||||
return
|
return
|
||||||
|
|
||||||
counter, paused, position, name = arg
|
counter, paused, position, name = args
|
||||||
|
|
||||||
self.manager.update_global_state(counter, paused, position, name)
|
self.manager.update_global_state(counter, paused, position, name)
|
||||||
|
|
||||||
def handle_connected_ping(self, arg):
|
def handle_connected_ping(self, args):
|
||||||
self.send_message('pong', arg)
|
if not len(args) == 1:
|
||||||
|
self.drop_with_error('Invalid arguments')
|
||||||
|
return
|
||||||
|
self.send_message('pong', args[0])
|
||||||
|
|
||||||
def send_state(self, counter, paused, position):
|
def send_state(self, counter, paused, position):
|
||||||
self.send_message('state', counter, ('paused' if paused else 'playing'), int(position*100))
|
self.send_message('state', counter, ('paused' if paused else 'playing'), int(position*1000))
|
||||||
|
|
||||||
|
def send_playing(self, filename):
|
||||||
|
self.send_message('playing', filename)
|
||||||
|
|
||||||
|
|
||||||
states = dict(
|
states = dict(
|
||||||
@ -44,6 +54,7 @@ class SyncClientProtocol(CommandProtocol):
|
|||||||
state = 'handle_connected_state',
|
state = 'handle_connected_state',
|
||||||
seek = 'handle_connected_seek',
|
seek = 'handle_connected_seek',
|
||||||
ping = 'handle_connected_ping',
|
ping = 'handle_connected_ping',
|
||||||
|
playing = 'handle_connected_playing',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
initial_state = 'connected'
|
initial_state = 'connected'
|
||||||
@ -63,7 +74,7 @@ class SyncClientFactory(ClientFactory):
|
|||||||
def clientConnectionLost(self, connector, reason):
|
def clientConnectionLost(self, connector, reason):
|
||||||
if self.retry:
|
if self.retry:
|
||||||
print 'Connection lost, reconnecting'
|
print 'Connection lost, reconnecting'
|
||||||
connector.connect()
|
reactor.callLater(0.1, connector.connect)
|
||||||
else:
|
else:
|
||||||
print 'Disconnected'
|
print 'Disconnected'
|
||||||
|
|
||||||
@ -150,7 +161,9 @@ class Manager(object):
|
|||||||
def init_protocol(self, protocol):
|
def init_protocol(self, protocol):
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.schedule_send_status()
|
self.schedule_send_status()
|
||||||
self.make_player(self)
|
self.send_filename()
|
||||||
|
if self.player is None:
|
||||||
|
self.make_player(self)
|
||||||
|
|
||||||
|
|
||||||
def schedule_ask_player(self, when=0.2):
|
def schedule_ask_player(self, when=0.2):
|
||||||
@ -181,6 +194,10 @@ class Manager(object):
|
|||||||
self.protocol.send_state(self.counter, self.player_paused, self.player_position)
|
self.protocol.send_state(self.counter, self.player_paused, self.player_position)
|
||||||
self.schedule_send_status()
|
self.schedule_send_status()
|
||||||
|
|
||||||
|
def send_filename(self):
|
||||||
|
if self.protocol and self.player_filename:
|
||||||
|
self.protocol.send_playing(self.player_filename)
|
||||||
|
|
||||||
|
|
||||||
def update_player_status(self, paused, position):
|
def update_player_status(self, paused, position):
|
||||||
old_paused = self.player_paused
|
old_paused = self.player_paused
|
||||||
@ -215,6 +232,7 @@ class Manager(object):
|
|||||||
|
|
||||||
def update_filename(self, filename):
|
def update_filename(self, filename):
|
||||||
self.player_filename = filename
|
self.player_filename = filename
|
||||||
|
self.send_filename()
|
||||||
|
|
||||||
def update_global_state(self, counter, paused, position, name):
|
def update_global_state(self, counter, paused, position, name):
|
||||||
curtime = time.time()
|
curtime = time.time()
|
||||||
|
|||||||
@ -15,6 +15,11 @@ from twisted.web.iweb import IBodyProducer
|
|||||||
|
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from .utils import (
|
||||||
|
join_args,
|
||||||
|
split_args,
|
||||||
|
)
|
||||||
|
|
||||||
class CommandProtocol(LineReceiver):
|
class CommandProtocol(LineReceiver):
|
||||||
states = None
|
states = None
|
||||||
|
|
||||||
@ -25,11 +30,16 @@ class CommandProtocol(LineReceiver):
|
|||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
return
|
return
|
||||||
line = line.split(None, 1)
|
#print '>>>', line
|
||||||
if len(line) != 2:
|
args = split_args(line)
|
||||||
|
if not args:
|
||||||
self.drop_with_error('Malformed line')
|
self.drop_with_error('Malformed line')
|
||||||
return
|
return
|
||||||
command, arg = line
|
command = args.pop(0)
|
||||||
|
|
||||||
|
if command == 'error':
|
||||||
|
self.handle_error(args)
|
||||||
|
return
|
||||||
|
|
||||||
available_commands = self.states.get(self._state)
|
available_commands = self.states.get(self._state)
|
||||||
handler = available_commands.get(command)
|
handler = available_commands.get(command)
|
||||||
@ -39,7 +49,10 @@ class CommandProtocol(LineReceiver):
|
|||||||
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(arg)
|
handler(args)
|
||||||
|
|
||||||
|
def handle_error(self, args):
|
||||||
|
print 'Error received from other side:', args
|
||||||
|
|
||||||
def change_state(self, new_state):
|
def change_state(self, new_state):
|
||||||
if new_state not in self.states:
|
if new_state not in self.states:
|
||||||
@ -47,11 +60,9 @@ class CommandProtocol(LineReceiver):
|
|||||||
self._state = new_state
|
self._state = new_state
|
||||||
|
|
||||||
def send_message(self, *args):
|
def send_message(self, *args):
|
||||||
self.sendLine(' '.join(
|
line = join_args(args)
|
||||||
(arg if isinstance(arg, basestring) else str(arg))
|
#print '<<<', line
|
||||||
for arg in args
|
self.sendLine(line)
|
||||||
if arg is not None
|
|
||||||
))
|
|
||||||
|
|
||||||
def drop(self):
|
def drop(self):
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|||||||
@ -25,32 +25,48 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self.factory.remove_watcher(self)
|
self.factory.remove_watcher(self)
|
||||||
|
|
||||||
def handle_init_iam(self, arg):
|
def handle_init_iam(self, args):
|
||||||
self.factory.add_watcher(self, arg.strip())
|
if not len(args) == 1:
|
||||||
|
self.drop_with_error('Invalid arguments')
|
||||||
|
return
|
||||||
|
self.factory.add_watcher(self, args[0])
|
||||||
self.change_state('connected')
|
self.change_state('connected')
|
||||||
|
|
||||||
def handle_connected_state(self, arg):
|
def handle_connected_state(self, args):
|
||||||
arg = parse_state(arg)
|
args = parse_state(args)
|
||||||
if not arg:
|
if not args:
|
||||||
self.drop_with_error('Malformed state attributes')
|
self.drop_with_error('Malformed state attributes')
|
||||||
return
|
return
|
||||||
|
|
||||||
counter, paused, position, _ = arg
|
counter, paused, position, _ = args
|
||||||
|
|
||||||
self.factory.update_state(self, counter, paused, position)
|
self.factory.update_state(self, counter, paused, position)
|
||||||
|
|
||||||
def handle_connected_seek(self, arg):
|
def handle_connected_seek(self, args):
|
||||||
|
if not len(args) == 1:
|
||||||
|
self.drop_with_error('Invalid arguments')
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
position = int(arg)
|
position = int(args[0])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.drop_with_error('Invalid position numeral')
|
self.drop_with_error('Invalid position numeral')
|
||||||
|
|
||||||
position /= 100.0
|
position /= 1000.0
|
||||||
|
|
||||||
self.factory.seek(self, position)
|
self.factory.seek(self, position)
|
||||||
|
|
||||||
def handle_connected_pong(self, arg):
|
def handle_connected_pong(self, args):
|
||||||
self.factory.pong_received(self, arg)
|
if not len(args) == 1:
|
||||||
|
self.drop_with_error('Invalid arguments')
|
||||||
|
return
|
||||||
|
self.factory.pong_received(self, args[0])
|
||||||
|
|
||||||
|
def handle_connected_playing(self, args):
|
||||||
|
if not len(args) == 1:
|
||||||
|
self.drop_with_error('Invalid arguments')
|
||||||
|
return
|
||||||
|
#self.factory.pong_received(self, args[0])
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash('|'.join((
|
return hash('|'.join((
|
||||||
@ -60,10 +76,15 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
|
|
||||||
|
|
||||||
def send_state(self, counter, paused, position, who_last_changed):
|
def send_state(self, counter, paused, position, who_last_changed):
|
||||||
self.send_message('state', counter, ('paused' if paused else 'playing'), int(position*100), who_last_changed)
|
paused = 'paused' if paused else 'playing'
|
||||||
|
position = int(position*1000)
|
||||||
|
if who_last_changed is None:
|
||||||
|
self.send_message('state', counter, paused, position)
|
||||||
|
else:
|
||||||
|
self.send_message('state', counter, paused, position, who_last_changed)
|
||||||
|
|
||||||
def send_seek(self, position, who_seeked):
|
def send_seek(self, position, who_seeked):
|
||||||
self.send_message('seek', int(position*100), who_seeked)
|
self.send_message('seek', int(position*1000), who_seeked)
|
||||||
|
|
||||||
def send_ping(self, value):
|
def send_ping(self, value):
|
||||||
self.send_message('ping', value)
|
self.send_message('ping', value)
|
||||||
@ -77,6 +98,7 @@ class SyncServerProtocol(CommandProtocol):
|
|||||||
state = 'handle_connected_state',
|
state = 'handle_connected_state',
|
||||||
seek = 'handle_connected_seek',
|
seek = 'handle_connected_seek',
|
||||||
pong = 'handle_connected_pong',
|
pong = 'handle_connected_pong',
|
||||||
|
playing = 'handle_connected_playing',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
initial_state = 'init'
|
initial_state = 'init'
|
||||||
|
|||||||
@ -1,19 +1,55 @@
|
|||||||
#coding:utf8
|
#coding:utf8
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
RE_ARG = re.compile(r"('(?:[^\\']+|\\\\|\\')*'|[^\s']+)(?:\s+|\Z)")
|
||||||
|
RE_NEED_QUOTING = re.compile(r"[\s'\\]")
|
||||||
|
RE_QUOTABLE = re.compile(r"['\\]")
|
||||||
|
RE_UNQUOTABLE = re.compile(r"\\(['\\])")
|
||||||
|
|
||||||
|
class InvalidArgumentException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def quote_arg(arg):
|
||||||
|
if isinstance(arg, unicode):
|
||||||
|
arg = arg.encode('utf8')
|
||||||
|
elif not isinstance(arg, str):
|
||||||
|
arg = str(arg)
|
||||||
|
|
||||||
|
if not arg or RE_NEED_QUOTING.search(arg):
|
||||||
|
return "'%s'" % RE_QUOTABLE.sub(r'\\\g<0>', arg)
|
||||||
|
return arg
|
||||||
|
|
||||||
|
def unqote_arg(arg):
|
||||||
|
if arg.startswith("'") and len(arg) > 1:
|
||||||
|
arg = RE_UNQUOTABLE.sub(r'\1', arg[1:-1])
|
||||||
|
return arg.decode('utf8', 'replace')
|
||||||
|
|
||||||
|
def _split_args(args):
|
||||||
|
pos = 0
|
||||||
|
while pos < len(args):
|
||||||
|
match = RE_ARG.match(args, pos)
|
||||||
|
if not match:
|
||||||
|
raise InvalidArgumentException()
|
||||||
|
pos = match.end()
|
||||||
|
yield unqote_arg(match.group(1))
|
||||||
|
|
||||||
|
def split_args(args):
|
||||||
|
try:
|
||||||
|
return list(_split_args(args))
|
||||||
|
except InvalidArgumentException:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def join_args(args):
|
||||||
|
return ' '.join(quote_arg(arg) for arg in args)
|
||||||
|
|
||||||
def split_args(args, number):
|
|
||||||
# FIXME Make argument format smarter
|
|
||||||
return args.split(None, number-1)
|
|
||||||
|
|
||||||
def parse_state(args):
|
def parse_state(args):
|
||||||
args = split_args(args, 4)
|
if len(args) == 3:
|
||||||
l = len(args)
|
|
||||||
if l == 3:
|
|
||||||
counter, state, position = args
|
counter, state, position = args
|
||||||
who_changed_state = None
|
who_changed_state = None
|
||||||
elif l == 4:
|
elif len(args) == 4:
|
||||||
counter, state, position, who_changed_state = args
|
counter, state, position, who_changed_state = args
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -33,7 +69,7 @@ def parse_state(args):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
position /= 100.0
|
position /= 1000.0
|
||||||
|
|
||||||
return counter, paused, position, who_changed_state
|
return counter, paused, position, who_changed_state
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user