Finished cleanup
This commit is contained in:
parent
4dea39a068
commit
aa4f95e4be
@ -1,22 +1,27 @@
|
||||
|
||||
import ast
|
||||
import collections
|
||||
import hashlib
|
||||
import os.path
|
||||
import time
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import ast
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from functools import wraps
|
||||
|
||||
from twisted.internet.protocol import ClientFactory
|
||||
from twisted.internet import reactor, task, defer, threads
|
||||
from functools import wraps
|
||||
from copy import deepcopy
|
||||
from syncplay.protocols import SyncClientProtocol
|
||||
|
||||
from syncplay import utils, constants, version
|
||||
from syncplay.utils import isMacOS
|
||||
from syncplay.messages import getMissingStrings, getMessage
|
||||
from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE, \
|
||||
PRIVACY_HIDDENFILENAME
|
||||
import collections
|
||||
from syncplay.messages import getMissingStrings, getMessage
|
||||
from syncplay.protocols import SyncClientProtocol
|
||||
from syncplay.utils import isMacOS
|
||||
|
||||
|
||||
class SyncClientFactory(ClientFactory):
|
||||
def __init__(self, client, retry=constants.RECONNECT_RETRIES):
|
||||
self._client = client
|
||||
@ -58,6 +63,7 @@ class SyncClientFactory(ClientFactory):
|
||||
def stopRetrying(self):
|
||||
self._timesTried = self.retry
|
||||
|
||||
|
||||
class SyncplayClient(object):
|
||||
def __init__(self, playerClass, ui, config):
|
||||
constants.SHOW_OSD = config['showOSD']
|
||||
@ -203,12 +209,19 @@ class SyncplayClient(object):
|
||||
self._playerPosition = position
|
||||
self._playerPaused = paused
|
||||
currentLength = self.userlist.currentUser.file["duration"] if self.userlist.currentUser.file else 0
|
||||
if pauseChange and paused and currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH\
|
||||
and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD:
|
||||
if (
|
||||
pauseChange and paused and currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH
|
||||
and abs(position - currentLength) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD
|
||||
):
|
||||
self.playlist.advancePlaylistCheck()
|
||||
elif pauseChange and "readiness" in self.serverFeatures and self.serverFeatures["readiness"]:
|
||||
if currentLength == 0 or currentLength == -1 or\
|
||||
not (not self.playlist.notJustChangedPlaylist() and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD):
|
||||
if (
|
||||
currentLength == 0 or currentLength == -1 or
|
||||
not (
|
||||
not self.playlist.notJustChangedPlaylist() and
|
||||
abs(position - currentLength) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD
|
||||
)
|
||||
):
|
||||
pauseChange = self._toggleReady(pauseChange, paused)
|
||||
|
||||
if self._lastGlobalUpdate:
|
||||
@ -304,7 +317,7 @@ class SyncplayClient(object):
|
||||
self.setPosition(self.getGlobalPosition())
|
||||
self._player.setPaused(True)
|
||||
madeChangeOnPlayer = True
|
||||
if (self.lastLeftTime < time.time() - constants.OSD_DURATION) or (hideFromOSD == True):
|
||||
if (self.lastLeftTime < time.time() - constants.OSD_DURATION) or hideFromOSD:
|
||||
self.ui.showMessage(getMessage("pause-notification").format(setBy), hideFromOSD)
|
||||
else:
|
||||
self.ui.showMessage(getMessage("left-paused-notification").format(self.lastLeftUser, setBy), hideFromOSD)
|
||||
@ -351,9 +364,9 @@ class SyncplayClient(object):
|
||||
self._lastGlobalUpdate = time.time()
|
||||
if doSeek:
|
||||
madeChangeOnPlayer = self._serverSeeked(position, setBy)
|
||||
if diff > self._config['rewindThreshold'] and not doSeek and not self._config['rewindOnDesync'] == False:
|
||||
if diff > self._config['rewindThreshold'] and not doSeek and self._config['rewindOnDesync']:
|
||||
madeChangeOnPlayer = self._rewindPlayerDueToTimeDifference(position, setBy)
|
||||
if self._config['fastforwardOnDesync'] and (self.userlist.currentUser.canControl() == False or self._config['dontSlowDownWithMe'] == True):
|
||||
if self._config['fastforwardOnDesync'] and (not self.userlist.currentUser.canControl() or self._config['dontSlowDownWithMe']):
|
||||
if diff < (constants.FASTFORWARD_BEHIND_THRESHOLD * -1) and not doSeek:
|
||||
if self.behindFirstDetected is None:
|
||||
self.behindFirstDetected = time.time()
|
||||
@ -365,11 +378,11 @@ class SyncplayClient(object):
|
||||
self.behindFirstDetected = time.time() + constants.FASTFORWARD_RESET_THRESHOLD
|
||||
else:
|
||||
self.behindFirstDetected = None
|
||||
if self._player.speedSupported and not doSeek and not paused and not self._config['slowOnDesync'] == False:
|
||||
if self._player.speedSupported and not doSeek and not paused and self._config['slowOnDesync']:
|
||||
madeChangeOnPlayer = self._slowDownToCoverTimeDifference(diff, setBy)
|
||||
if paused == False and pauseChanged:
|
||||
if not paused and pauseChanged:
|
||||
madeChangeOnPlayer = self._serverUnpaused(setBy)
|
||||
elif paused == True and pauseChanged:
|
||||
elif not paused and pauseChanged:
|
||||
madeChangeOnPlayer = self._serverPaused(setBy)
|
||||
return madeChangeOnPlayer
|
||||
|
||||
@ -566,11 +579,11 @@ class SyncplayClient(object):
|
||||
constants.MAX_ROOM_NAME_LENGTH = self.serverFeatures["maxRoomNameLength"]
|
||||
if self.serverFeatures["maxFilenameLength"] is not None:
|
||||
constants.MAX_FILENAME_LENGTH = self.serverFeatures["maxFilenameLength"]
|
||||
constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(constants.MAX_CHAT_MESSAGE_LENGTH),
|
||||
constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = [
|
||||
"MaxChatMessageLength={}".format(constants.MAX_CHAT_MESSAGE_LENGTH),
|
||||
"inputPromptStartCharacter={}".format(constants.MPV_INPUT_PROMPT_START_CHARACTER),
|
||||
"inputPromptEndCharacter={}".format(constants.MPV_INPUT_PROMPT_END_CHARACTER),
|
||||
"backslashSubstituteCharacter={}".format(
|
||||
constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||
"backslashSubstituteCharacter={}".format(constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||
self.ui.setFeatures(self.serverFeatures)
|
||||
if self._player:
|
||||
self._player.setFeatures(self.serverFeatures)
|
||||
@ -586,7 +599,6 @@ class SyncplayClient(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def sendFile(self):
|
||||
file_ = self.getSanitizedCurrentUserFile()
|
||||
if self._protocol and self._protocol.logged and file_:
|
||||
@ -762,7 +774,7 @@ class SyncplayClient(object):
|
||||
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
|
||||
ConfigurationGetter().setConfigOption("sharedPlaylistEnabled", newState)
|
||||
self._config["sharedPlaylistEnabled"] = newState
|
||||
if oldState == False and newState == True:
|
||||
if not oldState and newState:
|
||||
self.playlist.loadCurrentPlaylistIndex()
|
||||
|
||||
def changeAutoplayState(self, newState):
|
||||
@ -773,7 +785,7 @@ class SyncplayClient(object):
|
||||
oldAutoplayConditionsMet = self.autoplayConditionsMet()
|
||||
self.autoPlayThreshold = newThreshold
|
||||
newAutoplayConditionsMet = self.autoplayConditionsMet()
|
||||
if oldAutoplayConditionsMet == False and newAutoplayConditionsMet == True:
|
||||
if not oldAutoplayConditionsMet and newAutoplayConditionsMet:
|
||||
self.autoplayCheck()
|
||||
|
||||
def autoplayCheck(self):
|
||||
@ -799,9 +811,12 @@ class SyncplayClient(object):
|
||||
|
||||
def autoplayConditionsMet(self):
|
||||
recentlyReset = (self.lastRewindTime is not None and abs(time.time() - self.lastRewindTime) < 10) and self._playerPosition < 3
|
||||
return self._playerPaused and (self.autoPlay or recentlyReset) and self.userlist.currentUser.canControl() and self.userlist.isReadinessSupported()\
|
||||
and self.userlist.areAllUsersInRoomReady(requireSameFilenames=self._config["autoplayRequireSameFilenames"])\
|
||||
return (
|
||||
self._playerPaused and (self.autoPlay or recentlyReset) and
|
||||
self.userlist.currentUser.canControl() and self.userlist.isReadinessSupported()
|
||||
and self.userlist.areAllUsersInRoomReady(requireSameFilenames=self._config["autoplayRequireSameFilenames"])
|
||||
and ((self.autoPlayThreshold and self.userlist.usersInRoomCount() >= self.autoPlayThreshold) or recentlyReset)
|
||||
)
|
||||
|
||||
def autoplayTimerIsRunning(self):
|
||||
return self.autoplayTimer.running
|
||||
@ -1049,6 +1064,7 @@ class SyncplayClient(object):
|
||||
elif not self._userlist.currentUser.isReady(): # CurrentUser should always be reminded they are set to not ready
|
||||
self.checkReadyStates()
|
||||
|
||||
|
||||
class SyncplayUser(object):
|
||||
def __init__(self, username=None, room=None, file_=None):
|
||||
self.ready = None
|
||||
@ -1113,6 +1129,7 @@ class SyncplayUser(object):
|
||||
def setFeatures(self, features):
|
||||
self._features = features
|
||||
|
||||
|
||||
class SyncplayUserlist(object):
|
||||
def __init__(self, ui, client):
|
||||
self.currentUser = SyncplayUser()
|
||||
@ -1141,7 +1158,7 @@ class SyncplayUserlist(object):
|
||||
showOnOSD = constants.SHOW_OSD_WARNINGS
|
||||
else:
|
||||
showOnOSD = constants.SHOW_DIFFERENT_ROOM_OSD
|
||||
if constants.SHOW_NONCONTROLLER_OSD == False and self.canControl(username) == False:
|
||||
if not constants.SHOW_NONCONTROLLER_OSD and not self.canControl(username):
|
||||
showOnOSD = False
|
||||
hideFromOSD = not showOnOSD
|
||||
if not file_:
|
||||
@ -1258,18 +1275,22 @@ class SyncplayUserlist(object):
|
||||
return False
|
||||
for user in self._users.values():
|
||||
if user.room == self.currentUser.room:
|
||||
if user.isReadyWithFile() == False:
|
||||
if not user.isReadyWithFile():
|
||||
return False
|
||||
elif requireSameFilenames and\
|
||||
(self.currentUser.file is None
|
||||
elif (
|
||||
requireSameFilenames and
|
||||
(
|
||||
self.currentUser.file is None
|
||||
or user.file is None
|
||||
or not utils.sameFilename(self.currentUser.file['name'], user.file['name'])):
|
||||
or not utils.sameFilename(self.currentUser.file['name'], user.file['name'])
|
||||
)
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def areAllOtherUsersInRoomReady(self):
|
||||
for user in self._users.values():
|
||||
if user.room == self.currentUser.room and user.isReadyWithFile() == False:
|
||||
if user.room == self.currentUser.room and not user.isReadyWithFile():
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -1294,7 +1315,7 @@ class SyncplayUserlist(object):
|
||||
if not self.currentUser.isReady():
|
||||
notReady.append(self.currentUser.username)
|
||||
for user in self._users.values():
|
||||
if user.room == self.currentUser.room and user.isReadyWithFile() == False:
|
||||
if user.room == self.currentUser.room and not user.isReadyWithFile():
|
||||
notReady.append(user.username)
|
||||
return ", ".join(notReady)
|
||||
|
||||
@ -1394,6 +1415,7 @@ class SyncplayUserlist(object):
|
||||
rooms = collections.OrderedDict(sorted(list(rooms.items()), key=lambda s: s[0].lower()))
|
||||
return rooms
|
||||
|
||||
|
||||
class UiManager(object):
|
||||
def __init__(self, client, ui):
|
||||
self._client = client
|
||||
@ -1429,7 +1451,8 @@ class UiManager(object):
|
||||
self.__ui.showMessage(messageString)
|
||||
|
||||
def showMessage(self, message, noPlayer=False, noTimestamp=False, OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
|
||||
if not noPlayer: self.showOSDMessage(message, duration=constants.OSD_DURATION, OSDType=OSDType, mood=mood)
|
||||
if not noPlayer:
|
||||
self.showOSDMessage(message, duration=constants.OSD_DURATION, OSDType=OSDType, mood=mood)
|
||||
self.__ui.showMessage(message, noTimestamp)
|
||||
|
||||
def updateAutoPlayState(self, newState):
|
||||
@ -1487,6 +1510,7 @@ class UiManager(object):
|
||||
def drop(self):
|
||||
self.__ui.drop()
|
||||
|
||||
|
||||
class SyncplayPlaylist():
|
||||
def __init__(self, client):
|
||||
self._client = client
|
||||
@ -1686,9 +1710,11 @@ class SyncplayPlaylist():
|
||||
def advancePlaylistCheck(self):
|
||||
position = self._client.getStoredPlayerPosition()
|
||||
currentLength = self._client.userlist.currentUser.file["duration"] if self._client.userlist.currentUser.file else 0
|
||||
if currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH\
|
||||
and abs(position - currentLength ) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD\
|
||||
and self.notJustChangedPlaylist():
|
||||
if (
|
||||
currentLength > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH and
|
||||
abs(position - currentLength) < constants.PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD and
|
||||
self.notJustChangedPlaylist()
|
||||
):
|
||||
self.loadNextFileInPlaylist()
|
||||
|
||||
def notJustChangedPlaylist(self):
|
||||
@ -1759,6 +1785,7 @@ class SyncplayPlaylist():
|
||||
def _playlistBufferNeedsUpdating(self, newPlaylist):
|
||||
return self._previousPlaylist != self._playlist and self._playlist != newPlaylist
|
||||
|
||||
|
||||
class FileSwitchManager(object):
|
||||
def __init__(self, client):
|
||||
self._client = client
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
|
||||
|
||||
from syncplay import ui
|
||||
from syncplay.messages import getMessage
|
||||
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
|
||||
|
||||
|
||||
class SyncplayClientManager(object):
|
||||
def run(self):
|
||||
@ -13,4 +15,3 @@ class SyncplayClientManager(object):
|
||||
syncplayClient.start(config['host'], config['port'])
|
||||
else:
|
||||
interface.showErrorMessage(getMessage("unable-to-start-client-error"), True)
|
||||
|
||||
|
||||
@ -16,7 +16,12 @@ SHOW_CONTACT_INFO = True # Displays dev contact details below list in GUI
|
||||
SHOW_TOOLTIPS = True
|
||||
WARN_ABOUT_MISSING_STRINGS = False # (If debug mode is enabled)
|
||||
FALLBACK_INITIAL_LANGUAGE = "en"
|
||||
FALLBACK_PUBLIC_SYNCPLAY_SERVERS = [['syncplay.pl:8995 (France)', 'syncplay.pl:8995'],['syncplay.pl:8996 (France)', 'syncplay.pl:8996'],['syncplay.pl:8997 (France)', 'syncplay.pl:8997'],['syncplay.pl:8998 (France)', 'syncplay.pl:8998'],['syncplay.pl:8999 (France)', 'syncplay.pl:8999']]
|
||||
FALLBACK_PUBLIC_SYNCPLAY_SERVERS = [
|
||||
['syncplay.pl:8995 (France)', 'syncplay.pl:8995'],
|
||||
['syncplay.pl:8996 (France)', 'syncplay.pl:8996'],
|
||||
['syncplay.pl:8997 (France)', 'syncplay.pl:8997'],
|
||||
['syncplay.pl:8998 (France)', 'syncplay.pl:8998'],
|
||||
['syncplay.pl:8999 (France)', 'syncplay.pl:8999']]
|
||||
PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH = 10 # Seconds
|
||||
PLAYLIST_LOAD_NEXT_FILE_TIME_FROM_END_THRESHOLD = 5 # Seconds (only triggered if file is paused, e.g. due to EOF)
|
||||
|
||||
@ -200,7 +205,11 @@ MPV_SYNCPLAYINTF_OPTIONS_TO_SEND = ["chatInputEnabled","chatInputFontFamily", "c
|
||||
"chatTopMargin", "chatLeftMargin", "chatBottomMargin", "chatDirectInput",
|
||||
"notificationTimeout", "alertTimeout", "chatTimeout", "chatOutputEnabled"]
|
||||
|
||||
MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH),"inputPromptStartCharacter={}".format(MPV_INPUT_PROMPT_START_CHARACTER),"inputPromptEndCharacter={}".format(MPV_INPUT_PROMPT_END_CHARACTER),"backslashSubstituteCharacter={}".format(MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||
MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = [
|
||||
"MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH),
|
||||
"inputPromptStartCharacter={}".format(MPV_INPUT_PROMPT_START_CHARACTER),
|
||||
"inputPromptEndCharacter={}".format(MPV_INPUT_PROMPT_END_CHARACTER),
|
||||
"backslashSubstituteCharacter={}".format(MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER)]
|
||||
# Note: Constants updated in client.py->checkForFeatureSupport
|
||||
MPV_SYNCPLAYINTF_LANGUAGE_TO_SEND = ["mpv-key-tab-hint", "mpv-key-hint", "alphakey-mode-warning-first-line", "alphakey-mode-warning-second-line"]
|
||||
VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek',
|
||||
|
||||
@ -14,6 +14,7 @@ messages = {
|
||||
"CURRENT": None
|
||||
}
|
||||
|
||||
|
||||
def getLanguages():
|
||||
langList = {}
|
||||
for lang in messages:
|
||||
@ -21,9 +22,11 @@ def getLanguages():
|
||||
langList[lang] = getMessage("LANGUAGE", lang)
|
||||
return langList
|
||||
|
||||
|
||||
def setLanguage(lang):
|
||||
messages["CURRENT"] = lang
|
||||
|
||||
|
||||
def getMissingStrings():
|
||||
missingStrings = ""
|
||||
for lang in messages:
|
||||
@ -37,6 +40,7 @@ def getMissingStrings():
|
||||
|
||||
return missingStrings
|
||||
|
||||
|
||||
def getInitialLanguage():
|
||||
import locale
|
||||
try:
|
||||
@ -47,11 +51,13 @@ def getInitialLanguage():
|
||||
initialLanguage = constants.FALLBACK_INITIAL_LANGUAGE
|
||||
return initialLanguage
|
||||
|
||||
|
||||
def isValidLanguage(language):
|
||||
return language in messages
|
||||
|
||||
|
||||
def getMessage(type_, locale=None):
|
||||
if constants.SHOW_TOOLTIPS == False:
|
||||
if not constants.SHOW_TOOLTIPS:
|
||||
if "-tooltip" in type_:
|
||||
return ""
|
||||
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
# coding:utf8
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
import json
|
||||
import syncplay
|
||||
from functools import wraps
|
||||
import time
|
||||
from syncplay.messages import getMessage
|
||||
from functools import wraps
|
||||
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
|
||||
import syncplay
|
||||
from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT, CONTROLLED_ROOMS_MIN_VERSION, USER_READY_MIN_VERSION, SHARED_PLAYLIST_MIN_VERSION, CHAT_MIN_VERSION
|
||||
from syncplay.messages import getMessage
|
||||
from syncplay.utils import meetsMinVersion
|
||||
|
||||
|
||||
class JSONCommandProtocol(LineReceiver):
|
||||
def handleMessages(self, messages):
|
||||
for message in messages.items():
|
||||
@ -102,9 +105,11 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
hello = {}
|
||||
hello["username"] = self._client.getUsername()
|
||||
password = self._client.getPassword()
|
||||
if password: hello["password"] = password
|
||||
if password:
|
||||
hello["password"] = password
|
||||
room = self._client.getRoom()
|
||||
if room: hello["room"] = {"name" :room}
|
||||
if room:
|
||||
hello["room"] = {"name": room}
|
||||
hello["version"] = "1.2.255" # Used so newer clients work on 1.2.X server
|
||||
hello["realversion"] = syncplay.version
|
||||
hello["features"] = self._client.getFeatures()
|
||||
@ -160,7 +165,8 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
def sendRoomSetting(self, roomName, password=None):
|
||||
setting = {}
|
||||
setting["name"] = roomName
|
||||
if password: setting["password"] = password
|
||||
if password:
|
||||
setting["password"] = password
|
||||
self.sendSet({"room": setting})
|
||||
|
||||
def sendFileSetting(self, file_):
|
||||
@ -231,7 +237,8 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
state["playstate"] = {}
|
||||
state["playstate"]["position"] = position
|
||||
state["playstate"]["paused"] = paused
|
||||
if doSeek: state["playstate"]["doSeek"] = doSeek
|
||||
if doSeek:
|
||||
state["playstate"]["doSeek"] = doSeek
|
||||
state["ping"] = {}
|
||||
if latencyCalculation:
|
||||
state["ping"]["latencyCalculation"] = latencyCalculation
|
||||
@ -255,6 +262,7 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
"password": password
|
||||
}
|
||||
})
|
||||
|
||||
def handleChat(self, message):
|
||||
username = message['username']
|
||||
userMessage = message['message']
|
||||
@ -282,13 +290,13 @@ class SyncClientProtocol(JSONCommandProtocol):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def handleError(self, error):
|
||||
self.dropWithError(error["message"])
|
||||
|
||||
def sendError(self, message):
|
||||
self.sendMessage({"Error": {"message": message}})
|
||||
|
||||
|
||||
class SyncServerProtocol(JSONCommandProtocol):
|
||||
def __init__(self, factory):
|
||||
self._factory = factory
|
||||
@ -410,7 +418,8 @@ class SyncServerProtocol(JSONCommandProtocol):
|
||||
hello["username"] = username
|
||||
userIp = self.transport.getPeer().host
|
||||
room = self._watcher.getRoom()
|
||||
if room: hello["room"] = {"name": room.getName()}
|
||||
if room:
|
||||
hello["room"] = {"name": room.getName()}
|
||||
hello["version"] = clientVersion # Used so 1.2.X client works on newer server
|
||||
hello["realversion"] = syncplay.version
|
||||
hello["motd"] = self._factory.getMotd(userIp, username, room, clientVersion)
|
||||
@ -461,7 +470,6 @@ class SyncServerProtocol(JSONCommandProtocol):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def sendSetReady(self, username, isReady, manuallyInitiated=True):
|
||||
self.sendSet({
|
||||
"ready": {
|
||||
@ -556,7 +564,6 @@ class SyncServerProtocol(JSONCommandProtocol):
|
||||
if self.serverIgnoringOnTheFly == 0 or forced:
|
||||
self.sendMessage({"State": state})
|
||||
|
||||
|
||||
def _extractStatePlaystateArguments(self, state):
|
||||
position = state["playstate"]["position"] if "position" in state["playstate"] else 0
|
||||
paused = state["playstate"]["paused"] if "paused" in state["playstate"] else None
|
||||
@ -590,6 +597,7 @@ class SyncServerProtocol(JSONCommandProtocol):
|
||||
def sendError(self, message):
|
||||
self.sendMessage({"Error": {"message": message}})
|
||||
|
||||
|
||||
class PingService(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@ -1,20 +1,26 @@
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import hashlib
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
from string import Template
|
||||
|
||||
from twisted.internet import task, reactor
|
||||
from twisted.internet.protocol import Factory
|
||||
|
||||
import syncplay
|
||||
from syncplay.protocols import SyncServerProtocol
|
||||
import time
|
||||
from syncplay import constants
|
||||
from syncplay.messages import getMessage
|
||||
import codecs
|
||||
import os
|
||||
from string import Template
|
||||
import argparse
|
||||
from syncplay.protocols import SyncServerProtocol
|
||||
from syncplay.utils import RoomPasswordProvider, NotControlledRoom, RandomStringGenerator, meetsMinVersion, playlistIsValid, truncateText
|
||||
|
||||
|
||||
class SyncFactory(Factory):
|
||||
def __init__(self, password='', motdFilePath=None, isolateRooms=False, salt=None, disableReady=False,disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH, maxUsernameLength=constants.MAX_USERNAME_LENGTH):
|
||||
def __init__(self, password='', motdFilePath=None, isolateRooms=False, salt=None,
|
||||
disableReady=False, disableChat=False, maxChatMessageLength=constants.MAX_CHAT_MESSAGE_LENGTH,
|
||||
maxUsernameLength=constants.MAX_USERNAME_LENGTH):
|
||||
self.isolateRooms = isolateRooms
|
||||
print(getMessage("welcome-server-notification").format(syncplay.version))
|
||||
if password:
|
||||
@ -179,6 +185,7 @@ class SyncFactory(Factory):
|
||||
else:
|
||||
watcher.setPlaylistIndex(room.getName(), room.getPlaylistIndex())
|
||||
|
||||
|
||||
class RoomManager(object):
|
||||
def __init__(self):
|
||||
self._rooms = {}
|
||||
@ -341,6 +348,7 @@ class Room(object):
|
||||
def getPlaylistIndex(self):
|
||||
return self._playlistIndex
|
||||
|
||||
|
||||
class ControlledRoom(Room):
|
||||
def __init__(self, name):
|
||||
Room.__init__(self, name)
|
||||
@ -389,6 +397,7 @@ class ControlledRoom(Room):
|
||||
def getControllers(self):
|
||||
return self._controllers
|
||||
|
||||
|
||||
class Watcher(object):
|
||||
def __init__(self, server, connector, name):
|
||||
self._ready = None
|
||||
@ -531,6 +540,7 @@ class Watcher(object):
|
||||
return RoomPasswordProvider.isControlledRoom(self._room.getName()) \
|
||||
and self._room.canControl(self)
|
||||
|
||||
|
||||
class ConfigurationGetter(object):
|
||||
def getConfiguration(self):
|
||||
self._prepareArgParser()
|
||||
@ -540,7 +550,8 @@ class ConfigurationGetter(object):
|
||||
return args
|
||||
|
||||
def _prepareArgParser(self):
|
||||
self._argparser = argparse.ArgumentParser(description=getMessage("server-argument-description"),
|
||||
self._argparser = argparse.ArgumentParser(
|
||||
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"))
|
||||
|
||||
@ -1,35 +1,44 @@
|
||||
import time
|
||||
import re
|
||||
|
||||
import ast
|
||||
import datetime
|
||||
import hashlib
|
||||
import itertools
|
||||
import random
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import unicodedata
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from syncplay import constants
|
||||
from syncplay.messages import getMessage
|
||||
import sys
|
||||
import os
|
||||
import itertools
|
||||
import hashlib
|
||||
import random
|
||||
import string
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import ast
|
||||
import unicodedata
|
||||
import platform
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
folderSearchEnabled = True
|
||||
|
||||
|
||||
def isWindows():
|
||||
return sys.platform.startswith(constants.OS_WINDOWS)
|
||||
|
||||
|
||||
def isLinux():
|
||||
return sys.platform.startswith(constants.OS_LINUX)
|
||||
|
||||
|
||||
def isMacOS():
|
||||
return sys.platform.startswith(constants.OS_MACOS)
|
||||
|
||||
|
||||
def isBSD():
|
||||
return constants.OS_BSD in sys.platform or sys.platform.startswith(constants.OS_DRAGONFLY)
|
||||
|
||||
|
||||
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
||||
"""Retry calling the decorated function using an exponential backoff.
|
||||
|
||||
@ -71,6 +80,7 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
||||
return f_retry # true decorator
|
||||
return deco_retry
|
||||
|
||||
|
||||
def parseTime(timeStr):
|
||||
regex = re.compile(constants.PARSE_TIME_REGEX)
|
||||
parts = regex.match(timeStr)
|
||||
@ -86,6 +96,7 @@ def parseTime(timeStr):
|
||||
time_params[name] = int(param)
|
||||
return datetime.timedelta(**time_params).total_seconds()
|
||||
|
||||
|
||||
def formatTime(timeInSeconds, weeksAsTitles=True):
|
||||
if timeInSeconds < 0:
|
||||
timeInSeconds = -timeInSeconds
|
||||
@ -115,7 +126,8 @@ def formatTime(timeInSeconds, weeksAsTitles=True):
|
||||
formattedTime = "{0:} (Title {1:.0f})".format(formattedTime, title)
|
||||
return formattedTime
|
||||
|
||||
def formatSize (bytes, precise=False):
|
||||
|
||||
def formatSize(num_of_bytes, precise=False):
|
||||
if bytes == 0: # E.g. when file size privacy is enabled
|
||||
return "???"
|
||||
try:
|
||||
@ -128,9 +140,11 @@ def formatSize (bytes, precise=False):
|
||||
except: # E.g. when filesize is hashed
|
||||
return "???"
|
||||
|
||||
|
||||
def isASCII(s):
|
||||
return all(ord(c) < 128 for c in s)
|
||||
|
||||
|
||||
def findResourcePath(resourceName):
|
||||
if resourceName == "syncplay.lua":
|
||||
resourcePath = os.path.join(findWorkingDir(), "lua", "intf", "resources", resourceName)
|
||||
@ -138,6 +152,7 @@ def findResourcePath(resourceName):
|
||||
resourcePath = os.path.join(findWorkingDir(), "resources", resourceName)
|
||||
return resourcePath
|
||||
|
||||
|
||||
def findWorkingDir():
|
||||
frozen = getattr(sys, 'frozen', '')
|
||||
if not frozen:
|
||||
@ -153,15 +168,18 @@ def findWorkingDir():
|
||||
path = ""
|
||||
return path
|
||||
|
||||
|
||||
def getResourcesPath():
|
||||
if isWindows():
|
||||
return findWorkingDir() + "\\resources\\"
|
||||
else:
|
||||
return findWorkingDir() + "/resources/"
|
||||
|
||||
|
||||
resourcespath = getResourcesPath()
|
||||
posixresourcespath = findWorkingDir().replace("\\", "/") + "/resources/"
|
||||
|
||||
|
||||
def getDefaultMonospaceFont():
|
||||
if platform.system() == "Windows":
|
||||
return constants.DEFAULT_WINDOWS_MONOSPACE_FONT
|
||||
@ -170,15 +188,18 @@ def getDefaultMonospaceFont():
|
||||
else:
|
||||
return constants.FALLBACK_MONOSPACE_FONT
|
||||
|
||||
|
||||
def limitedPowerset(s, minLength):
|
||||
return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s), minLength, -1))
|
||||
|
||||
|
||||
def blackholeStdoutForFrozenWindow():
|
||||
if getattr(sys, 'frozen', '') == "windows_exe":
|
||||
class Stderr(object):
|
||||
softspace = 0
|
||||
_file = None
|
||||
_error = None
|
||||
|
||||
def write(self, text, fname='.syncplay.log'):
|
||||
if self._file is None and self._error is None:
|
||||
if os.name != 'nt':
|
||||
@ -190,21 +211,27 @@ def blackholeStdoutForFrozenWindow():
|
||||
if self._file is not None:
|
||||
self._file.write(text)
|
||||
self._file.flush()
|
||||
|
||||
def flush(self):
|
||||
if self._file is not None:
|
||||
self._file.flush()
|
||||
|
||||
sys.stderr = Stderr()
|
||||
del Stderr
|
||||
|
||||
class Blackhole(object):
|
||||
softspace = 0
|
||||
|
||||
def write(self, text):
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
sys.stdout = Blackhole()
|
||||
del Blackhole
|
||||
|
||||
|
||||
def truncateText(unicodeText, maxLength):
|
||||
try:
|
||||
unicodeText = unicodeText.decode('utf-8')
|
||||
@ -216,6 +243,7 @@ def truncateText(unicodeText, maxLength):
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def splitText(unicodeText, maxLength):
|
||||
try:
|
||||
unicodeText = unicodeText.decode('utf-8')
|
||||
@ -231,6 +259,7 @@ def splitText(unicodeText, maxLength):
|
||||
|
||||
# Relate to file hashing / difference checking:
|
||||
|
||||
|
||||
def stripfilename(filename, stripURL):
|
||||
if filename:
|
||||
try:
|
||||
@ -247,6 +276,7 @@ def stripfilename(filename, stripURL):
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def stripRoomName(RoomName):
|
||||
if RoomName:
|
||||
try:
|
||||
@ -256,6 +286,7 @@ def stripRoomName(RoomName):
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def hashFilename(filename, stripURL=False):
|
||||
if isURL(filename):
|
||||
stripURL = True
|
||||
@ -267,9 +298,11 @@ def hashFilename(filename, stripURL = False):
|
||||
filenameHash = hashlib.sha256(strippedFilename).hexdigest()[:12]
|
||||
return filenameHash
|
||||
|
||||
|
||||
def hashFilesize(size):
|
||||
return hashlib.sha256(str(size).encode('utf-8')).hexdigest()[:12]
|
||||
|
||||
|
||||
def sameHashed(string1raw, string1hashed, string2raw, string2hashed):
|
||||
try:
|
||||
if string1raw.lower() == string2raw.lower():
|
||||
@ -285,6 +318,7 @@ def sameHashed(string1raw, string1hashed, string2raw, string2hashed):
|
||||
elif string1hashed == string2hashed:
|
||||
return True
|
||||
|
||||
|
||||
def sameFilename(filename1, filename2):
|
||||
try:
|
||||
filename1 = filename1
|
||||
@ -302,6 +336,7 @@ def sameFilename (filename1, filename2):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def sameFilesize(filesize1, filesize2):
|
||||
if filesize1 == 0 or filesize2 == 0:
|
||||
return True
|
||||
@ -310,6 +345,7 @@ def sameFilesize (filesize1, filesize2):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def sameFileduration(duration1, duration2):
|
||||
if not constants.SHOW_DURATION_NOTIFICATION:
|
||||
return True
|
||||
@ -318,11 +354,13 @@ def sameFileduration (duration1, duration2):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def meetsMinVersion(version, minVersion):
|
||||
def versiontotuple(ver):
|
||||
return tuple(map(int, ver.split(".")))
|
||||
return versiontotuple(version) >= versiontotuple(minVersion)
|
||||
|
||||
|
||||
def isURL(path):
|
||||
if path is None:
|
||||
return False
|
||||
@ -331,22 +369,27 @@ def isURL(path):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def getPlayerArgumentsByPathAsArray(arguments, path):
|
||||
if arguments and not isinstance(arguments, str) and path in arguments:
|
||||
return arguments[path]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def getPlayerArgumentsByPathAsText(arguments, path):
|
||||
argsToReturn = getPlayerArgumentsByPathAsArray(arguments, path)
|
||||
return " ".join(argsToReturn) if argsToReturn else ""
|
||||
|
||||
|
||||
def getListAsMultilineString(pathArray):
|
||||
return "\n".join(pathArray) if pathArray else ""
|
||||
|
||||
|
||||
def convertMultilineStringToList(multilineString):
|
||||
return str.split(multilineString, "\n") if multilineString else ""
|
||||
|
||||
|
||||
def playlistIsValid(files):
|
||||
if len(files) > constants.PLAYLIST_MAX_ITEMS:
|
||||
return False
|
||||
@ -354,6 +397,7 @@ def playlistIsValid(files):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def getDomainFromURL(URL):
|
||||
try:
|
||||
URL = URL.split("//")[-1].split("/")[0]
|
||||
@ -363,6 +407,7 @@ def getDomainFromURL(URL):
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def open_system_file_browser(path):
|
||||
if isURL(path):
|
||||
return
|
||||
@ -374,6 +419,7 @@ def open_system_file_browser(path):
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", path])
|
||||
|
||||
|
||||
def getListOfPublicServers():
|
||||
try:
|
||||
import urllib.request, urllib.parse, urllib.error, syncplay, sys
|
||||
@ -394,12 +440,13 @@ def getListOfPublicServers():
|
||||
else:
|
||||
raise IOError
|
||||
except:
|
||||
if constants.DEBUG_MODE == True:
|
||||
if constants.DEBUG_MODE:
|
||||
traceback.print_exc()
|
||||
raise
|
||||
else:
|
||||
raise IOError(getMessage("failed-to-load-server-list-error"))
|
||||
|
||||
|
||||
class RoomPasswordProvider(object):
|
||||
CONTROLLED_ROOM_REGEX = re.compile("^\+(.*):(\w{12})$")
|
||||
PASSWORD_REGEX = re.compile("[A-Z]{2}-\d{3}-\d{3}")
|
||||
@ -433,6 +480,7 @@ class RoomPasswordProvider(object):
|
||||
provisionalHash = hashlib.sha256(roomName + salt).hexdigest()
|
||||
return hashlib.sha1(provisionalHash + salt + password).hexdigest()[:12].upper()
|
||||
|
||||
|
||||
class RandomStringGenerator(object):
|
||||
@staticmethod
|
||||
def generate_room_password():
|
||||
@ -458,6 +506,6 @@ class RandomStringGenerator(object):
|
||||
def _get_random_numbers(quantity):
|
||||
return ''.join(random.choice(string.digits) for _ in range(quantity))
|
||||
|
||||
|
||||
class NotControlledRoom(Exception):
|
||||
pass
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user