From c561b8edf1fab806c9e6d0e6abb6fad959b941ed Mon Sep 17 00:00:00 2001 From: danfai Date: Wed, 15 Sep 2021 20:49:25 +0200 Subject: [PATCH 1/4] syncplay server changes --- syncplay/ep_server.py | 12 ++++++++++-- syncplay/server.py | 8 ++++++-- syncplay/webapi.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 syncplay/webapi.py diff --git a/syncplay/ep_server.py b/syncplay/ep_server.py index 22aec440d..132ccc61c 100644 --- a/syncplay/ep_server.py +++ b/syncplay/ep_server.py @@ -1,10 +1,13 @@ import sys -from twisted.internet import reactor +from twisted.internet import reactor, endpoints from twisted.internet.endpoints import TCP4ServerEndpoint, TCP6ServerEndpoint from twisted.internet.error import CannotListenError +from twisted.web import server, resource + from syncplay.server import SyncFactory, ConfigurationGetter +from syncplay.webapi import WebAPI class ServerStatus: pass @@ -27,6 +30,7 @@ def failed4(f): print(f.value) print("IPv4 listening failed.") + def main(): argsGetter = ConfigurationGetter() args = argsGetter.getConfiguration() @@ -43,6 +47,9 @@ def main(): args.stats_db_file, args.tls ) + site = server.Site(WebAPI(factory)) + endpoint = endpoints.TCP4ServerEndpoint(reactor, 8080) + endpoint.listen(site) endpoint6 = TCP6ServerEndpoint(reactor, int(args.port)) endpoint6.listen(factory).addCallbacks(isListening6, failed6) endpoint4 = TCP4ServerEndpoint(reactor, int(args.port)) @@ -54,4 +61,5 @@ def main(): sys.exit() if __name__ == "__main__": - main() \ No newline at end of file + main() + diff --git a/syncplay/server.py b/syncplay/server.py index 21de91b94..b3f9408ab 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -260,6 +260,9 @@ def updateTLSContextFactory(self): if self._TLSattempts < constants.TLS_CERT_ROTATION_MAX_RETRIES: self.serverAcceptsTLS = True + def _getRoomManager(self): + return self._roomManager + class StatsRecorder(object): def __init__(self, dbHandle, roomManager): @@ -356,8 +359,9 @@ def _getRoom(self, roomName): return room def _deleteRoomIfEmpty(self, room): - if room.isEmpty() and room.getName() in self._rooms: - del self._rooms[room.getName()] + #if room.isEmpty() and room.getName() in self._rooms: + # del self._rooms[room.getName()] + print("Would delete room: " + room.getName()) def findFreeUsername(self, username): username = truncateText(username, constants.MAX_USERNAME_LENGTH) diff --git a/syncplay/webapi.py b/syncplay/webapi.py new file mode 100644 index 000000000..84e888356 --- /dev/null +++ b/syncplay/webapi.py @@ -0,0 +1,38 @@ +from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomStringGenerator, meetsMinVersion, playlistIsValid, truncateText +from twisted.web import server, resource +import syncplay +from syncplay import constants +import json + +class WebAPI(resource.Resource): + isLeaf = True + def __init__(self, factory): + self._factory = factory + + def render_GET(self, request): + return (b"" + b"" + b"
") + + def render_POST(self, request): + url = "" + r = "" + if b"url" in request.args and b"room" in request.args: + url = request.args[b"url"][0].decode("utf-8") + r = request.args[b"room"][0].decode("utf-8") + else: + obj = json.load(request.content) + #print(obj) + url = obj["text"] + r = obj["channel_name"] + + room = self._factory._getRoomManager()._getRoom(r) + pl = room.getPlaylist() + pl += [url] + room.setPlaylist(pl) + #print("Room: " + r + " - adding: " + url) + for receiver in room.getWatchers(): + receiver.setPlaylist(room.getName(), room.getPlaylist()) + + return b"Inserted" + From 9357eb9a53e12511f3dba86e87819afd66d01464 Mon Sep 17 00:00:00 2001 From: danfai Date: Tue, 8 Mar 2022 20:20:56 +0100 Subject: [PATCH 2/4] Add messages, doc and argument for Webhook API --- docs/syncplay-server.1 | 4 ++++ syncplay/ep_server.py | 5 +++-- syncplay/messages_de.py | 1 + syncplay/messages_en.py | 1 + syncplay/messages_es.py | 1 + syncplay/messages_fr.py | 1 + syncplay/messages_it.py | 1 + syncplay/messages_pt_BR.py | 1 + syncplay/messages_pt_PT.py | 1 + syncplay/messages_ru.py | 1 + syncplay/messages_tr.py | 1 + syncplay/messages_zh_CN.py | 1 + syncplay/server.py | 1 + syncplay/webapi.py | 1 + 14 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/syncplay-server.1 b/docs/syncplay-server.1 index 7bd1847db..8c4007adb 100644 --- a/docs/syncplay-server.1 +++ b/docs/syncplay-server.1 @@ -102,6 +102,10 @@ Enable server statistics using the SQLite database file. .B \-\-tls [path] Enable TLS connections using the certificate files in path. +.TP +.B \-\-webhook-port [port] +Enables the Webhook API over port port. This API enables adding entries to the playlist over HTTP. + .SH SEE ALSO .BR syncplay (1). diff --git a/syncplay/ep_server.py b/syncplay/ep_server.py index 7d97e7ab7..3a935a778 100644 --- a/syncplay/ep_server.py +++ b/syncplay/ep_server.py @@ -50,8 +50,9 @@ def main(): args.tls ) site = server.Site(WebAPI(factory)) - endpoint = endpoints.TCP4ServerEndpoint(reactor, 8080) - endpoint.listen(site) + if args.webhook_port: + endpoint = endpoints.TCP4ServerEndpoint(reactor, int(args.webhook_port)) + endpoint.listen(site) endpoint6 = TCP6ServerEndpoint(reactor, int(args.port)) endpoint6.listen(factory).addCallbacks(isListening6, failed6) endpoint4 = TCP4ServerEndpoint(reactor, int(args.port)) diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index 79e138891..ca9cfae46 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -483,6 +483,7 @@ "server-maxusernamelength-argument": "Maximale Zeichenzahl in einem Benutzernamen (Standard ist {})", "server-stats-db-file-argument": "Aktiviere Server-Statistiken mithilfe der bereitgestellten SQLite-db-Datei", "server-startTLS-argument": "Erlaube TLS-Verbindungen mit den Zertifikatdateien im Angegebenen Pfad", + "server-webhook-port-argument": 'Server Webhook TCP-port', "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 97491fed3..ccc5dcba3 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -484,6 +484,7 @@ "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-startTLS-argument": "Enable TLS connections using the certificate files in the path provided", + "server-webhook-port-argument": 'server Webhook TCP port', "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_es.py b/syncplay/messages_es.py index 5e867623e..37495dca3 100644 --- a/syncplay/messages_es.py +++ b/syncplay/messages_es.py @@ -483,6 +483,7 @@ "server-maxusernamelength-argument": "Número máximo de caracteres para el nombre de usuario (el valor predeterminado es {})", "server-stats-db-file-argument": "Habilitar estadísticas del servidor utilizando el archivo db SQLite proporcionado", "server-startTLS-argument": "Habilitar conexiones TLS usando los archivos de certificado en la ruta provista", + "server-webhook-port-argument": 'server Webhook TCP port', #TODO translate "server-messed-up-motd-unescaped-placeholders": "El mensaje del dia contiene marcadores de posición sin escapar. Todos los signos $ deberían ser dobles ($$).", "server-messed-up-motd-too-long": "El mensaje del día es muy largo - máximo de {} caracteres, se recibieron {}.", diff --git a/syncplay/messages_fr.py b/syncplay/messages_fr.py index f5c88a994..f6c50468a 100644 --- a/syncplay/messages_fr.py +++ b/syncplay/messages_fr.py @@ -483,6 +483,7 @@ "server-maxusernamelength-argument": "Nombre maximum de caractères dans un nom d'utilisateur (la valeur par défaut est {})", "server-stats-db-file-argument": "Activer les statistiques du serveur à l'aide du fichier db SQLite fourni", "server-startTLS-argument": "Activer les connexions TLS à l'aide des fichiers de certificat dans le chemin fourni", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "Le message du jour a des espaces réservés non échappés. Tous les signes $ doivent être doublés ($$).", "server-messed-up-motd-too-long": "Le message du jour est trop long: {}caractères maximum, {} donnés.", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index 9e80cb4b2..e7bb817f8 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -483,6 +483,7 @@ "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-startTLS-argument": "Abilita il protocollo TLS usando i certificati contenuti nel percorso indicato", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "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_pt_BR.py b/syncplay/messages_pt_BR.py index 96f8b97c7..86417e364 100644 --- a/syncplay/messages_pt_BR.py +++ b/syncplay/messages_pt_BR.py @@ -484,6 +484,7 @@ "server-maxusernamelength-argument": "Número máximos de caracteres num nome de usuário (o padrão é {})", "server-stats-db-file-argument": "Habilita estatísticas de servidor usando o arquivo db SQLite fornecido", "server-startTLS-argument": "Habilita conexões TLS usando os arquivos de certificado no caminho fornecido", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "A Mensagem do Dia possui placeholders não escapados. Todos os sinais de $ devem ser dobrados (como em $$).", "server-messed-up-motd-too-long": "A Mensagem do Dia é muito longa - máximo de {} caracteres, {} foram dados.", diff --git a/syncplay/messages_pt_PT.py b/syncplay/messages_pt_PT.py index d09a65ecd..bc0090587 100644 --- a/syncplay/messages_pt_PT.py +++ b/syncplay/messages_pt_PT.py @@ -483,6 +483,7 @@ "server-maxusernamelength-argument": "Número máximos de caracteres num nome de utilizador (o padrão é {})", "server-stats-db-file-argument": "Habilita estatísticas de servidor usando o arquivo db SQLite fornecido", "server-startTLS-argument": "Habilita conexões TLS usando os arquivos de certificado no caminho fornecido", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "A Mensagem do Dia possui placeholders não escapados. Todos os sinais de $ devem ser dobrados (como em $$).", "server-messed-up-motd-too-long": "A Mensagem do Dia é muito longa - máximo de {} caracteres, {} foram dados.", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index cb1302eab..bda562357 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -480,6 +480,7 @@ "server-maxusernamelength-argument": "Максимальное число символов в именах пользователей (по умолчанию {})", "server-stats-db-file-argument": "Включить статистику сервера в указанном файле SQLite", "server-startTLS-argument": "Включить TLS-соединения используя файлы сертификатов в указанном пути", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "MOTD-сообщение содержит неэкранированные спецсимволы. Все знаки $ должны быть продублированы ($$).", "server-messed-up-motd-too-long": "MOTD-сообщение слишком длинное: максимальная длина - {} символ(ов), текущая длина - {} символ(ов).", diff --git a/syncplay/messages_tr.py b/syncplay/messages_tr.py index b4f6cd94f..149faebb6 100644 --- a/syncplay/messages_tr.py +++ b/syncplay/messages_tr.py @@ -484,6 +484,7 @@ "server-maxusernamelength-argument": "Bir kullanıcı adındaki maksimum karakter sayısı (varsayılan {})", "server-stats-db-file-argument": "SQLite db dosyasını kullanarak sunucu istatistiklerini etkinleştirin", "server-startTLS-argument": "Dosya yolundaki sertifika dosyalarını kullanarak TLS bağlantılarını etkinleştirin", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "Günün Mesajında çıkış karaktersiz yer tutucular var. Tüm $ işaretleri iki katına çıkarılmalıdır ($$).", "server-messed-up-motd-too-long": "Günün Mesajı çok uzun - maksimum {} karakter olmalı, {} verildi.", diff --git a/syncplay/messages_zh_CN.py b/syncplay/messages_zh_CN.py index 19b4ab106..aff95eb3a 100644 --- a/syncplay/messages_zh_CN.py +++ b/syncplay/messages_zh_CN.py @@ -484,6 +484,7 @@ "server-maxusernamelength-argument": "用户名中的最大字符数(默认为{})。", "server-stats-db-file-argument": "使用提供的SQLite db文件启用服务器统计功能", "server-startTLS-argument": "使用提供的路径中的证书文件启用TLS连接", + "server-webhook-port-argument": 'server Webhook TCP port', # TODO translate "server-messed-up-motd-unescaped-placeholders": "每日信息中有未转义的占位符。所有 $ 字符应当重复两遍 ($$).", "server-messed-up-motd-too-long": "每日信息过长 - 最大{}个chars, 给出的长度{}", diff --git a/syncplay/server.py b/syncplay/server.py index d03fc3d13..d762197e1 100755 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -892,3 +892,4 @@ def _prepareArgParser(self): 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")) self._argparser.add_argument('--tls', metavar='path', type=str, nargs='?', help=getMessage("server-startTLS-argument")) + self._argparser.add_argument('--webhook-port', metavar='webhook_port', type=str, nargs='?', help=getMessage("server-webhook-port-argument")) diff --git a/syncplay/webapi.py b/syncplay/webapi.py index 84e888356..9ca288a5e 100644 --- a/syncplay/webapi.py +++ b/syncplay/webapi.py @@ -10,6 +10,7 @@ def __init__(self, factory): self._factory = factory def render_GET(self, request): + # return a simple form for browsers return (b"" b"" b"
") From ed5b4501ba7a254c767f7e7325787db38be3859e Mon Sep 17 00:00:00 2001 From: danfai Date: Tue, 8 Mar 2022 20:25:46 +0100 Subject: [PATCH 3/4] webapi make form more userfriendly --- syncplay/webapi.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/syncplay/webapi.py b/syncplay/webapi.py index 9ca288a5e..b98788ebd 100644 --- a/syncplay/webapi.py +++ b/syncplay/webapi.py @@ -13,7 +13,9 @@ def render_GET(self, request): # return a simple form for browsers return (b"" b"" - b"
") + b"

