Fixes #210, Refactors the dict[key] if key in dict pattern to dict.get(), handleMessages if/elif statement uses a dictionary, json.loads(line) is no longer with a bare except

This commit is contained in:
Daniel Ahn 2019-01-09 23:38:21 -08:00
parent bb5d53cc35
commit d7ec1571c5
2 changed files with 93 additions and 84 deletions

View File

@ -452,6 +452,7 @@ en = {
"password-required-server-error": "Password required", "password-required-server-error": "Password required",
"wrong-password-server-error": "Wrong password supplied", "wrong-password-server-error": "Wrong password supplied",
"hello-server-error": "Not enough Hello arguments", # DO NOT TRANSLATE "hello-server-error": "Not enough Hello arguments", # DO NOT TRANSLATE
"line-decode-server-error": "Not a utf-8 string",
# Playlists # Playlists
"playlist-selection-changed-notification": "{} changed the playlist selection", # Username "playlist-selection-changed-notification": "{} changed the playlist selection", # Username

View File

@ -6,41 +6,46 @@ from functools import wraps
from twisted.protocols.basic import LineReceiver from twisted.protocols.basic import LineReceiver
import syncplay import syncplay
from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT, CONTROLLED_ROOMS_MIN_VERSION, USER_READY_MIN_VERSION, SHARED_PLAYLIST_MIN_VERSION, CHAT_MIN_VERSION from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT, CONTROLLED_ROOMS_MIN_VERSION,\
USER_READY_MIN_VERSION, SHARED_PLAYLIST_MIN_VERSION, CHAT_MIN_VERSION
from syncplay.messages import getMessage from syncplay.messages import getMessage
from syncplay.utils import meetsMinVersion from syncplay.utils import meetsMinVersion
class JSONCommandProtocol(LineReceiver): class JSONCommandProtocol(LineReceiver):
def handleMessages(self, messages): def handleMessages(self, messages):
for message in messages.items(): message_handler = {
command = message[0] "Hello": self.handleHello,
if command == "Hello": "Set": self.handleSet,
self.handleHello(message[1]) "List": self.handleList,
elif command == "Set": "State": self.handleState,
self.handleSet(message[1]) "Error": self.handleError,
elif command == "List": "Chat": self.handleChat,
self.handleList(message[1]) }
elif command == "State": for command, values in messages.items():
self.handleState(message[1]) try:
elif command == "Error": message_handler[command](values)
self.handleError(message[1]) except KeyError:
elif command == "Chat": self.dropWithError(getMessage("unknown-command-server-error").format(values)) # TODO: log, not drop
self.handleChat(message[1])
else:
self.dropWithError(getMessage("unknown-command-server-error").format(message[1])) # TODO: log, not drop
def lineReceived(self, line): def lineReceived(self, line):
line = line.decode('utf-8').strip() try:
line = line.decode('utf-8').strip()
except UnicodeDecodeError:
self.dropWithError(getMessage("line-decode-server-error"))
return
if not line: if not line:
return return
self.showDebugMessage("client/server << {}".format(line))
try: try:
self.showDebugMessage("client/server << {}".format(line))
messages = json.loads(line) messages = json.loads(line)
except: except json.decoder.JSONDecodeError:
self.dropWithError(getMessage("not-json-server-error").format(line)) self.dropWithError(getMessage("not-json-server-error").format(line))
return else:
self.handleMessages(messages) self.handleMessages(messages)
def sendMessage(self, dict_): def sendMessage(self, dict_):
line = json.dumps(dict_) line = json.dumps(dict_)
@ -78,12 +83,12 @@ class SyncClientProtocol(JSONCommandProtocol):
self.drop() self.drop()
def _extractHelloArguments(self, hello): def _extractHelloArguments(self, hello):
username = hello["username"] if "username" in hello else None username = hello.get("username")
roomName = hello["room"]["name"] if "room" in hello else None roomName = hello.get("room", {}).get('name')
version = hello["version"] if "version" in hello else None version = hello.get("version")
version = hello["realversion"] if "realversion" in hello else version # Used for 1.2.X compatibility version = hello.get("realversion", version) # Used for 1.2.X compatibility
motd = hello["motd"] if "motd" in hello else None motd = hello.get("motd")
features = hello["features"] if "features" in hello else None features = hello.get("features")
return username, roomName, version, motd, features return username, roomName, version, motd, features
def handleHello(self, hello): def handleHello(self, hello):
@ -116,11 +121,9 @@ class SyncClientProtocol(JSONCommandProtocol):
self.sendMessage({"Hello": hello}) self.sendMessage({"Hello": hello})
def _SetUser(self, users): def _SetUser(self, users):
for user in users.items(): for username, settings in users.items():
username = user[0] room = settings.get("room", {}).get("name")
settings = user[1] file_ = settings.get("file")
room = settings["room"]["name"] if "room" in settings else None
file_ = settings["file"] if "file" in settings else None
if "event" in settings: if "event" in settings:
if "joined" in settings["event"]: if "joined" in settings["event"]:
self._client.userlist.addUser(username, room, file_) self._client.userlist.addUser(username, room, file_)
@ -132,7 +135,7 @@ class SyncClientProtocol(JSONCommandProtocol):
def handleSet(self, settings): def handleSet(self, settings):
for (command, values) in settings.items(): for (command, values) in settings.items():
if command == "room": if command == "room":
roomName = values["name"] if "name" in values else None roomName = values.get("name")
self._client.setRoom(roomName) self._client.setRoom(roomName)
elif command == "user": elif command == "user":
self._SetUser(values) self._SetUser(values)
@ -147,7 +150,7 @@ class SyncClientProtocol(JSONCommandProtocol):
self._client.controlledRoomCreated(roomName, controlPassword) self._client.controlledRoomCreated(roomName, controlPassword)
elif command == "ready": elif command == "ready":
user, isReady = values["username"], values["isReady"] user, isReady = values["username"], values["isReady"]
manuallyInitiated = values["manuallyInitiated"] if "manuallyInitiated" in values else True manuallyInitiated = values.get("manuallyInitiated", True)
self._client.setReady(user, isReady, manuallyInitiated) self._client.setReady(user, isReady, manuallyInitiated)
elif command == "playlistIndex": elif command == "playlistIndex":
self._client.playlist.changeToPlaylistIndex(values['index'], values['user']) self._client.playlist.changeToPlaylistIndex(values['index'], values['user'])
@ -178,32 +181,35 @@ class SyncClientProtocol(JSONCommandProtocol):
def handleList(self, userList): def handleList(self, userList):
self._client.userlist.clearList() self._client.userlist.clearList()
for room in userList.items(): for roomName, users in userList.items():
roomName = room[0] for userName, userData in users.items():
for user in room[1].items(): file_ = userData.get('file') if userData.get('file') else None # Sets None if file == {}
userName = user[0] isController = userData.get('controller', False)
file_ = user[1]['file'] if user[1]['file'] != {} else None isReady = userData.get('isReady')
isController = user[1]['controller'] if 'controller' in user[1] else False features = userData.get('features')
isReady = user[1]['isReady'] if 'isReady' in user[1] else None self._client.userlist.addUser(
features = user[1]['features'] if 'features' in user[1] else None userName, roomName, file_,
self._client.userlist.addUser(userName, roomName, file_, noMessage=True, isController=isController, isReady=isReady, features=features) noMessage=True, isController=isController,
isReady=isReady, features=features
)
self._client.userlist.showUserList() self._client.userlist.showUserList()
def sendList(self): def sendList(self):
self.sendMessage({"List": None}) self.sendMessage({"List": None})
def _extractStatePlaystateArguments(self, state): def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if "position" in state["playstate"] else 0 position = state["playstate"].get("position", 0)
paused = state["playstate"]["paused"] if "paused" in state["playstate"] else None paused = state["playstate"].get("paused")
doSeek = state["playstate"]["doSeek"] if "doSeek" in state["playstate"] else None doSeek = state["playstate"].get("doSeek")
setBy = state["playstate"]["setBy"] if "setBy" in state["playstate"] else None setBy = state["playstate"].get("setBy")
return position, paused, doSeek, setBy return position, paused, doSeek, setBy
def _handleStatePing(self, state): def _handleStatePing(self, state):
if "latencyCalculation" in state["ping"]: # NOTE: Previously, there was no else condition for setting latencyCalculation
latencyCalculation = state["ping"]["latencyCalculation"] # Which means, if latencyCalculation was not found... this would return NameError...
if "clientLatencyCalculation" in state["ping"]: latencyCalculation = state["ping"].get("latencyCalculation")
timestamp = state["ping"]["clientLatencyCalculation"] timestamp = state["ping"].get("clientLatencyCalculation")
if timestamp:
senderRtt = state["ping"]["serverRtt"] senderRtt = state["ping"]["serverRtt"]
self._pingService.receiveMessage(timestamp, senderRtt) self._pingService.receiveMessage(timestamp, senderRtt)
messageAge = self._pingService.getLastForwardDelay() messageAge = self._pingService.getLastForwardDelay()
@ -361,17 +367,17 @@ class SyncServerProtocol(JSONCommandProtocol):
username = username.strip() username = username.strip()
else: else:
username = None username = None
serverPassword = hello["password"] if "password" in hello else None serverPassword = hello.get("password")
room = hello["room"] if "room" in hello else None room = hello.get("room")
if room: if room:
if "name" in room: if "name" in room:
roomName = room["name"] roomName = room["name"]
roomName = roomName.strip() roomName = roomName.strip()
else: else:
roomName = None roomName = None
version = hello["version"] if "version" in hello else None version = hello.get("version")
version = hello["realversion"] if "realversion" in hello else version version = hello.get("realversion", version)
features = hello["features"] if "features" in hello else None features = hello.get("features")
return username, serverPassword, roomName, version, features return username, serverPassword, roomName, version, features
def _checkPassword(self, serverPassword): def _checkPassword(self, serverPassword):
@ -428,27 +434,29 @@ class SyncServerProtocol(JSONCommandProtocol):
@requireLogged @requireLogged
def handleSet(self, settings): def handleSet(self, settings):
for set_ in settings.items(): for command, values in settings.items():
command = set_[0]
if command == "room": if command == "room":
roomName = set_[1]["name"] if "name" in set_[1] else None roomName = values.get("name")
self._factory.setWatcherRoom(self._watcher, roomName) self._factory.setWatcherRoom(self._watcher, roomName)
elif command == "file": elif command == "file":
self._watcher.setFile(set_[1]) self._watcher.setFile(values)
elif command == "controllerAuth": elif command == "controllerAuth":
password = set_[1]["password"] if "password" in set_[1] else None password = values.get("password")
room = set_[1]["room"] if "room" in set_[1] else None room = values.get("room")
self._factory.authRoomController(self._watcher, password, room) self._factory.authRoomController(self._watcher, password, room)
elif command == "ready": elif command == "ready":
manuallyInitiated = set_[1]['manuallyInitiated'] if "manuallyInitiated" in set_[1] else False manuallyInitiated = values.get('manuallyInitiated', False)
self._factory.setReady(self._watcher, set_[1]['isReady'], manuallyInitiated=manuallyInitiated) self._factory.setReady(self._watcher, values['isReady'], manuallyInitiated=manuallyInitiated)
elif command == "playlistChange": elif command == "playlistChange":
self._factory.setPlaylist(self._watcher, set_[1]['files']) self._factory.setPlaylist(self._watcher, values['files'])
elif command == "playlistIndex": elif command == "playlistIndex":
self._factory.setPlaylistIndex(self._watcher, set_[1]['index']) self._factory.setPlaylistIndex(self._watcher, values['index'])
elif command == "features": elif command == "features":
# TODO: Check # TODO: Check
self._watcher.setFeatures(set_[1]) self._watcher.setFeatures(values)
else:
# TODO: Error Handling?
pass
def sendSet(self, setting): def sendSet(self, setting):
self.sendMessage({"Set": setting}) self.sendMessage({"Set": setting})
@ -536,22 +544,22 @@ class SyncServerProtocol(JSONCommandProtocol):
else: else:
processingTime = 0 processingTime = 0
playstate = { playstate = {
"position": position if position else 0, "position": position if position else 0,
"paused": paused, "paused": paused,
"doSeek": doSeek, "doSeek": doSeek,
"setBy": setBy.getName() if setBy else None "setBy": setBy.getName() if setBy else None
} }
ping = { ping = {
"latencyCalculation": self._pingService.newTimestamp(), "latencyCalculation": self._pingService.newTimestamp(),
"serverRtt": self._pingService.getRtt() "serverRtt": self._pingService.getRtt()
} }
if self._clientLatencyCalculation: if self._clientLatencyCalculation:
ping["clientLatencyCalculation"] = self._clientLatencyCalculation + processingTime ping["clientLatencyCalculation"] = self._clientLatencyCalculation + processingTime
self._clientLatencyCalculation = 0 self._clientLatencyCalculation = 0
state = { state = {
"ping": ping, "ping": ping,
"playstate": playstate, "playstate": playstate,
} }
if forced: if forced:
self.serverIgnoringOnTheFly += 1 self.serverIgnoringOnTheFly += 1
if self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly: if self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly:
@ -565,9 +573,9 @@ class SyncServerProtocol(JSONCommandProtocol):
self.sendMessage({"State": state}) self.sendMessage({"State": state})
def _extractStatePlaystateArguments(self, state): def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if "position" in state["playstate"] else 0 position = state["playstate"].get("position", 0)
paused = state["playstate"]["paused"] if "paused" in state["playstate"] else None paused = state["playstate"].get("paused")
doSeek = state["playstate"]["doSeek"] if "doSeek" in state["playstate"] else None doSeek = state["playstate"].get("doSeek")
return position, paused, doSeek return position, paused, doSeek
@requireLogged @requireLogged
@ -583,9 +591,9 @@ class SyncServerProtocol(JSONCommandProtocol):
if "playstate" in state: if "playstate" in state:
position, paused, doSeek = self._extractStatePlaystateArguments(state) position, paused, doSeek = self._extractStatePlaystateArguments(state)
if "ping" in state: if "ping" in state:
latencyCalculation = state["ping"]["latencyCalculation"] if "latencyCalculation" in state["ping"] else 0 latencyCalculation = state["ping"].get("latencyCalculation", 0)
clientRtt = state["ping"]["clientRtt"] if "clientRtt" in state["ping"] else 0 clientRtt = state["ping"].get("clientRtt", 0)
self._clientLatencyCalculation = state["ping"]["clientLatencyCalculation"] if "clientLatencyCalculation" in state["ping"] else 0 self._clientLatencyCalculation = state["ping"].get("clientLatencyCalculation", 0)
self._clientLatencyCalculationArrivalTime = time.time() self._clientLatencyCalculationArrivalTime = time.time()
self._pingService.receiveMessage(latencyCalculation, clientRtt) self._pingService.receiveMessage(latencyCalculation, clientRtt)
if self.serverIgnoringOnTheFly == 0: if self.serverIgnoringOnTheFly == 0: