It works now

This commit is contained in:
Uriziel 2012-07-10 19:22:30 +02:00
parent 24f097c709
commit 177821e170
6 changed files with 234 additions and 218 deletions

View File

@ -1,178 +1,176 @@
#coding:utf8 #coding:utf8
import time from .network_utils import argumentCount, CommandProtocol
import re from .utils import ArgumentParser, format_time
import threading from syncplay import utils
from twisted.internet import reactor from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory from twisted.internet.protocol import ClientFactory
from syncplay import utils import re
from .network_utils import ( import threading
argumentCount, import time
CommandProtocol,
)
from .utils import format_time
class SyncClientProtocol(CommandProtocol): class SyncClientProtocol(CommandProtocol):
def __init__(self, manager): def __init__(self, syncplayClient):
CommandProtocol.__init__(self) self.syncplayClient = syncplayClient
self.manager = manager self.handler = self._MessagesHandler(syncplayClient)
self.sender = self._MessagesSender(self)
def connectionMade(self): def connectionMade(self):
self.send_message('iam', self.manager.name) self.sendMessage('iam', self.syncplayClient.name)
self.manager.init_protocol(self) self.syncplayClient.initProtocol(self)
def connectionLost(self, reason): def connectionLost(self, reason):
self.manager.protocol = None self.syncplayClient.destroyProtocol()
self.syncplayClient.ui.showDebugMessage("Connection lost, reason: %s" % reason)
def handle_error(self, args): def sendMessage(self, *args):
self.manager.stop() line = ArgumentParser.joinArguments(args)
CommandProtocol.handle_error(self, args) self.sendLine(line)
self.syncplayClient.ui.showDebugMessage('NETWORK:\t<<' + line)
@argumentCount(1)
def handle_init_hello(self, args): def dropWithError(self, error):
message ='Connected as ' + args[0] self.syncplayClient.ui.showErrorMessage(error)
print message CommandProtocol.dropWithError(self, error)
self.manager.name = args[0]
self.change_state('connected') def lineReceived(self, line):
self.send_list() self.syncplayClient.ui.showDebugMessage('NETWORK:\t>>%s' % line)
self.manager.schedule_send_status() CommandProtocol.lineReceived(self, line)
@argumentCount(2, 3) class _MessagesHandler(object):
def handle_connected_present(self, args): def __init__(self, syncplayClient):
if len(args) == 3: self._syncplayClient = syncplayClient
self._lastServerTimestamp = 0
@argumentCount(1)
def hello(self, args):
message ='Connected as ' + args[0]
print message
self._syncplayClient.name = args[0]
self._syncplayClient.protocol.sender.send_list()
self._syncplayClient.schedule_send_status()
@argumentCount(2, 3)
def present(self, args):
if len(args) == 3:
who, where, what = args
else:
who, where, what = args[0], args[1], None
if what:
message = '%s is present and is playing \'%s\' in the room: \'%s\'' % (who, what, where)
print message
if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
else:
message = '%s is present in the room: \'%s\'' % (who, where)
print message
if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
@argumentCount(4, 5)
def state(self, args):
args = self.__parseState(args)
if not args:
self.dropWithError('Malformed state attributes')
return
counter, ctime, paused, position, name = args
self._syncplayClient.update_global_state(counter, ctime, paused, position, name)
@argumentCount(3)
def seek(self, args):
ctime, position, who = args
try:
ctime = int(ctime)
position = int(position)
except ValueError:
self.dropWithError('Invalid arguments')
ctime /= 1000.0
position /= 1000.0
self._syncplayClient.seek(ctime, position, who)
@argumentCount(1)
def ping(self, args):
self._syncplayClient.protocol.sendMessage('pong', args[0], int(time.time()*100000))
@argumentCount(3)
def playing(self, args):
who, where, what = args who, where, what = args
else: message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where)
who, where, what = args[0], args[1], None
if what:
message = '%s is present and is playing \'%s\' in the room: \'%s\'' % (who, what, where)
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
else:
message = '%s is present in the room: \'%s\'' % (who, where) @argumentCount(1)
def joined(self, args):
message = '%s joined' % args[0]
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
@argumentCount(4, 5) @argumentCount(2)
def handle_connected_state(self, args): def room(self, args):
args = self.__parseState(args) message = '%s entered the room: \'%s\'' % (args[0], args[1])
if not args: print message
self.dropWithError('Malformed state attributes') if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
return
counter, ctime, paused, position, name = args
self.manager.update_global_state(counter, ctime, paused, position, name)
@argumentCount(3)
def handle_connected_seek(self, args):
ctime, position, who = args
try:
ctime = int(ctime)
position = int(position)
except ValueError:
self.dropWithError('Invalid arguments')
ctime /= 1000.0
position /= 1000.0
self.manager.seek(ctime, position, who)
@argumentCount(1)
def handle_connected_ping(self, args):
self.send_message('pong', args[0], int(time.time()*100000))
@argumentCount(3)
def handle_connected_playing(self, args):
who, where, what = args
message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where)
print message
if(self.manager.player): self.manager.player.display_message(message)
@argumentCount(1)
def handle_connected_joined(self, args):
message = '%s joined' % args[0]
print message
if(self.manager.player): self.manager.player.display_message(message)
@argumentCount(2) @argumentCount(1)
def handle_connected_room(self, args): def left(self, args):
message = '%s entered the room: \'%s\'' % (args[0], args[1]) message = '%s left' % args[0]
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
@argumentCount(1) def __parseState(self, args):
def handle_connected_left(self, args): if len(args) == 4:
message = '%s left' % args[0] counter, ctime, state, position = args
print message who_changed_state = None
if(self.manager.player): self.manager.player.display_message(message) elif len(args) == 5:
counter, ctime, state, position, who_changed_state = args
def send_list(self): else:
self.send_message('list') return
def send_state(self, counter, ctime, paused, position): if not state in ('paused', 'playing'):
self.send_message('state', counter, int(ctime*1000), ('paused' if paused else 'playing'), int(position*1000)) return
def send_seek(self, counter, ctime, position): paused = state == 'paused'
self.send_message('seek', counter, int(ctime*1000), int(position*1000))
try:
counter = int(counter)
ctime = int(ctime)
position = int(position)
except ValueError:
return
ctime /= 1000.0
position /= 1000.0
return counter, ctime, paused, position, who_changed_state
class _MessagesSender(object):
def __init__(self, protocol):
self._protocol = protocol
def send_list(self):
self._protocol.sendMessage('list')
def send_room(self, where): def send_state(self, counter, ctime, paused, position):
self.send_message('room', where) self._protocol.sendMessage('state', counter, int(ctime*1000), ('paused' if paused else 'playing'), int(position*1000))
def send_seek(self, counter, ctime, position):
self._protocol.sendMessage('seek', counter, int(ctime*1000), int(position*1000))
def send_room(self, where):
self._protocol.sendMessage('room', where)
def send_playing(self, filename):
self._protocol.sendMessage('playing', filename)
def send_playing(self, filename):
self.send_message('playing', filename)
states = dict(
init = dict(
hello = 'handle_init_hello',
),
connected = dict(
room = 'handle_connected_room',
present = 'handle_connected_present',
state = 'handle_connected_state',
seek = 'handle_connected_seek',
ping = 'handle_connected_ping',
playing = 'handle_connected_playing',
joined = 'handle_connected_joined',
left = 'handle_connected_left',
),
)
initial_state = 'init'
def __parseState(self, args):
if len(args) == 4:
counter, ctime, state, position = args
who_changed_state = None
elif len(args) == 5:
counter, ctime, state, position, who_changed_state = args
else:
return
if not state in ('paused', 'playing'):
return
paused = state == 'paused'
try:
counter = int(counter)
ctime = int(ctime)
position = int(position)
except ValueError:
return
ctime /= 1000.0
position /= 1000.0
return counter, ctime, paused, position, who_changed_state
class SyncClientFactory(ClientFactory): class SyncClientFactory(ClientFactory):
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self._syncplayClient = manager
self.retry = True self.retry = True
def buildProtocol(self, addr): def buildProtocol(self, addr):
return SyncClientProtocol(self.manager) return SyncClientProtocol(self._syncplayClient)
def startedConnecting(self, connector): def startedConnecting(self, connector):
destination = connector.getDestination() destination = connector.getDestination()
@ -185,25 +183,26 @@ class SyncClientFactory(ClientFactory):
else: else:
message = 'Disconnected' message = 'Disconnected'
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
message = 'Connection failed' message = 'Connection failed'
print message print message
if(self.manager.player): self.manager.player.display_message(message) if(self._syncplayClient.player): self._syncplayClient.player.display_message(message)
self.manager.stop() self._syncplayClient.stop()
def stop_retrying(self): def stop_retrying(self):
self.retry = False self.retry = False
class Manager(object): class Manager(object):
def __init__(self, host, port, name, make_player): def __init__(self, host, port, name, make_player, ui, debug):
self.host = host self.host = host
self.port = port self.port = port
self.name = name self.name = name
self.ui = self.UiManager(ui, debug)
self.protocol_factory = None self.protocol_factory = None
self.protocol = None self.protocol = None
self.send_delayed = None self.send_delayed = None
@ -280,12 +279,17 @@ class Manager(object):
self.player.set_paused(True) self.player.set_paused(True)
self.schedule_ask_player() self.schedule_ask_player()
def init_protocol(self, protocol): def initProtocol(self, protocol):
self.protocol = protocol self.protocol = protocol
if self.make_player: if self.make_player:
self.make_player(self) self.make_player(self)
self.make_player = None self.make_player = None
def destroyProtocol(self):
if self.protocol:
self.protocol.drop()
self.protocol = None
def schedule_ask_player(self, when=0.2): def schedule_ask_player(self, when=0.2):
if self.ask_delayed and self.ask_delayed.active(): if self.ask_delayed and self.ask_delayed.active():
self.ask_delayed.reset(when) self.ask_delayed.reset(when)
@ -318,20 +322,20 @@ class Manager(object):
if not self.player_paused: if not self.player_paused:
position += curtime - self.last_player_update position += curtime - self.last_player_update
if self.protocol: if self.protocol:
self.protocol.send_state(self.counter, curtime, self.player_paused, self.player_position) self.protocol.sender.send_state(self.counter, curtime, self.player_paused, self.player_position)
def send_seek(self): def send_seek(self):
if not (self.running and self.protocol): if not (self.running and self.protocol):
return return
self.counter += 10 self.counter += 10
self.protocol.send_seek(self.counter, time.time(), self.player_position) self.protocol.sender.send_seek(self.counter, time.time(), self.player_position)
message = self.name +' seeked to ' + format_time(self.player_position) message = self.name +' seeked to ' + format_time(self.player_position)
print message print message
self.player.display_message(message) self.player.display_message(message)
def send_filename(self): def send_filename(self):
if self.protocol and self.player_filename: if self.protocol and self.player_filename:
self.protocol.send_playing(self.player_filename) self.protocol.sender.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
@ -355,7 +359,7 @@ class Manager(object):
room = matched_room.group(2) room = matched_room.group(2)
if room == None: if room == None:
room = 'default' room = 'default'
self.protocol.send_room(room) self.protocol.sender.send_room(room)
elif data == "r": elif data == "r":
tmp_pos = self.player_position tmp_pos = self.player_position
self.player.set_position(self.player_position_before_last_seek) self.player.set_position(self.player_position_before_last_seek)
@ -482,6 +486,7 @@ class Manager(object):
if changed: if changed:
self.ask_player() self.ask_player()
def seek(self, ctime, position, who): def seek(self, ctime, position, who):
curtime = time.time() curtime = time.time()
position += curtime - ctime position += curtime - ctime
@ -495,5 +500,22 @@ class Manager(object):
print message print message
self.player.display_message(message) self.player.display_message(message)
class UiManager(object):
def __init__(self, ui, debug = False):
self.__ui = ui
self.debug = debug
def showMessage(self, message):
self.__ui.showMessage(message)
def showDebugMessage(self, message):
if(self.debug):
self.__ui.showDebugMessage(message)
def showErrorMessage(self, message):
self.__ui.showErrorMessage(message)
def displayListOfPeople(self):
pass