" + b"
" + b"
") def render_POST(self, request): url = "" From 75b32bd9686bce4fff3a92e781acb5703df76d18 Mon Sep 17 00:00:00 2001 From: danfai Date: Tue, 8 Mar 2022 20:43:50 +0100 Subject: [PATCH 4/4] WebAPI IPv6 support --- syncplay/ep_server.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/syncplay/ep_server.py b/syncplay/ep_server.py index 3a935a778..081a571b6 100644 --- a/syncplay/ep_server.py +++ b/syncplay/ep_server.py @@ -49,10 +49,12 @@ def main(): args.stats_db_file, args.tls ) - site = server.Site(WebAPI(factory)) if args.webhook_port: - endpoint = endpoints.TCP4ServerEndpoint(reactor, int(args.webhook_port)) - endpoint.listen(site) + site = server.Site(WebAPI(factory)) + webapi6 = TCP6ServerEndpoint(reactor, int(args.webhook_port)) + webapi6.listen(site) + #webapi4 = TCP4ServerEndpoint(reactor, int(args.webhook_port)) + #webapi4.listen(site) endpoint6 = TCP6ServerEndpoint(reactor, int(args.port)) endpoint6.listen(factory).addCallbacks(isListening6, failed6) endpoint4 = TCP4ServerEndpoint(reactor, int(args.port))