diff --git a/syncplay/client.py b/syncplay/client.py index 284c156..1190566 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -4,8 +4,8 @@ from .network_utils import argumentCount, CommandProtocol from .utils import ArgumentParser, format_time from twisted.internet import reactor from twisted.internet.protocol import ClientFactory -import re import time +import itertools class SyncClientProtocol(CommandProtocol): def __init__(self, syncplayClient): @@ -14,7 +14,7 @@ class SyncClientProtocol(CommandProtocol): self.sender = self._MessagesSender(self) def connectionMade(self): - self.sendMessage('iam', self.syncplayClient.name) + self.sendMessage('iam', self.syncplayClient.users.currentUser.name) self.syncplayClient.initProtocol(self) def connectionLost(self, reason): @@ -36,16 +36,16 @@ class SyncClientProtocol(CommandProtocol): class _MessagesHandler(object): def __init__(self, syncplayClient): - self._syncplayClient = syncplayClient + 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.scheduleSendStatus() + self.__syncplayClient.ui.showMessage(message) + self.__syncplayClient.users.currentUser.name = args[0] + self.__syncplayClient.protocol.sender.send_list() + self.__syncplayClient.scheduleSendStatus() @argumentCount(2, 3) def present(self, args): @@ -53,14 +53,14 @@ class SyncClientProtocol(CommandProtocol): who, where, what = args else: who, where, what = args[0], args[1], None + self.__syncplayClient.users.addUser(SyncplayClient.SyncplayUser(who, what, where)) 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) + self.__syncplayClient.ui.showMessage(message) + self.__syncplayClient.checkIfFileMatchesOthers() else: message = '%s is present in the room: \'%s\'' % (who, where) - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) + self.__syncplayClient.ui.showMessage(message) @argumentCount(4, 5) def state(self, args): @@ -68,10 +68,8 @@ class SyncClientProtocol(CommandProtocol): if not args: self.dropWithError('Malformed state attributes') return - counter, ctime, paused, position, name = args - - self._syncplayClient.updateGlobalState(counter, ctime, paused, position, name) + self.__syncplayClient.updateGlobalState(counter, ctime, paused, position, name) @argumentCount(3) def seek(self, args): @@ -81,41 +79,41 @@ class SyncClientProtocol(CommandProtocol): position = int(position) except ValueError: self.dropWithError('Invalid arguments') - ctime /= 1000.0 position /= 1000.0 - - self._syncplayClient.seek(ctime, position, who) + self.__syncplayClient.seek(ctime, position, who) @argumentCount(1) def ping(self, args): - self._syncplayClient.protocol.sendMessage('pong', args[0], int(time.time()*100000)) + self.__syncplayClient.protocol.sendMessage('pong', args[0], int(time.time()*100000)) @argumentCount(3) def playing(self, args): who, where, what = args message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where) - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) + self.__syncplayClient.ui.showMessage(message) + self.__syncplayClient.users.setUsersRoom(who, where) + self.__syncplayClient.users.setUsersFilename(who, what) + self.__syncplayClient.checkIfFileMatchesOthers() @argumentCount(1) def joined(self, args): message = '%s joined' % args[0] - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) + self.__syncplayClient.ui.showMessage(message) @argumentCount(2) def room(self, args): message = '%s entered the room: \'%s\'' % (args[0], args[1]) - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) + self.__syncplayClient.users.setUsersRoom(args[0], args[1]) + self.__syncplayClient.checkIfFileMatchesOthers() + self.__syncplayClient.ui.showMessage(message) @argumentCount(1) def left(self, args): message = '%s left' % args[0] - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) - + self.__syncplayClient.ui.showMessage(message) + self.__syncplayClient.users.removeUser(args[0]) + def __parseState(self, args): if len(args) == 4: counter, ctime, state, position = args @@ -163,40 +161,39 @@ class SyncClientProtocol(CommandProtocol): class SyncClientFactory(ClientFactory): def __init__(self, manager): - self._syncplayClient = manager + self.__syncplayClient = manager self.retry = True def buildProtocol(self, addr): - return SyncClientProtocol(self._syncplayClient) + return SyncClientProtocol(self.__syncplayClient) def startedConnecting(self, connector): destination = connector.getDestination() - print 'Connecting to %s:%d' % (destination.host, destination.port) + self.__syncplayClient.ui.showMessage('Connecting to %s:%d' % (destination.host, destination.port)) def clientConnectionLost(self, connector, reason): if self.retry: - print 'Connection lost, reconnecting' + message = 'Connection lost, reconnecting' + self.__syncplayClient.ui.showMessage('Connection lost, reconnecting') reactor.callLater(0.1, connector.connect) else: message = 'Disconnected' - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) + self.__syncplayClient.ui.showMessage(message) def clientConnectionFailed(self, connector, reason): message = 'Connection failed' - print message - if(self._syncplayClient.player): self._syncplayClient.player.display_message(message) - - self._syncplayClient.stop() + self.__syncplayClient.ui.showMessage(message) + self.__syncplayClient.stop() def stop_retrying(self): self.retry = False -class SyncplayClientManager(object): +class SyncplayClient(object): def __init__(self, name, make_player, ui, debug): - self.name = name - - self.ui = self.UiManager(ui, debug) + self.users = self.UserList() + self.users.currentUser.name = name + self.users.currentUser.room = 'default' + self.ui = self.UiManager(self, ui, debug) self.protocol_factory = None self.protocol = None self.send_delayed = None @@ -215,7 +212,6 @@ class SyncplayClientManager(object): self.player_position = 0.0 self.last_player_update = None self.player_speed_fix = False - self.player_filename = None self.player_position_before_last_seek = 0 self.seek_sent_wait = False @@ -228,6 +224,9 @@ class SyncplayClientManager(object): def start(self, host, port): if self.running: return + if self.make_player: + self.make_player(self) + self.make_player = None self.protocol_factory = SyncClientFactory(self) reactor.connectTCP(host, port, self.protocol_factory) self.running = True @@ -245,6 +244,12 @@ class SyncplayClientManager(object): self.player.drop() reactor.callLater(0.1, reactor.stop) + def checkIfFileMatchesOthers(self): + notMatchingList = self.users.getUsersWithNotMatchingFilenames() + if (notMatchingList <> []): + for u in notMatchingList: + message = "File you're playing is different from %s's" % u.name + self.ui.showMessage(message) def getPlayerPosition(self): if not self.last_player_update: @@ -262,7 +267,6 @@ class SyncplayClientManager(object): position += time.time() - self.last_global_update return position - def initPlayer(self, player): self.player = player if self.last_global_update: @@ -272,16 +276,13 @@ class SyncplayClientManager(object): def initProtocol(self, protocol): self.protocol = protocol - if self.make_player: - self.make_player(self) - self.make_player = None def destroyProtocol(self): if self.protocol: self.protocol.drop() self.protocol = None - def scheduleAskPlayer(self, when=0.2): + def scheduleAskPlayer(self, when=0.3): if self.ask_delayed and self.ask_delayed.active(): self.ask_delayed.reset(when) else: @@ -320,52 +321,13 @@ class SyncplayClientManager(object): return self.counter += 10 self.protocol.sender.sendSeek(self.counter, time.time(), self.player_position) - message = self.name +' seeked to ' + format_time(self.player_position) - print message - self.player.display_message(message) + message = self.users.currentUser.name +' jumped to ' + format_time(self.player_position) + self.ui.showMessage(message) def sendFilename(self): - if self.protocol and self.player_filename: - self.protocol.sender.send_playing(self.player_filename) + if self.protocol and self.users.currentUser.filename: + self.protocol.sender.send_playing(self.users.currentUser.filename) - def __exectueSeekCmd(self, seek_type, minutes, seconds): - self.player_position_before_last_seek = self.player_position - if seek_type == 's': - seconds = int(seconds) if seconds <> None else 0 - seconds += int(minutes) * 60 if minutes <> None else 0 - self.player.set_position(seconds) - else: #seek_type s+ - seconds = int(seconds) if seconds <> None else 20 - seconds += int(minutes) * 60 if minutes <> None else 60 - self.player.set_position(self.player_position+seconds) - - def executeCommand(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.__exectueSeekCmd(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.sender.send_room(room) - elif data == "r": - tmp_pos = self.player_position - self.player.set_position(self.player_position_before_last_seek) - self.player_position_before_last_seek = tmp_pos - elif data == "p": - self.player.set_paused(not self.player_paused) - elif data == 'help': - print "Available commands:" - print "\thelp - this help" - print "\ts [time] - seek" - print "\ts+ [time] - seek to: current position += time" - print "\tr - revert last seek" - print "\tp - toggle pause" - print "\troom [room] - change room, if no supplied go to default" - def updatePlayerStatus(self, paused, position): self.status_ask_received += 1 if self.status_ask_received < self.status_ask_sent: @@ -380,38 +342,33 @@ class SyncplayClientManager(object): if old_paused != paused and self.global_paused != paused: self.sendStatus(True) if paused: - message = '%s paused' % self.name - print message - self.player.display_message(message) + message = '%s paused' % self.users.currentUser.name + self.ui.showMessage(message) if(diff > 0): self.player.set_position(self.getGlobalPosition()) self.askPlayer() else: - message = '%s unpaused' % self.name - print message - self.player.display_message(message) + message = '%s unpaused' % self.users.currentUser.name + self.ui.showMessage(message) if not (self.global_paused or self.seek_sent_wait): if (0.4 if self.player_speed_fix else 1.2) <= diff <= 4: - #print 'client is %0.2fs ahead of server, slowing down' % diff if not self.player_speed_fix: self.player.set_speed(0.95) self.player_speed_fix = True else: if self.player_speed_fix: - #print 'resetting speed' self.player.set_speed(1) self.player_speed_fix = False - if abs(diff) > 8:# and not self.seek_sent_wait: + if abs(diff) > 8 and not self.seek_sent_wait: self.sendSeek() self.seek_sent_wait = True if not paused and self.player_paused_at is not None and position >= self.player_paused_at: - #print 'Pausing %0.2fs after pause point' % (position - self.player_paused_at) self.player.set_paused(True) self.askPlayer() def updateFilename(self, filename): filename = unicode(filename, errors='replace') - self.player_filename = filename.encode('ascii','replace') + self.users.currentUser.filename = filename.encode('ascii','replace') self.sendFilename() def updateGlobalState(self, counter, ctime, paused, position, name): @@ -448,24 +405,20 @@ class SyncplayClientManager(object): self.player.set_position(position) #self.player.set_paused(True) message = "Rewinded due to time difference" - print message - self.player.display_message(message) - + self.ui.showMessage(message) if self.player_paused and not paused: self.player_paused_at = None self.player.set_paused(False) if self.global_noted_pause_change != paused: message = '%s unpaused' % name - print message - self.player.display_message(message) + self.ui.showMessage(message) elif paused and not self.player_paused: self.player_paused_at = position if self.global_noted_pause_change != paused: message = '%s paused' % name - print message - self.player.display_message(message) + self.ui.showMessage(message) if(diff > 0): self.player.set_position(self.getGlobalPosition()) self.askPlayer() @@ -487,16 +440,17 @@ class SyncplayClientManager(object): self.player_position_before_last_seek = self.player_position self.player.set_position(position) self.askPlayer() - message = who + ' seeked to ' + format_time(position) - print message - self.player.display_message(message) + message = who + ' jumped to ' + format_time(position) + self.ui.showMessage(message) class UiManager(object): - def __init__(self, ui, debug = False): + def __init__(self, client, ui, debug = False): + self.__syncplayClient = client self.__ui = ui self.debug = debug def showMessage(self, message): + if(self.__syncplayClient.player): self.__syncplayClient.player.display_message(message) self.__ui.showMessage(message) def showDebugMessage(self, message): @@ -505,8 +459,46 @@ class SyncplayClientManager(object): def showErrorMessage(self, message): self.__ui.showErrorMessage(message) - - def displayListOfPeople(self): - pass - - + + class SyncplayUser(object): + def __init__(self, name = None, filename = None, room = None): + self.name = name + self.filename = filename + self.room = room + + class UserList(object): + def __init__(self): + self.users = [] + self.currentUser = SyncplayClient.SyncplayUser() + + def addUser(self, user): + if(not isinstance(user,SyncplayClient.SyncplayUser)): + user = SyncplayClient.SyncplayUser(user) + self.users.append(user) + + def removeUser(self, user): + if(not isinstance(user,SyncplayClient.SyncplayUser)): + user = SyncplayClient.SyncplayUser(user) + self.users[:] = list(itertools.ifilterfalse(lambda x: user.name == x.name, self.users)) + + def getUsersWithNotMatchingFilenames(self): + if(self.currentUser.filename == None): + return [] + matchingFilename = lambda x: (x.filename == None or x.filename == self.currentUser.filename) or x.room <> self.currentUser.room + return list(itertools.ifilterfalse(matchingFilename, self.users)) + + def setUsersRoom(self, username, room): + for u in self.users: + if(u.name == username): + u.room = room + break + #did not find a user, add + self.users.append(SyncplayClient.SyncplayUser(username, None, room)) + + def setUsersFilename(self, username, filename): + for u in self.users: + if(u.name == username): + u.filename = filename + break + #did not find a user, add + self.users.append(SyncplayClient.SyncplayUser(username, filename, None)) \ No newline at end of file