View File

@ -16,11 +16,10 @@ def argumentCount(minimum, maximum=None):
return wrapper return wrapper
return decorator return decorator
class CommandProtocol(LineReceiver): class CommandProtocol(LineReceiver):
states = None
def __init__(self): def __init__(self):
self._state = self.initial_state self.handler = None
self.sender = None
def lineReceived(self, line): def lineReceived(self, line):
line = line.strip() line = line.strip()
@ -32,40 +31,21 @@ class CommandProtocol(LineReceiver):
self.dropWithError('Malformed line') self.dropWithError('Malformed line')
return return
command = args.pop(0) command = args.pop(0)
#if command not in ['ping', 'pong']: handler = getattr(self.handler, command, None)
# print '>>>', line
if command == 'error':
self.handle_error(args)
return
available_commands = self.states.get(self._state)
handler = available_commands.get(command)
if handler:
handler = getattr(self, handler, None)
if not handler: if not handler:
self.dropWithError('Unknown command: `%s`' % command) self.dropWithError('Unknown command: `%s`' % command)
return # TODO log it too return
handler(args) handler(args)
def handle_error(self, args): def sendMessage(self, *args):
print 'Error received from other side:', args
def change_state(self, new_state):
if new_state not in self.states:
raise RuntimeError('Unknown state: %s' % new_state)
self._state = new_state
def send_message(self, *args):
line = ArgumentParser.joinArguments(args) line = ArgumentParser.joinArguments(args)
#if args[0] not in ['ping', 'pong']:
# print '<<<', line
self.sendLine(line) self.sendLine(line)
def drop(self): def drop(self):
self.transport.loseConnection() self.transport.loseConnection()
def dropWithError(self, error): def dropWithError(self, error):
self.send_message('error', error) self.sendMessage('error', error)
self.drop() self.drop()

