Revert "Revert "Code Syntax Refactoring""

This reverts commit 51fba3722f653156eca30d45cb07846fb725a777.
This commit is contained in:
Daniel Ahn 2018-07-23 14:08:44 -07:00 committed by daniel.ahn
parent 2e987e8b0d
commit ac4da59479
30 changed files with 2643 additions and 2316 deletions

View File

@ -7,6 +7,7 @@ import os.path
application = defines.get('app', 'dist/Syncplay.app')
appname = os.path.basename(application)
def icon_from_app(app_path):
plist_path = os.path.join(app_path, 'Contents', 'Info.plist')
plist = biplist.readPlist(plist_path)
@ -17,6 +18,7 @@ def icon_from_app(app_path):
icon_name = icon_root + icon_ext
return os.path.join(app_path, 'Contents', 'Resources', icon_name)
# Volume format (see hdiutil create -help)
format = defines.get('format', 'UDZO')
@ -27,10 +29,19 @@ compression_level = 9
size = defines.get('size', None)
# Files to include
files = [ application, 'resources/lua/intf/.syncplay.lua', 'resources/.macos_vlc_install.command', 'resources/.macOS_readme.pdf' ]
files = [
application,
'resources/lua/intf/.syncplay.lua',
'resources/.macos_vlc_install.command',
'resources/.macOS_readme.pdf'
]
# Symlinks to create
symlinks = { 'Applications': '/Applications', 'Install for VLC': '.macos_vlc_install.command', 'Read Me': '.macOS_readme.pdf' }
symlinks = {
'Applications': '/Applications',
'Install for VLC': '.macos_vlc_install.command',
'Read Me': '.macOS_readme.pdf'
}
# Volume icon
#

View File

@ -5,8 +5,8 @@
# *** TROUBLESHOOTING ***
# 1) If you get the error "ImportError: No module named zope.interface" then add an empty __init__.py file to the PYTHONDIR/Lib/site-packages/zope directory
# 2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed.
import sys, codecs
import codecs
import sys
# try:
# if (sys.version_info.major != 2) or (sys.version_info.minor < 7):
# raise Exception("You must build Syncplay with Python 2.7!")
@ -14,18 +14,20 @@ import sys, codecs
# import warnings
# warnings.warn("You must build Syncplay with Python 2.7!")
import os
import subprocess
from string import Template
import syncplay
import os
import subprocess
from syncplay.messages import getMissingStrings
missingStrings = getMissingStrings()
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
@ -39,9 +41,11 @@ def get_nsis_path():
raise Exception("You must install NSIS 3 or later.")
except WindowsError:
return bin_name
NSIS_COMPILE = get_nsis_path()
OUT_DIR = "dist\Syncplay"
OUT_DIR = r"dist\Syncplay"
SETUP_SCRIPT_PATH = "syncplay_setup.nsi"
NSIS_SCRIPT_TEMPLATE = r"""
!include LogicLib.nsh
@ -608,6 +612,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
SectionEnd
"""
class NSISScript(object):
def create(self):
fileList, totalSize = self.getBuildDirContents(OUT_DIR)
@ -664,7 +669,9 @@ class NSISScript(object):
delete.append('RMdir "$INSTDIR\\{}"'.format(file_))
return "\n".join(delete)
guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png',
guiIcons = [
'resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png',
'resources/control_pause_blue.png', 'resources/cross.png', 'resources/door_in.png',
'resources/folder_explore.png', 'resources/help.png', 'resources/table_refresh.png',
'resources/timeline_marker.png', 'resources/control_play_blue.png',
@ -686,7 +693,13 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock
'resources/email_go.png',
'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng'
]
resources = ["resources/icon.ico", "resources/syncplay.png", "resources/syncplayintf.lua", "resources/license.rtf", "resources/third-party-notices.rtf"]
resources = [
"resources/icon.ico",
"resources/syncplay.png",
"resources/syncplayintf.lua",
"resources/license.rtf",
"resources/third-party-notices.rtf"
]
resources.extend(guiIcons)
intf_resources = ["resources/lua/intf/syncplay.lua"]

View File

