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