View File

@ -5,7 +5,7 @@ import time
class MPCHCAPIPlayer(object): class MPCHCAPIPlayer(object):
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self._syncplayClient = manager
self.mpc_api = MPC_API() self.mpc_api = MPC_API()
self.pinged = False self.pinged = False
@ -34,7 +34,7 @@ class MPCHCAPIPlayer(object):
def make_ping(self): def make_ping(self):
self.mpc_api.callbacks.on_file_ready = None self.mpc_api.callbacks.on_file_ready = None
self.test_mpc_ready() self.test_mpc_ready()
self.manager.init_player(self) self._syncplayClient.init_player(self)
self.pinged = True self.pinged = True
self.ask_for_status() self.ask_for_status()
@ -83,9 +83,9 @@ class MPCHCAPIPlayer(object):
paused = self.mpc_api.is_paused() paused = self.mpc_api.is_paused()
position = float(position) position = float(position)
self.tmp_position = position self.tmp_position = position
self.manager.update_player_status(paused, position) self._syncplayClient.update_player_status(paused, position)
else: else:
self.manager.update_player_status(True, self.manager.get_global_position()) self._syncplayClient.update_player_status(True, self._syncplayClient.get_global_position())
except Exception, err: except Exception, err:
self.mpc_error(err) self.mpc_error(err)
@ -105,22 +105,22 @@ class MPCHCAPIPlayer(object):
self.__force_pause(filename, position) self.__force_pause(filename, position)
def handle_updated_filename(self, filename): def handle_updated_filename(self, filename):
position = self.manager.get_global_position() position = self._syncplayClient.get_global_position()
if(self.semaphore_filename): if(self.semaphore_filename):
self.manager.update_player_status(True, position) self._syncplayClient.update_player_status(True, position)
return return
self.semaphore_filename = True self.semaphore_filename = True
self.__set_up_newly_opened_file(filename, position) self.__set_up_newly_opened_file(filename, position)
self.tmp_filename = filename self.tmp_filename = filename
self.manager.update_filename(str(self.tmp_filename)) self._syncplayClient.update_filename(str(self.tmp_filename))
self.manager.update_player_status(True, position) self._syncplayClient.update_player_status(True, position)
self.semaphore_filename = False self.semaphore_filename = False
def mpc_error(self, err=""): def mpc_error(self, err=""):
print "ERROR:", str(err) + ',', "desu" print "ERROR:", str(err) + ',', "desu"
if self.manager.running: if self._syncplayClient.running:
print 'Failed to connect to MPC-HC API!' print 'Failed to connect to MPC-HC API!'
self.manager.stop() self._syncplayClient.stop()
def run_mpc(manager, mpc_path, file_path, args): def run_mpc(manager, mpc_path, file_path, args):
mpc = MPCHCAPIPlayer(manager) mpc = MPCHCAPIPlayer(manager)

