Use sqlite for persistent/permanent rooms (#434)

This commit is contained in:
et0h 2021-10-31 00:16:32 +01:00
parent f044e2de10
commit 4329d22334
12 changed files with 204 additions and 94 deletions

View File

@ -78,12 +78,12 @@ Random string used to generate managed room passwords.
Path to a file from which motd (Message Of The Day) will be read.
.TP
.B \-\-rooms\-dir [directory]
Path to a directory from where room data will be written to and read from. This will enable rooms to persist without watchers and through restarts. Will not work if using \fB\-\-isolate\-rooms\fP.
.B \-\-rooms\-db-file [directory]
Enables room persistence. Path is to where a database file should be loaded/create where room data will be written to and read from. This will enable rooms to persist without watchers and through restarts. Will not work if using \fB\-\-isolate\-rooms\fP.
.TP
.B \-\-rooms\-timer [directory]
Requires \fB\-\-rooms\-timer\fP. Time in seconds that rooms will persist without users. \fB0\fP disables the timer, meaning rooms persist permanently.
.B \-\-permanent\-rooms-file [directory]
Specifies a list of rooms that will still be listed even if their playlist is empty. Path is to where a text file with one room per line. This will require persistent rooms to be enabled.
.TP
.B \-\-max\-chat\-message\-length [maxChatMessageLength]

View File

@ -34,8 +34,8 @@ def main():
args.port,
args.password,
args.motd_file,
args.rooms_dir,
args.rooms_timer,
args.rooms_db_file,
args.permanent_rooms_file,
args.isolate_rooms,
args.salt,
args.disable_ready,

View File

@ -474,8 +474,8 @@ de = {
"server-salt-argument": "zufällige Zeichenkette, die zur Erstellung von Passwörtern verwendet wird",
"server-disable-ready-argument": "Bereitschaftsfeature deaktivieren",
"server-motd-argument": "Pfad zur Datei, von der die Nachricht des Tages geladen wird",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "Soll Chat deaktiviert werden?",
"server-chat-maxchars-argument": "Maximale Zeichenzahl in einer Chatnachricht (Standard ist {})",
"server-maxusernamelength-argument": "Maximale Zeichenzahl in einem Benutzernamen (Standard ist {})",

View File

@ -475,8 +475,8 @@ en = {
"server-salt-argument": "random string used to generate managed room passwords",
"server-disable-ready-argument": "disable readiness feature",
"server-motd-argument": "path to file from which motd will be fetched",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts",
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning",
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts",
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled",
"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 characters in a username (default is {})",

View File

@ -474,8 +474,8 @@ es = {
"server-salt-argument": "cadena aleatoria utilizada para generar contraseñas de salas administradas",
"server-disable-ready-argument": "deshabilitar la función de preparación",
"server-motd-argument": "ruta al archivo del cual se obtendrá el texto motd",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "¿Debería deshabilitarse el chat?",
"server-chat-maxchars-argument": "Número máximo de caracteres en un mensaje de chat (el valor predeterminado es {})", # Default number of characters
"server-maxusernamelength-argument": "Número máximo de caracteres para el nombre de usuario (el valor predeterminado es {})",

View File

@ -474,8 +474,8 @@ it = {
"server-salt-argument": "usare stringhe casuali per generare le password delle stanze gestite",
"server-disable-ready-argument": "disabilita la funzionalità \"pronto\"",
"server-motd-argument": "percorso del file da cui verrà letto il messaggio del giorno",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"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": "Numero massimo di caratteri in un nome utente (default è {})",

View File

@ -475,8 +475,8 @@ pt_BR = {
"server-salt-argument": "string aleatória utilizada para gerar senhas de salas gerenciadas",
"server-disable-ready-argument": "desativar recurso de prontidão",
"server-motd-argument": "caminho para o arquivo o qual o motd será obtido",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "O chat deve ser desativado?",
"server-chat-maxchars-argument": "Número máximo de caracteres numa mensagem do chat (o padrão é {})", # Default number of characters
"server-maxusernamelength-argument": "Número máximos de caracteres num nome de usuário (o padrão é {})",

View File

@ -474,8 +474,8 @@ pt_PT = {
"server-salt-argument": "string aleatória utilizada para gerar senhas de salas gerenciadas",
"server-disable-ready-argument": "desativar recurso de prontidão",
"server-motd-argument": "caminho para o arquivo o qual o motd será obtido",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "O chat deve ser desativado?",
"server-chat-maxchars-argument": "Número máximo de caracteres numa mensagem do chat (o padrão é {})", # Default number of characters
"server-maxusernamelength-argument": "Número máximos de caracteres num nome de utilizador (o padrão é {})",

View File

@ -471,8 +471,8 @@ ru = {
"server-salt-argument": "генерировать пароли к управляемым комнатам на основании указанной строки (соли)",
"server-disable-ready-argument": "отключить статусы готов/не готов",
"server-motd-argument": "путь к файлу, из которого будет извлекаться MOTD-сообщение",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "Должен ли чат быть отключён?",
"server-chat-maxchars-argument": "Максимальное число символов в сообщениях в чате (по умолчанию {})",
"server-maxusernamelength-argument": "Максимальное число символов в именах пользователей (по умолчанию {})",

View File

@ -475,8 +475,8 @@ tr = {
"server-salt-argument": "yönetilen oda şifreleri oluşturmak için kullanılan rastgele dize",
"server-disable-ready-argument": "hazır olma özelliğini devre dışı bırak",
"server-motd-argument": "motd alınacak dosyanın yolu",
"server-rooms-argument": "path to directory to store/fetch room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-timer-argument": "time in seconds before a persistent room with no watchers is pruned. 0 disables pruning", # TODO: Translate
"server-rooms-argument": "path to database file to use and/or create to store persistent room data. Enables rooms to persist without watchers and through restarts", # TODO: Translate
"server-permanent-rooms-argument": "path to file which lists permenant rooms that will be listed even if the room is empty (in the form of a text file which lists one room per line) - requires persistent rooms to be enabled", # TODO: Translate
"server-chat-argument": "Sohbet devre dışı bırakılmalı mı?",
"server-chat-maxchars-argument": "Bir sohbet mesajındaki maksimum karakter sayısı (varsayılan: {})", # Default number of characters
"server-maxusernamelength-argument": "Bir kullanıcı adındaki maksimum karakter sayısı (varsayılan {})",

View File

@ -617,11 +617,27 @@ class SyncServerProtocol(JSONCommandProtocol):
}
userlist[room.getName()][watcher.getName()] = userFile
def _addDummyUserOnList(self, userlist, dummyRoom,dummyCount):
if dummyRoom not in userlist:
userlist[dummyRoom] = {}
dummyFile = {
"position": 0,
"file": {},
"controller": False,
"isReady": True,
"features": []
}
userlist[dummyRoom][" " * dummyCount] = dummyFile
def sendList(self):
userlist = {}
watchers = self._factory.getAllWatchersForUser(self._watcher)
dummyCount = 0
for watcher in watchers:
self._addUserOnList(userlist, watcher)
for emptyRoom in self._factory.getEmptyPersistentRooms():
dummyCount += 1
self._addDummyUserOnList(userlist, emptyRoom, dummyCount)
self.sendMessage({"List": userlist})
@requireLogged

View File

@ -22,11 +22,11 @@ import syncplay
from syncplay import constants
from syncplay.messages import getMessage
from syncplay.protocols import SyncServerProtocol
from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomStringGenerator, meetsMinVersion, playlistIsValid, truncateText
from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomStringGenerator, meetsMinVersion, playlistIsValid, truncateText, getListAsMultilineString, convertMultilineStringToList
class SyncFactory(Factory):
def __init__(self, port='', password='', motdFilePath=None, roomsDirPath=None, roomsTimer=31558149, isolateRooms=False, salt=None,
def __init__(self, port='', password='', motdFilePath=None, roomsDbFile=None, permanentRoomsFile=None, isolateRooms=False, salt=None,
disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH,
maxUsernameLength=constants.MAX_USERNAME_LENGTH, statsDbFile=None, tlsCertPath=None):
self.isolateRooms = isolateRooms
@ -41,18 +41,19 @@ class SyncFactory(Factory):
print(getMessage("no-salt-notification").format(salt))
self._salt = salt
self._motdFilePath = motdFilePath
self._roomsDirPath = roomsDirPath if roomsDirPath is not None and os.path.isdir(roomsDirPath) else None
self._roomsTimer = roomsTimer if roomsDirPath is not None and isinstance(roomsTimer, int) and roomsTimer > 0 else 0
self.roomsDbFile = roomsDbFile
self.disableReady = disableReady
self.disableChat = disableChat
self.maxChatMessageLength = maxChatMessageLength if maxChatMessageLength is not None else constants.MAX_CHAT_MESSAGE_LENGTH
self.maxUsernameLength = maxUsernameLength if maxUsernameLength is not None else constants.MAX_USERNAME_LENGTH
self.permanentRoomsFile = permanentRoomsFile if permanentRoomsFile is not None and os.path.isfile(permanentRoomsFile) else None
self.permanentRooms = self.loadListFromMultilineTextFile(self.permanentRoomsFile)
if not isolateRooms:
self._roomManager = RoomManager(self._roomsDirPath, self._roomsTimer)
self._roomManager = RoomManager(self.roomsDbFile, self.permanentRooms)
else:
self._roomManager = PublicRoomManager()
if statsDbFile is not None:
self._statsDbHandle = DBManager(statsDbFile)
self._statsDbHandle = StatsDBManager(statsDbFile)
self._statsRecorder = StatsRecorder(self._statsDbHandle, self._roomManager)
statsDelay = 5*(int(self.port)%10 + 1)
self._statsRecorder.startRecorder(statsDelay)
@ -67,6 +68,16 @@ class SyncFactory(Factory):
self.options = None
self.serverAcceptsTLS = False
def loadListFromMultilineTextFile(self, path):
if not os.path.isfile(path):
return []
with open(path) as f:
multiline = f.read().splitlines()
return multiline
def loadRoom(self):
rooms = self._roomsDbHandle.loadRooms()
def buildProtocol(self, addr):
return SyncServerProtocol(self)
@ -172,6 +183,9 @@ class SyncFactory(Factory):
def getAllWatchersForUser(self, forUser):
return self._roomManager.getAllWatchersForUser(forUser)
def getEmptyPersistentRooms(self):
return self._roomManager.getEmptyPersistentRooms()
def authRoomController(self, watcher, password, roomBaseName=None):
room = watcher.getRoom()
roomName = roomBaseName if roomBaseName else room.getName()
@ -290,8 +304,33 @@ class StatsRecorder(object):
except:
pass
class RoomsRecorder(StatsRecorder):
def __init__(self, dbHandle, roomManager):
self._dbHandle = dbHandle
self._roomManagerHandle = roomManager
class DBManager(object):
def startRecorder(self, delay):
try:
self._dbHandle.connect()
reactor.callLater(delay, self._scheduleClientSnapshot) # TODO: FIX THIS!
except:
print("--- Error in initializing the stats database. Server Stats not enabled. ---")
def _scheduleClientSnapshot(self):
self._clientSnapshotTimer = task.LoopingCall(self._runClientSnapshot)
self._clientSnapshotTimer.start(constants.SERVER_STATS_SNAPSHOT_INTERVAL)
def _runClientSnapshot(self):
try:
snapshotTime = int(time.time())
rooms = self._roomManagerHandle.exportRooms()
for room in rooms.values():
for watcher in room.getWatchers():
self._dbHandle.addVersionLog(snapshotTime, watcher.getVersion())
except:
pass
class StatsDBManager(object):
def __init__(self, dbpath):
self._dbPath = dbpath
self._connection = None
@ -305,31 +344,74 @@ class DBManager(object):
self._createSchema()
def _createSchema(self):
initQuery = 'create table if not exists clients_snapshots (snapshot_time integer, version string)'
self._connection.runQuery(initQuery)
initQuery = 'create table if not exists clients_snapshots (snapshot_time INTEGER, version STRING)'
return self._connection.runQuery(initQuery)
def addVersionLog(self, timestamp, version):
content = (timestamp, version, )
self._connection.runQuery("INSERT INTO clients_snapshots VALUES (?, ?)", content)
class RoomDBManager(object):
def __init__(self, dbpath, loadroomscallback):
self._dbPath = dbpath
self._connection = None
self._loadRoomsCallback = loadroomscallback
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().addCallback(self.loadRooms)
def _createSchema(self):
initQuery = 'create table if not exists persistent_rooms (name STRING PRIMARY KEY, playlist STRING, playlistIndex INTEGER, position REAL, lastSavedUpdate INTEGER)'
return self._connection.runQuery(initQuery)
def saveRoom(self, name, playlist, playlistIndex, position, lastUpdate):
content = (name, playlist, playlistIndex, position, lastUpdate)
self._connection.runQuery("INSERT OR REPLACE INTO persistent_rooms VALUES (?, ?, ?, ?, ?)", content)
def deleteRoom(self, name):
self._connection.runQuery("DELETE FROM persistent_rooms where name = ?", [name])
def loadRooms(self, result=None):
roomsQuery = "SELECT * FROM persistent_rooms"
rooms = self._connection.runQuery(roomsQuery)
rooms.addCallback(self.loadedRooms)
def loadedRooms(self, rooms):
self._loadRoomsCallback(rooms)
class RoomManager(object):
def __init__(self, roomsDir=None, timer=0):
self._roomsDir = roomsDir
self._timer = timer
def __init__(self, roomsdbfile=None, permanentRooms=[]):
self._roomsDbFile = roomsdbfile
self._rooms = {}
if self._roomsDir is not None:
for root, dirs, files in os.walk(self._roomsDir):
for file in files:
if file.endswith(".room"):
room = Room('', self._roomsDir)
room.loadFromFile(os.path.join(root, file))
roomName = truncateText(room.getName(), constants.MAX_ROOM_NAME_LENGTH)
if len(room.getPlaylist()) == 0 or room.isStale(self._timer):
os.remove(os.path.join(root, file))
del room
else:
self._rooms[roomName] = room
self._permanentRooms = permanentRooms
if self._roomsDbFile is not None:
self._roomsDbHandle = RoomDBManager(self._roomsDbFile, self.loadRooms)
self._roomsDbHandle.connect()
else:
self._roomsDbHandle = None
def loadRooms(self, rooms):
roomsLoaded = []
for roomDetails in rooms:
roomName = truncateText(roomDetails[0], constants.MAX_ROOM_NAME_LENGTH)
room = Room(roomDetails[0], self._roomsDbHandle)
room.loadRoom(roomDetails)
if roomName in self._permanentRooms:
room.setPermanent(True)
self._rooms[roomName] = room
roomsLoaded.append(roomName)
for roomName in self._permanentRooms:
if roomName not in roomsLoaded:
roomDetails = (roomName, "", 0, 0, 0)
room = Room(roomName, self._roomsDbHandle)
room.loadRoom(roomDetails)
room.setPermanent(True)
self._rooms[roomName] = room
def broadcastRoom(self, sender, whatLambda):
room = sender.getRoom()
@ -349,6 +431,20 @@ class RoomManager(object):
watchers.append(watcher)
return watchers
def getPersistentRooms(self, sender):
persistentRooms = []
for room in self._rooms.values():
if room.isPersistent():
persistentRooms.append(room.getName())
return persistentRooms
def getEmptyPersistentRooms(self):
emptyPersistentRooms = []
for room in self._rooms.values():
if len(room.getWatchers()) == 0:
emptyPersistentRooms.append(room.getName())
return emptyPersistentRooms
def moveWatcher(self, watcher, roomName):
roomName = truncateText(roomName, constants.MAX_ROOM_NAME_LENGTH)
self.removeWatcher(watcher)
@ -359,11 +455,10 @@ class RoomManager(object):
oldRoom = watcher.getRoom()
if oldRoom:
oldRoom.removeWatcher(watcher)
if self._roomsDir is None or oldRoom.isStale(self._timer):
self._deleteRoomIfEmpty(oldRoom)
self._deleteRoomIfEmpty(oldRoom)
def _getRoom(self, roomName):
if roomName in self._rooms and not self._rooms[roomName].isStale(self._timer):
if roomName in self._rooms:
return self._rooms[roomName]
else:
if RoomPasswordProvider.isControlledRoom(roomName):
@ -371,12 +466,17 @@ class RoomManager(object):
else:
if roomName in self._rooms:
self._deleteRoomIfEmpty(self._rooms[roomName])
room = Room(roomName, self._roomsDir)
room = Room(roomName, self._roomsDbHandle)
self._rooms[roomName] = room
return room
def _deleteRoomIfEmpty(self, room):
if room.isEmpty() and room.getName() in self._rooms:
if room.isEmpty() and room.isNotPermanent() and room.getName() in self._rooms:
if room.isPersistent():
if room.isPlaylistEmpty():
self._roomsDbHandle.deleteRoom(room.getName())
else:
return()
del self._rooms[room.getName()]
def findFreeUsername(self, username):
@ -412,9 +512,9 @@ class Room(object):
STATE_PAUSED = 0
STATE_PLAYING = 1
def __init__(self, name, _roomsDir=None):
def __init__(self, name, roomsdbhandle):
self._name = name
self._roomsDir = _roomsDir
self._roomsDbHandle = roomsdbhandle
self._watchers = {}
self._playState = self.STATE_PAUSED
self._setBy = None
@ -423,51 +523,42 @@ class Room(object):
self._lastUpdate = time.time()
self._lastSavedUpdate = 0
self._position = 0
self._permanent = False
def __str__(self, *args, **kwargs):
return self.getName()
def roomsCanPersist(self):
return self._roomsDir is not None
return self._roomsDbHandle is not None
def isPersistent(self):
return self.roomsCanPersist()
def isPlaylistEmpty(self):
return len(self._playlist) == 0
def isPermanent(self):
return self.roomsCanPersist()
return self._permanent
def isNotPermanent(self):
return not self.isPermanent()
def sanitizeFilename(self, filename, blacklist="<>:/\\|?*\"", placeholder="_"):
return ''.join([c if c not in blacklist and ord(c) >= 32 else placeholder for c in filename])
def writeToFile(self):
if not self.isPermanent():
def writeToDb(self):
if not self.isPersistent():
return
filename = os.path.join(self._roomsDir, self.sanitizeFilename(self._name)+'.room')
if len(self._playlist) == 0:
try:
os.remove(filename)
except Exception:
pass
return
data = {}
data['name'] = self._name
data['playlist'] = self._playlist
data['playlistIndex'] = self._playlistIndex
data['position'] = self._position
data['lastSavedUpdate'] = self._lastSavedUpdate
with open(filename, "w") as outfile:
json.dump(data, outfile)
processed_playlist = getListAsMultilineString(self._playlist)
self._roomsDbHandle.saveRoom(self._name, processed_playlist, self._playlistIndex, self._position, self._lastSavedUpdate)
def loadFromFile(self, filename):
with open(filename) as json_file:
data = json.load(json_file)
self._name = truncateText(data['name'], constants.MAX_ROOM_NAME_LENGTH)
self._playlist = data['playlist']
self._playlistIndex = data['playlistIndex']
self._position = data['position']
self._lastSavedUpdate = data['lastSavedUpdate']
def isStale(self, timer):
if timer == 0 or self._lastSavedUpdate == 0:
return False
return time.time() - self._lastSavedUpdate > timer
def loadRoom(self, room):
name, playlist, playlistindex, position, lastupdate = room
self._name = name
self._playlist = convertMultilineStringToList(playlist)
self._playlistIndex = playlistindex
self._position = position
self._lastSavedUpdate = lastupdate
def getName(self):
return self._name
@ -488,14 +579,17 @@ class Room(object):
def setPaused(self, paused=STATE_PAUSED, setBy=None):
self._playState = paused
self._setBy = setBy
self.writeToFile()
self.writeToDb()
def setPosition(self, position, setBy=None):
self._position = position
for watcher in self._watchers.values():
watcher.setPosition(position)
self._setBy = setBy
self.writeToFile()
self.writeToDb()
def setPermanent(self, newState):
self._permanent = newState
def isPlaying(self):
return self._playState == self.STATE_PLAYING
@ -507,7 +601,7 @@ class Room(object):
return list(self._watchers.values())
def addWatcher(self, watcher):
if self._watchers or self.isPermanent():
if self._watchers or self.isPersistent():
watcher.setPosition(self.getPosition())
self._watchers[watcher.getName()] = watcher
watcher.setRoom(self)
@ -517,9 +611,9 @@ class Room(object):
return
del self._watchers[watcher.getName()]
watcher.setRoom(None)
if not self._watchers and not self.isPermanent():
if not self._watchers and not self.isPersistent():
self._position = 0
self.writeToFile()
self.writeToDb()
def isEmpty(self):
return not bool(self._watchers)
@ -532,11 +626,11 @@ class Room(object):
def setPlaylist(self, files, setBy=None):
self._playlist = files
self.writeToFile()
self.writeToDb()
def setPlaylistIndex(self, index, setBy=None):
self._playlistIndex = index
self.writeToFile()
self.writeToDb()
def getPlaylist(self):
return self._playlist
@ -756,8 +850,8 @@ class ConfigurationGetter(object):
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"), 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('--rooms-dir', metavar='rooms', type=str, nargs='?', help=getMessage("server-rooms-argument"))
self._argparser.add_argument('--rooms-timer', metavar='timer', type=int, nargs='?',default=31558149, help=getMessage("server-timer-argument"))
self._argparser.add_argument('--rooms-db-file', metavar='rooms', type=str, nargs='?', help=getMessage("server-rooms-argument"))
self._argparser.add_argument('--permanent-rooms-file', metavar='permanentrooms', type=str, nargs='?', help=getMessage("server-permanent-rooms-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('--stats-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-stats-db-file-argument"))