@ -18,7 +18,12 @@ OPTIONS = {
'extra_scripts': 'syncplayServer.py',
'includes': {'PySide2.QtCore', 'PySide2.QtUiTools', 'PySide2.QtGui', 'PySide2.QtWidgets', 'certifi'},
'excludes': {'PySide', 'PySide.QtCore', 'PySide.QtUiTools', 'PySide.QtGui'},
'qt_plugins': ['platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib','platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib'],
'qt_plugins': [
'platforms/libqcocoa.dylib',
'platforms/libqminimal.dylib',
'platforms/libqoffscreen.dylib',
'styles/libqmacstyle.dylib'
],
'plist': {
'CFBundleName': 'Syncplay',
'CFBundleShortVersionString': syncplay.version,

View File

@ -6,7 +6,8 @@
# 1) If you get the error "ImportError: No module named zope.interface" then add an empty __init__.py file to the PYTHONDIR/Lib/site-packages/zope directory
# 2) It is expected that you will have NSIS 3 NSIS from http://nsis.sourceforge.net installed.
import sys, codecs
import codecs
import sys
# try:
# if (sys.version_info.major != 2) or (sys.version_info.minor < 7):
# raise Exception("You must build Syncplay with Python 2.7!")
@ -14,23 +15,26 @@ import sys, codecs
# import warnings
# warnings.warn("You must build Syncplay with Python 2.7!")
import os
import subprocess
from string import Template
from distutils.core import setup
try:
from py2exe.build_exe import py2exe
except ImportError:
from py2exe.distutils_buildexe import py2exe
from string import Template
import syncplay
import os
import subprocess
from syncplay.messages import getMissingStrings
missingStrings = getMissingStrings()
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
@ -44,6 +48,8 @@ def get_nsis_path():
raise Exception("You must install NSIS 3 or later.")
except WindowsError:
return bin_name
NSIS_COMPILE = get_nsis_path()
OUT_DIR = "syncplay_v{}".format(syncplay.version)
@ -613,6 +619,7 @@ NSIS_SCRIPT_TEMPLATE = r"""
SectionEnd
"""
class NSISScript(object):
def create(self):
fileList, totalSize = self.getBuildDirContents(OUT_DIR)
@ -669,6 +676,7 @@ class NSISScript(object):
delete.append('RMdir "$INSTDIR\\{}"'.format(file_))
return "\n".join(delete)
class build_installer(py2exe):
def run(self):
py2exe.run(self)
@ -678,7 +686,9 @@ class build_installer(py2exe):
script.compile()
print("*** DONE ***")
guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png',
guiIcons = [
'resources/accept.png', 'resources/arrow_undo.png', 'resources/clock_go.png',
'resources/control_pause_blue.png', 'resources/cross.png', 'resources/door_in.png',
'resources/folder_explore.png', 'resources/help.png', 'resources/table_refresh.png',
'resources/timeline_marker.png', 'resources/control_play_blue.png',
@ -700,7 +710,13 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock
'resources/email_go.png',
'resources/world_add.png', 'resources/film_add.png', 'resources/delete.png', 'resources/spinner.mng'
]
resources = ["resources/icon.ico", "resources/syncplay.png", "resources/syncplayintf.lua", "resources/license.rtf", "resources/third-party-notices.rtf"]
resources = [
"resources/icon.ico",
"resources/syncplay.png",
"resources/syncplayintf.lua",
"resources/license.rtf",
"resources/third-party-notices.rtf"
]
resources.extend(guiIcons)
intf_resources = ["resources/lua/intf/syncplay.lua"]
@ -714,11 +730,16 @@ common_info = dict(
info = dict(
common_info,
windows=[{"script":"syncplayClient.py", "icon_resources":[(1, "resources\\icon.ico")], 'dest_base': "Syncplay"},],
windows=[{
"script": "syncplayClient.py",
"icon_resources": [(1, "resources\\icon.ico")],
'dest_base': "Syncplay"},
],
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': {
options={
'py2exe': {
'dist_dir': OUT_DIR,
'packages': 'PySide.QtUiTools',
'includes': 'twisted, sys, encodings, datetime, os, time, math, PySide, liburl, ast, unicodedata, _ssl',

View File

@ -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 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 constants.SHOW_NONCONTROLLER_OSD == False and not self.canControl(username):
showOnOSD = False
hideFromOSD = not showOnOSD
if not file_:
@ -1258,12 +1275,16 @@ 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() == False:
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
@ -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

View File

@ -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)

View File

@ -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',

View File

@ -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,9 +51,11 @@ 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 "-tooltip" in type_:

View File

@ -12,5 +12,6 @@ except ImportError:
from syncplay.players.basePlayer import DummyPlayer
MpcBePlayer = DummyPlayer
def getAvailablePlayers():
return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer, VlcPlayer, MpcBePlayer]

View File

@ -1,4 +1,6 @@
from syncplay import constants
class BasePlayer(object):
'''
@ -12,7 +14,9 @@ class BasePlayer(object):
'''
Display given message on player's OSD or similar means
'''
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL):
def displayMessage(
self, message, duration=(constants.OSD_DURATION*1000), secondaryOSD=False, mood=constants.MESSAGE_NEUTRAL
):
raise NotImplementedError()
'''
@ -58,7 +62,6 @@ class BasePlayer(object):
def openFile(self, filePath, resetPosition=False):
raise NotImplementedError()
'''
@return: list of strings
'''
@ -107,6 +110,7 @@ class BasePlayer(object):
def getPlayerPathErrors(playerPath, filePath):
raise NotImplementedError()
class DummyPlayer(BasePlayer):
@staticmethod

View File

@ -1,15 +1,18 @@
#coding:utf8
import os.path
import re
import time
import threading
import _thread
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
from functools import wraps
from syncplay.players.basePlayer import BasePlayer
import re
from syncplay.utils import retry
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
from syncplay import constants
from syncplay.messages import getMessage
import os.path
from syncplay.players.basePlayer import BasePlayer
from syncplay.utils import retry
class MpcHcApi:
def __init__(self):
@ -49,7 +52,7 @@ class MpcHcApi:
self.__listener.SendCommand(self.CMD_OPENFILE, filePath)
def isPaused(self):
return self.playState != self.__MPC_PLAYSTATE.PS_PLAY and self.playState != None
return self.playState != self.__MPC_PLAYSTATE.PS_PLAY and self.playState is not None
def askForVersion(self):
self.__listener.SendCommand(self.CMD_GETVERSION)
@ -103,7 +106,11 @@ class MpcHcApi:
elif cmd == self.CMD_STATE:
self.loadState = int(value)
fileNotReady = self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or self.loadState == self.__MPC_LOADSTATE.MLS_LOADING or self.loadState == self.__MPC_LOADSTATE.MLS_CLOSED
fileNotReady = (
self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or
self.loadState == self.__MPC_LOADSTATE.MLS_LOADING or
self.loadState == self.__MPC_LOADSTATE.MLS_CLOSED
)
if fileNotReady:
self.playState = None
self.__locks.fileReady.clear()
@ -272,7 +279,6 @@ class MpcHcApi:
self.locks.listenerStart.set()
win32gui.PumpMessages()
def OnCopyData(self, hwnd, msg, wparam, lparam):
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
# print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
@ -304,6 +310,7 @@ class MpcHcApi:
('lpData', ctypes.c_void_p)
]
class MPCHCAPIPlayer(BasePlayer):
speedSupported = False
alertOSDSupported = False
@ -400,7 +407,10 @@ class MPCHCAPIPlayer(BasePlayer):
def openFile(self, filePath, resetPosition=False):
self._mpcApi.openFile(filePath)
def displayMessage(self, message, duration = (constants.OSD_DURATION*1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
def displayMessage(
self, message,
duration=(constants.OSD_DURATION*1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL
):
self._mpcApi.sendOsd(message, constants.MPC_OSD_POSITION, duration)
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
@ -412,6 +422,7 @@ class MPCHCAPIPlayer(BasePlayer):
self._mpcApi.pause()
else:
self._mpcApi.unpause()
def setFeatures(self, featureList):
pass
@ -482,7 +493,10 @@ class MPCHCAPIPlayer(BasePlayer):
@staticmethod
def getIconPath(path):
if MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64.exe'.lower()) or MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64_nvo.exe'.lower()):
if (
MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64.exe'.lower()) or
MPCHCAPIPlayer.getExpandedPath(path).lower().endswith('mpc-hc64_nvo.exe'.lower())
):
return constants.MPC64_ICONPATH
else:
return constants.MPC_ICONPATH
@ -496,7 +510,11 @@ class MPCHCAPIPlayer(BasePlayer):
@staticmethod
def getExpandedPath(path):
if os.path.isfile(path):
if path.lower().endswith('mpc-hc.exe'.lower()) or path.lower().endswith('mpc-hcportable.exe'.lower()) or path.lower().endswith('mpc-hc64.exe'.lower()) or path.lower().endswith('mpc-hc64_nvo.exe'.lower()) or path.lower().endswith('mpc-hc_nvo.exe'.lower()):
if (
path.lower().endswith('mpc-hc.exe'.lower()) or path.lower().endswith('mpc-hcportable.exe'.lower()) or
path.lower().endswith('mpc-hc64.exe'.lower()) or path.lower().endswith('mpc-hc64_nvo.exe'.lower()) or
path.lower().endswith('mpc-hc_nvo.exe'.lower())
):
return path
if os.path.isfile(path + "mpc-hc.exe"):
path += "mpc-hc.exe"
@ -528,4 +546,3 @@ class MPCHCAPIPlayer(BasePlayer):
if os.path.isfile(path + "\\mpc-hc64_nvo.exe"):
path += "\\mpc-hc64_nvo.exe"
return path

View File

@ -1,8 +1,11 @@
from syncplay import constants
import os.path
from syncplay import constants
from syncplay.messages import getMessage
from syncplay.players.mpc import MPCHCAPIPlayer
class MpcBePlayer(MPCHCAPIPlayer):
@staticmethod
def run(client, playerPath, filePath, args):
@ -30,7 +33,11 @@ class MpcBePlayer(MPCHCAPIPlayer):
@staticmethod
def getExpandedPath(path):
if os.path.isfile(path):
if path.lower().endswith('mpc-be.exe'.lower()) or path.lower().endswith('mpc-be64.exe'.lower() or path.lower().endswith('mpc-beportable.exe'.lower())):
if (
path.lower().endswith('mpc-be.exe'.lower()) or
path.lower().endswith('mpc-be64.exe'.lower()) or
path.lower().endswith('mpc-beportable.exe'.lower())
):
return path
if os.path.isfile(path + "mpc-be.exe"):
path += "mpc-be.exe"

View File

@ -1,14 +1,18 @@
# coding:utf8
import subprocess
import os
import re
import subprocess
import sys
import threading
import time
from syncplay.players.basePlayer import BasePlayer
from syncplay import constants, utils
from syncplay.players.basePlayer import BasePlayer
from syncplay.messages import getMessage
import os, sys
from syncplay.utils import isWindows
class MplayerPlayer(BasePlayer):
speedSupported = True
customOpenDialog = False
@ -90,15 +94,20 @@ class MplayerPlayer(BasePlayer):
def _getProperty(self, property_):
self._listener.sendLine("get_property {}".format(property_))
def displayMessage(self, message, duration=(constants.OSD_DURATION * 1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL):
def displayMessage(
self, message,
duration=(constants.OSD_DURATION * 1000), OSDType=constants.OSD_NOTIFICATION, mood=constants.MESSAGE_NEUTRAL
):
messageString = self._sanitizeText(message.replace("\\n", "<NEWLINE>")).replace("<NEWLINE>", "\\n")
self._listener.sendLine('{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL))
self._listener.sendLine('{} "{!s}" {} {}'.format(
self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL))
def displayChatMessage(self, username, message):
messageString = "<{}> {}".format(username, message)
messageString = self._sanitizeText(messageString.replace("\\n", "<NEWLINE>")).replace("<NEWLINE>", "\\n")
duration = int(constants.OSD_DURATION * 1000)
self._listener.sendLine('{} "{!s}" {} {}'.format(self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL))
self._listener.sendLine('{} "{!s}" {} {}'.format(
self.OSD_QUERY, messageString, duration, constants.MPLAYER_OSD_LEVEL))
def setSpeed(self, value):
self._setProperty('speed', "{:.2f}".format(value))
@ -181,7 +190,13 @@ class MplayerPlayer(BasePlayer):
line = line.replace("[term-msg] ", "") # -v workaround
line = line.replace(" cplayer: ", "") # --msg-module workaround
line = line.replace(" term-msg: ", "")
if "Failed to get value of property" in line or "=(unavailable)" in line or line == "ANS_filename=" or line == "ANS_length=" or line == "ANS_path=":
if (
"Failed to get value of property" in line or
"=(unavailable)" in line or
line == "ANS_filename=" or
line == "ANS_length=" or
line == "ANS_path="
):
if "filename" in line:
self._getFilename()
elif "length" in line:
@ -250,7 +265,7 @@ class MplayerPlayer(BasePlayer):
@staticmethod
def isValidPlayerPath(path):
if "mplayer" in path and MplayerPlayer.getExpandedPath(path) and not "mplayerc.exe" in path: # "mplayerc.exe" is Media Player Classic (not Home Cinema):
if "mplayer" in path and MplayerPlayer.getExpandedPath(path) and "mplayerc.exe" not in path: # "mplayerc.exe" is Media Player Classic (not Home Cinema):
return True
return False
@ -324,12 +339,15 @@ class MplayerPlayer(BasePlayer):
if 'TERM' in env:
del env['TERM']
if filePath:
self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env, bufsize=0)
self.__process = subprocess.Popen(
call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
cwd=self.__getCwd(filePath, env), env=env, bufsize=0)
else:
self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, bufsize=0)
self.__process = subprocess.Popen(
call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
env=env, bufsize=0)
threading.Thread.__init__(self, name="MPlayer Listener")
def __getCwd(self, filePath, env):
if not filePath:
return None
@ -365,8 +383,8 @@ class MplayerPlayer(BasePlayer):
if command and command[:1] == "/":
message = message[1:]
else:
self.__playerController.reactor.callFromThread(self.__playerController._client.ui.executeCommand,
command)
self.__playerController.reactor.callFromThread(
self.__playerController._client.ui.executeCommand, command)
return
self.__playerController.reactor.callFromThread(self.__playerController._client.sendChat, message)
@ -378,11 +396,11 @@ class MplayerPlayer(BasePlayer):
oldState = self.readyToSend
self.readyToSend = newReadyState
self.lastNotReadyTime = time.time() if newReadyState == False else None
if self.readyToSend == True:
if self.readyToSend:
self.__playerController._client.ui.showDebugMessage("<mpv> Ready to send: True")
else:
self.__playerController._client.ui.showDebugMessage("<mpv> Ready to send: False")
if self.readyToSend == True and oldState == False:
if self.readyToSend and oldState == False:
self.processSendQueue()
def checkForReadinessOverride(self):
@ -391,7 +409,7 @@ class MplayerPlayer(BasePlayer):
def sendLine(self, line, notReadyAfterThis=None):
self.checkForReadinessOverride()
if self.readyToSend == False and "print_text ANS_pause" in line:
if not self.readyToSend and "print_text ANS_pause" in line:
self.__playerController._client.ui.showDebugMessage("<mpv> Not ready to get status update, so skipping")
return
try:
@ -401,11 +419,13 @@ class MplayerPlayer(BasePlayer):
if line.startswith(command):
for itemID, deletionCandidate in enumerate(self.sendQueue):
if deletionCandidate.startswith(command):
self.__playerController._client.ui.showDebugMessage("<mpv> Remove duplicate (supersede): {}".format(self.sendQueue[itemID]))
self.__playerController._client.ui.showDebugMessage(
"<mpv> Remove duplicate (supersede): {}".format(self.sendQueue[itemID]))
try:
self.sendQueue.remove(self.sendQueue[itemID])
except UnicodeWarning:
self.__playerController._client.ui.showDebugMessage("<mpv> Unicode mismatch occured when trying to remove duplicate")
self.__playerController._client.ui.showDebugMessage(
"<mpv> Unicode mismatch occured when trying to remove duplicate")
# TODO: Prevent this from being triggered
pass
break
@ -415,7 +435,8 @@ class MplayerPlayer(BasePlayer):
if line == command:
for itemID, deletionCandidate in enumerate(self.sendQueue):
if deletionCandidate == command:
self.__playerController._client.ui.showDebugMessage("<mpv> Remove duplicate (delete both): {}".format(self.sendQueue[itemID]))
self.__playerController._client.ui.showDebugMessage(
"<mpv> Remove duplicate (delete both): {}".format(self.sendQueue[itemID]))
self.__playerController._client.ui.showDebugMessage(self.sendQueue[itemID])
return
except:
@ -428,7 +449,9 @@ class MplayerPlayer(BasePlayer):
def processSendQueue(self):
while self.sendQueue and self.readyToSend:
if self.lastSendTime and time.time() - self.lastSendTime < constants.MPV_SENDMESSAGE_COOLDOWN_TIME:
self.__playerController._client.ui.showDebugMessage("<mpv> Throttling message send, so sleeping for {}".format(constants.MPV_SENDMESSAGE_COOLDOWN_TIME))
self.__playerController._client.ui.showDebugMessage(
"<mpv> Throttling message send, so sleeping for {}".format(
constants.MPV_SENDMESSAGE_COOLDOWN_TIME))
time.sleep(constants.MPV_SENDMESSAGE_COOLDOWN_TIME)
try:
lineToSend = self.sendQueue.pop()

View File

@ -1,14 +1,18 @@
# coding:utf8
import os
import re
import sys
import time
import subprocess
from syncplay import constants
from syncplay.players.mplayer import MplayerPlayer
from syncplay.messages import getMessage
from syncplay import constants
from syncplay.utils import isURL, findResourcePath
import os, sys, time
class MpvPlayer(MplayerPlayer):
RE_VERSION = re.compile('.*mpv (\d+)\.(\d+)\.\d+.*')
RE_VERSION = re.compile(r'.*mpv (\d+)\.(\d+)\.\d+.*')
osdMessageSeparator = "\\n"
osdMessageSeparator = "; " # TODO: Make conditional
@ -21,7 +25,9 @@ class MpvPlayer(MplayerPlayer):
constants.MPV_NEW_VERSION = ver is None or int(ver.group(1)) > 0 or int(ver.group(2)) >= 6
constants.MPV_OSC_VISIBILITY_CHANGE_VERSION = False if ver is None else int(ver.group(1)) > 0 or int(ver.group(2)) >= 28
if not constants.MPV_OSC_VISIBILITY_CHANGE_VERSION:
client.ui.showDebugMessage("This version of mpv is not known to be compatible with changing the OSC visibility. Please use mpv >=0.28.0.")
client.ui.showDebugMessage(
"This version of mpv is not known to be compatible with changing the OSC visibility. "
"Please use mpv >=0.28.0.")
if constants.MPV_NEW_VERSION:
return NewMpvPlayer(client, MpvPlayer.getExpandedPath(playerPath), filePath, args)
else:
@ -77,6 +83,7 @@ class MpvPlayer(MplayerPlayer):
def getPlayerPathErrors(playerPath, filePath):
return None
class OldMpvPlayer(MpvPlayer):
POSITION_QUERY = 'time-pos'
OSD_QUERY = 'show_text'
@ -111,6 +118,7 @@ class OldMpvPlayer(MpvPlayer):
self.setPaused(self._client.getGlobalPaused())
self.setPosition(self._client.getGlobalPosition())
class NewMpvPlayer(OldMpvPlayer):
lastResetTime = None
lastMPVPositionUpdate = None
@ -122,14 +130,15 @@ class NewMpvPlayer(OldMpvPlayer):
if not self._client._config["chatOutputEnabled"]:
super(self.__class__, self).displayMessage(message=message, duration=duration, OSDType=OSDType, mood=mood)
return
messageString = self._sanitizeText(message.replace("\\n", "<NEWLINE>")).replace("\\\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER).replace("<NEWLINE>", "\\n")
messageString = self._sanitizeText(message.replace("\\n", "<NEWLINE>")).replace(
"\\\\", constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER).replace("<NEWLINE>", "\\n")
self._listener.sendLine('script-message-to syncplayintf {}-osd-{} "{}"'.format(OSDType, mood, messageString))
def displayChatMessage(self, username, message):
if not self._client._config["chatOutputEnabled"]:
super(self.__class__, self).displayChatMessage(username, message)
return
username = self._sanitizeText(username.replace("\\",constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER))
username = self._sanitizeText(username.replace("\\", sconstants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER))
message = self._sanitizeText(message.replace("\\", constants.MPV_INPUT_BACKSLASH_SUBSTITUTE_CHARACTER))
messageString = "<{}> {}".format(username, message)
self._listener.sendLine('script-message-to syncplayintf chat "{}"'.format(messageString))
@ -155,25 +164,34 @@ class NewMpvPlayer(OldMpvPlayer):
self._listener.sendLine("print_text ""ANS_{}=${{{}}}""".format(property_, propertyID))
def getCalculatedPosition(self):
if self.fileLoaded == False:
self._client.ui.showDebugMessage("File not loaded so using GlobalPosition for getCalculatedPosition({})".format(self._client.getGlobalPosition()))
if not self.fileLoaded:
self._client.ui.showDebugMessage(
"File not loaded so using GlobalPosition for getCalculatedPosition({})".format(
self._client.getGlobalPosition()))
return self._client.getGlobalPosition()
if self.lastMPVPositionUpdate is None:
self._client.ui.showDebugMessage("MPV not updated position so using GlobalPosition for getCalculatedPosition ({})".format(self._client.getGlobalPosition()))
self._client.ui.showDebugMessage(
"MPV not updated position so using GlobalPosition for getCalculatedPosition ({})".format(
self._client.getGlobalPosition()))
return self._client.getGlobalPosition()
if self._recentlyReset():
self._client.ui.showDebugMessage("Recently reset so using self.position for getCalculatedPosition ({})".format(self._position))
self._client.ui.showDebugMessage(
"Recently reset so using self.position for getCalculatedPosition ({})".format(
self._position))
return self._position
diff = time.time() - self.lastMPVPositionUpdate
if diff > constants.MPV_UNRESPONSIVE_THRESHOLD:
self.reactor.callFromThread(self._client.ui.showErrorMessage, getMessage("mpv-unresponsive-error").format(int(diff)), True)
self.reactor.callFromThread(
self._client.ui.showErrorMessage, getMessage("mpv-unresponsive-error").format(int(diff)), True)
self.drop()
if diff > constants.PLAYER_ASK_DELAY and not self._paused:
self._client.ui.showDebugMessage("mpv did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff))
self._client.ui.showDebugMessage(
"mpv did not response in time, so assuming position is {} ({}+{})".format(
self._position + diff, self._position, diff))
return self._position + diff
else:
return self._position
@ -186,7 +204,8 @@ class NewMpvPlayer(OldMpvPlayer):
elif self._fileIsLoaded() or (value < constants.MPV_NEWFILE_IGNORE_TIME and self._fileIsLoaded(ignoreDelay=True)):
self._position = max(value, 0)
else:
self._client.ui.showDebugMessage("No file loaded so storing position as GlobalPosition ({})".format(self._client.getGlobalPosition()))
self._client.ui.showDebugMessage(
"No file loaded so storing position as GlobalPosition ({})".format(self._client.getGlobalPosition()))
self._position = self._client.getGlobalPosition()
def _storePauseState(self, value):
@ -205,7 +224,8 @@ class NewMpvPlayer(OldMpvPlayer):
self._getPausedAndPosition()
self._positionAsk.wait(constants.MPV_LOCK_WAIT_TIME)
self._pausedAsk.wait(constants.MPV_LOCK_WAIT_TIME)
self._client.updatePlayerStatus(self._paused if self.fileLoaded else self._client.getGlobalPaused(), self.getCalculatedPosition())
self._client.updatePlayerStatus(
self._paused if self.fileLoaded else self._client.getGlobalPaused(), self.getCalculatedPosition())
def _getPausedAndPosition(self):
self._listener.sendLine("print_text ANS_pause=${pause}\r\nprint_text ANS_time-pos=${=time-pos}")
@ -229,7 +249,8 @@ class NewMpvPlayer(OldMpvPlayer):
def setPosition(self, value):
if value < constants.DO_NOT_RESET_POSITION_THRESHOLD and self._recentlyReset():
self._client.ui.showDebugMessage("Did not seek as recently reset and {} below 'do not reset position' threshold".format(value))
self._client.ui.showDebugMessage(
"Did not seek as recently reset and {} below 'do not reset position' threshold".format(value))
return
super(self.__class__, self).setPosition(value)
self.lastMPVPositionUpdate = time.time()
@ -245,7 +266,7 @@ class NewMpvPlayer(OldMpvPlayer):
self._client.ui.showDebugMessage("Want to set paused to {}".format(self._client.getGlobalPaused()))
else:
self._client.ui.showDebugMessage("Don't want to set paused to {}".format(self._client.getGlobalPaused()))
if resetPosition == False:
if not resetPosition:
self.setPosition(self._client.getGlobalPosition())
else:
self._storePosition(0)
@ -285,7 +306,14 @@ class NewMpvPlayer(OldMpvPlayer):
self._listener.setReadyToSend(True)
def _setOSDPosition(self):
if self._client._config['chatMoveOSD'] and (self._client._config['chatOutputEnabled'] or (self._client._config['chatInputEnabled'] and self._client._config['chatInputPosition'] == constants.INPUT_POSITION_TOP)):
if (
self._client._config['chatMoveOSD'] and (
self._client._config['chatOutputEnabled'] or (
self._client._config['chatInputEnabled'] and
self._client._config['chatInputPosition'] == constants.INPUT_POSITION_TOP
)
)
):
self._setProperty("osd-align-y", "bottom")
self._setProperty("osd-margin-y", int(self._client._config['chatOSDMargin']))
@ -309,9 +337,9 @@ class NewMpvPlayer(OldMpvPlayer):
def _fileIsLoaded(self, ignoreDelay=False):
if ignoreDelay:
self._client.ui.showDebugMessage("Ignoring _fileIsLoaded MPV_NEWFILE delay")
return True if self.fileLoaded else False
return bool(self.fileLoaded)
if self.fileLoaded == True and self.lastLoadedTime != None and time.time() > (self.lastLoadedTime + constants.MPV_NEWFILE_IGNORE_TIME):
return True
else:
return False
return (
self.fileLoaded and self.lastLoadedTime is not None and
time.time() > (self.lastLoadedTime + constants.MPV_NEWFILE_IGNORE_TIME)
)

View File

@ -1,5 +1,6 @@
import syncplay.players
class PlayerFactory(object):
def __init__(self):
self._players = syncplay.players.getAvailablePlayers()

View File

@ -1,18 +1,24 @@
import subprocess
import re
import threading
from syncplay.players.basePlayer import BasePlayer
from syncplay import constants, utils
import asynchat
import asyncore
import os
import sys
import random
import re
import socket
import asynchat, asyncore
import urllib.request, urllib.parse, urllib.error
import subprocess
import sys
import threading
import time
import urllib.error
import urllib.parse
import urllib.request
from syncplay import constants, utils
from syncplay.messages import getMessage
from syncplay.players.basePlayer import BasePlayer
from syncplay.utils import isBSD, isLinux, isWindows, isMacOS
class VlcPlayer(BasePlayer):
speedSupported = True
customOpenDialog = False
@ -47,7 +53,8 @@ class VlcPlayer(BasePlayer):
if self.radixChar == "" or self.radixChar == "1" or self.radixChar == "5":
raise ValueError
except:
self._client.ui.showErrorMessage("Failed to determine locale. As a fallback Syncplay is using the following radix character: \".\".")
self._client.ui.showErrorMessage(
"Failed to determine locale. As a fallback Syncplay is using the following radix character: \".\".")
self.radixChar = "."
self._durationAsk = threading.Event()
@ -110,7 +117,8 @@ class VlcPlayer(BasePlayer):
return self._client.getGlobalPosition()
diff = time.time() - self._lastVLCPositionUpdate
if diff > constants.PLAYER_ASK_DELAY and not self._paused:
self._client.ui.showDebugMessage("VLC did not response in time, so assuming position is {} ({}+{})".format(self._position + diff, self._position, diff))
self._client.ui.showDebugMessage("VLC did not response in time, so assuming position is {} ({}+{})".format(
self._position + diff, self._position, diff))
if diff > constants.VLC_LATENCY_ERROR_THRESHOLD:
if not self.shownVLCLatencyError or constants.DEBUG_MODE:
self._client.ui.showErrorMessage(getMessage("media-player-latency-warning").format(int(diff)))
@ -119,7 +127,10 @@ class VlcPlayer(BasePlayer):
else:
return self._position
def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL):
def displayMessage(
self, message,
duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL
):
duration /= 1000
if OSDType != constants.OSD_ALERT:
self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message))
@ -212,21 +223,25 @@ class VlcPlayer(BasePlayer):
self._duration = float(value.replace(",", "."))
self._durationAsk.set()
elif name == "playstate":
self._paused = bool(value != 'playing') if(value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused()
self._paused = bool(value != 'playing') if (value != "no-input" and not self._filechanged) else self._client.getGlobalPaused()
diff = time.time() - self._lastVLCPositionUpdate if self._lastVLCPositionUpdate else 0
if self._paused == False \
and self._position == self._previousPreviousPosition \
and self._previousPosition == self._position \
and self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH \
and (self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD \
and diff > constants.VLC_LATENCY_ERROR_THRESHOLD:
if (
self._paused == False and
self._position == self._previousPreviousPosition and
self._previousPosition == self._position and
self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH and
(self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD and
diff > constants.VLC_LATENCY_ERROR_THRESHOLD
):
self._client.ui.showDebugMessage("Treating 'playing' response as 'paused' due to VLC EOF bug")
self.setPaused(True)
self._pausedAsk.set()
elif name == "position":
newPosition = float(value.replace(",", ".")) if (value != "no-input" and self._filechanged == False) else self._client.getGlobalPosition()
if newPosition == self._previousPosition and newPosition != self._duration and not self._paused:
self._client.ui.showDebugMessage("Not considering position {} duplicate as new time because of VLC time precision bug".format(newPosition))
newPosition = float(value.replace(",", ".")) if (value != "no-input" and not self._filechanged) else self._client.getGlobalPosition()
if newPosition == self._previousPosition and newPosition != self._duration and self._paused is False:
self._client.ui.showDebugMessage(
"Not considering position {} duplicate as new time because of VLC time precision bug".format(
newPosition))
self._previousPreviousPosition = self._previousPosition
self._previousPosition = self._position
self._positionAsk.set()
@ -326,6 +341,7 @@ class VlcPlayer(BasePlayer):
call.append(filePath)
else:
call.append(self.__playerController.getMRL(filePath))
def _usevlcintf(vlcIntfPath, vlcIntfUserPath):
vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua"
if not os.path.isfile(vlcSyncplayInterfacePath):
@ -347,7 +363,8 @@ class VlcPlayer(BasePlayer):
playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/")
elif isMacOS():
playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/"
playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/")
playerController.vlcIntfUserPath = os.path.join(
os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/")
elif isBSD():
# *BSD ports/pkgs install to /usr/local by default.
# This should also work for all the other BSDs, such as OpenBSD or DragonFly.
@ -358,14 +375,17 @@ class VlcPlayer(BasePlayer):
playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\")
playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac"
if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath):
playerController.SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(playerController.vlcport)))
playerController.SLAVE_ARGS.append(
'--lua-config=syncplay={{port=\"{}\"}}'.format(str(playerController.vlcport)))
else:
if isLinux():
playerController.vlcDataPath = "/usr/lib/syncplay/resources"
else:
playerController.vlcDataPath = utils.findWorkingDir() + "\\resources"
playerController.SLAVE_ARGS.append('--data-path={}'.format(playerController.vlcDataPath))
playerController.SLAVE_ARGS.append('--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format(playerController.vlcModulePath, str(playerController.vlcport)))
playerController.SLAVE_ARGS.append(
'--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format(
playerController.vlcModulePath, str(playerController.vlcport)))
call.extend(playerController.SLAVE_ARGS)
if args:
@ -376,11 +396,15 @@ class VlcPlayer(BasePlayer):
self._vlcVersion = None
if self.oldIntfVersion:
self.__playerController.drop(getMessage("vlc-interface-version-mismatch").format(self.oldIntfVersion,constants.VLC_INTERFACE_MIN_VERSION))
self.__playerController.drop(
getMessage("vlc-interface-version-mismatch").format(
self.oldIntfVersion, constants.VLC_INTERFACE_MIN_VERSION))
else:
if isWindows() and getattr(sys, 'frozen', '') and getattr(sys, '_MEIPASS', '') is not None: # Needed for pyinstaller --onefile bundle
self.__process = subprocess.Popen(call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000)
self.__process = subprocess.Popen(
call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE,
shell=False, creationflags=0x08000000)
else:
self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
self.timeVLCLaunched = time.time()
@ -416,10 +440,7 @@ class VlcPlayer(BasePlayer):
self._sendingData = threading.Lock()
def _shouldListenForSTDOUT(self):
if isWindows():
return False # Due to VLC3 not using STDOUT/STDERR
else:
return True
return not isWindows()
def initiate_send(self):
with self._sendingData:
@ -462,7 +483,6 @@ class VlcPlayer(BasePlayer):
break
out.close()
def found_terminator(self):
self.vlcHasResponded = True
self.__playerController.lineReceived(b"".join(self._ibuffer))

View File

@ -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):

View File

@ -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"))

View File

@ -1,19 +1,23 @@
from configparser import SafeConfigParser, DEFAULTSECT
import argparse
import ast
import codecs
import re
import os
import sys
import ast
from configparser import SafeConfigParser, DEFAULTSECT
from syncplay import constants, utils, version, milestone
from syncplay.messages import getMessage, setLanguage, isValidLanguage
from syncplay.players.playerFactory import PlayerFactory
from syncplay.utils import isMacOS
import codecs
import re
class InvalidConfigValue(Exception):
def __init__(self, message):
Exception.__init__(self, message)
class ConfigurationGetter(object):
def __init__(self):
self._config = {
@ -175,7 +179,8 @@ class ConfigurationGetter(object):
self._iniStructure = {
"server_data": ["host", "port", "password"],
"client_settings": ["name", "room", "playerPath",
"client_settings": [
"name", "room", "playerPath",
"perPlayerArguments", "slowdownThreshold",
"rewindThreshold", "fastforwardThreshold",
"slowOnDesync", "rewindOnDesync",
@ -187,7 +192,8 @@ class ConfigurationGetter(object):
"sharedPlaylistEnabled", "loopAtEndOfPlaylist",
"loopSingleFiles",
"onlySwitchToTrustedDomains", "trustedDomains", "publicServers"],
"gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD",
"gui": [
"showOSD", "showOSDWarnings", "showSlowdownOSD",
"showDifferentRoomOSD", "showSameRoomOSD",
"showNonControllerOSD", "showDurationNotification",
"chatInputEnabled", "chatInputFontUnderline",
@ -202,7 +208,8 @@ class ConfigurationGetter(object):
"chatMoveOSD", "chatOSDMargin",
"notificationTimeout", "alertTimeout",
"chatTimeout", "chatOutputEnabled"],
"general": ["language", "checkForUpdatesAutomatically",
"general": [
"language", "checkForUpdatesAutomatically",
"lastCheckedForUpdates"]
}
@ -224,7 +231,7 @@ class ConfigurationGetter(object):
try:
if varToTest == "" or varToTest is None:
return False
if str(varToTest).isdigit() == False:
if not str(varToTest).isdigit():
return False
varToTest = int(varToTest)
if varToTest > 65535 or varToTest < 1:
@ -232,6 +239,7 @@ class ConfigurationGetter(object):
return True
except:
return False
for key in self._boolean:
if self._config[key] == "True":
self._config[key] = True
@ -269,13 +277,14 @@ class ConfigurationGetter(object):
self._config["playerClass"] = player
else:
raise InvalidConfigValue(getMessage("player-path-config-error"))
playerPathErrors = player.getPlayerPathErrors(self._config["playerPath"], self._config['file'] if self._config['file'] else None)
playerPathErrors = player.getPlayerPathErrors(
self._config["playerPath"], self._config['file'] if self._config['file'] else None)
if playerPathErrors:
raise InvalidConfigValue(playerPathErrors)
elif key == "host":
self._config["host"], self._config["port"] = self._splitPortAndHost(self._config["host"])
hostNotValid = (self._config["host"] == "" or self._config["host"] is None)
portNotValid = (_isPortValid(self._config["port"]) == False)
portNotValid = (not _isPortValid(self._config["port"]))
if hostNotValid:
raise InvalidConfigValue(getMessage("no-hostname-config-error"))
elif portNotValid:
@ -408,7 +417,6 @@ class ConfigurationGetter(object):
if changed:
parser.write(codecs.open(iniPath, "wb", "utf_8_sig"))
def _forceGuiPrompt(self):
from syncplay.ui.GuiConfiguration import GuiConfiguration
try:
@ -452,7 +460,8 @@ class ConfigurationGetter(object):
#
if self._config['language']:
setLanguage(self._config['language'])
self._argparser = argparse.ArgumentParser(description=getMessage("argument-description"),
self._argparser = argparse.ArgumentParser(
description=getMessage("argument-description"),
epilog=getMessage("argument-epilog"))
self._argparser.add_argument('--no-gui', action='store_true', help=getMessage("nogui-argument"))
self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help=getMessage("host-argument"))
@ -514,6 +523,7 @@ class ConfigurationGetter(object):
self._saveConfig(path)
self._config = backup
class SafeConfigParserUnicode(SafeConfigParser):
def write(self, fp):
"""Write an .ini-format representation of the configuration state."""

View File

@ -1,19 +1,24 @@
import os
import sys
import threading
from datetime import datetime
from syncplay import constants
from syncplay import utils
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
from syncplay.players.playerFactory import PlayerFactory
from syncplay.utils import isBSD, isLinux, isMacOS, isWindows
from syncplay.utils import resourcespath, posixresourcespath
from syncplay.vendor.Qt import QtCore, QtWidgets, QtGui, __binding__, IsPySide, IsPySide2
from syncplay.vendor.Qt.QtCore import Qt, QSettings, QCoreApplication, QSize, QPoint, QUrl, QLine, QEventLoop, Signal
from syncplay.vendor.Qt.QtWidgets import QApplication, QLineEdit, QLabel, QCheckBox, QButtonGroup, QRadioButton, QDoubleSpinBox, QPlainTextEdit
from syncplay.vendor.Qt.QtGui import QCursor, QIcon, QImage, QDesktopServices
if IsPySide2:
from PySide2.QtCore import QStandardPaths
from syncplay.players.playerFactory import PlayerFactory
from datetime import datetime
from syncplay import utils
import os
import sys
import threading
from syncplay.messages import getMessage, getLanguages, setLanguage, getInitialLanguage
from syncplay import constants
from syncplay.utils import isBSD, isLinux, isMacOS, isWindows
from syncplay.utils import resourcespath, posixresourcespath
class GuiConfiguration:
def __init__(self, config, error=None, defaultConfig=None):
self.defaultConfig = defaultConfig
@ -82,8 +87,10 @@ class ConfigDialog(QtWidgets.QDialog):
def automaticUpdatePromptCheck(self):
if self.automaticupdatesCheckbox.checkState() == Qt.PartiallyChecked:
reply = QtWidgets.QMessageBox.question(self, "Syncplay",
getMessage("promptforupdate-label"), QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
reply = QtWidgets.QMessageBox.question(
self, "Syncplay",
getMessage("promptforupdate-label"),
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
if reply == QtWidgets.QMessageBox.Yes:
self.automaticupdatesCheckbox.setChecked(True)
else:
@ -91,7 +98,7 @@ class ConfigDialog(QtWidgets.QDialog):
def moreToggled(self):
self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
if self.moreToggling == False:
if self.moreToggling is False:
self.moreToggling = True
if self.showmoreCheckbox.isChecked():
@ -154,7 +161,7 @@ class ConfigDialog(QtWidgets.QDialog):
settings.endGroup()
foundpath = ""
if playerpath != None and playerpath != "":
if playerpath is not None and playerpath != "":
if utils.isURL(playerpath):
foundpath = playerpath
self.executablepathCombobox.addItem(foundpath)
@ -162,7 +169,7 @@ class ConfigDialog(QtWidgets.QDialog):
else:
if not os.path.isfile(playerpath):
expandedpath = PlayerFactory().getExpandedPlayerPathByPath(playerpath)
if expandedpath != None and os.path.isfile(expandedpath):
if expandedpath is not None and os.path.isfile(expandedpath):
playerpath = expandedpath
if os.path.isfile(playerpath):
@ -252,7 +259,8 @@ class ConfigDialog(QtWidgets.QDialog):
elif isBSD():
defaultdirectory = "/usr/local/bin"
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self,
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(
self,
"Browse for media player executable",
defaultdirectory,
browserfilter, "", options)
@ -387,7 +395,8 @@ class ConfigDialog(QtWidgets.QDialog):
else:
defaultdirectory = ""
browserfilter = "All files (*)"
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self, "Browse for media files", defaultdirectory,
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(
self, "Browse for media files", defaultdirectory,
browserfilter, "", options)
if fileName:
self.mediapathTextbox.setText(os.path.normpath(fileName))
@ -542,10 +551,10 @@ class ConfigDialog(QtWidgets.QDialog):
config = self.config
playerpaths = self.playerpaths
error = self.error
if self.datacleared == True:
if self.datacleared:
error = constants.ERROR_MESSAGE_MARKER + "{}".format(getMessage("gui-data-cleared-notification"))
self.error = error
if config['host'] == None:
if config['host'] is None:
host = ""
elif ":" in config['host']:
host = config['host']
@ -566,7 +575,7 @@ class ConfigDialog(QtWidgets.QDialog):
serverAddressPort = publicServer[1]
self.hostCombobox.addItem(serverAddressPort)
self.hostCombobox.setItemData(i, serverTitle, Qt.ToolTipRole)
if not serverAddressPort in self.publicServerAddresses:
if serverAddressPort not in self.publicServerAddresses:
self.publicServerAddresses.append(serverAddressPort)
i += 1
self.hostCombobox.setEditable(True)
@ -1224,7 +1233,7 @@ class ConfigDialog(QtWidgets.QDialog):
def populateEmptyServerList(self):
if self.publicServers is None:
if self.config["checkForUpdatesAutomatically"] == True:
if self.config["checkForUpdatesAutomatically"]:
self.updateServerList()
else:
currentServer = self.hostCombobox.currentText()
@ -1264,7 +1273,7 @@ class ConfigDialog(QtWidgets.QDialog):
self._playerProbeThread.done.connect(self._updateExecutableIcon)
self._playerProbeThread.start()
if self.config['clearGUIData'] == True:
if self.config['clearGUIData']:
self.config['clearGUIData'] = False
self.clearGUIData()
@ -1305,7 +1314,7 @@ class ConfigDialog(QtWidgets.QDialog):
self.addBottomLayout()
self.updatePasswordVisibilty()
if self.getMoreState() == False:
if self.getMoreState() is False:
self.tabListFrame.hide()
self.resetButton.hide()
self.playerargsTextbox.hide()

View File

@ -1,4 +1,5 @@
import os
if "QT_PREFERRED_BINDING" not in os.environ:
os.environ["QT_PREFERRED_BINDING"] = os.pathsep.join(
["PySide2", "PySide", "PyQt5", "PyQt4"]
@ -10,8 +11,9 @@ except ImportError:
pass
from syncplay.ui.consoleUI import ConsoleUI
def getUi(graphical=True):
if graphical: #TODO: Add graphical ui
if graphical:
ui = GraphicalUI()
else:
ui = ConsoleUI()

View File

@ -1,14 +1,16 @@
import re
import sys
import threading
import time
import syncplay
import re
from syncplay import utils
from syncplay import constants
from syncplay import utils
from syncplay.messages import getMessage
import sys
from syncplay.utils import formatTime
class ConsoleUI(threading.Thread):
def __init__(self):
self.promptMode = threading.Event()
@ -157,7 +159,7 @@ class ConsoleUI(threading.Thread):
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
elif command.group('command') in constants.COMMANDS_ROOM:
room = command.group('parameter')
if room == None:
if room is None:
if self._syncplayClient.userlist.currentUser.file:
room = self._syncplayClient.userlist.currentUser.file["name"]
else:
@ -167,7 +169,7 @@ class ConsoleUI(threading.Thread):
self._syncplayClient.sendRoom()
elif command.group('command') in constants.COMMANDS_CREATE:
roombasename = command.group('parameter')
if roombasename == None:
if roombasename is None:
roombasename = self._syncplayClient.getRoom()
roombasename = utils.stripRoomName(roombasename)
self._syncplayClient.createControlledRoom(roombasename)

View File

@ -1,28 +1,34 @@
import os
import re
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime
from functools import wraps
from platform import python_version
from twisted.internet import task
from syncplay import utils, constants, version, revision, release_number
from syncplay.messages import getMessage
from syncplay.ui.consoleUI import ConsoleUI
from syncplay.utils import resourcespath
from syncplay.utils import isLinux, isWindows, isMacOS
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
from syncplay.vendor import Qt
from syncplay.vendor.Qt import QtWidgets, QtGui, __binding__, __binding_version__, __qt_version__, IsPySide, IsPySide2
from syncplay.vendor.Qt.QtCore import Qt, QSettings, QSize, QPoint, QUrl, QLine, QDateTime
from platform import python_version
if IsPySide2:
from PySide2.QtCore import QStandardPaths
from syncplay import utils, constants, version, revision, release_number
from syncplay.messages import getMessage
from syncplay.utils import resourcespath
import sys
import time
import urllib.request, urllib.parse, urllib.error
from datetime import datetime
from syncplay.utils import isLinux, isWindows, isMacOS
import re
import os
from syncplay.utils import formatTime, sameFilename, sameFilesize, sameFileduration, RoomPasswordProvider, formatSize, isURL
from functools import wraps
from twisted.internet import task
from syncplay.ui.consoleUI import ConsoleUI
if isMacOS() and IsPySide:
from Foundation import NSURL
from Cocoa import NSString, NSUTF8StringEncoding
lastCheckedForUpdates = None
class ConsoleInGUI(ConsoleUI):
def showMessage(self, message, noTimestamp=False):
self._syncplayClient.ui.showMessage(message, True)
@ -39,6 +45,7 @@ class ConsoleInGUI(ConsoleUI):
def getUserlist(self):
self._syncplayClient.showUserList(self)
class UserlistItemDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self):
QtWidgets.QStyledItemDelegate.__init__(self)
@ -102,6 +109,7 @@ class UserlistItemDelegate(QtWidgets.QStyledItemDelegate):
optionQStyleOptionViewItem.rect.setX(optionQStyleOptionViewItem.rect.x()+16)
QtWidgets.QStyledItemDelegate.paint(self, itemQPainter, optionQStyleOptionViewItem, indexQModelIndex)
class AboutDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(AboutDialog, self).__init__(parent)
@ -117,9 +125,13 @@ class AboutDialog(QtWidgets.QDialog):
linkLabel = QtWidgets.QLabel("<center><a href=\"https://syncplay.pl\">syncplay.pl</a></center>")
linkLabel.setOpenExternalLinks(True)
versionExtString = version + revision
versionLabel = QtWidgets.QLabel("<p><center>" + getMessage("about-dialog-release").format(versionExtString, release_number) + "<br />Python " + python_version() + " - " + __binding__ + " " + __binding_version__ + " - Qt " + __qt_version__ + "</center></p>")
#versionLabel = QtWidgets.QLabel("<p><center>Version 1.5.4 release 62<br />Python 3.4.5 - PySide 1.2.4 - Qt 4.8.7</center></p>")
licenseLabel = QtWidgets.QLabel("<center><p>Copyright &copy; 2012&ndash;2018 Syncplay</p><p>" + getMessage("about-dialog-license-text") + "</p></center>")
versionLabel = QtWidgets.QLabel(
"<p><center>" + getMessage("about-dialog-release").format(versionExtString, release_number) +
"<br />Python " + python_version() + " - " + __binding__ + " " + __binding_version__ +
" - Qt " + __qt_version__ + "</center></p>")
licenseLabel = QtWidgets.QLabel(
"<center><p>Copyright &copy; 2012&ndash;2018 Syncplay</p><p>" +
getMessage("about-dialog-license-text") + "</p></center>")
aboutIconPixmap = QtGui.QPixmap(resourcespath + "syncplay.png")
aboutIconLabel = QtWidgets.QLabel()
aboutIconLabel.setPixmap(aboutIconPixmap.scaled(65, 65, Qt.KeepAspectRatio))
@ -154,6 +166,7 @@ class AboutDialog(QtWidgets.QDialog):
else:
QtGui.QDesktopServices.openUrl(QUrl("file://" + resourcespath + "third-party-notices.rtf"))
class MainWindow(QtWidgets.QMainWindow):
insertPosition = None
playlistState = []
@ -347,8 +360,6 @@ class MainWindow(QtWidgets.QMainWindow):
else:
super(MainWindow.PlaylistWidget, self).dropEvent(event)
class topSplitter(QtWidgets.QSplitter):
def createHandle(self):
return self.topSplitterHandle(self.orientation(), self)
@ -470,9 +481,16 @@ class MainWindow(QtWidgets.QMainWindow):
def showUserList(self, currentUser, rooms):
self._usertreebuffer = QtGui.QStandardItemModel()
self._usertreebuffer.setHorizontalHeaderLabels(
(getMessage("roomuser-heading-label"), getMessage("size-heading-label"), getMessage("duration-heading-label"), getMessage("filename-heading-label") ))
(
getMessage("roomuser-heading-label"), getMessage("size-heading-label"),
getMessage("duration-heading-label"), getMessage("filename-heading-label")
))
usertreeRoot = self._usertreebuffer.invisibleRootItem()
if self._syncplayClient.userlist.currentUser.file and self._syncplayClient.userlist.currentUser.file and os.path.isfile(self._syncplayClient.userlist.currentUser.file["path"]):
if (
self._syncplayClient.userlist.currentUser.file and
self._syncplayClient.userlist.currentUser.file and
os.path.isfile(self._syncplayClient.userlist.currentUser.file["path"])
):
self._syncplayClient.fileSwitch.setCurrentDirectory(os.path.dirname(self._syncplayClient.userlist.currentUser.file["path"]))
for room in rooms:
@ -613,7 +631,6 @@ class MainWindow(QtWidgets.QMainWindow):
menu.addAction(QtGui.QPixmap(resourcespath + "shield_edit.png"), getMessage("settrusteddomains-menu-label"), lambda: self.openSetTrustedDomainsDialog())
menu.exec_(self.playlist.viewport().mapToGlobal(position))
def openRoomMenu(self, position):
# TODO: Deselect items after right click
indexes = self.listTreeView.selectedIndexes()
@ -694,7 +711,7 @@ class MainWindow(QtWidgets.QMainWindow):
def updateReadyState(self, newState):
oldState = self.readyPushButton.isChecked()
if newState != oldState and newState != None:
if newState != oldState and newState is not None:
self.readyPushButton.blockSignals(True)
self.readyPushButton.setChecked(newState)
self.readyPushButton.blockSignals(False)
@ -768,7 +785,7 @@ class MainWindow(QtWidgets.QMainWindow):
@needsClient
def joinRoom(self, room=None):
if room == None:
if room is None:
room = self.roomInput.text()
if room == "":
if self._syncplayClient.userlist.currentUser.file:
@ -781,14 +798,13 @@ class MainWindow(QtWidgets.QMainWindow):
self._syncplayClient.sendRoom()
def seekPositionDialog(self):
seekTime, ok = QtWidgets.QInputDialog.getText(self, getMessage("seektime-menu-label"),
seekTime, ok = QtWidgets.QInputDialog.getText(
self, getMessage("seektime-menu-label"),
getMessage("seektime-msgbox-label"), QtWidgets.QLineEdit.Normal,
"0:00")
if ok and seekTime != '':
self.seekPosition(seekTime)
def seekFromButton(self):
self.seekPosition(self.seekInput.text())
@ -871,7 +887,7 @@ class MainWindow(QtWidgets.QMainWindow):
@needsClient
def browseMediapath(self):
if self._syncplayClient._player.customOpenDialog == True:
if self._syncplayClient._player.customOpenDialog:
self._syncplayClient._player.openCustomOpenDialog()
return
@ -887,7 +903,8 @@ class MainWindow(QtWidgets.QMainWindow):
else:
defaultdirectory = self.getInitialMediaDirectory()
browserfilter = "All files (*)"
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(self, getMessage("browseformedia-label"), defaultdirectory,
fileName, filtr = QtWidgets.QFileDialog.getOpenFileName(
self, getMessage("browseformedia-label"), defaultdirectory,
browserfilter, "", options)
if fileName:
if isWindows():
@ -899,7 +916,7 @@ class MainWindow(QtWidgets.QMainWindow):
@needsClient
def OpenAddFilesToPlaylistDialog(self):
if self._syncplayClient._player.customOpenDialog == True:
if self._syncplayClient._player.customOpenDialog:
self._syncplayClient._player.openCustomOpenDialog()
return
@ -915,7 +932,8 @@ class MainWindow(QtWidgets.QMainWindow):
else:
defaultdirectory = self.getInitialMediaDirectory()
browserfilter = "All files (*)"
fileNames, filtr = QtWidgets.QFileDialog.getOpenFileNames(self, getMessage("browseformedia-label"), defaultdirectory,
fileNames, filtr = QtWidgets.QFileDialog.getOpenFileNames(
self, getMessage("browseformedia-label"), defaultdirectory,
browserfilter, "", options)
self.updatingPlaylist = True
if fileNames:
@ -1046,6 +1064,7 @@ class MainWindow(QtWidgets.QMainWindow):
if result == QtWidgets.QDialog.Accepted:
newTrustedDomains = utils.convertMultilineStringToList(TrustedDomainsTextbox.toPlainText())
self._syncplayClient.setTrustedDomains(newTrustedDomains)
@needsClient
def addTrustedDomain(self, newDomain):
trustedDomains = self.config["trustedDomains"][:]
@ -1059,7 +1078,8 @@ class MainWindow(QtWidgets.QMainWindow):
options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
else:
options = QtWidgets.QFileDialog.Options(QtWidgets.QFileDialog.ShowDirsOnly)
folderName = str(QtWidgets.QFileDialog.getExistingDirectory(self,None,self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False),options))
folderName = str(QtWidgets.QFileDialog.getExistingDirectory(
self, None, self.getInitialMediaDirectory(includeUserSpecifiedDirectories=False), options))
if folderName:
existingMediaDirs = MediaDirectoriesTextbox.toPlainText()
@ -1073,15 +1093,16 @@ class MainWindow(QtWidgets.QMainWindow):
@needsClient
def promptForStreamURL(self):
streamURL, ok = QtWidgets.QInputDialog.getText(self, getMessage("promptforstreamurl-msgbox-label"),
getMessage("promptforstreamurlinfo-msgbox-label"), QtWidgets.QLineEdit.Normal,
"")
streamURL, ok = QtWidgets.QInputDialog.getText(
self, getMessage("promptforstreamurl-msgbox-label"),
getMessage("promptforstreamurlinfo-msgbox-label"), QtWidgets.QLineEdit.Normal, "")
if ok and streamURL != '':
self._syncplayClient._player.openFile(streamURL)
@needsClient
def createControlledRoom(self):
controlroom, ok = QtWidgets.QInputDialog.getText(self, getMessage("createcontrolledroom-msgbox-label"),
controlroom, ok = QtWidgets.QInputDialog.getText(
self, getMessage("createcontrolledroom-msgbox-label"),
getMessage("controlledroominfo-msgbox-label"), QtWidgets.QLineEdit.Normal,
utils.stripRoomName(self._syncplayClient.getRoom()))
if ok and controlroom != '':
@ -1106,9 +1127,9 @@ class MainWindow(QtWidgets.QMainWindow):
@needsClient
def setOffset(self):
newoffset, ok = QtWidgets.QInputDialog.getText(self, getMessage("setoffset-msgbox-label"),
getMessage("offsetinfo-msgbox-label"), QtWidgets.QLineEdit.Normal,
"")
newoffset, ok = QtWidgets.QInputDialog.getText(
self, getMessage("setoffset-msgbox-label"),
getMessage("offsetinfo-msgbox-label"), QtWidgets.QLineEdit.Normal, "")
if ok and newoffset != '':
o = re.match(constants.UI_OFFSET_REGEX, "o " + newoffset)
if o:
@ -1187,7 +1208,8 @@ class MainWindow(QtWidgets.QMainWindow):
window.chatInput = QtWidgets.QLineEdit()
window.chatInput.setMaxLength(constants.MAX_CHAT_MESSAGE_LENGTH)
window.chatInput.returnPressed.connect(self.sendChatMessage)
window.chatButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + 'email_go.png'),
window.chatButton = QtWidgets.QPushButton(
QtGui.QPixmap(resourcespath + 'email_go.png'),
getMessage("sendmessage-label"))
window.chatButton.pressed.connect(self.sendChatMessage)
window.chatLayout = QtWidgets.QHBoxLayout()
@ -1242,7 +1264,8 @@ class MainWindow(QtWidgets.QMainWindow):
window.roomInput = QtWidgets.QLineEdit()
window.roomInput.setMaxLength(constants.MAX_ROOM_NAME_LENGTH)
window.roomInput.returnPressed.connect(self.joinRoom)
window.roomButton = QtWidgets.QPushButton(QtGui.QPixmap(resourcespath + 'door_in.png'),
window.roomButton = QtWidgets.QPushButton(
QtGui.QPixmap(resourcespath + 'door_in.png'),
getMessage("joinroom-label"))
window.roomButton.pressed.connect(self.joinRoom)
window.roomLayout = QtWidgets.QHBoxLayout()
@ -1405,7 +1428,6 @@ class MainWindow(QtWidgets.QMainWindow):
getMessage("setmediadirectories-menu-label"))
window.openAction.triggered.connect(self.openSetMediaDirectoriesDialog)
window.exitAction = window.fileMenu.addAction(QtGui.QPixmap(resourcespath + 'cross.png'),
getMessage("exit-menu-label"))
window.exitAction.triggered.connect(self.exitSyncplay)
@ -1414,13 +1436,21 @@ class MainWindow(QtWidgets.QMainWindow):
# Playback menu
window.playbackMenu = QtWidgets.QMenu(getMessage("playback-menu-label"), self)
window.playAction = window.playbackMenu.addAction(QtGui.QPixmap(resourcespath + 'control_play_blue.png'), getMessage("play-menu-label"))
window.playAction = window.playbackMenu.addAction(
QtGui.QPixmap(resourcespath + 'control_play_blue.png'),
getMessage("play-menu-label"))
window.playAction.triggered.connect(self.play)
window.pauseAction = window.playbackMenu.addAction(QtGui.QPixmap(resourcespath + 'control_pause_blue.png'), getMessage("pause-menu-label"))
window.pauseAction = window.playbackMenu.addAction(
QtGui.QPixmap(resourcespath + 'control_pause_blue.png'),
getMessage("pause-menu-label"))
window.pauseAction.triggered.connect(self.pause)
window.seekAction = window.playbackMenu.addAction(QtGui.QPixmap(resourcespath + 'clock_go.png'), getMessage("seektime-menu-label"))
window.seekAction = window.playbackMenu.addAction(
QtGui.QPixmap(resourcespath + 'clock_go.png'),
getMessage("seektime-menu-label"))
window.seekAction.triggered.connect(self.seekPositionDialog)
window.unseekAction = window.playbackMenu.addAction(QtGui.QPixmap(resourcespath + 'arrow_undo.png'), getMessage("undoseek-menu-label"))
window.unseekAction = window.playbackMenu.addAction(
QtGui.QPixmap(resourcespath + 'arrow_undo.png'),
getMessage("undoseek-menu-label"))
window.unseekAction.triggered.connect(self.undoSeek)
window.menuBar.addMenu(window.playbackMenu)
@ -1428,10 +1458,12 @@ class MainWindow(QtWidgets.QMainWindow):
# Advanced menu
window.advancedMenu = QtWidgets.QMenu(getMessage("advanced-menu-label"), self)
window.setoffsetAction = window.advancedMenu.addAction(QtGui.QPixmap(resourcespath + 'timeline_marker.png'),
window.setoffsetAction = window.advancedMenu.addAction(
QtGui.QPixmap(resourcespath + 'timeline_marker.png'),
getMessage("setoffset-menu-label"))
window.setoffsetAction.triggered.connect(self.setOffset)
window.setTrustedDomainsAction = window.advancedMenu.addAction(QtGui.QPixmap(resourcespath + 'shield_edit.png'),
window.setTrustedDomainsAction = window.advancedMenu.addAction(
QtGui.QPixmap(resourcespath + 'shield_edit.png'),
getMessage("settrusteddomains-menu-label"))
window.setTrustedDomainsAction.triggered.connect(self.openSetTrustedDomainsDialog)
window.createcontrolledroomAction = window.advancedMenu.addAction(
@ -1456,21 +1488,23 @@ class MainWindow(QtWidgets.QMainWindow):
window.autoplayAction.triggered.connect(self.updateAutoplayVisibility)
window.menuBar.addMenu(window.windowMenu)
# Help menu
window.helpMenu = QtWidgets.QMenu(getMessage("help-menu-label"), self)
window.userguideAction = window.helpMenu.addAction(QtGui.QPixmap(resourcespath + 'help.png'),
window.userguideAction = window.helpMenu.addAction(
QtGui.QPixmap(resourcespath + 'help.png'),
getMessage("userguide-menu-label"))
window.userguideAction.triggered.connect(self.openUserGuide)
window.updateAction = window.helpMenu.addAction(QtGui.QPixmap(resourcespath + 'application_get.png'),
window.updateAction = window.helpMenu.addAction(
QtGui.QPixmap(resourcespath + 'application_get.png'),
getMessage("update-menu-label"))
window.updateAction.triggered.connect(self.userCheckForUpdates)
if not isMacOS():
window.helpMenu.addSeparator()
window.about = window.helpMenu.addAction(QtGui.QPixmap(resourcespath + 'syncplay.png'),
window.about = window.helpMenu.addAction(
QtGui.QPixmap(resourcespath + 'syncplay.png'),
getMessage("about-menu-label"))
else:
window.about = window.helpMenu.addAction("&About")
@ -1528,7 +1562,7 @@ class MainWindow(QtWidgets.QMainWindow):
def updateAutoPlayState(self, newState):
oldState = self.autoplayPushButton.isChecked()
if newState != oldState and newState != None:
if newState != oldState and newState is not None:
self.autoplayPushButton.blockSignals(True)
self.autoplayPushButton.setChecked(newState)
self.autoplayPushButton.blockSignals(False)
@ -1591,10 +1625,11 @@ class MainWindow(QtWidgets.QMainWindow):
else:
import syncplay
updateMessage = getMessage("update-check-failed-notification").format(syncplay.version)
if userInitiated == True:
if userInitiated:
updateURL = constants.SYNCPLAY_DOWNLOAD_URL
if updateURL is not None:
reply = QtWidgets.QMessageBox.question(self, "Syncplay",
reply = QtWidgets.QMessageBox.question(
self, "Syncplay",
updateMessage, QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
if reply == QtWidgets.QMessageBox.Yes:
self.QtGui.QDesktopServices.openUrl(QUrl(updateURL))
@ -1624,7 +1659,7 @@ class MainWindow(QtWidgets.QMainWindow):
dropfilepath = os.path.abspath(NSURL.URLWithString_(pathString).filePathURL().path())
else:
dropfilepath = os.path.abspath(str(url.toLocalFile()))
if rewindFile == False:
if not rewindFile:
self._syncplayClient._player.openFile(dropfilepath)
else:
self._syncplayClient.setPosition(0)

View File

@ -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

View File

@ -17,4 +17,3 @@ from syncplay.utils import blackholeStdoutForFrozenWindow
if __name__ == '__main__':
blackholeStdoutForFrozenWindow()
SyncplayClientManager().run()

View File

@ -19,5 +19,14 @@ from syncplay.server import SyncFactory, ConfigurationGetter
if __name__ == '__main__':
argsGetter = ConfigurationGetter()
args = argsGetter.getConfiguration()
reactor.listenTCP(int(args.port), SyncFactory(args.password, args.motd_file, args.isolate_rooms, args.salt, args.disable_ready,args.disable_chat, args.max_chat_message_length))
reactor.listenTCP(
int(args.port),
SyncFactory(
args.password,
args.motd_file,
args.isolate_rooms,
args.salt,
args.disable_ready,
args.disable_chat,
args.max_chat_message_length))
reactor.run()