View File

@ -43,7 +43,7 @@ class MplayerProtocol(LineProcessProtocol):
speed_supported = True speed_supported = True
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self._syncplayClient = manager
self.ignore_end = False self.ignore_end = False
self.error_lines = deque(maxlen=50) self.error_lines = deque(maxlen=50)
self.tmp_paused = None self.tmp_paused = None
@ -52,7 +52,7 @@ class MplayerProtocol(LineProcessProtocol):
reactor.callLater(0.1, self.prepare_player) reactor.callLater(0.1, self.prepare_player)
def processEnded(self, reason): def processEnded(self, reason):
self.manager.player = None self._syncplayClient.player = None
if not self.ignore_end: if not self.ignore_end:
if reason.value.signal is not None: if reason.value.signal is not None:
print 'Mplayer interrupted by signal %d.' % reason.value.signal print 'Mplayer interrupted by signal %d.' % reason.value.signal
@ -64,7 +64,7 @@ class MplayerProtocol(LineProcessProtocol):
print 'Up to 50 last lines from its error output below:' print 'Up to 50 last lines from its error output below:'
for line in self.error_lines: for line in self.error_lines:
print line print line
self.manager.stop() self._syncplayClient.stop()
def errLineReceived(self, line): def errLineReceived(self, line):
if line: if line:
@ -106,8 +106,8 @@ class MplayerProtocol(LineProcessProtocol):
self.send_get_property('filename') self.send_get_property('filename')
def mplayer_answer_filename(self, value): def mplayer_answer_filename(self, value):
self.manager.init_player(self) self._syncplayClient.init_player(self)
self.manager.update_filename(value) self._syncplayClient.update_filename(value)
def set_paused(self, value): def set_paused(self, value):
@ -130,7 +130,7 @@ class MplayerProtocol(LineProcessProtocol):
def mplayer_answer_time_pos(self, value): def mplayer_answer_time_pos(self, value):
value = float(value) value = float(value)
self.manager.update_player_status(self.tmp_paused, value) self._syncplayClient.update_player_status(self.tmp_paused, value)
def set_speed(self, value): def set_speed(self, value):
@ -141,7 +141,7 @@ class MplayerProtocol(LineProcessProtocol):
#def mplayer_answer_speed(self, value): #def mplayer_answer_speed(self, value):
# value = float(value) # value = float(value)
# self.manager.update_player_speed(value) # self._syncplayClient.update_player_speed(value)
def drop(self): def drop(self):

