commit
bb5d53cc35
@ -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:
|
||||
|
||||
15
.travis.yml
15
.travis.yml
@ -18,11 +18,12 @@ before_install:
|
||||
- python3 --version
|
||||
- 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
|
||||
- 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 -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/
|
||||
- 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
|
||||
@ -31,9 +32,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
|
||||
|
||||
@ -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)/
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"subject": "syncplay"
|
||||
},
|
||||
"version": {
|
||||
"name": "v1.5.6"
|
||||
"name": "v1.6.0"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
|
||||
59
buildPy2exe.py
Executable file → Normal file
59
buildPy2exe.py
Executable file → Normal file
@ -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)
|
||||
@ -1,5 +1,5 @@
|
||||
version = '1.5.6'
|
||||
revision = ' beta'
|
||||
version = '1.6.1'
|
||||
revision = ''
|
||||
milestone = 'Yoitsu'
|
||||
release_number = '66'
|
||||
release_number = '70'
|
||||
projectURL = 'https://syncplay.pl/'
|
||||
|
||||
@ -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
|
||||
@ -53,6 +53,7 @@ DIFFERENT_DURATION_THRESHOLD = 2.5
|
||||
PROTOCOL_TIMEOUT = 12.5
|
||||
RECONNECT_RETRIES = 999
|
||||
SERVER_STATE_INTERVAL = 1
|
||||
SERVER_STATS_SNAPSHOT_INTERVAL = 3600
|
||||
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
|
||||
AUTOPLAY_DELAY = 3.0
|
||||
DO_NOT_RESET_POSITION_THRESHOLD = 1.0
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -434,7 +435,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 {}.",
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -437,8 +438,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.",
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -437,8 +438,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.",
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -440,9 +441,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
|
||||
@ -460,7 +462,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": "Добавить файлы в очередь",
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import hashlib
|
||||
@ -7,6 +6,7 @@ import random
|
||||
import time
|
||||
from string import Template
|
||||
|
||||
from twisted.enterprise import adbapi
|
||||
from twisted.internet import task, reactor
|
||||
from twisted.internet.protocol import Factory
|
||||
|
||||
@ -18,12 +18,14 @@ 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, statsDbFile=None):
|
||||
self.isolateRooms = isolateRooms
|
||||
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:
|
||||
@ -39,6 +41,13 @@ class SyncFactory(Factory):
|
||||
self._roomManager = RoomManager()
|
||||
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._statsRecorder.startRecorder(statsDelay)
|
||||
else:
|
||||
self._statsDbHandle = None
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
return SyncServerProtocol(self)
|
||||
@ -186,6 +195,55 @@ class SyncFactory(Factory):
|
||||
watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex())
|
||||
|
||||
|
||||
class StatsRecorder(object):
|
||||
def __init__(self, dbHandle, roomManager):
|
||||
self._dbHandle = dbHandle
|
||||
self._roomManagerHandle = roomManager
|
||||
|
||||
def startRecorder(self, delay):
|
||||
try:
|
||||
self._dbHandle.connect()
|
||||
reactor.callLater(delay, self._scheduleClientSnapshot)
|
||||
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 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):
|
||||
self._rooms = {}
|
||||
@ -244,6 +302,9 @@ class RoomManager(object):
|
||||
while username.lower() in allnames:
|
||||
username += '_'
|
||||
return username
|
||||
|
||||
def exportRooms(self):
|
||||
return self._rooms
|
||||
|
||||
|
||||
class PublicRoomManager(RoomManager):
|
||||
@ -554,11 +615,12 @@ 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))
|
||||
self._argparser.add_argument('--stats-db-file', metavar='file', type=str, nargs='?', help=getMessage("server-stats-db-file-argument"))
|
||||
|
||||
@ -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):
|
||||
@ -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"))
|
||||
@ -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] == "--":
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
@ -22,11 +22,14 @@ if __name__ == '__main__':
|
||||
reactor.listenTCP(
|
||||
int(args.port),
|
||||
SyncFactory(
|
||||
args.port,
|
||||
args.password,
|
||||
args.motd_file,
|
||||
args.isolate_rooms,
|
||||
args.salt,
|
||||
args.disable_ready,
|
||||
args.disable_chat,
|
||||
args.max_chat_message_length))
|
||||
args.max_chat_message_length,
|
||||
args.max_username_length,
|
||||
args.stats_db_file))
|
||||
reactor.run()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user