From a2116b0235c173708c2a3a4dabb821734307d7a4 Mon Sep 17 00:00:00 2001 From: Et0h Date: Sat, 29 Dec 2012 15:10:30 +0000 Subject: [PATCH] Undo change to client.py --- syncplay/client.py | 1025 ++++++++++++++++++++++---------------------- 1 file changed, 515 insertions(+), 510 deletions(-) diff --git a/syncplay/client.py b/syncplay/client.py index bb81fb0..04bd22f 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -1,510 +1,515 @@ -#coding:utf8 -import hashlib -import os.path -import time -from twisted.internet.protocol import ClientFactory -from twisted.internet import reactor, task -from syncplay.protocols import SyncClientProtocol -from syncplay import utils, constants -from syncplay.messages import getMessage - -class SyncClientFactory(ClientFactory): - def __init__(self, client, retry = constants.RECONNECT_RETRIES): - self._client = client - self.retry = retry - self._timesTried = 0 - self.reconnecting = False - - def buildProtocol(self, addr): - return SyncClientProtocol(self._client) - - def startedConnecting(self, connector): - destination = connector.getDestination() - message = getMessage("en", "connection-attempt-notification").format(destination.host, destination.port) - self._client.ui.showMessage(message) - - def clientConnectionLost(self, connector, reason): - if self._timesTried < self.retry: - self._timesTried += 1 - self._client.ui.showMessage(getMessage("en", "reconnection-attempt-notification")) - self.reconnecting = True - reactor.callLater(0.1*(2**self._timesTried), connector.connect) - else: - message = getMessage("en", "disconnection-notification") - self._client.ui.showMessage(message) - - def clientConnectionFailed(self, connector, reason): - if not self.reconnecting: - self._client.ui.showMessage(getMessage("en", "connection-failed-notification")) - self._client.stop(True) - else: - self.clientConnectionLost(connector, reason) - - def resetRetrying(self): - self._timesTried = 0 - - def stopRetrying(self): - self._timesTried = self.retry - -class SyncplayClient(object): - def __init__(self, playerClass, ui, args): - self.protocolFactory = SyncClientFactory(self) - self.ui = UiManager(self, ui) - self.userlist = SyncplayUserlist(self.ui, self) - self._protocol = None - if(args.room == None or args.room == ''): - args.room = constants.DEFAULT_ROOM - self.defaultRoom = args.room - self.playerPositionBeforeLastSeek = 0.0 - self.setUsername(args.name) - self.setRoom(args.room) - if(args.password): - args.password = hashlib.md5(args.password).hexdigest() - self._serverPassword = args.password - if(not args.file): - self.__getUserlistOnLogon = True - else: - self.__getUserlistOnLogon = False - self._player = None - self._playerClass = playerClass - self._startupArgs = args - - self._running = False - self._askPlayerTimer = None - - self._lastPlayerUpdate = None - self._playerPosition = 0.0 - self._playerPaused = True - - self._lastGlobalUpdate = None - self._globalPosition = 0.0 - self._globalPaused = 0.0 - self._userOffset = 0.0 - self._speedChanged = False - - def initProtocol(self, protocol): - self._protocol = protocol - - def destroyProtocol(self): - if(self._protocol): - self._protocol.drop() - self._protocol = None - - def initPlayer(self, player): - self._player = player - self.scheduleAskPlayer() - - def scheduleAskPlayer(self, when=constants.PLAYER_ASK_DELAY): - self._askPlayerTimer = task.LoopingCall(self.askPlayer) - self._askPlayerTimer.start(when) - - def askPlayer(self): - if(not self._running): - return - if(self._player): - self._player.askForStatus() - self.checkIfConnected() - - def checkIfConnected(self): - if(self._lastGlobalUpdate and self._protocol and time.time() - self._lastGlobalUpdate > constants.PROTOCOL_TIMEOUT): - self._lastGlobalUpdate = None - self.ui.showErrorMessage(getMessage("en", "server-timeout-error")) - self._protocol.drop() - return False - return True - - def _determinePlayerStateChange(self, paused, position): - pauseChange = self.getPlayerPaused() != paused and self.getGlobalPaused() != paused - _playerDiff = abs(self.getPlayerPosition() - position) - _globalDiff = abs(self.getGlobalPosition() - position) - seeked = _playerDiff > constants.SEEK_BOUNDARY and _globalDiff > constants.SEEK_BOUNDARY - return pauseChange, seeked - - def updatePlayerStatus(self, paused, position): - position -= self.getUserOffset() - pauseChange, seeked = self._determinePlayerStateChange(paused, position) - self._playerPosition = position - self._playerPaused = paused - if(self._lastGlobalUpdate): - self._lastPlayerUpdate = time.time() - if((pauseChange or seeked) and self._protocol): - if(seeked): - self.playerPositionBeforeLastSeek = self.getGlobalPosition() - self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), seeked, None, True) - - def getLocalState(self): - paused = self.getPlayerPaused() - position = self.getPlayerPosition() - pauseChange, _ = self._determinePlayerStateChange(paused, position) - if(self._lastGlobalUpdate): - return position, paused, _, pauseChange - else: - return None, None, None, None - - def _initPlayerState(self, position, paused): - self.setPosition(position) - self._player.setPaused(paused) - madeChangeOnPlayer = True - return madeChangeOnPlayer - - def _rewindPlayerDueToTimeDifference(self, position, setBy): - self.setPosition(position) - self.ui.showMessage(getMessage("en", "rewind-notification").format(setBy)) - madeChangeOnPlayer = True - return madeChangeOnPlayer - - def _serverUnpaused(self, setBy): - self._player.setPaused(False) - madeChangeOnPlayer = True - self.ui.showMessage(getMessage("en", "unpause-notification").format(setBy)) - return madeChangeOnPlayer - - def _serverPaused(self, setBy, diff): - if (diff > 0): - self.setPosition(self.getGlobalPosition()) - self._player.setPaused(True) - madeChangeOnPlayer = True - self.ui.showMessage(getMessage("en", "pause-notification").format(setBy)) - return madeChangeOnPlayer - - def _serverSeeked(self, position, setBy): - if(self.getUsername() <> setBy): - self.playerPositionBeforeLastSeek = self.getPlayerPosition() - self.setPosition(position) - madeChangeOnPlayer = True - else: - madeChangeOnPlayer = False - message = getMessage("en", "seek-notification").format(setBy, utils.formatTime(self.playerPositionBeforeLastSeek), utils.formatTime(position)) - self.ui.showMessage(message) - return madeChangeOnPlayer - - def _slowDownToCoverTimeDifference(self, diff, setBy): - if(constants.SLOWDOWN_KICKIN_BOUNDARY < diff and not self._speedChanged): - self._player.setSpeed(constants.SLOWDOWN_RATE) - self._speedChanged = True - self.ui.showMessage(getMessage("en", "slowdown-notification").format(setBy)) - elif(self._speedChanged and diff < constants.SLOWDOWN_RESET_BOUNDARY): - self._player.setSpeed(1.00) - self._speedChanged = False - self.ui.showMessage(getMessage("en", "revert-notification")) - madeChangeOnPlayer = True - return madeChangeOnPlayer - - def _changePlayerStateAccordingToGlobalState(self, position, paused, doSeek, setBy): - madeChangeOnPlayer = False - pauseChanged = paused != self.getGlobalPaused() - diff = self.getPlayerPosition() - position - if(self._lastGlobalUpdate is None): - madeChangeOnPlayer = self._initPlayerState(position, paused) - self._globalPaused = paused - self._globalPosition = position - self._lastGlobalUpdate = time.time() - if (doSeek): - madeChangeOnPlayer = self._serverSeeked(position, setBy) - if (diff > 4 and not doSeek): - madeChangeOnPlayer = self._rewindPlayerDueToTimeDifference(position, setBy) - if (self._player.speedSupported and not doSeek and not paused): - madeChangeOnPlayer = self._slowDownToCoverTimeDifference(diff, setBy) - if (paused == False and pauseChanged): - madeChangeOnPlayer = self._serverUnpaused(setBy) - elif (paused == True and pauseChanged): - madeChangeOnPlayer = self._serverPaused(setBy, diff) - return madeChangeOnPlayer - - def updateGlobalState(self, position, paused, doSeek, setBy, latency): - if(self.__getUserlistOnLogon): - self.__getUserlistOnLogon = False - self.getUserList() - madeChangeOnPlayer = False - - if(not paused): - position += latency - if(self._player): - madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy) - if(madeChangeOnPlayer): - self.askPlayer() - - def getUserOffset(self): - return self._userOffset - - def setUserOffset(self, time): - self._userOffset = time - message =getMessage("en", "current-offset-notification").format(self._userOffset) - self.setPosition(self.getGlobalPosition()) - self.ui.showMessage(message) - - def getPlayerPosition(self): - if(not self._lastPlayerUpdate): - if(self._lastGlobalUpdate): - return self.getGlobalPosition() - else: - return 0.0 - position = self._playerPosition - if(not self._playerPaused): - diff = time.time() - self._lastPlayerUpdate - if diff < 0.5: - position += diff - return position - - def getPlayerPaused(self): - if(not self._lastPlayerUpdate): - if(self._lastGlobalUpdate): - return self.getGlobalPaused() - else: - return True - return self._playerPaused - - def getGlobalPosition(self): - if not self._lastGlobalUpdate: - return 0.0 - position = self._globalPosition - if not self._globalPaused: - position += time.time() - self._lastGlobalUpdate - return position - - def getGlobalPaused(self): - if(not self._lastGlobalUpdate): - return True - return self._globalPaused - - def updateFile(self, filename, duration, path): - size = os.path.getsize(path) - self.userlist.currentUser.setFile(filename, duration, size) - self.sendFile() - - def sendFile(self): - file_ = self.userlist.currentUser.file - if(self._protocol and self._protocol.logged and file_): - self._protocol.sendFileSetting(file_) - - def setUsername(self, username): - self.userlist.currentUser.username = username - - def getUsername(self): - return self.userlist.currentUser.username - - def setRoom(self, roomName): - self.userlist.currentUser.room = roomName - self.getUserList() - - def sendRoom(self): - room = self.userlist.currentUser.room - if(self._protocol and self._protocol.logged and room): - self._protocol.sendRoomSetting(room) - - def getRoom(self): - return self.userlist.currentUser.room - - def getUserList(self): - if(self._protocol and self._protocol.logged): - self._protocol.sendList() - - def getPassword(self): - return self._serverPassword - - def setPosition(self, position): - position += self.getUserOffset() - if(self._player): - if(position < 0): - position = 0 - self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), True, None, True) - self._player.setPosition(position) - - def setPaused(self, paused): - if(self._player): - self._player.setPaused(paused) - - def start(self, host, port): - if self._running: - return - self._running = True - if self._playerClass: - self._playerClass.run(self, self._startupArgs.player_path, self._startupArgs.file, self._startupArgs._args) - self._playerClass = None - self.protocolFactory = SyncClientFactory(self) - reactor.connectTCP(host, port, self.protocolFactory) - reactor.run() - - def stop(self, promptForAction = False): - if not self._running: - return - self._running = False - if self.protocolFactory: - self.protocolFactory.stopRetrying() - self.destroyProtocol() - if self._player: - self._player.drop() - reactor.callLater(0.1, reactor.stop) - if(promptForAction): - self.ui.promptFor(getMessage("en", "enter-to-exit-prompt")) - -class SyncplayUser(object): - def __init__(self, username = None, room = None, file_ = None, position = 0): - self.username = username - self.room = room - self.file = file_ - self.lastPosition = position - - def setFile(self, filename, duration, size): - file_ = { - "name": filename, - "duration": duration, - "size":size - } - self.file = file_ - - def isFileSame(self, file_): - if(not self.file): - return False - sameName = self.file['name'] == file_['name'] - sameSize = self.file['size'] == file_['size'] - sameDuration = int(self.file['duration']) - int(file_['duration']) < constants.DIFFFERENT_DURATION_BOUNDARY - return sameName and sameSize and sameDuration - - def __lt__(self, other): - return self.username < other.username - -class SyncplayUserlist(object): - def __init__(self, ui, client): - self.currentUser = SyncplayUser() - self._users = {} - self.ui = ui - self._client = client - - def __showUserChangeMessage(self, username, room, file_): - if (room and not file_): - message = getMessage("en", "room-join-notification").format(username, room) - self.ui.showMessage(message) - elif (room and file_ and username != self.currentUser.username): - duration = utils.formatTime(file_['duration']) - message = getMessage("en", "playing-notification").format(username, file_['name'], duration) - if(self.currentUser.room <> room or self.currentUser.username == username): - message += getMessage("en", "playing-notification/room-addendum").format(room) - self.ui.showMessage(message) - if(self.currentUser.file and not self.currentUser.isFileSame(file_) and self.currentUser.room == room): - message = getMessage("en", "file-different-notification").format(username) - self.ui.showMessage(message) - differences = [] - if(self.currentUser.file['name'] <> file_['name']): - differences.append("filename") - if(self.currentUser.file['size'] <> file_['size']): - differences.append("size") - if(self.currentUser.file['duration'] <> file_['duration']): - differences.append("duration") - message = getMessage("en", "file-differences-notification")+ ", ".join(differences) - self.ui.showMessage(message) - - def addUser(self, username, room, file_, position = 0, noMessage = False): - if(username == self.currentUser.username): - self.currentUser.lastPosition = position - return - user = SyncplayUser(username, room, file_, position) - self._users[username] = user - if(not noMessage): - self.__showUserChangeMessage(username, room, file_) - - def removeUser(self, username): - if(self._users.has_key(username)): - self._users.pop(username) - message = getMessage("en", "left-notification").format(username) - self.ui.showMessage(message) - - def __displayModUserMessage(self, username, room, file_, user): - if (file_ and not user.isFileSame(file_)): - self.__showUserChangeMessage(username, room, file_) - elif (room and room != user.room): - self.__showUserChangeMessage(username, room, None) - - def modUser(self, username, room, file_): - if(self._users.has_key(username)): - user = self._users[username] - self.__displayModUserMessage(username, room, file_, user) - user.room = room - user.file = file_ - elif(username == self.currentUser.username): - self.__showUserChangeMessage(username, room, file_) - else: - self.addUser(username, room, file_) - - def __addUserWithFileToList(self, rooms, user): - currentPosition = utils.formatTime(user.lastPosition) - file_key = '\'{}\' ({}/{})'.format(user.file['name'], currentPosition, utils.formatTime(user.file['duration'])) - if (not rooms[user.room].has_key(file_key)): - rooms[user.room][file_key] = {} - rooms[user.room][file_key][user.username] = user - - def __addUserWithoutFileToList(self, rooms, user): - if (not rooms[user.room].has_key("__noFile__")): - rooms[user.room]["__noFile__"] = {} - rooms[user.room]["__noFile__"][user.username] = user - - def __createListOfPeople(self, rooms): - if(not rooms.has_key(self.currentUser.room)): - rooms[self.currentUser.room] = {} - for user in self._users.itervalues(): - if (not rooms.has_key(user.room)): - rooms[user.room] = {} - if(user.file): - self.__addUserWithFileToList(rooms, user) - else: - self.__addUserWithoutFileToList(rooms, user) - if(self.currentUser.file): - self.__addUserWithFileToList(rooms, self.currentUser) - else: - self.__addUserWithoutFileToList(rooms, self.currentUser) - return rooms - - def __addDifferentFileMessageIfNecessary(self, user, message): - if(self.currentUser.file): - fileHasSameSizeAsYour = user.file['size'] == self.currentUser.file['size'] - fileHasSameNameYour = user.file['name'] == self.currentUser.file['name'] - differentFileMessage = getMessage("en", "different-filesize-notification") - message += differentFileMessage if not fileHasSameSizeAsYour and fileHasSameNameYour else "" - return message - - def __displayFileWatchersInRoomList(self, key, users): - self.ui.showMessage(getMessage("en", "file-played-by-notification").format(key), True, True) - for user in sorted(users.itervalues()): - message = "<"+user.username+">" - if(self.currentUser.username == user.username): - message = "*" + message + "*" - message = self.__addDifferentFileMessageIfNecessary(user, message) - self.ui.showMessage("\t" + message, True, True) - - def __displayPeopleInRoomWithNoFile(self, noFileList): - if (noFileList): - self.ui.showMessage(getMessage("en", "notplaying-notification"), True, True) - for user in sorted(noFileList.itervalues()): - self.ui.showMessage("\t<" + user.username + ">", True, True) - - def __displayListOfPeople(self, rooms): - for roomName in sorted(rooms.iterkeys()): - self.ui.showMessage("In room '{}':".format(roomName), True, False) - noFileList = rooms[roomName].pop("__noFile__") if (rooms[roomName].has_key("__noFile__")) else None - for key in sorted(rooms[roomName].iterkeys()): - self.__displayFileWatchersInRoomList(key, rooms[roomName][key]) - self.__displayPeopleInRoomWithNoFile(noFileList) - - def showUserList(self): - rooms = {} - self.__createListOfPeople(rooms) - self.__displayListOfPeople(rooms) - - def clearList(self): - self._users = {} - -class UiManager(object): - def __init__(self, client, ui): - self._client = client - self.__ui = ui - - def showMessage(self, message, noPlayer = False, noTimestamp = False): - if(self._client._player and not noPlayer): self._client._player.displayMessage(message) - self.__ui.showMessage(message, noTimestamp) - - def showErrorMessage(self, message): - self.__ui.showErrorMessage(message) - - def promptFor(self, prompt): - return self.__ui.promptFor(prompt) - +#coding:utf8 +import hashlib +import os.path +import time +from twisted.internet.protocol import ClientFactory +from twisted.internet import reactor, task +from syncplay.protocols import SyncClientProtocol +from syncplay import utils, constants + +class SyncClientFactory(ClientFactory): + def __init__(self, client, retry = constants.RECONNECT_RETRIES): + self._client = client + self.retry = retry + self._timesTried = 0 + self.reconnecting = False + + def buildProtocol(self, addr): + return SyncClientProtocol(self._client) + + def startedConnecting(self, connector): + destination = connector.getDestination() + self._client.ui.showMessage('Attempting to connect to {}:{}'.format(destination.host, destination.port)) + + def clientConnectionLost(self, connector, reason): + if self._timesTried < self.retry: + self._timesTried += 1 + message = 'Connection with server lost, attempting to reconnect' + self._client.ui.showMessage(message) + self.reconnecting = True + reactor.callLater(0.1*(2**self._timesTried), connector.connect) + else: + message = 'Disconnected from server' + self._client.ui.showMessage(message) + + def clientConnectionFailed(self, connector, reason): + if not self.reconnecting: + message = 'Connection with server failed' + self._client.ui.showMessage(message) + self._client.stop(True) + else: + self.clientConnectionLost(connector, reason) + + def resetRetrying(self): + self._timesTried = 0 + + def stopRetrying(self): + self._timesTried = self.retry + +class SyncplayClient(object): + def __init__(self, playerClass, ui, args): + self.protocolFactory = SyncClientFactory(self) + self.ui = UiManager(self, ui) + self.userlist = SyncplayUserlist(self.ui, self) + self._protocol = None + if(args.room == None or args.room == ''): + args.room = constants.DEFAULT_ROOM + self.defaultRoom = args.room + self.playerPositionBeforeLastSeek = 0.0 + self.setUsername(args.name) + self.setRoom(args.room) + if(args.password): + args.password = hashlib.md5(args.password).hexdigest() + self._serverPassword = args.password + if(not args.file): + self.__getUserlistOnLogon = True + else: + self.__getUserlistOnLogon = False + self._player = None + self._playerClass = playerClass + self._startupArgs = args + + self._running = False + self._askPlayerTimer = None + + self._lastPlayerUpdate = None + self._playerPosition = 0.0 + self._playerPaused = True + + self._lastGlobalUpdate = None + self._globalPosition = 0.0 + self._globalPaused = 0.0 + self._userOffset = 0.0 + self._speedChanged = False + + def initProtocol(self, protocol): + self._protocol = protocol + + def destroyProtocol(self): + if(self._protocol): + self._protocol.drop() + self._protocol = None + + def initPlayer(self, player): + self._player = player + self.scheduleAskPlayer() + + def scheduleAskPlayer(self, when=constants.PLAYER_ASK_DELAY): + self._askPlayerTimer = task.LoopingCall(self.askPlayer) + self._askPlayerTimer.start(when) + + def askPlayer(self): + if(not self._running): + return + if(self._player): + self._player.askForStatus() + self.checkIfConnected() + + def checkIfConnected(self): + if(self._lastGlobalUpdate and self._protocol and time.time() - self._lastGlobalUpdate > constants.PROTOCOL_TIMEOUT): + self._lastGlobalUpdate = None + self.ui.showErrorMessage("Connection with server timed out") + self._protocol.drop() + return False + return True + + def _determinePlayerStateChange(self, paused, position): + pauseChange = self.getPlayerPaused() != paused and self.getGlobalPaused() != paused + _playerDiff = abs(self.getPlayerPosition() - position) + _globalDiff = abs(self.getGlobalPosition() - position) + seeked = _playerDiff > constants.SEEK_BOUNDARY and _globalDiff > constants.SEEK_BOUNDARY + return pauseChange, seeked + + def updatePlayerStatus(self, paused, position): + position -= self.getUserOffset() + pauseChange, seeked = self._determinePlayerStateChange(paused, position) + self._playerPosition = position + self._playerPaused = paused + if(self._lastGlobalUpdate): + self._lastPlayerUpdate = time.time() + if((pauseChange or seeked) and self._protocol): + if(seeked): + self.playerPositionBeforeLastSeek = self.getGlobalPosition() + self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), seeked, None, True) + + def getLocalState(self): + paused = self.getPlayerPaused() + position = self.getPlayerPosition() + pauseChange, _ = self._determinePlayerStateChange(paused, position) + if(self._lastGlobalUpdate): + return position, paused, _, pauseChange + else: + return None, None, None, None + + def _initPlayerState(self, position, paused): + self.setPosition(position) + self._player.setPaused(paused) + madeChangeOnPlayer = True + return madeChangeOnPlayer + + def _rewindPlayerDueToTimeDifference(self, position, setBy): + self.setPosition(position) + message = "Rewinded due to time difference with <{}>".format(setBy) + self.ui.showMessage(message) + madeChangeOnPlayer = True + return madeChangeOnPlayer + + def _serverUnpaused(self, setBy): + self._player.setPaused(False) + madeChangeOnPlayer = True + message = '<{}> unpaused'.format(setBy) + self.ui.showMessage(message) + return madeChangeOnPlayer + + def _serverPaused(self, setBy, diff): + if (diff > 0): + self.setPosition(self.getGlobalPosition()) + self._player.setPaused(True) + madeChangeOnPlayer = True + message = '<{}> paused'.format(setBy) + self.ui.showMessage(message) + return madeChangeOnPlayer + + def _serverSeeked(self, position, setBy): + if(self.getUsername() <> setBy): + self.playerPositionBeforeLastSeek = self.getPlayerPosition() + self.setPosition(position) + madeChangeOnPlayer = True + else: + madeChangeOnPlayer = False + message = '<{}> jumped from {} to {}'.format(setBy, utils.formatTime(self.playerPositionBeforeLastSeek), utils.formatTime(position)) + self.ui.showMessage(message) + return madeChangeOnPlayer + + def _slowDownToCoverTimeDifference(self, diff, setBy): + if(constants.SLOWDOWN_KICKIN_BOUNDARY < diff and not self._speedChanged): + self._player.setSpeed(constants.SLOWDOWN_RATE) + self._speedChanged = True + message = "Slowing down due to time difference with <{}>".format(setBy) + self.ui.showMessage(message) + elif(self._speedChanged and diff < constants.SLOWDOWN_RESET_BOUNDARY): + self._player.setSpeed(1.00) + self._speedChanged = False + message = "Reverting speed back to normal" + self.ui.showMessage(message) + madeChangeOnPlayer = True + return madeChangeOnPlayer + + def _changePlayerStateAccordingToGlobalState(self, position, paused, doSeek, setBy): + madeChangeOnPlayer = False + pauseChanged = paused != self.getGlobalPaused() + diff = self.getPlayerPosition() - position + if(self._lastGlobalUpdate is None): + madeChangeOnPlayer = self._initPlayerState(position, paused) + self._globalPaused = paused + self._globalPosition = position + self._lastGlobalUpdate = time.time() + if (doSeek): + madeChangeOnPlayer = self._serverSeeked(position, setBy) + if (diff > 4 and not doSeek): + madeChangeOnPlayer = self._rewindPlayerDueToTimeDifference(position, setBy) + if (self._player.speedSupported and not doSeek and not paused): + madeChangeOnPlayer = self._slowDownToCoverTimeDifference(diff, setBy) + if (paused == False and pauseChanged): + madeChangeOnPlayer = self._serverUnpaused(setBy) + elif (paused == True and pauseChanged): + madeChangeOnPlayer = self._serverPaused(setBy, diff) + return madeChangeOnPlayer + + def updateGlobalState(self, position, paused, doSeek, setBy, latency): + if(self.__getUserlistOnLogon): + self.__getUserlistOnLogon = False + self.getUserList() + madeChangeOnPlayer = False + + if(not paused): + position += latency + if(self._player): + madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy) + if(madeChangeOnPlayer): + self.askPlayer() + + def getUserOffset(self): + return self._userOffset + + def setUserOffset(self, time): + self._userOffset = time + message = "Current offset: {} seconds".format(self._userOffset) + self.setPosition(self.getGlobalPosition()) + self.ui.showMessage(message) + + def getPlayerPosition(self): + if(not self._lastPlayerUpdate): + if(self._lastGlobalUpdate): + return self.getGlobalPosition() + else: + return 0.0 + position = self._playerPosition + if(not self._playerPaused): + diff = time.time() - self._lastPlayerUpdate + if diff < 0.5: + position += diff + return position + + def getPlayerPaused(self): + if(not self._lastPlayerUpdate): + if(self._lastGlobalUpdate): + return self.getGlobalPaused() + else: + return True + return self._playerPaused + + def getGlobalPosition(self): + if not self._lastGlobalUpdate: + return 0.0 + position = self._globalPosition + if not self._globalPaused: + position += time.time() - self._lastGlobalUpdate + return position + + def getGlobalPaused(self): + if(not self._lastGlobalUpdate): + return True + return self._globalPaused + + def updateFile(self, filename, duration, path): + size = os.path.getsize(path) + self.userlist.currentUser.setFile(filename, duration, size) + self.sendFile() + + def sendFile(self): + file_ = self.userlist.currentUser.file + if(self._protocol and self._protocol.logged and file_): + self._protocol.sendFileSetting(file_) + + def setUsername(self, username): + self.userlist.currentUser.username = username + + def getUsername(self): + return self.userlist.currentUser.username + + def setRoom(self, roomName): + self.userlist.currentUser.room = roomName + self.getUserList() + + def sendRoom(self): + room = self.userlist.currentUser.room + if(self._protocol and self._protocol.logged and room): + self._protocol.sendRoomSetting(room) + + def getRoom(self): + return self.userlist.currentUser.room + + def getUserList(self): + if(self._protocol and self._protocol.logged): + self._protocol.sendList() + + def getPassword(self): + return self._serverPassword + + def setPosition(self, position): + position += self.getUserOffset() + if(self._player): + if(position < 0): + position = 0 + self._protocol.sendState(self.getPlayerPosition(), self.getPlayerPaused(), True, None, True) + self._player.setPosition(position) + + def setPaused(self, paused): + if(self._player): + self._player.setPaused(paused) + + def start(self, host, port): + if self._running: + return + self._running = True + if self._playerClass: + self._playerClass.run(self, self._startupArgs.player_path, self._startupArgs.file, self._startupArgs._args) + self._playerClass = None + self.protocolFactory = SyncClientFactory(self) + reactor.connectTCP(host, port, self.protocolFactory) + reactor.run() + + def stop(self, promptForAction = False): + if not self._running: + return + self._running = False + if self.protocolFactory: + self.protocolFactory.stopRetrying() + self.destroyProtocol() + if self._player: + self._player.drop() + reactor.callLater(0.1, reactor.stop) + if(promptForAction): + self.ui.promptFor("Press enter to exit\n") + +class SyncplayUser(object): + def __init__(self, username = None, room = None, file_ = None, position = 0): + self.username = username + self.room = room + self.file = file_ + self.lastPosition = position + + def setFile(self, filename, duration, size): + file_ = { + "name": filename, + "duration": duration, + "size":size + } + self.file = file_ + + def isFileSame(self, file_): + if(not self.file): + return False + sameName = self.file['name'] == file_['name'] + sameSize = self.file['size'] == file_['size'] + sameDuration = int(self.file['duration']) - int(file_['duration']) < constants.DIFFFERENT_DURATION_BOUNDARY + return sameName and sameSize and sameDuration + + def __lt__(self, other): + return self.username < other.username + +class SyncplayUserlist(object): + def __init__(self, ui, client): + self.currentUser = SyncplayUser() + self._users = {} + self.ui = ui + self._client = client + + def __showUserChangeMessage(self, username, room, file_): + if (room and not file_): + message = "<{}> has joined the room: '{}'".format(username, room) + self.ui.showMessage(message) + elif (room and file_ and username != self.currentUser.username): + duration = utils.formatTime(file_['duration']) + message = "<{}> is playing '{}' ({})".format(username, file_['name'], duration) + if(self.currentUser.room <> room or self.currentUser.username == username): + message += " in room: '{}'".format(room) + self.ui.showMessage(message) + if(self.currentUser.file and not self.currentUser.isFileSame(file_) and self.currentUser.room == room): + message = "File you are playing appears to be different from <{}>'s".format(username) + self.ui.showMessage(message) + differences = [] + if(self.currentUser.file['name'] <> file_['name']): + differences.append("filename") + if(self.currentUser.file['size'] <> file_['size']): + differences.append("size") + if(self.currentUser.file['duration'] <> file_['duration']): + differences.append("duration") + message = "Your file differs in the following way(s): " + ", ".join(differences) + self.ui.showMessage(message) + + def addUser(self, username, room, file_, position = 0, noMessage = False): + if(username == self.currentUser.username): + self.currentUser.lastPosition = position + return + user = SyncplayUser(username, room, file_, position) + self._users[username] = user + if(not noMessage): + self.__showUserChangeMessage(username, room, file_) + + def removeUser(self, username): + if(self._users.has_key(username)): + self._users.pop(username) + message = "<{}> has left".format(username) + self.ui.showMessage(message) + + def __displayModUserMessage(self, username, room, file_, user): + if (file_ and not user.isFileSame(file_)): + self.__showUserChangeMessage(username, room, file_) + elif (room and room != user.room): + self.__showUserChangeMessage(username, room, None) + + def modUser(self, username, room, file_): + if(self._users.has_key(username)): + user = self._users[username] + self.__displayModUserMessage(username, room, file_, user) + user.room = room + user.file = file_ + elif(username == self.currentUser.username): + self.__showUserChangeMessage(username, room, file_) + else: + self.addUser(username, room, file_) + + def __addUserWithFileToList(self, rooms, user): + currentPosition = utils.formatTime(user.lastPosition) + file_key = '\'{}\' ({}/{})'.format(user.file['name'], currentPosition, utils.formatTime(user.file['duration'])) + if (not rooms[user.room].has_key(file_key)): + rooms[user.room][file_key] = {} + rooms[user.room][file_key][user.username] = user + + def __addUserWithoutFileToList(self, rooms, user): + if (not rooms[user.room].has_key("__noFile__")): + rooms[user.room]["__noFile__"] = {} + rooms[user.room]["__noFile__"][user.username] = user + + def __createListOfPeople(self, rooms): + if(not rooms.has_key(self.currentUser.room)): + rooms[self.currentUser.room] = {} + for user in self._users.itervalues(): + if (not rooms.has_key(user.room)): + rooms[user.room] = {} + if(user.file): + self.__addUserWithFileToList(rooms, user) + else: + self.__addUserWithoutFileToList(rooms, user) + if(self.currentUser.file): + self.__addUserWithFileToList(rooms, self.currentUser) + else: + self.__addUserWithoutFileToList(rooms, self.currentUser) + return rooms + + def __addDifferentFileMessageIfNecessary(self, user, message): + if(self.currentUser.file): + fileHasSameSizeAsYour = user.file['size'] == self.currentUser.file['size'] + fileHasSameNameYour = user.file['name'] == self.currentUser.file['name'] + differentFileMessage = " (their file size is different from yours!)" + message += differentFileMessage if not fileHasSameSizeAsYour and fileHasSameNameYour else "" + return message + + def __displayFileWatchersInRoomList(self, key, users): + self.ui.showMessage("File: {} is being played by:".format(key), True, True) + for user in sorted(users.itervalues()): + message = "<"+user.username+">" + if(self.currentUser.username == user.username): + message = "*" + message + "*" + message = self.__addDifferentFileMessageIfNecessary(user, message) + self.ui.showMessage("\t" + message, True, True) + + def __displayPeopleInRoomWithNoFile(self, noFileList): + if (noFileList): + self.ui.showMessage("People who are not playing any file:", True, True) + for user in sorted(noFileList.itervalues()): + self.ui.showMessage("\t<" + user.username + ">", True, True) + + def __displayListOfPeople(self, rooms): + for roomName in sorted(rooms.iterkeys()): + self.ui.showMessage("In room '{}':".format(roomName), True, False) + noFileList = rooms[roomName].pop("__noFile__") if (rooms[roomName].has_key("__noFile__")) else None + for key in sorted(rooms[roomName].iterkeys()): + self.__displayFileWatchersInRoomList(key, rooms[roomName][key]) + self.__displayPeopleInRoomWithNoFile(noFileList) + + def showUserList(self): + rooms = {} + self.__createListOfPeople(rooms) + self.__displayListOfPeople(rooms) + + def clearList(self): + self._users = {} + +class UiManager(object): + def __init__(self, client, ui): + self._client = client + self.__ui = ui + + def showMessage(self, message, noPlayer = False, noTimestamp = False): + if(self._client._player and not noPlayer): self._client._player.displayMessage(message) + self.__ui.showMessage(message, noTimestamp) + + def showErrorMessage(self, message): + self.__ui.showErrorMessage(message) + + def promptFor(self, prompt): + return self.__ui.promptFor(prompt) +