diff --git a/syncplay/client.py b/syncplay/client.py index 6feab5b..deb62b9 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -552,6 +552,7 @@ class SyncplayClient(object): self.ui.showErrorMessage(getMessage("shared-playlists-not-supported-by-server-error").format(constants.SHARED_PLAYLIST_MIN_VERSION, self.serverVersion)) elif not self.serverFeatures["sharedPlaylists"]: self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error")) + # TODO: Have messages for all unsupported & disabled features self.ui.setFeatures(self.serverFeatures) def getSanitizedCurrentUserFile(self): @@ -581,6 +582,24 @@ class SyncplayClient(object): def getUsername(self): return self.userlist.currentUser.username + def chatIsEnabled(self): + return True + # TODO: Allow chat to be disabled + + def getFeatures(self): + features = dict() + + # Can change during runtime: + features["sharedPlaylists"] = self.sharedPlaylistIsEnabled() # Can change during runtime + features["chat"] = self.chatIsEnabled() # Can change during runtime + + # Static for this version/release of Syncplay: + features["featureList"] = True + features["readiness"] = True + features["managedRooms"] = True + + return features + def setRoom(self, roomName, resetAutoplay=False): self.userlist.currentUser.room = roomName if resetAutoplay: @@ -702,6 +721,9 @@ class SyncplayClient(object): message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH) self._protocol.sendChatMessage(message) + def sendFeaturesUpdate(self, features): + self._protocol.sendFeaturesUpdate(features) + def changePlaylistEnabledState(self, newState): oldState = self.sharedPlaylistIsEnabled() from syncplay.ui.ConfigurationGetter import ConfigurationGetter @@ -799,6 +821,11 @@ class SyncplayClient(object): if oldReadyState != isReady: self._warnings.checkReadyStates() + @requireServerFeature("managedRooms") + def setUserFeatures(self, username, features): + self.userlist.setFeatures(username, features) + self.ui.userListChange() + @requireServerFeature("managedRooms") def createControlledRoom(self, roomName): controlPassword = utils.RandomStringGenerator.generate_room_password() @@ -989,6 +1016,7 @@ class SyncplayUser(object): self.room = room self.file = file_ self._controller = False + self._features = {} def setFile(self, filename, duration, size, path=None): file_ = { @@ -1042,6 +1070,9 @@ class SyncplayUser(object): def setReady(self, ready): self.ready = ready + def setFeatures(self, features): + self._features = features + class SyncplayUserlist(object): def __init__(self, ui, client): self.currentUser = SyncplayUser() @@ -1120,7 +1151,7 @@ class SyncplayUserlist(object): if differentDuration: differences.append(getMessage("file-difference-duration")) return ", ".join(differences) - def addUser(self, username, room, file_, noMessage=False, isController=None, isReady=None): + def addUser(self, username, room, file_, noMessage=False, isController=None, isReady=None, features={}): if username == self.currentUser.username: if isController is not None: self.currentUser.setControllerStatus(isController) @@ -1131,7 +1162,7 @@ class SyncplayUserlist(object): user.setControllerStatus(isController) self._users[username] = user user.setReady(isReady) - + user.setFeatures(features) if not noMessage: self.__showUserChangeMessage(username, room, file_) self.userListChange(room) diff --git a/syncplay/protocols.py b/syncplay/protocols.py index d1b35e7..84bb3e7 100644 --- a/syncplay/protocols.py +++ b/syncplay/protocols.py @@ -5,8 +5,7 @@ import syncplay from functools import wraps import time from syncplay.messages import getMessage -from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT - +from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT, CONTROLLED_ROOMS_MIN_VERSION, USER_READY_MIN_VERSION, SHARED_PLAYLIST_MIN_VERSION, CHAT_MIN_VERSION class JSONCommandProtocol(LineReceiver): def handleMessages(self, messages): @@ -107,6 +106,7 @@ class SyncClientProtocol(JSONCommandProtocol): if room: hello["room"] = {"name" :room} hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server hello["realversion"] = syncplay.version + hello["features"] = self._client.getFeatures() self.sendMessage({"Hello": hello}) def _SetUser(self, users): @@ -147,6 +147,11 @@ class SyncClientProtocol(JSONCommandProtocol): self._client.playlist.changeToPlaylistIndex(values['index'], values['user']) elif command == "playlistChange": self._client.playlist.changePlaylist(values['files'], values['user']) + elif command == "features": + self._client.setUserFeatures(values["username"],values['features']) + + def sendFeaturesUpdate(self, features): + self.sendSet({"features": features}) def sendSet(self, setting): self.sendMessage({"Set": setting}) @@ -173,7 +178,8 @@ class SyncClientProtocol(JSONCommandProtocol): file_ = user[1]['file'] if user[1]['file'] <> {} else None isController = user[1]['controller'] if 'controller' in user[1] else False isReady = user[1]['isReady'] if 'isReady' in user[1] else None - self._client.userlist.addUser(userName, roomName, file_, noMessage=True, isController=isController, isReady=isReady) + features = user[1]['features'] if 'features' in user[1] else None + self._client.userlist.addUser(userName, roomName, file_, noMessage=True, isController=isController, isReady=isReady, features=features) self._client.userlist.showUserList() def sendList(self): @@ -286,6 +292,7 @@ class SyncServerProtocol(JSONCommandProtocol): def __init__(self, factory): self._factory = factory self._version = None + self._features = None self._logged = False self.clientIgnoringOnTheFly = 0 self.serverIgnoringOnTheFly = 0 @@ -319,6 +326,16 @@ class SyncServerProtocol(JSONCommandProtocol): def connectionLost(self, reason): self._factory.removeWatcher(self._watcher) + def getFeatures(self): + if not self._features: + self._features = {} + self._features["sharedPlaylists"] = self._version >= SHARED_PLAYLIST_MIN_VERSION + self._features["chat"] = self._version >= CHAT_MIN_VERSION + self._features["featureList"] = False + self._features["readiness"] = self._version >= USER_READY_MIN_VERSION + self._features["managedRooms"] = self._version >= CONTROLLED_ROOMS_MIN_VERSION + return self._features + def isLogged(self): return self._logged @@ -339,7 +356,8 @@ class SyncServerProtocol(JSONCommandProtocol): roomName = roomName.strip() version = hello["version"] if hello.has_key("version") else None version = hello["realversion"] if hello.has_key("realversion") else version - return username, serverPassword, roomName, version + features = hello["features"] if hello.has_key("features") else None + return username, serverPassword, roomName, version, features def _checkPassword(self, serverPassword): if self._factory.password: @@ -352,7 +370,7 @@ class SyncServerProtocol(JSONCommandProtocol): return True def handleHello(self, hello): - username, serverPassword, roomName, version = self._extractHelloArguments(hello) + username, serverPassword, roomName, version, features = self._extractHelloArguments(hello) if not username or not roomName or not version: self.dropWithError(getMessage("hello-server-error")) return @@ -360,14 +378,22 @@ class SyncServerProtocol(JSONCommandProtocol): if not self._checkPassword(serverPassword): return self._version = version + self.setFeatures(features) self._factory.addWatcher(self, username, roomName) self._logged = True self.sendHello(version) + @requireLogged def handleChat(self,chatMessage): if not self._factory.disableChat: self._factory.sendChat(self._watcher,chatMessage) + def setFeatures(self, features): + self._features = features + + def sendFeaturesUpdate(self): + self.sendSet({"features": self.getFeatures()}) + def setWatcher(self, watcher): self._watcher = watcher @@ -404,6 +430,9 @@ class SyncServerProtocol(JSONCommandProtocol): self._factory.setPlaylist(self._watcher, set_[1]['files']) elif command == "playlistIndex": self._factory.setPlaylistIndex(self._watcher, set_[1]['index']) + elif command == "features": + #TODO: Check + self._watcher.setFeatures(set_[1]) def sendSet(self, setting): self.sendMessage({"Set": setting}) @@ -470,7 +499,8 @@ class SyncServerProtocol(JSONCommandProtocol): "position": 0, "file": watcher.getFile() if watcher.getFile() else {}, "controller": watcher.isController(), - "isReady": watcher.isReady() + "isReady": watcher.isReady(), + "features": watcher.getFeatures() } userlist[room.getName()][watcher.getName()] = userFile diff --git a/syncplay/server.py b/syncplay/server.py index 399325a..4761875 100644 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -108,7 +108,7 @@ class SyncFactory(Factory): self._roomManager.broadcast(watcher, l) def sendJoinMessage(self, watcher): - l = lambda w: w.sendSetting(watcher.getName(), watcher.getRoom(), None, {"joined": True, "version": watcher.getVersion()}) if w != watcher else None + l = lambda w: w.sendSetting(watcher.getName(), watcher.getRoom(), None, {"joined": True, "version": watcher.getVersion(), "features": watcher.getFeatures()}) if w != watcher else None self._roomManager.broadcast(watcher, l) self._roomManager.broadcastRoom(watcher, lambda w: w.sendSetReady(watcher.getName(), watcher.isReady(), False)) @@ -414,6 +414,10 @@ class Watcher(object): def setReady(self, ready): self._ready = ready + def getFeatures(self): + features = self._connector.getFeatures() + return features + def isReady(self): if self._server.disableReady: return None