Initial Client Features support

This commit is contained in:
Etoh 2017-04-18 10:37:32 +01:00
parent 06f6eb183b
commit 7307fb4eb5
3 changed files with 74 additions and 9 deletions

View File

@ -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)) self.ui.showErrorMessage(getMessage("shared-playlists-not-supported-by-server-error").format(constants.SHARED_PLAYLIST_MIN_VERSION, self.serverVersion))
elif not self.serverFeatures["sharedPlaylists"]: elif not self.serverFeatures["sharedPlaylists"]:
self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error")) self.ui.showErrorMessage(getMessage("shared-playlists-disabled-by-server-error"))
# TODO: Have messages for all unsupported & disabled features
self.ui.setFeatures(self.serverFeatures) self.ui.setFeatures(self.serverFeatures)
def getSanitizedCurrentUserFile(self): def getSanitizedCurrentUserFile(self):
@ -581,6 +582,24 @@ class SyncplayClient(object):
def getUsername(self): def getUsername(self):
return self.userlist.currentUser.username 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): def setRoom(self, roomName, resetAutoplay=False):
self.userlist.currentUser.room = roomName self.userlist.currentUser.room = roomName
if resetAutoplay: if resetAutoplay:
@ -702,6 +721,9 @@ class SyncplayClient(object):
message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH) message = utils.truncateText(message,constants.MAX_CHAT_MESSAGE_LENGTH)
self._protocol.sendChatMessage(message) self._protocol.sendChatMessage(message)
def sendFeaturesUpdate(self, features):
self._protocol.sendFeaturesUpdate(features)
def changePlaylistEnabledState(self, newState): def changePlaylistEnabledState(self, newState):
oldState = self.sharedPlaylistIsEnabled() oldState = self.sharedPlaylistIsEnabled()
from syncplay.ui.ConfigurationGetter import ConfigurationGetter from syncplay.ui.ConfigurationGetter import ConfigurationGetter
@ -799,6 +821,11 @@ class SyncplayClient(object):
if oldReadyState != isReady: if oldReadyState != isReady:
self._warnings.checkReadyStates() self._warnings.checkReadyStates()
@requireServerFeature("managedRooms")
def setUserFeatures(self, username, features):
self.userlist.setFeatures(username, features)
self.ui.userListChange()
@requireServerFeature("managedRooms") @requireServerFeature("managedRooms")
def createControlledRoom(self, roomName): def createControlledRoom(self, roomName):
controlPassword = utils.RandomStringGenerator.generate_room_password() controlPassword = utils.RandomStringGenerator.generate_room_password()
@ -989,6 +1016,7 @@ class SyncplayUser(object):
self.room = room self.room = room
self.file = file_ self.file = file_
self._controller = False self._controller = False
self._features = {}
def setFile(self, filename, duration, size, path=None): def setFile(self, filename, duration, size, path=None):
file_ = { file_ = {
@ -1042,6 +1070,9 @@ class SyncplayUser(object):
def setReady(self, ready): def setReady(self, ready):
self.ready = ready self.ready = ready
def setFeatures(self, features):
self._features = features
class SyncplayUserlist(object): class SyncplayUserlist(object):
def __init__(self, ui, client): def __init__(self, ui, client):
self.currentUser = SyncplayUser() self.currentUser = SyncplayUser()
@ -1120,7 +1151,7 @@ class SyncplayUserlist(object):
if differentDuration: differences.append(getMessage("file-difference-duration")) if differentDuration: differences.append(getMessage("file-difference-duration"))
return ", ".join(differences) 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 username == self.currentUser.username:
if isController is not None: if isController is not None:
self.currentUser.setControllerStatus(isController) self.currentUser.setControllerStatus(isController)
@ -1131,7 +1162,7 @@ class SyncplayUserlist(object):
user.setControllerStatus(isController) user.setControllerStatus(isController)
self._users[username] = user self._users[username] = user
user.setReady(isReady) user.setReady(isReady)
user.setFeatures(features)
if not noMessage: if not noMessage:
self.__showUserChangeMessage(username, room, file_) self.__showUserChangeMessage(username, room, file_)
self.userListChange(room) self.userListChange(room)

View File

@ -5,8 +5,7 @@ import syncplay
from functools import wraps from functools import wraps
import time import time
from syncplay.messages import getMessage 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): class JSONCommandProtocol(LineReceiver):
def handleMessages(self, messages): def handleMessages(self, messages):
@ -107,6 +106,7 @@ class SyncClientProtocol(JSONCommandProtocol):
if room: hello["room"] = {"name" :room} if room: hello["room"] = {"name" :room}
hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server
hello["realversion"] = syncplay.version hello["realversion"] = syncplay.version
hello["features"] = self._client.getFeatures()
self.sendMessage({"Hello": hello}) self.sendMessage({"Hello": hello})
def _SetUser(self, users): def _SetUser(self, users):
@ -147,6 +147,11 @@ class SyncClientProtocol(JSONCommandProtocol):
self._client.playlist.changeToPlaylistIndex(values['index'], values['user']) self._client.playlist.changeToPlaylistIndex(values['index'], values['user'])
elif command == "playlistChange": elif command == "playlistChange":
self._client.playlist.changePlaylist(values['files'], values['user']) 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): def sendSet(self, setting):
self.sendMessage({"Set": setting}) self.sendMessage({"Set": setting})
@ -173,7 +178,8 @@ class SyncClientProtocol(JSONCommandProtocol):
file_ = user[1]['file'] if user[1]['file'] <> {} else None file_ = user[1]['file'] if user[1]['file'] <> {} else None
isController = user[1]['controller'] if 'controller' in user[1] else False isController = user[1]['controller'] if 'controller' in user[1] else False
isReady = user[1]['isReady'] if 'isReady' in user[1] else None 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() self._client.userlist.showUserList()
def sendList(self): def sendList(self):
@ -286,6 +292,7 @@ class SyncServerProtocol(JSONCommandProtocol):
def __init__(self, factory): def __init__(self, factory):
self._factory = factory self._factory = factory
self._version = None self._version = None
self._features = None
self._logged = False self._logged = False
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0 self.serverIgnoringOnTheFly = 0
@ -319,6 +326,16 @@ class SyncServerProtocol(JSONCommandProtocol):
def connectionLost(self, reason): def connectionLost(self, reason):
self._factory.removeWatcher(self._watcher) 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): def isLogged(self):
return self._logged return self._logged
@ -339,7 +356,8 @@ class SyncServerProtocol(JSONCommandProtocol):
roomName = roomName.strip() roomName = roomName.strip()
version = hello["version"] if hello.has_key("version") else None version = hello["version"] if hello.has_key("version") else None
version = hello["realversion"] if hello.has_key("realversion") else version 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): def _checkPassword(self, serverPassword):
if self._factory.password: if self._factory.password:
@ -352,7 +370,7 @@ class SyncServerProtocol(JSONCommandProtocol):
return True return True
def handleHello(self, hello): 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: if not username or not roomName or not version:
self.dropWithError(getMessage("hello-server-error")) self.dropWithError(getMessage("hello-server-error"))
return return
@ -360,14 +378,22 @@ class SyncServerProtocol(JSONCommandProtocol):
if not self._checkPassword(serverPassword): if not self._checkPassword(serverPassword):
return return
self._version = version self._version = version
self.setFeatures(features)
self._factory.addWatcher(self, username, roomName) self._factory.addWatcher(self, username, roomName)
self._logged = True self._logged = True
self.sendHello(version) self.sendHello(version)
@requireLogged
def handleChat(self,chatMessage): def handleChat(self,chatMessage):
if not self._factory.disableChat: if not self._factory.disableChat:
self._factory.sendChat(self._watcher,chatMessage) 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): def setWatcher(self, watcher):
self._watcher = watcher self._watcher = watcher
@ -404,6 +430,9 @@ class SyncServerProtocol(JSONCommandProtocol):
self._factory.setPlaylist(self._watcher, set_[1]['files']) self._factory.setPlaylist(self._watcher, set_[1]['files'])
elif command == "playlistIndex": elif command == "playlistIndex":
self._factory.setPlaylistIndex(self._watcher, set_[1]['index']) self._factory.setPlaylistIndex(self._watcher, set_[1]['index'])
elif command == "features":
#TODO: Check
self._watcher.setFeatures(set_[1])
def sendSet(self, setting): def sendSet(self, setting):
self.sendMessage({"Set": setting}) self.sendMessage({"Set": setting})
@ -470,7 +499,8 @@ class SyncServerProtocol(JSONCommandProtocol):
"position": 0, "position": 0,
"file": watcher.getFile() if watcher.getFile() else {}, "file": watcher.getFile() if watcher.getFile() else {},
"controller": watcher.isController(), "controller": watcher.isController(),
"isReady": watcher.isReady() "isReady": watcher.isReady(),
"features": watcher.getFeatures()
} }
userlist[room.getName()][watcher.getName()] = userFile userlist[room.getName()][watcher.getName()] = userFile

View File

@ -108,7 +108,7 @@ class SyncFactory(Factory):
self._roomManager.broadcast(watcher, l) self._roomManager.broadcast(watcher, l)
def sendJoinMessage(self, watcher): 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.broadcast(watcher, l)
self._roomManager.broadcastRoom(watcher, lambda w: w.sendSetReady(watcher.getName(), watcher.isReady(), False)) self._roomManager.broadcastRoom(watcher, lambda w: w.sendSetReady(watcher.getName(), watcher.isReady(), False))
@ -414,6 +414,10 @@ class Watcher(object):
def setReady(self, ready): def setReady(self, ready):
self._ready = ready self._ready = ready
def getFeatures(self):
features = self._connector.getFeatures()
return features
def isReady(self): def isReady(self):
if self._server.disableReady: if self._server.disableReady:
return None return None