From 8a8859694cebb1a51ae599e5768875008d61404c Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 14:04:58 +0200 Subject: [PATCH 01/29] Enable logging support on server for client versions --- syncplay/constants.py | 1 + syncplay/server.py | 31 +++++++++++++++++++++++++++++-- syncplayServer.py | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 9cad6c0..62501f0 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -53,6 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5 PROTOCOL_TIMEOUT = 12.5 RECONNECT_RETRIES = 999 SERVER_STATE_INTERVAL = 1 +SERVER_LOG_SNAPSHOT_INTERVAL = 3600 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 DO_NOT_RESET_POSITION_THRESHOLD = 1.0 diff --git a/syncplay/server.py b/syncplay/server.py index 4b8cdaa..a2c467d 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -4,6 +4,7 @@ import codecs import hashlib import os import random +import sqlite3 import time from string import Template @@ -18,11 +19,12 @@ from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomString class SyncFactory(Factory): - def __init__(self, password='', motdFilePath=None, isolateRooms=False, salt=None, + def __init__(self, port='', password='', motdFilePath=None, isolateRooms=False, salt=None, disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, - maxUsernameLength=constants.MAX_USERNAME_LENGTH): + maxUsernameLength=constants.MAX_USERNAME_LENGTH, logDbFile=None): self.isolateRooms = isolateRooms print(getMessage("welcome-server-notification").format(syncplay.version)) + self.port = port if password: password = hashlib.md5(password).hexdigest() self.password = password @@ -39,6 +41,11 @@ class SyncFactory(Factory): self._roomManager = RoomManager() else: self._roomManager = PublicRoomManager() + if logDbFile is not None: + self.logDbHandle = self._connectToLogDb(logDbFile) + reactor.callLater(random.randint(1,60), self._scheduleVersionSnapshot, self.logDbHandle, self.port) + else: + self.logDbHandle = None def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -185,6 +192,17 @@ class SyncFactory(Factory): else: watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) + def _connectToLogDb(self, dbPath): + conn = sqlite3.connect(dbPath) + c = conn.cursor() + c.execute('create table if not exists versionSnapshots (snapshotTime integer, port integer, version string)') + conn.commit() + return conn + + def _scheduleVersionSnapshot(self, dbHandler, portNumber): + self._versionSnapshotTimer = task.LoopingCall(self._roomManager.runVersionSnapshot, dbHandler, portNumber) + self._versionSnapshotTimer.start(constants.SERVER_LOG_SNAPSHOT_INTERVAL) + class RoomManager(object): def __init__(self): @@ -260,6 +278,14 @@ class PublicRoomManager(RoomManager): RoomManager.moveWatcher(self, watcher, room) watcher.setFile(watcher.getFile()) + def runVersionSnapshot(self, dbHandler, portNumber): + snapshotTime = str(int(time.time())) + c = dbHandler.cursor() + for room in self._rooms.values(): + for watcher in room.getWatchers(): + c.execute("INSERT INTO versionSnapshots VALUES (" + snapshotTime + ", " + str(portNumber) + ", '" + watcher.getVersion() + "')") + dbHandler.commit() + class Room(object): STATE_PAUSED = 0 @@ -562,3 +588,4 @@ class ConfigurationGetter(object): self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument")) self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH)) self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH)) + self._argparser.add_argument('--log-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-log-db-file-argument")) diff --git a/syncplayServer.py b/syncplayServer.py index 3aa9866..bdd265c 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -22,6 +22,7 @@ if __name__ == '__main__': reactor.listenTCP( int(args.port), SyncFactory( + args.port, args.password, args.motd_file, args.isolate_rooms, From 9f52834ae48d0d2be44bef6d6afab76cedef33f3 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 14:29:44 +0200 Subject: [PATCH 02/29] Server logger: fix bug and sanitize version string --- syncplay/constants.py | 2 +- syncplay/server.py | 10 ++++++++-- syncplayServer.py | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 62501f0..c514d97 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -53,7 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5 PROTOCOL_TIMEOUT = 12.5 RECONNECT_RETRIES = 999 SERVER_STATE_INTERVAL = 1 -SERVER_LOG_SNAPSHOT_INTERVAL = 3600 +SERVER_LOG_SNAPSHOT_INTERVAL = 1 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 DO_NOT_RESET_POSITION_THRESHOLD = 1.0 diff --git a/syncplay/server.py b/syncplay/server.py index a2c467d..3efe65b 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -4,6 +4,7 @@ import codecs import hashlib import os import random +import re import sqlite3 import time from string import Template @@ -43,7 +44,7 @@ class SyncFactory(Factory): self._roomManager = PublicRoomManager() if logDbFile is not None: self.logDbHandle = self._connectToLogDb(logDbFile) - reactor.callLater(random.randint(1,60), self._scheduleVersionSnapshot, self.logDbHandle, self.port) + reactor.callLater(0.1, self._scheduleVersionSnapshot, self.logDbHandle, self.port) else: self.logDbHandle = None @@ -471,7 +472,12 @@ class Watcher(object): return self._name def getVersion(self): - return self._connector.getVersion() + pattern = r'\A[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}\Z' + versionString = self._connector.getVersion() + if re.match(pattern, versionString) is not None: + return versionString + else: + return None def getFile(self): return self._file diff --git a/syncplayServer.py b/syncplayServer.py index bdd265c..b6bbd89 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -29,5 +29,7 @@ if __name__ == '__main__': args.salt, args.disable_ready, args.disable_chat, - args.max_chat_message_length)) + args.max_chat_message_length, + args.max_username_length, + args.log_db_file)) reactor.run() From c32bd1191c73efd3f31b98739fd900bcde81f6dd Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 15:40:37 +0200 Subject: [PATCH 03/29] Server logger: add roomIndex and playStatus --- syncplay/constants.py | 2 +- syncplay/server.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index c514d97..62501f0 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -53,7 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5 PROTOCOL_TIMEOUT = 12.5 RECONNECT_RETRIES = 999 SERVER_STATE_INTERVAL = 1 -SERVER_LOG_SNAPSHOT_INTERVAL = 1 +SERVER_LOG_SNAPSHOT_INTERVAL = 3600 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 DO_NOT_RESET_POSITION_THRESHOLD = 1.0 diff --git a/syncplay/server.py b/syncplay/server.py index 3efe65b..0e7c4f5 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -44,7 +44,8 @@ class SyncFactory(Factory): self._roomManager = PublicRoomManager() if logDbFile is not None: self.logDbHandle = self._connectToLogDb(logDbFile) - reactor.callLater(0.1, self._scheduleVersionSnapshot, self.logDbHandle, self.port) + logDelay = 5*(int(self.port)%10 + 1) + reactor.callLater(logDelay, self._scheduleVersionSnapshot, self.logDbHandle, self.port) else: self.logDbHandle = None @@ -196,7 +197,7 @@ class SyncFactory(Factory): def _connectToLogDb(self, dbPath): conn = sqlite3.connect(dbPath) c = conn.cursor() - c.execute('create table if not exists versionSnapshots (snapshotTime integer, port integer, version string)') + c.execute('create table if not exists versionSnapshots (snapshotTime integer, port integer, version string, roomIndex integer, playStatus integer)') conn.commit() return conn @@ -282,9 +283,11 @@ class PublicRoomManager(RoomManager): def runVersionSnapshot(self, dbHandler, portNumber): snapshotTime = str(int(time.time())) c = dbHandler.cursor() - for room in self._rooms.values(): + for idx, room in enumerate(self._rooms.values()): + playStatus = str(room.isPlaying()) for watcher in room.getWatchers(): - c.execute("INSERT INTO versionSnapshots VALUES (" + snapshotTime + ", " + str(portNumber) + ", '" + watcher.getVersion() + "')") + c.execute("INSERT INTO versionSnapshots VALUES (" + snapshotTime + ", " + str(portNumber) + + ", '" + watcher.getVersion() + "', " + str(idx) + ", " + playStatus + ")") dbHandler.commit() From 14863fbbe7fe2e976fedf8dda1e5811577b9c9a3 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 16:09:22 +0200 Subject: [PATCH 04/29] Server Stats: use parametrized query and change column definitions --- syncplay/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 0e7c4f5..8e001c4 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -197,7 +197,7 @@ class SyncFactory(Factory): def _connectToLogDb(self, dbPath): conn = sqlite3.connect(dbPath) c = conn.cursor() - c.execute('create table if not exists versionSnapshots (snapshotTime integer, port integer, version string, roomIndex integer, playStatus integer)') + c.execute('create table if not exists clients_snapshots (snapshot_time integer, port integer, version string, room_index integer, play_status integer)') conn.commit() return conn @@ -286,8 +286,8 @@ class PublicRoomManager(RoomManager): for idx, room in enumerate(self._rooms.values()): playStatus = str(room.isPlaying()) for watcher in room.getWatchers(): - c.execute("INSERT INTO versionSnapshots VALUES (" + snapshotTime + ", " + str(portNumber) + - ", '" + watcher.getVersion() + "', " + str(idx) + ", " + playStatus + ")") + content = (snapshotTime, str(portNumber), watcher.getVersion(), str(idx), playStatus, ) + c.execute("INSERT INTO clients_snapshots VALUES (?, ?, ?, ?, ?)", content) dbHandler.commit() From a630860d2e53c35095d4bd4769e7d8fc74c32aa0 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 16:19:12 +0200 Subject: [PATCH 05/29] Server Stats: rename feature --- syncplay/constants.py | 2 +- syncplay/messages_de.py | 3 ++- syncplay/messages_en.py | 5 +++-- syncplay/messages_it.py | 5 +++-- syncplay/messages_ru.py | 7 ++++--- syncplay/server.py | 22 +++++++++++----------- syncplayServer.py | 2 +- 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/syncplay/constants.py b/syncplay/constants.py index 62501f0..3b04d2d 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -53,7 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5 PROTOCOL_TIMEOUT = 12.5 RECONNECT_RETRIES = 999 SERVER_STATE_INTERVAL = 1 -SERVER_LOG_SNAPSHOT_INTERVAL = 3600 +SERVER_STATS_SNAPSHOT_INTERVAL = 3600 WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1 AUTOPLAY_DELAY = 3.0 DO_NOT_RESET_POSITION_THRESHOLD = 1.0 diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 1dcab6d..318f0a3 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -434,7 +434,8 @@ de = { "server-motd-argument": "Pfad zur Datei, von der die Nachricht des Tages geladen wird", "server-chat-argument": "Should chat be disabled?", # TODO: Translate "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate - "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate + "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", # TODO: Translate + "server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", # TODO: Translate "server-messed-up-motd-unescaped-placeholders": "Die Nachricht des Tages hat unmaskierte Platzhalter. Alle $-Zeichen sollten verdoppelt werden ($$).", "server-messed-up-motd-too-long": "Die Nachricht des Tages ist zu lang - Maximal {} Zeichen, aktuell {}.", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index f22015b..267cbce 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -437,8 +437,9 @@ en = { "server-disable-ready-argument": "disable readiness feature", "server-motd-argument": "path to file from which motd will be fetched", "server-chat-argument": "Should chat be disabled?", - "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # Default number of characters - "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", + "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # Default number of characters + "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", + "server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", "server-messed-up-motd-unescaped-placeholders": "Message of the Day has unescaped placeholders. All $ signs should be doubled ($$).", "server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 8904733..0b804aa 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -437,8 +437,9 @@ it = { "server-disable-ready-argument": "disabilita la funzionalità \"pronto\"", "server-motd-argument": "percorso del file da cui verrà letto il messaggio del giorno", "server-chat-argument": "abilita o disabilita la chat", - "server-chat-maxchars-argument": "Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters - "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate + "server-chat-maxchars-argument": "Numero massimo di caratteri in un messaggio di chat (default è {})", # Default number of characters + "server-maxusernamelength-argument": "Numero massimo di caratteri in un nome utente (default è {})", + "server-stats-db-file-argument": "Abilita la raccolta dei dati statistici nel file SQLite indicato", "server-messed-up-motd-unescaped-placeholders": "Il messaggio del giorno ha dei caratteri non 'escaped'. Tutti i simboli $ devono essere doppi ($$).", "server-messed-up-motd-too-long": "Il messaggio del giorno è troppo lungo - numero massimo di caratteri è {}, {} trovati.", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 9daecb6..6c31d62 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -440,9 +440,10 @@ ru = { "server-motd-argument": "путь к файлу, из которого будет извлекаться MOTD-сообщение", "server-chat-argument": "Should chat be disabled?", # TODO: Translate "server-chat-maxchars-argument": "Maximum number of characters in a chat message (default is {})", # TODO: Translate - "server-maxusernamelength-argument": "Maximum number of charactrs in a username (default is {})", # TODO: Translate - "server-messed-up-motd-unescaped-placeholders": "MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", - "server-messed-up-motd-too-long": "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", + "server-maxusernamelength-argument": "Maximum number of characters in a username (default is {})", # TODO: Translate + "server-stats-db-file-argument": "Enable server stats using the SQLite db file provided", # TODO: Translate + "server-messed-up-motd-unescaped-placeholders" : "MOTD-сообщение содержит неэкранированные спец.символы. Все знаки $ должны быть продублированы ($$).", + "server-messed-up-motd-too-long" : "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", # Server errors "unknown-command-server-error": "Неизвестная команда: {}", # message diff --git a/syncplay/server.py b/syncplay/server.py index 8e001c4..77d99af 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -22,7 +22,7 @@ from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomString class SyncFactory(Factory): def __init__(self, port='', password='', motdFilePath=None, isolateRooms=False, salt=None, disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, - maxUsernameLength=constants.MAX_USERNAME_LENGTH, logDbFile=None): + maxUsernameLength=constants.MAX_USERNAME_LENGTH, statsDbFile=None): self.isolateRooms = isolateRooms print(getMessage("welcome-server-notification").format(syncplay.version)) self.port = port @@ -42,12 +42,12 @@ class SyncFactory(Factory): self._roomManager = RoomManager() else: self._roomManager = PublicRoomManager() - if logDbFile is not None: - self.logDbHandle = self._connectToLogDb(logDbFile) + if statsDbFile is not None: + self.statsDbHandle = self._connectToStatsDb(statsDbFile) logDelay = 5*(int(self.port)%10 + 1) - reactor.callLater(logDelay, self._scheduleVersionSnapshot, self.logDbHandle, self.port) + reactor.callLater(logDelay, self._scheduleClientSnapshot, self.statsDbHandle, self.port) else: - self.logDbHandle = None + self.statsDbHandle = None def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -194,16 +194,16 @@ class SyncFactory(Factory): else: watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) - def _connectToLogDb(self, dbPath): + def _connectToStatsDb(self, dbPath): conn = sqlite3.connect(dbPath) c = conn.cursor() c.execute('create table if not exists clients_snapshots (snapshot_time integer, port integer, version string, room_index integer, play_status integer)') conn.commit() return conn - def _scheduleVersionSnapshot(self, dbHandler, portNumber): - self._versionSnapshotTimer = task.LoopingCall(self._roomManager.runVersionSnapshot, dbHandler, portNumber) - self._versionSnapshotTimer.start(constants.SERVER_LOG_SNAPSHOT_INTERVAL) + def _scheduleClientSnapshot(self, dbHandler, portNumber): + self._clientSnapshotTimer = task.LoopingCall(self._roomManager.runClientSnapshot, dbHandler, portNumber) + self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) class RoomManager(object): @@ -280,7 +280,7 @@ class PublicRoomManager(RoomManager): RoomManager.moveWatcher(self, watcher, room) watcher.setFile(watcher.getFile()) - def runVersionSnapshot(self, dbHandler, portNumber): + def runClientSnapshot(self, dbHandler, portNumber): snapshotTime = str(int(time.time())) c = dbHandler.cursor() for idx, room in enumerate(self._rooms.values()): @@ -597,4 +597,4 @@ class ConfigurationGetter(object): self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument")) self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH)) self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH)) - self._argparser.add_argument('--log-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-log-db-file-argument")) + self._argparser.add_argument('--stats-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-stats-db-file-argument")) diff --git a/syncplayServer.py b/syncplayServer.py index b6bbd89..28cfd1b 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -31,5 +31,5 @@ if __name__ == '__main__': args.disable_chat, args.max_chat_message_length, args.max_username_length, - args.log_db_file)) + args.stats_db_file)) reactor.run() From bd766b4dfacf9a646762fbdb7356282a88fe746f Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 25 Jul 2018 17:31:17 +0200 Subject: [PATCH 06/29] Server Stats: remove unnecessary type casting before query --- syncplay/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 77d99af..22f20b7 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -281,12 +281,12 @@ class PublicRoomManager(RoomManager): watcher.setFile(watcher.getFile()) def runClientSnapshot(self, dbHandler, portNumber): - snapshotTime = str(int(time.time())) + snapshotTime = int(time.time()) c = dbHandler.cursor() for idx, room in enumerate(self._rooms.values()): - playStatus = str(room.isPlaying()) + playStatus = room.isPlaying() for watcher in room.getWatchers(): - content = (snapshotTime, str(portNumber), watcher.getVersion(), str(idx), playStatus, ) + content = (snapshotTime, int(portNumber), watcher.getVersion(), idx, playStatus, ) c.execute("INSERT INTO clients_snapshots VALUES (?, ?, ?, ?, ?)", content) dbHandler.commit() From d1e0c974da3ef66ca4806ba4eb6636b03b801aea Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 26 Jul 2018 10:03:09 +0200 Subject: [PATCH 07/29] Server Stats: remove port, room_index, and play_status --- syncplay/server.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 22f20b7..f3e7859 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -4,7 +4,6 @@ import codecs import hashlib import os import random -import re import sqlite3 import time from string import Template @@ -45,7 +44,7 @@ class SyncFactory(Factory): if statsDbFile is not None: self.statsDbHandle = self._connectToStatsDb(statsDbFile) logDelay = 5*(int(self.port)%10 + 1) - reactor.callLater(logDelay, self._scheduleClientSnapshot, self.statsDbHandle, self.port) + reactor.callLater(logDelay, self._scheduleClientSnapshot, self.statsDbHandle) else: self.statsDbHandle = None @@ -197,12 +196,12 @@ class SyncFactory(Factory): def _connectToStatsDb(self, dbPath): conn = sqlite3.connect(dbPath) c = conn.cursor() - c.execute('create table if not exists clients_snapshots (snapshot_time integer, port integer, version string, room_index integer, play_status integer)') + c.execute('create table if not exists clients_snapshots (snapshot_time integer, version string)') conn.commit() return conn - def _scheduleClientSnapshot(self, dbHandler, portNumber): - self._clientSnapshotTimer = task.LoopingCall(self._roomManager.runClientSnapshot, dbHandler, portNumber) + def _scheduleClientSnapshot(self, dbHandler): + self._clientSnapshotTimer = task.LoopingCall(self._roomManager.runClientSnapshot, dbHandler) self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) @@ -280,14 +279,14 @@ class PublicRoomManager(RoomManager): RoomManager.moveWatcher(self, watcher, room) watcher.setFile(watcher.getFile()) - def runClientSnapshot(self, dbHandler, portNumber): + def runClientSnapshot(self, dbHandler): snapshotTime = int(time.time()) c = dbHandler.cursor() for idx, room in enumerate(self._rooms.values()): playStatus = room.isPlaying() for watcher in room.getWatchers(): - content = (snapshotTime, int(portNumber), watcher.getVersion(), idx, playStatus, ) - c.execute("INSERT INTO clients_snapshots VALUES (?, ?, ?, ?, ?)", content) + content = (snapshotTime, watcher.getVersion(), ) + c.execute("INSERT INTO clients_snapshots VALUES (?, ?)", content) dbHandler.commit() @@ -475,12 +474,7 @@ class Watcher(object): return self._name def getVersion(self): - pattern = r'\A[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}\Z' - versionString = self._connector.getVersion() - if re.match(pattern, versionString) is not None: - return versionString - else: - return None + return self._connector.getVersion() def getFile(self): return self._file From 6564f22d3afc5a02e96e495c9103d4715b965803 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 26 Jul 2018 11:42:17 +0200 Subject: [PATCH 08/29] Server Stats: use twisted asynchronous adbapi --- syncplay/server.py | 63 ++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index f3e7859..ee2b2f0 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -8,6 +8,7 @@ import sqlite3 import time from string import Template +from twisted.enterprise import adbapi from twisted.internet import task, reactor from twisted.internet.protocol import Factory @@ -41,12 +42,11 @@ class SyncFactory(Factory): self._roomManager = RoomManager() else: self._roomManager = PublicRoomManager() - if statsDbFile is not None: - self.statsDbHandle = self._connectToStatsDb(statsDbFile) - logDelay = 5*(int(self.port)%10 + 1) - reactor.callLater(logDelay, self._scheduleClientSnapshot, self.statsDbHandle) - else: - self.statsDbHandle = None + if statsDbFile is not None: + statsDelay = 5*(int(self.port)%10 + 1) + StatsRecorder(statsDbFile, self._roomManager, statsDelay) + else: + self.statsDbHandle = None def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -193,17 +193,33 @@ class SyncFactory(Factory): else: watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) - def _connectToStatsDb(self, dbPath): - conn = sqlite3.connect(dbPath) - c = conn.cursor() - c.execute('create table if not exists clients_snapshots (snapshot_time integer, version string)') - conn.commit() - return conn - - def _scheduleClientSnapshot(self, dbHandler): - self._clientSnapshotTimer = task.LoopingCall(self._roomManager.runClientSnapshot, dbHandler) - self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) - +class StatsRecorder(object): + def __init__(self, dbpath, roomManager, delay): + self._roomManagerHandle = roomManager + self._dbpool = self._initDatabase(dbpath) + reactor.callLater(delay, self._scheduleClientSnapshot) + + def __del__(self): + self._dbpool.close() + + def _initDatabase(self, dbPath): + dbpool = adbapi.ConnectionPool("sqlite3", dbPath) + query = 'create table if not exists clients_snapshots (snapshot_time integer, version string)' + dbpool.runQuery(query) + return dbpool + + def _scheduleClientSnapshot(self): + self._clientSnapshotTimer = task.LoopingCall(self._runClientSnapshot) + self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) + + def _runClientSnapshot(self): + snapshotTime = int(time.time()) + rooms = self._roomManagerHandle.exportRooms() + for room in rooms.values(): + for watcher in room.getWatchers(): + content = (snapshotTime, watcher.getVersion(), ) + self._dbpool.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + class RoomManager(object): def __init__(self): @@ -263,6 +279,9 @@ class RoomManager(object): while username.lower() in allnames: username += '_' return username + + def exportRooms(self): + return self._rooms class PublicRoomManager(RoomManager): @@ -279,16 +298,6 @@ class PublicRoomManager(RoomManager): RoomManager.moveWatcher(self, watcher, room) watcher.setFile(watcher.getFile()) - def runClientSnapshot(self, dbHandler): - snapshotTime = int(time.time()) - c = dbHandler.cursor() - for idx, room in enumerate(self._rooms.values()): - playStatus = room.isPlaying() - for watcher in room.getWatchers(): - content = (snapshotTime, watcher.getVersion(), ) - c.execute("INSERT INTO clients_snapshots VALUES (?, ?)", content) - dbHandler.commit() - class Room(object): STATE_PAUSED = 0 From 6012a2b109b551b9cd8e9df31c4ba07e1ab24921 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 26 Jul 2018 16:03:56 +0200 Subject: [PATCH 09/29] Server Stats: incapsulate collection code in try-catch --- syncplay/server.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index ee2b2f0..32da0b0 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -195,15 +195,21 @@ class SyncFactory(Factory): class StatsRecorder(object): def __init__(self, dbpath, roomManager, delay): + self._dbPath = dbpath self._roomManagerHandle = roomManager - self._dbpool = self._initDatabase(dbpath) - reactor.callLater(delay, self._scheduleClientSnapshot) + try: + self._dbPool = self._initDatabase() + reactor.callLater(delay, self._scheduleClientSnapshot) + except: + self._dbPool = None + print("--- Error in initializing the stats database. Server Stats not enabled. ---") def __del__(self): - self._dbpool.close() + if self._dbPool is not None: + self._dbPool.close() - def _initDatabase(self, dbPath): - dbpool = adbapi.ConnectionPool("sqlite3", dbPath) + def _initDatabase(self): + dbpool = adbapi.ConnectionPool("sqlite3", self._dbPath, check_same_thread=False) query = 'create table if not exists clients_snapshots (snapshot_time integer, version string)' dbpool.runQuery(query) return dbpool @@ -213,12 +219,15 @@ class StatsRecorder(object): self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) def _runClientSnapshot(self): - snapshotTime = int(time.time()) - rooms = self._roomManagerHandle.exportRooms() - for room in rooms.values(): - for watcher in room.getWatchers(): - content = (snapshotTime, watcher.getVersion(), ) - self._dbpool.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + try: + snapshotTime = int(time.time()) + rooms = self._roomManagerHandle.exportRooms() + for room in rooms.values(): + for watcher in room.getWatchers(): + content = (snapshotTime, watcher.getVersion(), ) + self._dbPool.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + except: + pass class RoomManager(object): From c99c6e57acc77199576cdf05196390c1125f756b Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Thu, 26 Jul 2018 23:53:16 +0200 Subject: [PATCH 10/29] Server Stats: remove sqlite3 dependency and enable stats for non-isolated servers --- syncplay/server.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 32da0b0..45ece4d 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -1,10 +1,8 @@ - import argparse import codecs import hashlib import os import random -import sqlite3 import time from string import Template @@ -42,11 +40,12 @@ class SyncFactory(Factory): self._roomManager = RoomManager() else: self._roomManager = PublicRoomManager() - if statsDbFile is not None: - statsDelay = 5*(int(self.port)%10 + 1) - StatsRecorder(statsDbFile, self._roomManager, statsDelay) - else: - self.statsDbHandle = None + if statsDbFile is not None: + statsDelay = 5*(int(self.port)%10 + 1) + self._statsDbHandle = StatsRecorder() + self._statsDbHandle.startRecorder(statsDbFile, self._roomManager, statsDelay) + else: + self._statsDbHandle = None def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -194,7 +193,14 @@ class SyncFactory(Factory): watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) class StatsRecorder(object): - def __init__(self, dbpath, roomManager, delay): + def __init__(self): + self._dbPool = None + + def __del__(self): + if self._dbPool is not None: + self._dbPool.close() + + def startRecorder(self, dbpath, roomManager, delay): self._dbPath = dbpath self._roomManagerHandle = roomManager try: @@ -203,11 +209,7 @@ class StatsRecorder(object): except: self._dbPool = None print("--- Error in initializing the stats database. Server Stats not enabled. ---") - - def __del__(self): - if self._dbPool is not None: - self._dbPool.close() - + def _initDatabase(self): dbpool = adbapi.ConnectionPool("sqlite3", self._dbPath, check_same_thread=False) query = 'create table if not exists clients_snapshots (snapshot_time integer, version string)' From de4e9892f4034b286cc61d38bda2e1da86fe442f Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Fri, 27 Jul 2018 00:20:28 +0200 Subject: [PATCH 11/29] Server Stats: addressing review comments --- syncplay/server.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 45ece4d..fc80a6a 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -42,8 +42,8 @@ class SyncFactory(Factory): self._roomManager = PublicRoomManager() if statsDbFile is not None: statsDelay = 5*(int(self.port)%10 + 1) - self._statsDbHandle = StatsRecorder() - self._statsDbHandle.startRecorder(statsDbFile, self._roomManager, statsDelay) + self._statsDbHandle = StatsRecorder(statsDbFile, self._roomManager) + self._statsDbHandle.startRecorder(statsDelay) else: self._statsDbHandle = None @@ -193,21 +193,21 @@ class SyncFactory(Factory): watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) class StatsRecorder(object): - def __init__(self): - self._dbPool = None - - def __del__(self): - if self._dbPool is not None: - self._dbPool.close() - - def startRecorder(self, dbpath, roomManager, delay): + def __init__(self, dbpath, roomManager): self._dbPath = dbpath self._roomManagerHandle = roomManager + self._connection = None + + def __del__(self): + if self._connection is not None: + self._connection.close() + + def startRecorder(self, delay): try: - self._dbPool = self._initDatabase() + self._connection = self._initDatabase() reactor.callLater(delay, self._scheduleClientSnapshot) except: - self._dbPool = None + self._connection = None print("--- Error in initializing the stats database. Server Stats not enabled. ---") def _initDatabase(self): @@ -227,7 +227,7 @@ class StatsRecorder(object): for room in rooms.values(): for watcher in room.getWatchers(): content = (snapshotTime, watcher.getVersion(), ) - self._dbPool.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + self._connection.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) except: pass From 3234d9bc1a44be747a9fdecda0fa6f2c3c17d596 Mon Sep 17 00:00:00 2001 From: Alberto Sottile Date: Fri, 27 Jul 2018 00:54:44 +0200 Subject: [PATCH 12/29] Server Stats: use a separate class to manage the database --- syncplay/server.py | 51 ++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index fc80a6a..af06a50 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -41,9 +41,10 @@ class SyncFactory(Factory): else: self._roomManager = PublicRoomManager() if statsDbFile is not None: + self._statsDbHandle = DBManager(statsDbFile) + self._statsRecorder = StatsRecorder(self._statsDbHandle, self._roomManager) statsDelay = 5*(int(self.port)%10 + 1) - self._statsDbHandle = StatsRecorder(statsDbFile, self._roomManager) - self._statsDbHandle.startRecorder(statsDelay) + self._statsRecorder.startRecorder(statsDelay) else: self._statsDbHandle = None @@ -192,30 +193,19 @@ class SyncFactory(Factory): else: watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex()) + class StatsRecorder(object): - def __init__(self, dbpath, roomManager): - self._dbPath = dbpath + def __init__(self, dbHandle, roomManager): + self._dbHandle = dbHandle self._roomManagerHandle = roomManager - self._connection = None - - def __del__(self): - if self._connection is not None: - self._connection.close() def startRecorder(self, delay): try: - self._connection = self._initDatabase() + self._dbHandle.connect() reactor.callLater(delay, self._scheduleClientSnapshot) except: - self._connection = None print("--- Error in initializing the stats database. Server Stats not enabled. ---") - def _initDatabase(self): - dbpool = adbapi.ConnectionPool("sqlite3", self._dbPath, check_same_thread=False) - query = 'create table if not exists clients_snapshots (snapshot_time integer, version string)' - dbpool.runQuery(query) - return dbpool - def _scheduleClientSnapshot(self): self._clientSnapshotTimer = task.LoopingCall(self._runClientSnapshot) self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL) @@ -226,11 +216,32 @@ class StatsRecorder(object): rooms = self._roomManagerHandle.exportRooms() for room in rooms.values(): for watcher in room.getWatchers(): - content = (snapshotTime, watcher.getVersion(), ) - self._connection.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + self._dbHandle.addVersionLog(snapshotTime, watcher.getVersion()) except: pass - + + +class DBManager(object): + def __init__(self, dbpath): + self._dbPath = dbpath + self._connection = None + + def __del__(self): + if self._connection is not None: + self._connection.close() + + def connect(self): + self._connection = adbapi.ConnectionPool("sqlite3", self._dbPath, check_same_thread=False) + self._createSchema() + + def _createSchema(self): + initQuery = 'create table if not exists clients_snapshots (snapshot_time integer, version string)' + self._connection.runQuery(initQuery) + + def addVersionLog(self, timestamp, version): + content = (timestamp, version, ) + self._connection.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content) + class RoomManager(object): def __init__(self): From 64df31aca305c74698d6c5238fe2e8e5e82166a1 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sat, 28 Jul 2018 18:59:20 +0100 Subject: [PATCH 13/29] Upver to build 67 --- syncplay/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 561ff5f..ef31308 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ version = '1.5.6' revision = ' beta' milestone = 'Yoitsu' -release_number = '66' +release_number = '67' projectURL = 'https://syncplay.pl/' From 11638969512a0e9fe700835652673c657012ada2 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sat, 28 Jul 2018 21:01:13 +0100 Subject: [PATCH 14/29] Import site for non-Windows OS to fix makefile without breaking py2exe --- syncplayClient.py | 3 +++ syncplayServer.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/syncplayClient.py b/syncplayClient.py index dad7ac3..f73d62c 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -1,6 +1,9 @@ #!/usr/bin/env python3 import sys +from syncplay.utils import isWindows +if not isWindows(): + import site # libpath diff --git a/syncplayServer.py b/syncplayServer.py index 28cfd1b..5081e29 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -2,6 +2,9 @@ #coding:utf8 import sys +from syncplay.utils import isWindows +if not isWindows(): + import site # libpath From d3021a0e98eee715ecb67d346b55d3000fb1d5e4 Mon Sep 17 00:00:00 2001 From: Ricardo Constantino Date: Sat, 28 Jul 2018 22:10:41 +0100 Subject: [PATCH 15/29] Import site in makefile --- GNUmakefile | 4 ++-- syncplayClient.py | 3 --- syncplayServer.py | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index be70750..29842fa 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -61,7 +61,7 @@ u-common: client: -mkdir -p $(BIN_PATH) cp syncplayClient.py $(BIN_PATH)/syncplay - sed -i -e 's%# libpath%site.addsitedir\("${PREFIX}/lib/syncplay"\)%' $(BIN_PATH)/syncplay + sed -i -e '/# libpath/ a\import site\nsite.addsitedir\("${PREFIX}/lib/syncplay"\)' $(BIN_PATH)/syncplay chmod 755 $(BIN_PATH)/syncplay cp syncplayClient.py $(LIB_PATH)/syncplay/ cp resources/syncplay.desktop $(APP_SHORTCUT_PATH)/ @@ -87,7 +87,7 @@ u-client: server: -mkdir -p $(BIN_PATH) cp syncplayServer.py $(BIN_PATH)/syncplay-server - sed -i -e 's%# libpath%site.addsitedir\("${PREFIX}/lib/syncplay"\)%' $(BIN_PATH)/syncplay-server + sed -i -e '/# libpath/ a\import site\nsite.addsitedir\("${PREFIX}/lib/syncplay"\)' $(BIN_PATH)/syncplay-server chmod 755 $(BIN_PATH)/syncplay-server cp syncplayServer.py $(LIB_PATH)/syncplay/ cp resources/syncplay-server.desktop $(APP_SHORTCUT_PATH)/ diff --git a/syncplayClient.py b/syncplayClient.py index f73d62c..dad7ac3 100755 --- a/syncplayClient.py +++ b/syncplayClient.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 import sys -from syncplay.utils import isWindows -if not isWindows(): - import site # libpath diff --git a/syncplayServer.py b/syncplayServer.py index 5081e29..28cfd1b 100755 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -2,9 +2,6 @@ #coding:utf8 import sys -from syncplay.utils import isWindows -if not isWindows(): - import site # libpath From 9e4ea6817123555bc76d207c920e70b85e02858d Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 29 Jul 2018 17:15:20 +0200 Subject: [PATCH 16/29] AppVeyor: embed Python 3.5 and PySide2 5.11.1 --- .appveyor.yml | 12 +++++----- buildPy2exe.py | 59 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 15 deletions(-) mode change 100755 => 100644 buildPy2exe.py diff --git a/.appveyor.yml b/.appveyor.yml index 4b12967..bfdd1b1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,7 @@ environment: MINICONDA: "C:\\Miniconda" - PYTHON: "C:\\Python34" - PYTHON_VERSION: 3.4 + PYTHON: "C:\\Python35" + PYTHON_VERSION: 3.5 PYTHON_ARCH: 32 platform: x86 @@ -15,11 +15,13 @@ init: - python --version - python -m pip install -U pip setuptools wheel - pip install -U pypiwin32==219 - - pip install pyside - pip install twisted - - pip install py2exe - pip install zope.interface - - type nul > C:\Python34\lib\site-packages\zope\__init__.py + - type nul > %PYTHON%\lib\site-packages\zope\__init__.py + - curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=py2exe-0.9.2.2-py33.py34.py35-none-any.whl -o py2exe-0.9.2.2-py33.py34.py35-none-any.whl + - pip install py2exe-0.9.2.2-py33.py34.py35-none-any.whl + - del py2exe-0.9.2.2-py33.py34.py35-none-any.whl + - pip install PySide2==5.11.1 - pip freeze install: diff --git a/buildPy2exe.py b/buildPy2exe.py old mode 100755 new mode 100644 index cd8e694..e4a122d --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -15,7 +15,6 @@ import sys # import warnings # warnings.warn("You must build Syncplay with Python 2.7!") - import os import subprocess from string import Template @@ -34,7 +33,6 @@ if missingStrings is not None and missingStrings is not "": import warnings warnings.warn("MISSING/UNUSED STRINGS DETECTED:\n{}".format(missingStrings)) - def get_nsis_path(): bin_name = "makensis.exe" from winreg import HKEY_LOCAL_MACHINE as HKLM @@ -49,7 +47,6 @@ def get_nsis_path(): except WindowsError: return bin_name - NSIS_COMPILE = get_nsis_path() OUT_DIR = "syncplay_v{}".format(syncplay.version) @@ -619,7 +616,6 @@ NSIS_SCRIPT_TEMPLATE = r""" SectionEnd """ - class NSISScript(object): def create(self): fileList, totalSize = self.getBuildDirContents(OUT_DIR) @@ -676,17 +672,57 @@ class NSISScript(object): delete.append('RMdir "$INSTDIR\\{}"'.format(file_)) return "\n".join(delete) +def pruneUnneededLibraries(): + from pathlib import Path + cwd = os.getcwd() + libDir = cwd + '\\' + OUT_DIR + '\\lib\\' + unneededModules = ['PySide2.Qt3D*', 'PySide2.QtAxContainer.pyd', 'PySide2.QtCharts.pyd', 'PySide2.QtConcurrent.pyd', + 'PySide2.QtDataVisualization.pyd', 'PySide2.QtHelp.pyd', 'PySide2.QtLocation.pyd', + 'PySide2.QtMultimedia.pyd', 'PySide2.QtMultimediaWidgets.pyd', 'PySide2.QtOpenGL.pyd', + 'PySide2.QtPositioning.pyd', 'PySide2.QtPrintSupport.pyd', 'PySide2.QtQml.pyd', + 'PySide2.QtQuick.pyd', 'PySide2.QtQuickWidgets.pyd', 'PySide2.QtScxml.pyd', 'PySide2.QtSensors.pyd', + 'PySide2.QtSql.pyd', 'PySide2.QtSvg.pyd', 'PySide2.QtTest.pyd', 'PySide2.QtTextToSpeech.pyd', + 'PySide2.QtUiTools.pyd', 'PySide2.QtWebChannel.pyd', 'PySide2.QtWebSockets.pyd', + 'PySide2.QtWinExtras.pyd', 'PySide2.QtXml.pyd', 'PySide2.QtXmlPatterns.pyd'] + unneededLibs = ['Qt53D*', 'Qt5Charts.dll', 'Qt5Concurrent.dll', 'Qt5DataVisualization.dll', 'Qt5Gamepad.dll', 'Qt5Help.dll', + 'Qt5Location.dll', 'Qt5Multimedia.dll', 'Qt5MultimediaWidgets.dll', 'Qt5OpenGL.dll', 'Qt5Positioning.dll', + 'Qt5PrintSupport.dll', 'Qt5Quick.dll', 'Qt5QuickWidgets.dll', 'Qt5Scxml.dll', 'Qt5Sensors.dll', 'Qt5Sql.dll', + 'Qt5Svg.dll', 'Qt5Test.dll', 'Qt5TextToSpeech.dll', 'Qt5WebChannel.dll', 'Qt5WebSockets.dll', 'Qt5WinExtras.dll', + 'Qt5Xml.dll', 'Qt5XmlPatterns.dll'] + windowsDLL = ['MSVCP140.dll', 'VCRUNTIME140.dll'] + deleteList = unneededModules + unneededLibs + windowsDLL + deleteList.append('api-*') + for filename in deleteList: + for p in Path(libDir).glob(filename): + p.unlink() + +def copyQtPlugins(paths): + import shutil + from PySide2 import QtCore + basePath = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PluginsPath) + basePath = basePath.replace('/', '\\') + destBase = os.getcwd() + '\\' + OUT_DIR + for elem in paths: + elemDir, elemName = os.path.split(elem) + source = basePath + '\\' + elem + dest = destBase + '\\' + elem + destDir = destBase + '\\' + elemDir + os.makedirs(destDir, exist_ok=True) + shutil.copy(source, dest) class build_installer(py2exe): def run(self): py2exe.run(self) + print('*** deleting unnecessary libraries and modules ***') + pruneUnneededLibraries() + print('*** copying qt plugins ***') + copyQtPlugins(qt_plugins) script = NSISScript() script.create() - print("*** compiling the NSIS setup script***") + print("*** compiling the NSIS setup script ***") script.compile() print("*** DONE ***") - guiIcons = [ 'resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png', 'resources/control_pause_blue.png', 'resources/cross.png', 'resources/door_in.png', @@ -720,6 +756,8 @@ resources = [ resources.extend(guiIcons) intf_resources = ["resources/lua/intf/syncplay.lua"] +qt_plugins = ['platforms\\qwindows.dll', 'styles\\qwindowsvistastyle.dll'] + common_info = dict( name='Syncplay', version=syncplay.version, @@ -738,11 +776,12 @@ info = dict( console=['syncplayServer.py'], # *** If you wish to make the Syncplay client use console mode (for --no-gui to work) then comment out the above two lines and uncomment the following line: # console=['syncplayServer.py', {"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"}], + options={ 'py2exe': { 'dist_dir': OUT_DIR, - 'packages': 'PySide.QtUiTools', - 'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast, unicodedata, _ssl', + 'packages': 'PySide2', + 'includes': 'twisted, sys, encodings, datetime, os, time, math, liburl, ast, unicodedata, _ssl', 'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter', 'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll', 'optimize': 2, @@ -754,5 +793,5 @@ info = dict( cmdclass={"py2exe": build_installer}, ) -sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-i PySide.QtCore', '-i PySide.QtGui']) -setup(**info) +sys.argv.extend(['py2exe', '-p win32com ', '-i twisted.web.resource', '-p PySide2']) +setup(**info) \ No newline at end of file From ee0bd5261572c276d3838412b0a373f24cc8bfc0 Mon Sep 17 00:00:00 2001 From: Flisk Date: Thu, 2 Aug 2018 05:25:00 +0200 Subject: [PATCH 17/29] Remove obsolete encode/decode calls --- syncplay/ui/gui.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/syncplay/ui/gui.py b/syncplay/ui/gui.py index e656518..05acea3 100755 --- a/syncplay/ui/gui.py +++ b/syncplay/ui/gui.py @@ -972,12 +972,7 @@ class MainWindow(QtWidgets.QMainWindow): self.updatingPlaylist = True for URI in URIsToAdd: URI = URI.rstrip() - try: - URI = URI.encode('utf-8') - except UnicodeDecodeError: - pass URI = urllib.parse.unquote(URI) - URI = URI.decode('utf-8') if URI != "": self.addStreamToPlaylist(URI) self.updatingPlaylist = False From cae874b7b45c4364dd750fc2e5f9f9f0a0d118d9 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Tue, 31 Jul 2018 14:08:56 +0200 Subject: [PATCH 18/29] Travis: update PySide2 and requests --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c9dd37..06d1674 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ before_install: - which pip3 - pip3 --version - brew install qt -- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl -- pip3 install PySide2-5.11.0-5.11.0-cp36-cp36m-macosx_10_11_x86_64.whl +- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl +- pip3 install PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl - ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ - ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ - python3 -c "from PySide2 import __version__; print(__version__)" @@ -31,9 +31,7 @@ before_install: - python3 -c "from py2app.recipes import pyside2" install: -- pip3 install twisted appnope -- pip3 install -U https://github.com/requests/requests/zipball/master - +- pip3 install twisted appnope requests before_deploy: - pip3 install dmgbuild From 8100c35f3185b99bd30e9671da17850e678dd438 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 1 Aug 2018 12:34:43 +0200 Subject: [PATCH 19/29] Travis: use self-built limited ABI PySide2 wheels --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06d1674..f5fd65e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,10 +19,10 @@ before_install: - which pip3 - pip3 --version - brew install qt -- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl -o PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl -- pip3 install PySide2-5.11.1-5.11.1-cp36-cp36m-macosx_10_11_x86_64.whl -- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ -- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.cpython-36m-darwin.5.11.dylib /usr/local/lib/ +- curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl -o PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl +- pip3 install PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.abi3.5.11.dylib /usr/local/lib/ +- ln -s /usr/local/lib/python3.6/site-packages/PySide2/libshiboken2.abi3.5.11.dylib /usr/local/lib/ - python3 -c "from PySide2 import __version__; print(__version__)" - python3 -c "from PySide2.QtCore import __version__; print(__version__)" - cd py2app From 9a590afe4d9f3006f65246bb5d8520ce94677c94 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 2 Sep 2018 20:51:34 +0100 Subject: [PATCH 20/29] Fix server password Python 3 bug (#200) --- syncplay/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/syncplay/server.py b/syncplay/server.py index af06a50..390c27f 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -25,6 +25,7 @@ class SyncFactory(Factory): print(getMessage("welcome-server-notification").format(syncplay.version)) self.port = port if password: + password = password.encode('utf-8') password = hashlib.md5(password).hexdigest() self.password = password if salt is None: From b4c8bd9548a9cb6609f2521fee8b2cf735478da0 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Thu, 13 Sep 2018 14:52:33 +0200 Subject: [PATCH 21/29] Fix filename argument in syncplayClient --- syncplay/ui/ConfigurationGetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 8af06f4..c6896c6 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -473,7 +473,7 @@ class ConfigurationGetter(object): self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("password-argument")) self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("player-path-argument")) self._argparser.add_argument('--language', metavar='language', type=str, help=getMessage("language-argument")) - self._argparser.add_argument('file', metavar='file', type=lambda s: str(s, 'utf8'), nargs='?', help=getMessage("file-argument")) + self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help=getMessage("file-argument")) self._argparser.add_argument('--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) From 80a0638de9bdb52260b37af7bcaced7baffa2cf1 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 16 Sep 2018 20:38:42 +0100 Subject: [PATCH 22/29] Upver to 1.6.0 RC1 / build 68 --- bintray.json | 2 +- syncplay/__init__.py | 6 +++--- syncplay/messages_ru.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bintray.json b/bintray.json index eb4f953..5fb36fd 100644 --- a/bintray.json +++ b/bintray.json @@ -5,7 +5,7 @@ "subject": "syncplay" }, "version": { - "name": "v1.5.6" + "name": "v1.6.0" }, "files": [ { diff --git a/syncplay/__init__.py b/syncplay/__init__.py index ef31308..8ff91fb 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ -version = '1.5.6' -revision = ' beta' +version = '1.6.0' +revision = ' RC1' milestone = 'Yoitsu' -release_number = '67' +release_number = '68' projectURL = 'https://syncplay.pl/' diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 6c31d62..a94b54f 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -461,7 +461,7 @@ ru = { "cannot-add-unsafe-path-error": "Не удалось автоматически переключиться на {}, потому что ссылка не соответствует доверенным сайтам. Её можно включить вручную, дважны кливнув по ссылке в списке воспроизведения. Добавить доверенный сайт можно в выпадающем меню 'Дополнительно' или просто кликнув по ссылке правой кнопкой мыши.", # Filename "sharedplaylistenabled-label": "Включить общий список воспроизведения", "removefromplaylist-menu-label": "Удалить", - "shufflepremaininglaylist-menuu-label": "Shuffle remaining playlist", # Was: Перемешать список # TODO: Translate + "shufflepremaininglaylist-menu-label": "Shuffle remaining playlist", # Was: Перемешать список # TODO: Translate "shuffleentireplaylist-menu-label": "Shuffle entire playlist", # TODO: Translate "undoplaylist-menu-label": "Отменить последнее действие", "addfilestoplaylist-menu-label": "Добавить файлы в очередь", From 0f2bdb6a616405b52861d1556c13e1f18714d813 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 23 Sep 2018 14:41:40 +0100 Subject: [PATCH 23/29] Mark as 1.6.0 Final --- syncplay/__init__.py | 4 ++-- syncplay/constants.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 8ff91fb..366c94a 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ version = '1.6.0' -revision = ' RC1' +revision = '' milestone = 'Yoitsu' -release_number = '68' +release_number = '69' projectURL = 'https://syncplay.pl/' diff --git a/syncplay/constants.py b/syncplay/constants.py index 3b04d2d..76fdd17 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -9,7 +9,7 @@ MPLAYER_OSD_LEVEL = 1 UI_TIME_FORMAT = "[%X] " CONFIG_NAMES = [".syncplay", "syncplay.ini"] # Syncplay searches first to last DEFAULT_CONFIG_NAME = "syncplay.ini" -RECENT_CLIENT_THRESHOLD = "1.5.5" # This and higher considered 'recent' clients (no warnings) +RECENT_CLIENT_THRESHOLD = "1.6.0" # This and higher considered 'recent' clients (no warnings) WARN_OLD_CLIENTS = True # Use MOTD to inform old clients to upgrade LIST_RELATIVE_CONFIGS = True # Print list of relative configs loaded SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI From c3d2092f3d71da264fcbb2bf25ba60dc9d6828cc Mon Sep 17 00:00:00 2001 From: albertosottile Date: Sun, 23 Sep 2018 22:08:39 +0200 Subject: [PATCH 24/29] Travis: force qt 5.11.1 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f5fd65e..b876108 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,8 @@ before_install: - python3 --version - which pip3 - pip3 --version -- brew install qt +- curl -O https://raw.githubusercontent.com/Homebrew/homebrew-core/a57d0deab976cd8dee32404abe73f63cc5fbd60d/Formula/qt.rb +- brew install ./qt.rb - curl -L https://bintray.com/alby128/Syncplay/download_file?file_path=PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl -o PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl - pip3 install PySide2-5.11.1-5.11.1-cp35.cp36.cp37-abi3-macosx_10_11_x86_64.whl - ln -s /usr/local/lib/python3.6/site-packages/PySide2/libpyside2.abi3.5.11.dylib /usr/local/lib/ From 3042efddbc88836d0b6b5fefdc9bcf8686393776 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 3 Oct 2018 10:02:16 +0200 Subject: [PATCH 25/29] Fixes issue #205 with managed room names --- syncplay/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/syncplay/utils.py b/syncplay/utils.py index b9eafa8..50a1a24 100755 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -476,8 +476,10 @@ class RoomPasswordProvider(object): @staticmethod def _computeRoomHash(roomName, password, salt): roomName = roomName.encode('utf8') - salt = hashlib.sha256(salt).hexdigest() - provisionalHash = hashlib.sha256(roomName + salt).hexdigest() + salt = salt.encode('utf8') + password = password.encode('utf8') + salt = hashlib.sha256(salt).hexdigest().encode('utf8') + provisionalHash = hashlib.sha256(roomName + salt).hexdigest().encode('utf8') return hashlib.sha1(provisionalHash + salt + password).hexdigest()[:12].upper() From cb3372478437b14e93a2a79db6aad9bb4ac71045 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Wed, 3 Oct 2018 10:04:14 +0200 Subject: [PATCH 26/29] Upver to 1.6.1 / build 70 --- syncplay/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/__init__.py b/syncplay/__init__.py index 366c94a..d729808 100755 --- a/syncplay/__init__.py +++ b/syncplay/__init__.py @@ -1,5 +1,5 @@ -version = '1.6.0' +version = '1.6.1' revision = '' milestone = 'Yoitsu' -release_number = '69' +release_number = '70' projectURL = 'https://syncplay.pl/' From 6f840e4b4c5f9e5f515d4be45a76eb61dfede508 Mon Sep 17 00:00:00 2001 From: albertosottile Date: Mon, 29 Oct 2018 15:39:46 +0100 Subject: [PATCH 27/29] Add custom error message if Twisted is not installed Fixes #206. --- syncplay/messages_de.py | 1 + syncplay/messages_en.py | 1 + syncplay/messages_it.py | 1 + syncplay/messages_ru.py | 1 + syncplay/ui/ConfigurationGetter.py | 5 +++++ 5 files changed, 9 insertions(+) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 318f0a3..978685b 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -116,6 +116,7 @@ de = { "empty-error": "{} darf nicht leer sein", # Configuration "media-player-error": "Player-Fehler: \"{}\"", # Error line "unable-import-gui-error": "Konnte die GUI-Bibliotheken nicht importieren. PySide muss installiert sein, damit die grafische Oberfläche funktioniert.", + "unable-import-twisted-error": "Could not import Twisted. Please install Twisted v12.1.0 or later.", #To do: translate "arguments-missing-error": "Notwendige Argumente fehlen, siehe --help", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 267cbce..acf5c37 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -115,6 +115,7 @@ en = { "empty-error": "{} can't be empty", # Configuration "media-player-error": "Media player error: \"{}\"", # Error line "unable-import-gui-error": "Could not import GUI libraries. If you do not have PySide installed then you will need to install it for the GUI to work.", + "unable-import-twisted-error": "Could not import Twisted. Please install Twisted v12.1.0 or later.", "arguments-missing-error": "Some necessary arguments are missing, refer to --help", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 0b804aa..2ecff68 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -115,6 +115,7 @@ it = { "empty-error": "Il campo {} non può esssere vuoto", # Configuration "media-player-error": "Errore media player: \"{}\"", # Error line "unable-import-gui-error": "Non è possibile importare le librerie di interfaccia grafica. Hai bisogno di PySide per poter utilizzare l'interfaccia grafica.", + "unable-import-twisted-error": "Non è possibile importare Twisted. Si prega di installare Twisted v12.1. o superiore.", "arguments-missing-error": "Alcuni argomenti obbligatori non sono stati trovati. Fai riferimento a --help", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index a94b54f..18eeebe 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -116,6 +116,7 @@ ru = { "empty-error": "{} не может быть пустым.", # Configuration "media-player-error": "Ошибка проигрывателя: \"{}\"", # Error line "unable-import-gui-error": "Невозможно импортировать библиотеки GUI (графического интерфейса). Необходимо установить PySide, иначе графический интерфейс не будет работать.", + "unable-import-twisted-error": "Could not import Twisted. Please install Twisted v12.1.0 or later.", #To do: translate "arguments-missing-error": "Некоторые необходимые аргументы отсутствуют, обратитесь к --help", diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index c6896c6..aadc2e0 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -496,6 +496,11 @@ class ConfigurationGetter(object): import appnope appnope.nope() except ImportError: + try: + from twisted.trial import unittest + except: + print(getMessage("unable-import-twisted-error")) + sys.exit() print(getMessage("unable-import-gui-error")) self._config['noGui'] = True if self._config['file'] and self._config['file'][:2] == "--": From 4362f1f37279c409e922263167783967f4026a30 Mon Sep 17 00:00:00 2001 From: Tobias Umbach Date: Thu, 6 Dec 2018 11:34:40 +0100 Subject: [PATCH 28/29] Create XDG_CONFIG_HOME with safe permissions "If, when attempting to write a file, the destination directory is non-existant an attempt should be made to create it with permission 0700." https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html --- syncplay/ui/ConfigurationGetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index aadc2e0..cc67d11 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -352,7 +352,7 @@ class ConfigurationGetter(object): def _getXdgConfigHome(self): path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) if not os.path.isdir(path): - os.mkdir(path, 0o755) + os.mkdir(path, 0o700) return path def _parseConfigFile(self, iniPath, createConfig=True): From 553c023b4c40109087fb2236a4b421ed3f6d4c2c Mon Sep 17 00:00:00 2001 From: mordner Date: Sun, 16 Dec 2018 04:46:42 +0100 Subject: [PATCH 29/29] add-environment-support For --password and --salt we now check the environment for the corresponding variables SYNCPLAY_PASSWORD and SYNCPLAY_SALT. If they are found their values are used as long as the options are not given via CLI. Setting both options this way is highly encouraged. It fixes leaking these secret options to other system users (e.g. on Linux everybody can see them with 'ps' or looking at '/proc//cmdline'. --- syncplay/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncplay/server.py b/syncplay/server.py index 390c27f..d62d8d5 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -615,11 +615,11 @@ class ConfigurationGetter(object): description=getMessage("server-argument-description"), epilog=getMessage("server-argument-epilog")) self._argparser.add_argument('--port', metavar='port', type=str, nargs='?', help=getMessage("server-port-argument")) - self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help=getMessage("server-password-argument")) + self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help=getMessage("server-password-argument"), default=os.environ.get('SYNCPLAY_PASSWORD')) self._argparser.add_argument('--isolate-rooms', action='store_true', help=getMessage("server-isolate-room-argument")) self._argparser.add_argument('--disable-ready', action='store_true', help=getMessage("server-disable-ready-argument")) self._argparser.add_argument('--disable-chat', action='store_true', help=getMessage("server-chat-argument")) - self._argparser.add_argument('--salt', metavar='salt', type=str, nargs='?', help=getMessage("server-salt-argument")) + self._argparser.add_argument('--salt', metavar='salt', type=str, nargs='?', help=getMessage("server-salt-argument"), default=os.environ.get('SYNCPLAY_SALT')) self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("server-motd-argument")) self._argparser.add_argument('--max-chat-message-length', metavar='maxChatMessageLength', type=int, nargs='?', help=getMessage("server-chat-maxchars-argument").format(constants.MAX_CHAT_MESSAGE_LENGTH)) self._argparser.add_argument('--max-username-length', metavar='maxUsernameLength', type=int, nargs='?', help=getMessage("server-maxusernamelength-argument").format(constants.MAX_USERNAME_LENGTH))