View File

@ -43,3 +43,8 @@ class ConsoleUI(threading.Thread):
def showMessage(self, message): def showMessage(self, message):
print(message) print(message)
def showDebugMessage(self, message):
print(message)
def showErrorMessage(self, message):
print(message)

View File

@ -1,21 +1,30 @@
#coding:utf8 #coding:utf8
from syncplay import client from syncplay import client
from syncplay.players import mpc from syncplay.players import mpc
from syncplay import ui
from syncplay import utils from syncplay import utils
class SyncplayMPC:
def prepareArguments(): def runClient(self):
args = utils.MPCConfigurationGetter() self._prepareArguments()
args.prepareClientConfiguration() interface = ui.getUi(graphical = not self.args.no_gui)
return args.getClientConfiguration() self._promptForMissingArguments()
manager = client.Manager(self.args.host, self.args.port, self.args.name, lambda m: mpc.run_mpc(m, self.args.mpc_path, self.args.file, self.args._args), interface, self.args.debug)
manager.start()
def _prepareArguments(self):
args = utils.MPCConfigurationGetter()
args.prepareClientConfiguration()
self.args = args.getClientConfiguration()
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.")
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.")
if (self.args.mpc_path == None):
self.args.mpc_path = self.interface.promptFor(promptName = "Path to mpc-hc.exe", message = "You must supply path to mpc on the first run, it's easier trough command line arguments.")
if __name__ == '__main__': if __name__ == '__main__':
manager = None SyncplayMPC().runClient()
try:
args = prepareArguments()
manager = client.Manager(args.host, args.port, args.name, lambda m: mpc.run_mpc(m, args.mpc_path, args.file, args._args))
manager.start()
finally:
if(manager): manager.stop()