Merge 35f3c3e6b3a827df86c8ce611265a4985b99b992 into d4bd954ad1d0371b21685217f1f10a52c92dffb1
This commit is contained in:
commit
34c5de941e
@ -1,26 +1,27 @@
|
|||||||
from syncplay.client import SyncplayClient
|
from syncplay.client import SyncplayClient
|
||||||
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
|
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
|
||||||
from syncplay import ui
|
from syncplay import ui
|
||||||
try:
|
from syncplay.messages import getMessage
|
||||||
from syncplay.players.mpc import MPCHCAPIPlayer
|
try:
|
||||||
except ImportError:
|
from syncplay.players.mpc import MPCHCAPIPlayer
|
||||||
MPCHCAPIPlayer = None
|
except ImportError:
|
||||||
from syncplay.players.mplayer import MplayerPlayer
|
MPCHCAPIPlayer = None
|
||||||
|
from syncplay.players.mplayer import MplayerPlayer
|
||||||
class SyncplayClientManager(object):
|
|
||||||
def run(self):
|
class SyncplayClientManager(object):
|
||||||
config = ConfigurationGetter().getConfiguration()
|
def run(self):
|
||||||
interface = ui.getUi(graphical=not config["noGui"])
|
config = ConfigurationGetter().getConfiguration()
|
||||||
syncplayClient = None
|
interface = ui.getUi(graphical=not config["noGui"])
|
||||||
if(config['playerType'] == "mpc"):
|
syncplayClient = None
|
||||||
syncplayClient = SyncplayClient(MPCHCAPIPlayer, interface, config)
|
if(config['playerType'] == "mpc"):
|
||||||
elif(config['playerType'] == "mplayer"):
|
syncplayClient = SyncplayClient(MPCHCAPIPlayer, interface, config)
|
||||||
syncplayClient = SyncplayClient(MplayerPlayer, interface, config)
|
elif(config['playerType'] == "mplayer"):
|
||||||
if(syncplayClient):
|
syncplayClient = SyncplayClient(MplayerPlayer, interface, config)
|
||||||
interface.addClient(syncplayClient)
|
if(syncplayClient):
|
||||||
syncplayClient.start(config['host'], config['port'])
|
interface.addClient(syncplayClient)
|
||||||
else:
|
syncplayClient.start(config['host'], config['port'])
|
||||||
interface.showErrorMessage("Unable to start client")
|
else:
|
||||||
|
interface.showErrorMessage(getMessage("en", "unable-to-start-client-error"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@ en = {
|
|||||||
"reconnection-attempt-notification" : "Connection with server lost, attempting to reconnect",
|
"reconnection-attempt-notification" : "Connection with server lost, attempting to reconnect",
|
||||||
"disconnection-notification" : "Disconnected from server",
|
"disconnection-notification" : "Disconnected from server",
|
||||||
"connection-failed-notification" : "Connection with server failed",
|
"connection-failed-notification" : "Connection with server failed",
|
||||||
|
"connected-successful-notification" : "Successfully connected to server",
|
||||||
|
"retrying-notification" : "%s, Retrying in %d seconds...", #Seconds
|
||||||
|
|
||||||
"rewind-notification" : "Rewinded due to time difference with <{}>", #User
|
"rewind-notification" : "Rewinded due to time difference with <{}>", #User
|
||||||
"slowdown-notification" : "Slowing down due to time difference with <{}>", #User
|
"slowdown-notification" : "Slowing down due to time difference with <{}>", #User
|
||||||
@ -31,11 +33,85 @@ en = {
|
|||||||
"notplaying-notification" : "People who are not playing any file:",
|
"notplaying-notification" : "People who are not playing any file:",
|
||||||
"userlist-room-notification" : "In room '{}':", #Room
|
"userlist-room-notification" : "In room '{}':", #Room
|
||||||
|
|
||||||
|
"mplayer-file-required-notification" : "Syncplay using mplayer requires you to provide file when starting",
|
||||||
|
"mplayer-file-required-notification/example" : "Usage example: syncplay [options] [url|path/]filename",
|
||||||
|
|
||||||
|
"unrecognized-command-notification" : "Unrecognized command",
|
||||||
|
"commandlist-notification" : "Available commands:",
|
||||||
|
"commandlist-notification/room" : "\tr [name] - change room",
|
||||||
|
"commandlist-notification/list" : "\tl - show user list",
|
||||||
|
"commandlist-notification/undo" : "\tu - undo last seek",
|
||||||
|
"commandlist-notification/pause" : "\tp - toggle pause",
|
||||||
|
"commandlist-notification/seek" : "\t[s][+-]time - seek to the given value of time, if + or - is not specified it's absolute time in seconds or min:sec",
|
||||||
|
"commandlist-notification/help" : "\th - this help",
|
||||||
|
"syncplay-version-notification" : "Syncplay version: {}", #syncplay.version
|
||||||
|
"more-info-notification" : "More info available at: {}", #projectURL
|
||||||
|
|
||||||
# Client prompts
|
# Client prompts
|
||||||
"enter-to-exit-prompt" : "Press enter to exit\n",
|
"enter-to-exit-prompt" : "Press enter to exit\n",
|
||||||
|
|
||||||
# Client errors
|
# Client errors
|
||||||
"server-timeout-error" : "Connection with server timed out"
|
"server-timeout-error" : "Connection with server timed out",
|
||||||
|
"mpc-slave-error" : "Unable to start MPC in slave mode!",
|
||||||
|
"mpc-version-insufficient-error" : "MPC version not sufficient, please use `mpc-hc` >= `1.6.4`",
|
||||||
|
"player-file-open-error" : "Player failed opening file",
|
||||||
|
"player-path-error" : "Player path is not set properly",
|
||||||
|
"hostname-empty-error" : "Hostname can't be empty",
|
||||||
|
"empty-error" : "{} can't be empty", #Configuration
|
||||||
|
|
||||||
|
"arguments-missing-error" : "Some necessary arguments are missing, refer to --help",
|
||||||
|
|
||||||
|
"unable-to-start-client-error" : "Unable to start client",
|
||||||
|
|
||||||
|
"not-json-error" : "Not a json encoded string\n",
|
||||||
|
"hello-arguments-error" : "Not enough Hello arguments\n",
|
||||||
|
"version-mismatch-error" : "Mismatch between versions of client and server\n",
|
||||||
|
|
||||||
|
# Client arguments
|
||||||
|
"argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
|
||||||
|
"argument-epilog" : 'If no options supplied _config values will be used',
|
||||||
|
"nogui-argument" : 'show no GUI',
|
||||||
|
"host-argument" : 'server\'s address',
|
||||||
|
"name-argument" : 'desired username',
|
||||||
|
"debug-argument" : 'debug mode',
|
||||||
|
"force-gui-prompt-argument" : 'make configuration prompt appear',
|
||||||
|
"no-store-argument" : 'don\'t store values in .syncplay',
|
||||||
|
"room-argument" : 'default room',
|
||||||
|
"password-argument" : 'server password',
|
||||||
|
"player-path-argument" : 'path to your player executable',
|
||||||
|
"file-argument" : 'file to play',
|
||||||
|
"args-argument" : 'player options, if you need to pass options starting with - prepend them with single \'--\' argument',
|
||||||
|
|
||||||
|
# Client labels
|
||||||
|
"host-label" : 'Host: ',
|
||||||
|
"username-label" : 'Username: ',
|
||||||
|
"room-label" : 'Default room (optional): ',
|
||||||
|
"password-label" : 'Server password (optional): ',
|
||||||
|
"path-label" : 'Path to player executable: ',
|
||||||
|
|
||||||
|
# Server notifications
|
||||||
|
"welcome-server-notification" : "Welcome to Syncplay server, ver. {0}", #version
|
||||||
|
"client-connected-room-server-notification" : "{0}({2}) connected to room '{1}'", #username, host, room
|
||||||
|
"client-left-server-notification" : "{0} left server", #name
|
||||||
|
|
||||||
|
|
||||||
|
#Server arguments
|
||||||
|
"server-argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network. Server instance',
|
||||||
|
"server-argument-epilog" : 'If no options supplied _config values will be used',
|
||||||
|
"server-port-argument" : 'server TCP port',
|
||||||
|
"server-password-argument" : 'server password',
|
||||||
|
"server-isolate-room-argument" : 'should rooms be isolated?',
|
||||||
|
|
||||||
|
#Server errors
|
||||||
|
"not-known-server-error" : "You must be known to server before sending this command",
|
||||||
|
"client-drop-server-error" : "Client drop: %s -- %s", #host, error
|
||||||
|
"password-required-server-error" : "Password required",
|
||||||
|
"wrong-password-server-error" : "Wrong password supplied",
|
||||||
|
"hello-server-error" : "Not enough Hello arguments",
|
||||||
|
"version-mismatch-server-error" : "Mismatch between versions of client and server",
|
||||||
|
"wrong-password-server-error" : "Wrong password supplied"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pl = {
|
pl = {
|
||||||
|
|||||||
@ -1,454 +1,455 @@
|
|||||||
#coding:utf8
|
#coding:utf8
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import thread
|
import thread
|
||||||
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
|
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from syncplay.players.basePlayer import BasePlayer
|
from syncplay.players.basePlayer import BasePlayer
|
||||||
import re
|
import re
|
||||||
from syncplay.utils import retry
|
from syncplay.utils import retry
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
|
from syncplay.messages import getMessage
|
||||||
class MpcHcApi:
|
|
||||||
def __init__(self):
|
class MpcHcApi:
|
||||||
self.callbacks = self.__Callbacks()
|
def __init__(self):
|
||||||
self.loadState = None
|
self.callbacks = self.__Callbacks()
|
||||||
self.playState = None
|
self.loadState = None
|
||||||
self.filePlaying = None
|
self.playState = None
|
||||||
self.fileDuration = None
|
self.filePlaying = None
|
||||||
self.filePath = None
|
self.fileDuration = None
|
||||||
self.lastFilePosition = None
|
self.filePath = None
|
||||||
self.version = None
|
self.lastFilePosition = None
|
||||||
self.__playpause_warden = False
|
self.version = None
|
||||||
self.__locks = self.__Locks()
|
self.__playpause_warden = False
|
||||||
self.__mpcExistenceChecking = threading.Thread(target=self.__mpcReadyInSlaveMode, name="Check MPC window")
|
self.__locks = self.__Locks()
|
||||||
self.__mpcExistenceChecking.setDaemon(True)
|
self.__mpcExistenceChecking = threading.Thread(target=self.__mpcReadyInSlaveMode, name="Check MPC window")
|
||||||
self.__listener = self.__Listener(self, self.__locks)
|
self.__mpcExistenceChecking.setDaemon(True)
|
||||||
self.__listener.setDaemon(True)
|
self.__listener = self.__Listener(self, self.__locks)
|
||||||
self.__listener.start()
|
self.__listener.setDaemon(True)
|
||||||
self.__locks.listenerStart.wait()
|
self.__listener.start()
|
||||||
|
self.__locks.listenerStart.wait()
|
||||||
def waitForFileStateReady(f): #@NoSelf
|
|
||||||
@wraps(f)
|
def waitForFileStateReady(f): #@NoSelf
|
||||||
def wrapper(self, *args, **kwds):
|
@wraps(f)
|
||||||
if(not self.__locks.fileReady.wait(constants.MPC_LOCK_WAIT_TIME)):
|
def wrapper(self, *args, **kwds):
|
||||||
raise self.PlayerNotReadyException()
|
if(not self.__locks.fileReady.wait(constants.MPC_LOCK_WAIT_TIME)):
|
||||||
return f(self, *args, **kwds)
|
raise self.PlayerNotReadyException()
|
||||||
return wrapper
|
return f(self, *args, **kwds)
|
||||||
|
return wrapper
|
||||||
def startMpc(self, path, args=()):
|
|
||||||
args = "%s /slave %s" % (" ".join(args), str(self.__listener.hwnd))
|
def startMpc(self, path, args=()):
|
||||||
win32api.ShellExecute(0, "open", path, args, None, 1)
|
args = "%s /slave %s" % (" ".join(args), str(self.__listener.hwnd))
|
||||||
if(not self.__locks.mpcStart.wait(constants.MPC_OPEN_MAX_WAIT_TIME)):
|
win32api.ShellExecute(0, "open", path, args, None, 1)
|
||||||
raise self.NoSlaveDetectedException("Unable to start MPC in slave mode!")
|
if(not self.__locks.mpcStart.wait(constants.MPC_OPEN_MAX_WAIT_TIME)):
|
||||||
self.__mpcExistenceChecking.start()
|
raise self.NoSlaveDetectedException(getMessage("en", "mpc-slave-error"))
|
||||||
|
self.__mpcExistenceChecking.start()
|
||||||
def openFile(self, filePath):
|
|
||||||
self.__listener.SendCommand(self.CMD_OPENFILE, filePath)
|
def openFile(self, filePath):
|
||||||
|
self.__listener.SendCommand(self.CMD_OPENFILE, filePath)
|
||||||
def isPaused(self):
|
|
||||||
return (self.playState <> self.__MPC_PLAYSTATE.PS_PLAY and self.playState <> None)
|
def isPaused(self):
|
||||||
|
return (self.playState <> self.__MPC_PLAYSTATE.PS_PLAY and self.playState <> None)
|
||||||
def askForVersion(self):
|
|
||||||
self.__listener.SendCommand(self.CMD_GETVERSION)
|
def askForVersion(self):
|
||||||
|
self.__listener.SendCommand(self.CMD_GETVERSION)
|
||||||
@waitForFileStateReady
|
|
||||||
def pause(self):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_PAUSE)
|
def pause(self):
|
||||||
|
self.__listener.SendCommand(self.CMD_PAUSE)
|
||||||
@waitForFileStateReady
|
|
||||||
def playPause(self):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_PLAYPAUSE)
|
def playPause(self):
|
||||||
|
self.__listener.SendCommand(self.CMD_PLAYPAUSE)
|
||||||
@waitForFileStateReady
|
|
||||||
def unpause(self):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_PLAY)
|
def unpause(self):
|
||||||
|
self.__listener.SendCommand(self.CMD_PLAY)
|
||||||
@waitForFileStateReady
|
|
||||||
def askForCurrentPosition(self):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_GETCURRENTPOSITION)
|
def askForCurrentPosition(self):
|
||||||
|
self.__listener.SendCommand(self.CMD_GETCURRENTPOSITION)
|
||||||
@waitForFileStateReady
|
|
||||||
def seek(self, position):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_SETPOSITION, unicode(position))
|
def seek(self, position):
|
||||||
|
self.__listener.SendCommand(self.CMD_SETPOSITION, unicode(position))
|
||||||
@waitForFileStateReady
|
|
||||||
def setSpeed(self, rate):
|
@waitForFileStateReady
|
||||||
self.__listener.SendCommand(self.CMD_SETSPEED, unicode(rate))
|
def setSpeed(self, rate):
|
||||||
|
self.__listener.SendCommand(self.CMD_SETSPEED, unicode(rate))
|
||||||
def sendOsd(self, message, MsgPos=constants.MPC_OSD_POSITION, DurationMs=constants.OSD_DURATION):
|
|
||||||
class __OSDDATASTRUCT(ctypes.Structure):
|
def sendOsd(self, message, MsgPos=constants.MPC_OSD_POSITION, DurationMs=constants.OSD_DURATION):
|
||||||
_fields_ = [
|
class __OSDDATASTRUCT(ctypes.Structure):
|
||||||
('nMsgPos', ctypes.c_int32),
|
_fields_ = [
|
||||||
('nDurationMS', ctypes.c_int32),
|
('nMsgPos', ctypes.c_int32),
|
||||||
('strMsg', ctypes.c_wchar * (len(message) + 1))
|
('nDurationMS', ctypes.c_int32),
|
||||||
]
|
('strMsg', ctypes.c_wchar * (len(message) + 1))
|
||||||
cmessage = __OSDDATASTRUCT()
|
]
|
||||||
cmessage.nMsgPos = MsgPos
|
cmessage = __OSDDATASTRUCT()
|
||||||
cmessage.nDurationMS = DurationMs
|
cmessage.nMsgPos = MsgPos
|
||||||
cmessage.strMsg = message
|
cmessage.nDurationMS = DurationMs
|
||||||
self.__listener.SendCommand(self.CMD_OSDSHOWMESSAGE, cmessage)
|
cmessage.strMsg = message
|
||||||
|
self.__listener.SendCommand(self.CMD_OSDSHOWMESSAGE, cmessage)
|
||||||
def sendRawCommand(self, cmd, value):
|
|
||||||
self.__listener.SendCommand(cmd, value)
|
def sendRawCommand(self, cmd, value):
|
||||||
|
self.__listener.SendCommand(cmd, value)
|
||||||
def handleCommand(self, cmd, value):
|
|
||||||
if (cmd == self.CMD_CONNECT):
|
def handleCommand(self, cmd, value):
|
||||||
self.__listener.mpcHandle = int(value)
|
if (cmd == self.CMD_CONNECT):
|
||||||
self.__locks.mpcStart.set()
|
self.__listener.mpcHandle = int(value)
|
||||||
if(self.callbacks.onConnected):
|
self.__locks.mpcStart.set()
|
||||||
thread.start_new_thread(self.callbacks.onConnected, ())
|
if(self.callbacks.onConnected):
|
||||||
|
thread.start_new_thread(self.callbacks.onConnected, ())
|
||||||
elif(cmd == self.CMD_STATE):
|
|
||||||
self.loadState = int(value)
|
elif(cmd == self.CMD_STATE):
|
||||||
fileNotReady = self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or self.loadState == self.__MPC_LOADSTATE.MLS_LOADING
|
self.loadState = int(value)
|
||||||
if(fileNotReady):
|
fileNotReady = self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or self.loadState == self.__MPC_LOADSTATE.MLS_LOADING
|
||||||
self.playState = None
|
if(fileNotReady):
|
||||||
self.__locks.fileReady.clear()
|
self.playState = None
|
||||||
else:
|
self.__locks.fileReady.clear()
|
||||||
self.__locks.fileReady.set()
|
else:
|
||||||
if(self.callbacks.onFileStateChange):
|
self.__locks.fileReady.set()
|
||||||
thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,))
|
if(self.callbacks.onFileStateChange):
|
||||||
|
thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,))
|
||||||
elif(cmd == self.CMD_PLAYMODE):
|
|
||||||
self.playState = int(value)
|
elif(cmd == self.CMD_PLAYMODE):
|
||||||
if(self.callbacks.onUpdatePlaystate):
|
self.playState = int(value)
|
||||||
thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,))
|
if(self.callbacks.onUpdatePlaystate):
|
||||||
|
thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,))
|
||||||
elif(cmd == self.CMD_NOWPLAYING):
|
|
||||||
value = re.split(r'(?<!\\)\|', value)
|
elif(cmd == self.CMD_NOWPLAYING):
|
||||||
self.filePath = value[3]
|
value = re.split(r'(?<!\\)\|', value)
|
||||||
self.filePlaying = value[3].split('\\').pop()
|
self.filePath = value[3]
|
||||||
self.fileDuration = float(value[4])
|
self.filePlaying = value[3].split('\\').pop()
|
||||||
if(self.callbacks.onUpdatePath):
|
self.fileDuration = float(value[4])
|
||||||
thread.start_new_thread(self.callbacks.onUpdatePath, (self.onUpdatePath,))
|
if(self.callbacks.onUpdatePath):
|
||||||
if(self.callbacks.onUpdateFilename):
|
thread.start_new_thread(self.callbacks.onUpdatePath, (self.onUpdatePath,))
|
||||||
thread.start_new_thread(self.callbacks.onUpdateFilename, (self.filePlaying,))
|
if(self.callbacks.onUpdateFilename):
|
||||||
if(self.callbacks.onUpdateFileDuration):
|
thread.start_new_thread(self.callbacks.onUpdateFilename, (self.filePlaying,))
|
||||||
thread.start_new_thread(self.callbacks.onUpdateFileDuration, (self.fileDuration,))
|
if(self.callbacks.onUpdateFileDuration):
|
||||||
|
thread.start_new_thread(self.callbacks.onUpdateFileDuration, (self.fileDuration,))
|
||||||
elif(cmd == self.CMD_CURRENTPOSITION):
|
|
||||||
self.lastFilePosition = float(value)
|
elif(cmd == self.CMD_CURRENTPOSITION):
|
||||||
if(self.callbacks.onGetCurrentPosition):
|
self.lastFilePosition = float(value)
|
||||||
thread.start_new_thread(self.callbacks.onGetCurrentPosition, (self.lastFilePosition,))
|
if(self.callbacks.onGetCurrentPosition):
|
||||||
|
thread.start_new_thread(self.callbacks.onGetCurrentPosition, (self.lastFilePosition,))
|
||||||
elif(cmd == self.CMD_NOTIFYSEEK):
|
|
||||||
if(self.lastFilePosition <> float(value)): #Notify seek is sometimes sent twice
|
elif(cmd == self.CMD_NOTIFYSEEK):
|
||||||
self.lastFilePosition = float(value)
|
if(self.lastFilePosition <> float(value)): #Notify seek is sometimes sent twice
|
||||||
if(self.callbacks.onSeek):
|
self.lastFilePosition = float(value)
|
||||||
thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,))
|
if(self.callbacks.onSeek):
|
||||||
|
thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,))
|
||||||
elif(cmd == self.CMD_DISCONNECT):
|
|
||||||
if(self.callbacks.onMpcClosed):
|
elif(cmd == self.CMD_DISCONNECT):
|
||||||
thread.start_new_thread(self.callbacks.onMpcClosed, (None,))
|
if(self.callbacks.onMpcClosed):
|
||||||
|
thread.start_new_thread(self.callbacks.onMpcClosed, (None,))
|
||||||
elif(cmd == self.CMD_VERSION):
|
|
||||||
if(self.callbacks.onVersion):
|
elif(cmd == self.CMD_VERSION):
|
||||||
self.version = value
|
if(self.callbacks.onVersion):
|
||||||
thread.start_new_thread(self.callbacks.onVersion, (value,))
|
self.version = value
|
||||||
|
thread.start_new_thread(self.callbacks.onVersion, (value,))
|
||||||
class PlayerNotReadyException(Exception):
|
|
||||||
pass
|
class PlayerNotReadyException(Exception):
|
||||||
|
pass
|
||||||
class __Callbacks:
|
|
||||||
def __init__(self):
|
class __Callbacks:
|
||||||
self.onConnected = None
|
def __init__(self):
|
||||||
self.onSeek = None
|
self.onConnected = None
|
||||||
self.onUpdatePath = None
|
self.onSeek = None
|
||||||
self.onUpdateFilename = None
|
self.onUpdatePath = None
|
||||||
self.onUpdateFileDuration = None
|
self.onUpdateFilename = None
|
||||||
self.onGetCurrentPosition = None
|
self.onUpdateFileDuration = None
|
||||||
self.onUpdatePlaystate = None
|
self.onGetCurrentPosition = None
|
||||||
self.onFileStateChange = None
|
self.onUpdatePlaystate = None
|
||||||
self.onMpcClosed = None
|
self.onFileStateChange = None
|
||||||
self.onVersion = None
|
self.onMpcClosed = None
|
||||||
|
self.onVersion = None
|
||||||
class __Locks:
|
|
||||||
def __init__(self):
|
class __Locks:
|
||||||
self.listenerStart = threading.Event()
|
def __init__(self):
|
||||||
self.mpcStart = threading.Event()
|
self.listenerStart = threading.Event()
|
||||||
self.fileReady = threading.Event()
|
self.mpcStart = threading.Event()
|
||||||
|
self.fileReady = threading.Event()
|
||||||
def __mpcReadyInSlaveMode(self):
|
|
||||||
while(True):
|
def __mpcReadyInSlaveMode(self):
|
||||||
time.sleep(10)
|
while(True):
|
||||||
if not win32gui.IsWindow(self.__listener.mpcHandle):
|
time.sleep(10)
|
||||||
if(self.callbacks.onMpcClosed):
|
if not win32gui.IsWindow(self.__listener.mpcHandle):
|
||||||
self.callbacks.onMpcClosed(None)
|
if(self.callbacks.onMpcClosed):
|
||||||
break
|
self.callbacks.onMpcClosed(None)
|
||||||
|
break
|
||||||
CMD_CONNECT = 0x50000000
|
|
||||||
CMD_STATE = 0x50000001
|
CMD_CONNECT = 0x50000000
|
||||||
CMD_PLAYMODE = 0x50000002
|
CMD_STATE = 0x50000001
|
||||||
CMD_NOWPLAYING = 0x50000003
|
CMD_PLAYMODE = 0x50000002
|
||||||
CMD_LISTSUBTITLETRACKS = 0x50000004
|
CMD_NOWPLAYING = 0x50000003
|
||||||
CMD_LISTAUDIOTRACKS = 0x50000005
|
CMD_LISTSUBTITLETRACKS = 0x50000004
|
||||||
CMD_CURRENTPOSITION = 0x50000007
|
CMD_LISTAUDIOTRACKS = 0x50000005
|
||||||
CMD_NOTIFYSEEK = 0x50000008
|
CMD_CURRENTPOSITION = 0x50000007
|
||||||
CMD_NOTIFYENDOFSTREAM = 0x50000009
|
CMD_NOTIFYSEEK = 0x50000008
|
||||||
CMD_PLAYLIST = 0x50000006
|
CMD_NOTIFYENDOFSTREAM = 0x50000009
|
||||||
CMD_OPENFILE = 0xA0000000
|
CMD_PLAYLIST = 0x50000006
|
||||||
CMD_STOP = 0xA0000001
|
CMD_OPENFILE = 0xA0000000
|
||||||
CMD_CLOSEFILE = 0xA0000002
|
CMD_STOP = 0xA0000001
|
||||||
CMD_PLAYPAUSE = 0xA0000003
|
CMD_CLOSEFILE = 0xA0000002
|
||||||
CMD_ADDTOPLAYLIST = 0xA0001000
|
CMD_PLAYPAUSE = 0xA0000003
|
||||||
CMD_CLEARPLAYLIST = 0xA0001001
|
CMD_ADDTOPLAYLIST = 0xA0001000
|
||||||
CMD_STARTPLAYLIST = 0xA0001002
|
CMD_CLEARPLAYLIST = 0xA0001001
|
||||||
CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO
|
CMD_STARTPLAYLIST = 0xA0001002
|
||||||
CMD_SETPOSITION = 0xA0002000
|
CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO
|
||||||
CMD_SETAUDIODELAY = 0xA0002001
|
CMD_SETPOSITION = 0xA0002000
|
||||||
CMD_SETSUBTITLEDELAY = 0xA0002002
|
CMD_SETAUDIODELAY = 0xA0002001
|
||||||
CMD_SETINDEXPLAYLIST = 0xA0002003 # DOESNT WORK
|
CMD_SETSUBTITLEDELAY = 0xA0002002
|
||||||
CMD_SETAUDIOTRACK = 0xA0002004
|
CMD_SETINDEXPLAYLIST = 0xA0002003 # DOESNT WORK
|
||||||
CMD_SETSUBTITLETRACK = 0xA0002005
|
CMD_SETAUDIOTRACK = 0xA0002004
|
||||||
CMD_GETSUBTITLETRACKS = 0xA0003000
|
CMD_SETSUBTITLETRACK = 0xA0002005
|
||||||
CMD_GETCURRENTPOSITION = 0xA0003004
|
CMD_GETSUBTITLETRACKS = 0xA0003000
|
||||||
CMD_JUMPOFNSECONDS = 0xA0003005
|
CMD_GETCURRENTPOSITION = 0xA0003004
|
||||||
CMD_GETAUDIOTRACKS = 0xA0003001
|
CMD_JUMPOFNSECONDS = 0xA0003005
|
||||||
CMD_GETNOWPLAYING = 0xA0003002
|
CMD_GETAUDIOTRACKS = 0xA0003001
|
||||||
CMD_GETPLAYLIST = 0xA0003003
|
CMD_GETNOWPLAYING = 0xA0003002
|
||||||
CMD_TOGGLEFULLSCREEN = 0xA0004000
|
CMD_GETPLAYLIST = 0xA0003003
|
||||||
CMD_JUMPFORWARDMED = 0xA0004001
|
CMD_TOGGLEFULLSCREEN = 0xA0004000
|
||||||
CMD_JUMPBACKWARDMED = 0xA0004002
|
CMD_JUMPFORWARDMED = 0xA0004001
|
||||||
CMD_INCREASEVOLUME = 0xA0004003
|
CMD_JUMPBACKWARDMED = 0xA0004002
|
||||||
CMD_DECREASEVOLUME = 0xA0004004
|
CMD_INCREASEVOLUME = 0xA0004003
|
||||||
CMD_SHADER_TOGGLE = 0xA0004005
|
CMD_DECREASEVOLUME = 0xA0004004
|
||||||
CMD_CLOSEAPP = 0xA0004006
|
CMD_SHADER_TOGGLE = 0xA0004005
|
||||||
CMD_OSDSHOWMESSAGE = 0xA0005000
|
CMD_CLOSEAPP = 0xA0004006
|
||||||
CMD_VERSION = 0x5000000A
|
CMD_OSDSHOWMESSAGE = 0xA0005000
|
||||||
CMD_DISCONNECT = 0x5000000B
|
CMD_VERSION = 0x5000000A
|
||||||
CMD_PLAY = 0xA0000004
|
CMD_DISCONNECT = 0x5000000B
|
||||||
CMD_PAUSE = 0xA0000005
|
CMD_PLAY = 0xA0000004
|
||||||
CMD_GETVERSION = 0xA0003006
|
CMD_PAUSE = 0xA0000005
|
||||||
CMD_SETSPEED = 0xA0004008
|
CMD_GETVERSION = 0xA0003006
|
||||||
|
CMD_SETSPEED = 0xA0004008
|
||||||
class __MPC_LOADSTATE:
|
|
||||||
MLS_CLOSED = 0
|
class __MPC_LOADSTATE:
|
||||||
MLS_LOADING = 1
|
MLS_CLOSED = 0
|
||||||
MLS_LOADED = 2
|
MLS_LOADING = 1
|
||||||
MLS_CLOSING = 3
|
MLS_LOADED = 2
|
||||||
|
MLS_CLOSING = 3
|
||||||
class __MPC_PLAYSTATE:
|
|
||||||
PS_PLAY = 0
|
class __MPC_PLAYSTATE:
|
||||||
PS_PAUSE = 1
|
PS_PLAY = 0
|
||||||
PS_STOP = 2
|
PS_PAUSE = 1
|
||||||
PS_UNUSED = 3
|
PS_STOP = 2
|
||||||
|
PS_UNUSED = 3
|
||||||
class __Listener(threading.Thread):
|
|
||||||
def __init__(self, mpcApi, locks):
|
class __Listener(threading.Thread):
|
||||||
self.__mpcApi = mpcApi
|
def __init__(self, mpcApi, locks):
|
||||||
self.locks = locks
|
self.__mpcApi = mpcApi
|
||||||
self.mpcHandle = None
|
self.locks = locks
|
||||||
self.hwnd = None
|
self.mpcHandle = None
|
||||||
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT)
|
self.hwnd = None
|
||||||
threading.Thread.__init__(self, name="MPC Listener")
|
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT)
|
||||||
|
threading.Thread.__init__(self, name="MPC Listener")
|
||||||
def run(self):
|
|
||||||
message_map = {
|
def run(self):
|
||||||
win32con.WM_COPYDATA: self.OnCopyData
|
message_map = {
|
||||||
}
|
win32con.WM_COPYDATA: self.OnCopyData
|
||||||
wc = win32gui.WNDCLASS()
|
}
|
||||||
wc.lpfnWndProc = message_map
|
wc = win32gui.WNDCLASS()
|
||||||
wc.lpszClassName = 'MPCApiListener'
|
wc.lpfnWndProc = message_map
|
||||||
hinst = wc.hInstance = win32api.GetModuleHandle(None)
|
wc.lpszClassName = 'MPCApiListener'
|
||||||
classAtom = win32gui.RegisterClass(wc)
|
hinst = wc.hInstance = win32api.GetModuleHandle(None)
|
||||||
self.hwnd = win32gui.CreateWindow (
|
classAtom = win32gui.RegisterClass(wc)
|
||||||
classAtom,
|
self.hwnd = win32gui.CreateWindow (
|
||||||
"ListenerGUI",
|
classAtom,
|
||||||
0,
|
"ListenerGUI",
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
win32con.CW_USEDEFAULT,
|
0,
|
||||||
win32con.CW_USEDEFAULT,
|
win32con.CW_USEDEFAULT,
|
||||||
0,
|
win32con.CW_USEDEFAULT,
|
||||||
0,
|
0,
|
||||||
hinst,
|
0,
|
||||||
None
|
hinst,
|
||||||
)
|
None
|
||||||
self.locks.listenerStart.set()
|
)
|
||||||
win32gui.PumpMessages()
|
self.locks.listenerStart.set()
|
||||||
|
win32gui.PumpMessages()
|
||||||
|
|
||||||
def OnCopyData(self, hwnd, msg, wparam, lparam):
|
|
||||||
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
|
def OnCopyData(self, hwnd, msg, wparam, lparam):
|
||||||
#print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
|
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
|
||||||
self.__mpcApi.handleCommand(pCDS.contents.dwData, ctypes.wstring_at(pCDS.contents.lpData))
|
#print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
|
||||||
|
self.__mpcApi.handleCommand(pCDS.contents.dwData, ctypes.wstring_at(pCDS.contents.lpData))
|
||||||
def SendCommand(self, cmd, message=u''):
|
|
||||||
#print "API:\t<out\t 0x%X\t" % int(cmd), message
|
def SendCommand(self, cmd, message=u''):
|
||||||
if not win32gui.IsWindow(self.mpcHandle):
|
#print "API:\t<out\t 0x%X\t" % int(cmd), message
|
||||||
if(self.__mpcApi.callbacks.onMpcClosed):
|
if not win32gui.IsWindow(self.mpcHandle):
|
||||||
self.__mpcApi.callbacks.onMpcClosed(None)
|
if(self.__mpcApi.callbacks.onMpcClosed):
|
||||||
cs = self.__COPYDATASTRUCT()
|
self.__mpcApi.callbacks.onMpcClosed(None)
|
||||||
cs.dwData = cmd;
|
cs = self.__COPYDATASTRUCT()
|
||||||
|
cs.dwData = cmd;
|
||||||
if(isinstance(message, (unicode, str))):
|
|
||||||
message = ctypes.create_unicode_buffer(message, len(message) + 1)
|
if(isinstance(message, (unicode, str))):
|
||||||
elif(isinstance(message, ctypes.Structure)):
|
message = ctypes.create_unicode_buffer(message, len(message) + 1)
|
||||||
pass
|
elif(isinstance(message, ctypes.Structure)):
|
||||||
else:
|
pass
|
||||||
raise TypeError
|
else:
|
||||||
cs.lpData = ctypes.addressof(message)
|
raise TypeError
|
||||||
cs.cbData = ctypes.sizeof(message)
|
cs.lpData = ctypes.addressof(message)
|
||||||
ptr = ctypes.addressof(cs)
|
cs.cbData = ctypes.sizeof(message)
|
||||||
win32api.SendMessage(self.mpcHandle, win32con.WM_COPYDATA, self.hwnd, ptr)
|
ptr = ctypes.addressof(cs)
|
||||||
|
win32api.SendMessage(self.mpcHandle, win32con.WM_COPYDATA, self.hwnd, ptr)
|
||||||
class __COPYDATASTRUCT(ctypes.Structure):
|
|
||||||
_fields_ = [
|
class __COPYDATASTRUCT(ctypes.Structure):
|
||||||
('dwData', ctypes.wintypes.LPARAM),
|
_fields_ = [
|
||||||
('cbData', ctypes.wintypes.DWORD),
|
('dwData', ctypes.wintypes.LPARAM),
|
||||||
('lpData', ctypes.c_void_p)
|
('cbData', ctypes.wintypes.DWORD),
|
||||||
]
|
('lpData', ctypes.c_void_p)
|
||||||
|
]
|
||||||
class MPCHCAPIPlayer(BasePlayer):
|
|
||||||
speedSupported = False
|
class MPCHCAPIPlayer(BasePlayer):
|
||||||
|
speedSupported = False
|
||||||
def __init__(self, client):
|
|
||||||
self.__client = client
|
def __init__(self, client):
|
||||||
self._mpcApi = MpcHcApi()
|
self.__client = client
|
||||||
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing()
|
self._mpcApi = MpcHcApi()
|
||||||
self._mpcApi.callbacks.onMpcClosed = lambda _: self.__client.stop(False)
|
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing()
|
||||||
self._mpcApi.callbacks.onFileStateChange = lambda _: self.__lockAsking()
|
self._mpcApi.callbacks.onMpcClosed = lambda _: self.__client.stop(False)
|
||||||
self._mpcApi.callbacks.onUpdatePlaystate = lambda _: self.__unlockAsking()
|
self._mpcApi.callbacks.onFileStateChange = lambda _: self.__lockAsking()
|
||||||
self._mpcApi.callbacks.onGetCurrentPosition = lambda _: self.__onGetPosition()
|
self._mpcApi.callbacks.onUpdatePlaystate = lambda _: self.__unlockAsking()
|
||||||
self._mpcApi.callbacks.onVersion = lambda _: self.__versionUpdate.set()
|
self._mpcApi.callbacks.onGetCurrentPosition = lambda _: self.__onGetPosition()
|
||||||
self.__switchPauseCalls = False
|
self._mpcApi.callbacks.onVersion = lambda _: self.__versionUpdate.set()
|
||||||
self.__preventAsking = threading.Event()
|
self.__switchPauseCalls = False
|
||||||
self.__positionUpdate = threading.Event()
|
self.__preventAsking = threading.Event()
|
||||||
self.__versionUpdate = threading.Event()
|
self.__positionUpdate = threading.Event()
|
||||||
self.__fileUpdate = threading.RLock()
|
self.__versionUpdate = threading.Event()
|
||||||
self.__versionUpdate.clear()
|
self.__fileUpdate = threading.RLock()
|
||||||
|
self.__versionUpdate.clear()
|
||||||
def drop(self):
|
|
||||||
self.__preventAsking.set()
|
def drop(self):
|
||||||
self.__positionUpdate.set()
|
self.__preventAsking.set()
|
||||||
self.__versionUpdate.set()
|
self.__positionUpdate.set()
|
||||||
self._mpcApi.sendRawCommand(MpcHcApi.CMD_CLOSEAPP, "")
|
self.__versionUpdate.set()
|
||||||
|
self._mpcApi.sendRawCommand(MpcHcApi.CMD_CLOSEAPP, "")
|
||||||
@staticmethod
|
|
||||||
def run(client, playerPath, filePath, args):
|
@staticmethod
|
||||||
mpc = MPCHCAPIPlayer(client)
|
def run(client, playerPath, filePath, args):
|
||||||
mpc._mpcApi.callbacks.onConnected = lambda: mpc.initPlayer(filePath if(filePath) else None)
|
mpc = MPCHCAPIPlayer(client)
|
||||||
mpc._mpcApi.startMpc(playerPath, args)
|
mpc._mpcApi.callbacks.onConnected = lambda: mpc.initPlayer(filePath if(filePath) else None)
|
||||||
return mpc
|
mpc._mpcApi.startMpc(playerPath, args)
|
||||||
|
return mpc
|
||||||
def __lockAsking(self):
|
|
||||||
self.__preventAsking.clear()
|
def __lockAsking(self):
|
||||||
|
self.__preventAsking.clear()
|
||||||
def __unlockAsking(self):
|
|
||||||
self.__preventAsking.set()
|
def __unlockAsking(self):
|
||||||
|
self.__preventAsking.set()
|
||||||
def __onGetPosition(self):
|
|
||||||
self.__positionUpdate.set()
|
def __onGetPosition(self):
|
||||||
|
self.__positionUpdate.set()
|
||||||
def setSpeed(self, value):
|
|
||||||
try:
|
def setSpeed(self, value):
|
||||||
self._mpcApi.setSpeed(value)
|
try:
|
||||||
except MpcHcApi.PlayerNotReadyException:
|
self._mpcApi.setSpeed(value)
|
||||||
self.setSpeed(value)
|
except MpcHcApi.PlayerNotReadyException:
|
||||||
|
self.setSpeed(value)
|
||||||
def __dropIfNotSufficientVersion(self):
|
|
||||||
self._mpcApi.askForVersion()
|
def __dropIfNotSufficientVersion(self):
|
||||||
if(not self.__versionUpdate.wait(0.1) or not self._mpcApi.version):
|
self._mpcApi.askForVersion()
|
||||||
self.__mpcError("MPC version not sufficient, please use `mpc-hc` >= `1.6.4`")
|
if(not self.__versionUpdate.wait(0.1) or not self._mpcApi.version):
|
||||||
self.__client.stop(True)
|
self.__mpcError(getMessage("en", "mpc-version-insufficient-error"))
|
||||||
|
self.__client.stop(True)
|
||||||
def __testMpcReady(self):
|
|
||||||
if(not self.__preventAsking.wait(10)):
|
def __testMpcReady(self):
|
||||||
raise Exception("Player failed opening file")
|
if(not self.__preventAsking.wait(10)):
|
||||||
|
raise Exception(getMessage("en", "player-file-open-error"))
|
||||||
def __makePing(self):
|
|
||||||
try:
|
def __makePing(self):
|
||||||
self.__testMpcReady()
|
try:
|
||||||
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__handleUpdatedFilename()
|
self.__testMpcReady()
|
||||||
self.__client.initPlayer(self)
|
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__handleUpdatedFilename()
|
||||||
self.__handleUpdatedFilename()
|
self.__client.initPlayer(self)
|
||||||
self.askForStatus()
|
self.__handleUpdatedFilename()
|
||||||
except Exception, err:
|
self.askForStatus()
|
||||||
self.__client.ui.showErrorMessage(err.message)
|
except Exception, err:
|
||||||
self.__client.stop()
|
self.__client.ui.showErrorMessage(err.message)
|
||||||
|
self.__client.stop()
|
||||||
def initPlayer(self, filePath):
|
|
||||||
self.__dropIfNotSufficientVersion()
|
def initPlayer(self, filePath):
|
||||||
if(not self._mpcApi.version):
|
self.__dropIfNotSufficientVersion()
|
||||||
return
|
if(not self._mpcApi.version):
|
||||||
self.__mpcVersion = self._mpcApi.version.split('.')
|
return
|
||||||
if(self.__mpcVersion[0:3] == ['1', '6', '4']):
|
self.__mpcVersion = self._mpcApi.version.split('.')
|
||||||
self.__switchPauseCalls = True
|
if(self.__mpcVersion[0:3] == ['1', '6', '4']):
|
||||||
if(self.__mpcVersion[0:3] >= ['1', '6', '5']):
|
self.__switchPauseCalls = True
|
||||||
self.speedSupported = True
|
if(self.__mpcVersion[0:3] >= ['1', '6', '5']):
|
||||||
if(filePath):
|
self.speedSupported = True
|
||||||
self._mpcApi.openFile(filePath)
|
if(filePath):
|
||||||
|
self._mpcApi.openFile(filePath)
|
||||||
def displayMessage(self, message):
|
|
||||||
self._mpcApi.sendOsd(message)
|
def displayMessage(self, message):
|
||||||
|
self._mpcApi.sendOsd(message)
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
|
||||||
def setPaused(self, value):
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
if self.__switchPauseCalls:
|
def setPaused(self, value):
|
||||||
value = not value
|
if self.__switchPauseCalls:
|
||||||
if value:
|
value = not value
|
||||||
self._mpcApi.pause()
|
if value:
|
||||||
else:
|
self._mpcApi.pause()
|
||||||
self._mpcApi.unpause()
|
else:
|
||||||
|
self._mpcApi.unpause()
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
|
||||||
def setPosition(self, value):
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
self._mpcApi.seek(value)
|
def setPosition(self, value):
|
||||||
|
self._mpcApi.seek(value)
|
||||||
def __getPosition(self):
|
|
||||||
self.__positionUpdate.clear()
|
def __getPosition(self):
|
||||||
self._mpcApi.askForCurrentPosition()
|
self.__positionUpdate.clear()
|
||||||
self.__positionUpdate.wait(constants.MPC_LOCK_WAIT_TIME)
|
self._mpcApi.askForCurrentPosition()
|
||||||
return self._mpcApi.lastFilePosition
|
self.__positionUpdate.wait(constants.MPC_LOCK_WAIT_TIME)
|
||||||
|
return self._mpcApi.lastFilePosition
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
|
||||||
def askForStatus(self):
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
|
def askForStatus(self):
|
||||||
self.__fileUpdate.release()
|
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
|
||||||
position = self.__getPosition()
|
self.__fileUpdate.release()
|
||||||
paused = self._mpcApi.isPaused()
|
position = self.__getPosition()
|
||||||
position = float(position)
|
paused = self._mpcApi.isPaused()
|
||||||
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
|
position = float(position)
|
||||||
self.__client.updatePlayerStatus(paused, position)
|
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
|
||||||
self.__fileUpdate.release()
|
self.__client.updatePlayerStatus(paused, position)
|
||||||
return
|
self.__fileUpdate.release()
|
||||||
self.__echoGlobalStatus()
|
return
|
||||||
|
self.__echoGlobalStatus()
|
||||||
def __echoGlobalStatus(self):
|
|
||||||
self.__client.updatePlayerStatus(self.__client.getGlobalPaused(), self.__client.getGlobalPosition())
|
def __echoGlobalStatus(self):
|
||||||
|
self.__client.updatePlayerStatus(self.__client.getGlobalPaused(), self.__client.getGlobalPosition())
|
||||||
def __forcePause(self):
|
|
||||||
for _ in xrange(constants.MPC_MAX_RETRIES):
|
def __forcePause(self):
|
||||||
self.setPaused(True)
|
for _ in xrange(constants.MPC_MAX_RETRIES):
|
||||||
time.sleep(constants.MPC_RETRY_WAIT_TIME)
|
self.setPaused(True)
|
||||||
|
time.sleep(constants.MPC_RETRY_WAIT_TIME)
|
||||||
def __refreshMpcPlayState(self):
|
|
||||||
for _ in xrange(2):
|
def __refreshMpcPlayState(self):
|
||||||
self._mpcApi.playPause()
|
for _ in xrange(2):
|
||||||
time.sleep(constants.MPC_PAUSE_TOGGLE_DELAY)
|
self._mpcApi.playPause()
|
||||||
|
time.sleep(constants.MPC_PAUSE_TOGGLE_DELAY)
|
||||||
def _setPausedAccordinglyToServer(self):
|
|
||||||
self.__forcePause()
|
def _setPausedAccordinglyToServer(self):
|
||||||
self.setPaused(self.__client.getGlobalPaused())
|
self.__forcePause()
|
||||||
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
|
self.setPaused(self.__client.getGlobalPaused())
|
||||||
self.__refreshMpcPlayState()
|
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
|
||||||
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
|
self.__refreshMpcPlayState()
|
||||||
self.__setUpStateForNewlyOpenedFile()
|
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
|
||||||
|
self.__setUpStateForNewlyOpenedFile()
|
||||||
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
|
||||||
def __setUpStateForNewlyOpenedFile(self):
|
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
|
||||||
self._setPausedAccordinglyToServer()
|
def __setUpStateForNewlyOpenedFile(self):
|
||||||
self._mpcApi.seek(self.__client.getGlobalPosition())
|
self._setPausedAccordinglyToServer()
|
||||||
|
self._mpcApi.seek(self.__client.getGlobalPosition())
|
||||||
def __handleUpdatedFilename(self):
|
|
||||||
with self.__fileUpdate:
|
def __handleUpdatedFilename(self):
|
||||||
self.__setUpStateForNewlyOpenedFile()
|
with self.__fileUpdate:
|
||||||
self.__client.updateFile(self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath)
|
self.__setUpStateForNewlyOpenedFile()
|
||||||
|
self.__client.updateFile(self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath)
|
||||||
def __mpcError(self, err=""):
|
|
||||||
self.__client.ui.showErrorMessage(err)
|
def __mpcError(self, err=""):
|
||||||
self.__client.stop()
|
self.__client.ui.showErrorMessage(err)
|
||||||
|
self.__client.stop()
|
||||||
def sendCustomCommand(self, cmd, val):
|
|
||||||
self._mpcApi.sendRawCommand(cmd, val)
|
def sendCustomCommand(self, cmd, val):
|
||||||
|
self._mpcApi.sendRawCommand(cmd, val)
|
||||||
|
|||||||
@ -1,170 +1,171 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
from syncplay.players.basePlayer import BasePlayer
|
from syncplay.players.basePlayer import BasePlayer
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
|
from syncplay.messages import getMessage
|
||||||
class MplayerPlayer(BasePlayer):
|
|
||||||
speedSupported = True
|
class MplayerPlayer(BasePlayer):
|
||||||
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
|
speedSupported = True
|
||||||
def __init__(self, client, playerPath, filePath, args):
|
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
|
||||||
self._client = client
|
def __init__(self, client, playerPath, filePath, args):
|
||||||
self._paused = None
|
self._client = client
|
||||||
self._duration = None
|
self._paused = None
|
||||||
self._filename = None
|
self._duration = None
|
||||||
self._filepath = None
|
self._filename = None
|
||||||
try:
|
self._filepath = None
|
||||||
self._listener = self.__Listener(self, playerPath, filePath, args)
|
try:
|
||||||
except ValueError:
|
self._listener = self.__Listener(self, playerPath, filePath, args)
|
||||||
self._client.ui.showMessage("Syncplay using mplayer requires you to provide file when starting")
|
except ValueError:
|
||||||
self._client.ui.showMessage("Usage example: syncplay [options] [url|path/]filename")
|
self._client.ui.showMessage(getMessage("en", "mplayer-file-required-notification"))
|
||||||
self._client.stop(True)
|
self._client.ui.showMessage(getMessage("en", "mplayer-file-required-notification/example"))
|
||||||
return
|
self._client.stop(True)
|
||||||
self._listener.setDaemon(True)
|
return
|
||||||
self._listener.start()
|
self._listener.setDaemon(True)
|
||||||
|
self._listener.start()
|
||||||
self._durationAsk = threading.Event()
|
|
||||||
self._filenameAsk = threading.Event()
|
self._durationAsk = threading.Event()
|
||||||
self._pathAsk = threading.Event()
|
self._filenameAsk = threading.Event()
|
||||||
|
self._pathAsk = threading.Event()
|
||||||
self._positionAsk = threading.Event()
|
|
||||||
self._pausedAsk = threading.Event()
|
self._positionAsk = threading.Event()
|
||||||
|
self._pausedAsk = threading.Event()
|
||||||
self._preparePlayer()
|
|
||||||
|
self._preparePlayer()
|
||||||
def _fileUpdateClearEvents(self):
|
|
||||||
self._durationAsk.clear()
|
def _fileUpdateClearEvents(self):
|
||||||
self._filenameAsk.clear()
|
self._durationAsk.clear()
|
||||||
self._pathAsk.clear()
|
self._filenameAsk.clear()
|
||||||
|
self._pathAsk.clear()
|
||||||
def _fileUpdateWaitEvents(self):
|
|
||||||
self._durationAsk.wait()
|
def _fileUpdateWaitEvents(self):
|
||||||
self._filenameAsk.wait()
|
self._durationAsk.wait()
|
||||||
self._pathAsk.wait()
|
self._filenameAsk.wait()
|
||||||
|
self._pathAsk.wait()
|
||||||
def _onFileUpdate(self):
|
|
||||||
self._fileUpdateClearEvents()
|
def _onFileUpdate(self):
|
||||||
self._getFilename()
|
self._fileUpdateClearEvents()
|
||||||
self._getLength()
|
self._getFilename()
|
||||||
self._getFilepath()
|
self._getLength()
|
||||||
self._fileUpdateWaitEvents()
|
self._getFilepath()
|
||||||
self._client.updateFile(self._filename, self._duration, self._filepath)
|
self._fileUpdateWaitEvents()
|
||||||
|
self._client.updateFile(self._filename, self._duration, self._filepath)
|
||||||
def _preparePlayer(self):
|
|
||||||
self.setPaused(self._client.getGlobalPaused())
|
def _preparePlayer(self):
|
||||||
self.setPosition(self._client.getGlobalPosition())
|
self.setPaused(self._client.getGlobalPaused())
|
||||||
self._client.initPlayer(self)
|
self.setPosition(self._client.getGlobalPosition())
|
||||||
self._onFileUpdate()
|
self._client.initPlayer(self)
|
||||||
|
self._onFileUpdate()
|
||||||
def askForStatus(self):
|
|
||||||
self._positionAsk.clear()
|
def askForStatus(self):
|
||||||
self._pausedAsk.clear()
|
self._positionAsk.clear()
|
||||||
self._getPaused()
|
self._pausedAsk.clear()
|
||||||
self._getPosition()
|
self._getPaused()
|
||||||
self._positionAsk.wait()
|
self._getPosition()
|
||||||
self._pausedAsk.wait()
|
self._positionAsk.wait()
|
||||||
self._client.updatePlayerStatus(self._paused, self._position)
|
self._pausedAsk.wait()
|
||||||
|
self._client.updatePlayerStatus(self._paused, self._position)
|
||||||
def _setProperty(self, property_, value):
|
|
||||||
self._listener.sendLine("set_property {} {}".format(property_, value))
|
def _setProperty(self, property_, value):
|
||||||
|
self._listener.sendLine("set_property {} {}".format(property_, value))
|
||||||
def _getProperty(self, property_):
|
|
||||||
self._listener.sendLine("get_property {}".format(property_))
|
def _getProperty(self, property_):
|
||||||
|
self._listener.sendLine("get_property {}".format(property_))
|
||||||
def displayMessage(self, message):
|
|
||||||
self._listener.sendLine('osd_show_text "{!s}" {} {}'.format(message, constants.OSD_DURATION, constants.MPLAYER_OSD_LEVEL))
|
def displayMessage(self, message):
|
||||||
|
self._listener.sendLine('osd_show_text "{!s}" {} {}'.format(message, constants.OSD_DURATION, constants.MPLAYER_OSD_LEVEL))
|
||||||
def setSpeed(self, value):
|
|
||||||
self._setProperty('speed', "{:.2f}".format(value))
|
def setSpeed(self, value):
|
||||||
|
self._setProperty('speed', "{:.2f}".format(value))
|
||||||
def setPosition(self, value):
|
|
||||||
self._position = value
|
def setPosition(self, value):
|
||||||
self._setProperty('time_pos', "{}".format(value))
|
self._position = value
|
||||||
|
self._setProperty('time_pos', "{}".format(value))
|
||||||
def setPaused(self, value):
|
|
||||||
self._paused = value
|
def setPaused(self, value):
|
||||||
self._setProperty('pause', 'yes' if value else 'no')
|
self._paused = value
|
||||||
|
self._setProperty('pause', 'yes' if value else 'no')
|
||||||
def _getFilename(self):
|
|
||||||
self._getProperty('filename')
|
def _getFilename(self):
|
||||||
|
self._getProperty('filename')
|
||||||
def _getLength(self):
|
|
||||||
self._getProperty('length')
|
def _getLength(self):
|
||||||
|
self._getProperty('length')
|
||||||
def _getFilepath(self):
|
|
||||||
self._getProperty('path')
|
def _getFilepath(self):
|
||||||
|
self._getProperty('path')
|
||||||
def _getPaused(self):
|
|
||||||
self._getProperty('pause')
|
def _getPaused(self):
|
||||||
|
self._getProperty('pause')
|
||||||
def _getPosition(self):
|
|
||||||
self._getProperty('time_pos')
|
def _getPosition(self):
|
||||||
|
self._getProperty('time_pos')
|
||||||
def lineReceived(self, line):
|
|
||||||
match = self.RE_ANSWER.match(line)
|
def lineReceived(self, line):
|
||||||
if not match:
|
match = self.RE_ANSWER.match(line)
|
||||||
return
|
if not match:
|
||||||
name, value = match.group(1).lower(), match.group(2)
|
return
|
||||||
|
name, value = match.group(1).lower(), match.group(2)
|
||||||
if(name == "time_pos"):
|
|
||||||
self._position = float(value)
|
if(name == "time_pos"):
|
||||||
self._positionAsk.set()
|
self._position = float(value)
|
||||||
elif(name == "pause"):
|
self._positionAsk.set()
|
||||||
self._paused = bool(value == 'yes')
|
elif(name == "pause"):
|
||||||
self._pausedAsk.set()
|
self._paused = bool(value == 'yes')
|
||||||
elif(name == "length"):
|
self._pausedAsk.set()
|
||||||
self._duration = float(value)
|
elif(name == "length"):
|
||||||
self._durationAsk.set()
|
self._duration = float(value)
|
||||||
elif(name == "path"):
|
self._durationAsk.set()
|
||||||
self._filepath = value
|
elif(name == "path"):
|
||||||
self._pathAsk.set()
|
self._filepath = value
|
||||||
elif(name == "filename"):
|
self._pathAsk.set()
|
||||||
self._filename = value
|
elif(name == "filename"):
|
||||||
self._filenameAsk.set()
|
self._filename = value
|
||||||
|
self._filenameAsk.set()
|
||||||
@staticmethod
|
|
||||||
def run(client, playerPath, filePath, args):
|
@staticmethod
|
||||||
mplayer = MplayerPlayer(client, playerPath, filePath, args)
|
def run(client, playerPath, filePath, args):
|
||||||
return mplayer
|
mplayer = MplayerPlayer(client, playerPath, filePath, args)
|
||||||
|
return mplayer
|
||||||
def drop(self):
|
|
||||||
self._listener.sendLine('quit')
|
def drop(self):
|
||||||
self._durationAsk.set()
|
self._listener.sendLine('quit')
|
||||||
self._filenameAsk.set()
|
self._durationAsk.set()
|
||||||
self._pathAsk.set()
|
self._filenameAsk.set()
|
||||||
self._positionAsk.set()
|
self._pathAsk.set()
|
||||||
self._pausedAsk.set()
|
self._positionAsk.set()
|
||||||
self._client.stop(False)
|
self._pausedAsk.set()
|
||||||
for line in self._listener.readStderrLine():
|
self._client.stop(False)
|
||||||
self._client.ui.showMessage(line, True, True)
|
for line in self._listener.readStderrLine():
|
||||||
|
self._client.ui.showMessage(line, True, True)
|
||||||
class __Listener(threading.Thread):
|
|
||||||
def __init__(self, playerController, playerPath, filePath, args):
|
class __Listener(threading.Thread):
|
||||||
self.__playerController = playerController
|
def __init__(self, playerController, playerPath, filePath, args):
|
||||||
if(not filePath):
|
self.__playerController = playerController
|
||||||
raise ValueError
|
if(not filePath):
|
||||||
call = [playerPath, filePath]
|
raise ValueError
|
||||||
call.extend(constants.MPLAYER_SLAVE_ARGS)
|
call = [playerPath, filePath]
|
||||||
if(args):
|
call.extend(constants.MPLAYER_SLAVE_ARGS)
|
||||||
call.extend(args)
|
if(args):
|
||||||
self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
call.extend(args)
|
||||||
threading.Thread.__init__(self, name="MPlayer Listener")
|
self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
threading.Thread.__init__(self, name="MPlayer Listener")
|
||||||
def run(self):
|
|
||||||
while(self.__process.poll() is None):
|
def run(self):
|
||||||
line = self.__process.stdout.readline()
|
while(self.__process.poll() is None):
|
||||||
line = line.rstrip("\r\n")
|
line = self.__process.stdout.readline()
|
||||||
self.__playerController.lineReceived(line)
|
line = line.rstrip("\r\n")
|
||||||
self.__playerController.drop()
|
self.__playerController.lineReceived(line)
|
||||||
|
self.__playerController.drop()
|
||||||
def sendLine(self, line):
|
|
||||||
try:
|
def sendLine(self, line):
|
||||||
self.__process.stdin.write(line + "\n")
|
try:
|
||||||
except IOError:
|
self.__process.stdin.write(line + "\n")
|
||||||
pass
|
except IOError:
|
||||||
|
pass
|
||||||
def readStderrLine(self):
|
|
||||||
for line in self.__process.stderr.readlines():
|
def readStderrLine(self):
|
||||||
yield line
|
for line in self.__process.stderr.readlines():
|
||||||
|
yield line
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,395 +1,396 @@
|
|||||||
#coding:utf8
|
#coding:utf8
|
||||||
from twisted.protocols.basic import LineReceiver
|
from twisted.protocols.basic import LineReceiver
|
||||||
import json
|
import json
|
||||||
import syncplay
|
import syncplay
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import time
|
import time
|
||||||
|
from syncplay.messages import getMessage
|
||||||
class JSONCommandProtocol(LineReceiver):
|
|
||||||
def handleMessages(self, messages):
|
class JSONCommandProtocol(LineReceiver):
|
||||||
for message in messages.iteritems():
|
def handleMessages(self, messages):
|
||||||
command = message[0]
|
for message in messages.iteritems():
|
||||||
if command == "Hello":
|
command = message[0]
|
||||||
self.handleHello(message[1])
|
if command == "Hello":
|
||||||
elif command == "Set":
|
self.handleHello(message[1])
|
||||||
self.handleSet(message[1])
|
elif command == "Set":
|
||||||
elif command == "List":
|
self.handleSet(message[1])
|
||||||
self.handleList(message[1])
|
elif command == "List":
|
||||||
elif command == "State":
|
self.handleList(message[1])
|
||||||
self.handleState(message[1])
|
elif command == "State":
|
||||||
elif command == "Error":
|
self.handleState(message[1])
|
||||||
self.handleError(message[1])
|
elif command == "Error":
|
||||||
else:
|
self.handleError(message[1])
|
||||||
self.dropWithError("Unknown Command\n" + message[1]) #TODO: log, not drop
|
else:
|
||||||
|
self.dropWithError("Unknown Command\n" + message[1]) #TODO: log, not drop
|
||||||
def printReceived(self, line): #TODO: remove
|
|
||||||
#print ">>i", line
|
def printReceived(self, line): #TODO: remove
|
||||||
pass
|
#print ">>i", line
|
||||||
|
pass
|
||||||
def printSent(self, line):
|
|
||||||
#print "o<<", line
|
def printSent(self, line):
|
||||||
pass
|
#print "o<<", line
|
||||||
|
pass
|
||||||
def lineReceived(self, line):
|
|
||||||
line = line.strip()
|
def lineReceived(self, line):
|
||||||
if not line:
|
line = line.strip()
|
||||||
return
|
if not line:
|
||||||
self.printReceived(line)
|
return
|
||||||
try:
|
self.printReceived(line)
|
||||||
messages = json.loads(line)
|
try:
|
||||||
except:
|
messages = json.loads(line)
|
||||||
self.dropWithError("Not a json encoded string\n" + line)
|
except:
|
||||||
return
|
self.dropWithError(getMessage("en", "not-json-error") + line)
|
||||||
self.handleMessages(messages)
|
return
|
||||||
|
self.handleMessages(messages)
|
||||||
def sendMessage(self, dict_):
|
|
||||||
line = json.dumps(dict_)
|
def sendMessage(self, dict_):
|
||||||
self.printSent(line)
|
line = json.dumps(dict_)
|
||||||
self.sendLine(line)
|
self.printSent(line)
|
||||||
|
self.sendLine(line)
|
||||||
def drop(self):
|
|
||||||
self.transport.loseConnection()
|
def drop(self):
|
||||||
|
self.transport.loseConnection()
|
||||||
def dropWithError(self, error):
|
|
||||||
raise NotImplementedError()
|
def dropWithError(self, error):
|
||||||
|
raise NotImplementedError()
|
||||||
class SyncClientProtocol(JSONCommandProtocol):
|
|
||||||
def __init__(self, client):
|
class SyncClientProtocol(JSONCommandProtocol):
|
||||||
self._client = client
|
def __init__(self, client):
|
||||||
self.clientIgnoringOnTheFly = 0
|
self._client = client
|
||||||
self.serverIgnoringOnTheFly = 0
|
self.clientIgnoringOnTheFly = 0
|
||||||
self.logged = False
|
self.serverIgnoringOnTheFly = 0
|
||||||
|
self.logged = False
|
||||||
def connectionMade(self):
|
|
||||||
self._client.initProtocol(self)
|
def connectionMade(self):
|
||||||
self.sendHello()
|
self._client.initProtocol(self)
|
||||||
|
self.sendHello()
|
||||||
def connectionLost(self, reason):
|
|
||||||
self._client.destroyProtocol()
|
def connectionLost(self, reason):
|
||||||
|
self._client.destroyProtocol()
|
||||||
def dropWithError(self, error):
|
|
||||||
self._client.ui.showErrorMessage(error)
|
def dropWithError(self, error):
|
||||||
self._client.protocolFactory.stopRetrying()
|
self._client.ui.showErrorMessage(error)
|
||||||
self.drop()
|
self._client.protocolFactory.stopRetrying()
|
||||||
|
self.drop()
|
||||||
def _extractHelloArguments(self, hello):
|
|
||||||
username = hello["username"] if hello.has_key("username") else None
|
def _extractHelloArguments(self, hello):
|
||||||
roomName = hello["room"]["name"] if hello.has_key("room") else None
|
username = hello["username"] if hello.has_key("username") else None
|
||||||
version = hello["version"] if hello.has_key("version") else None
|
roomName = hello["room"]["name"] if hello.has_key("room") else None
|
||||||
return username, roomName, version
|
version = hello["version"] if hello.has_key("version") else None
|
||||||
|
return username, roomName, version
|
||||||
def handleHello(self, hello):
|
|
||||||
username, roomName, version = self._extractHelloArguments(hello)
|
def handleHello(self, hello):
|
||||||
if(not username or not roomName or not version):
|
username, roomName, version = self._extractHelloArguments(hello)
|
||||||
self.dropWithError("Not enough Hello arguments\n" + hello)
|
if(not username or not roomName or not version):
|
||||||
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
|
self.dropWithError(getMessage("en", "hello-arguments-error") + hello)
|
||||||
self.dropWithError("Mismatch between versions of client and server\n" + hello)
|
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
|
||||||
else:
|
self.dropWithError(getMessage("en", "version-mismatch-error") + hello)
|
||||||
self._client.setUsername(username)
|
else:
|
||||||
self._client.setRoom(roomName)
|
self._client.setUsername(username)
|
||||||
self.logged = True
|
self._client.setRoom(roomName)
|
||||||
self._client.ui.showMessage("Successfully connected to server")
|
self.logged = True
|
||||||
self._client.sendFile()
|
self._client.ui.showMessage(getMessage("en", "connected-successful-notification"))
|
||||||
|
self._client.sendFile()
|
||||||
def sendHello(self):
|
|
||||||
hello = {}
|
def sendHello(self):
|
||||||
hello["username"] = self._client.getUsername()
|
hello = {}
|
||||||
password = self._client.getPassword()
|
hello["username"] = self._client.getUsername()
|
||||||
if(password): hello["password"] = password
|
password = self._client.getPassword()
|
||||||
room = self._client.getRoom()
|
if(password): hello["password"] = password
|
||||||
if(room): hello["room"] = {"name" :room}
|
room = self._client.getRoom()
|
||||||
hello["version"] = syncplay.version
|
if(room): hello["room"] = {"name" :room}
|
||||||
self.sendMessage({"Hello": hello})
|
hello["version"] = syncplay.version
|
||||||
|
self.sendMessage({"Hello": hello})
|
||||||
def _SetUser(self, users):
|
|
||||||
for user in users.iteritems():
|
def _SetUser(self, users):
|
||||||
username = user[0]
|
for user in users.iteritems():
|
||||||
settings = user[1]
|
username = user[0]
|
||||||
room = settings["room"]["name"] if settings.has_key("room") else None
|
settings = user[1]
|
||||||
file_ = settings["file"] if settings.has_key("file") else None
|
room = settings["room"]["name"] if settings.has_key("room") else None
|
||||||
if(settings.has_key("event")):
|
file_ = settings["file"] if settings.has_key("file") else None
|
||||||
if(settings["event"].has_key("joined")):
|
if(settings.has_key("event")):
|
||||||
self._client.userlist.addUser(username, room, file_)
|
if(settings["event"].has_key("joined")):
|
||||||
elif(settings["event"].has_key("left")):
|
self._client.userlist.addUser(username, room, file_)
|
||||||
self._client.userlist.removeUser(username)
|
elif(settings["event"].has_key("left")):
|
||||||
else:
|
self._client.userlist.removeUser(username)
|
||||||
self._client.userlist.modUser(username, room, file_)
|
else:
|
||||||
|
self._client.userlist.modUser(username, room, file_)
|
||||||
def handleSet(self, settings):
|
|
||||||
for set_ in settings.iteritems():
|
def handleSet(self, settings):
|
||||||
command = set_[0]
|
for set_ in settings.iteritems():
|
||||||
if command == "room":
|
command = set_[0]
|
||||||
roomName = set_[1]["name"] if set_[1].has_key("name") else None
|
if command == "room":
|
||||||
self._client.setRoom(roomName)
|
roomName = set_[1]["name"] if set_[1].has_key("name") else None
|
||||||
elif command == "user":
|
self._client.setRoom(roomName)
|
||||||
self._SetUser(set_[1])
|
elif command == "user":
|
||||||
|
self._SetUser(set_[1])
|
||||||
def sendSet(self, setting):
|
|
||||||
self.sendMessage({"Set": setting})
|
def sendSet(self, setting):
|
||||||
|
self.sendMessage({"Set": setting})
|
||||||
def sendRoomSetting(self, roomName, password=None):
|
|
||||||
setting = {}
|
def sendRoomSetting(self, roomName, password=None):
|
||||||
setting["name"] = roomName
|
setting = {}
|
||||||
if(password): setting["password"] = password
|
setting["name"] = roomName
|
||||||
self.sendSet({"room": setting})
|
if(password): setting["password"] = password
|
||||||
|
self.sendSet({"room": setting})
|
||||||
def sendFileSetting(self, file_):
|
|
||||||
self.sendSet({"file": file_})
|
def sendFileSetting(self, file_):
|
||||||
self.sendList()
|
self.sendSet({"file": file_})
|
||||||
|
self.sendList()
|
||||||
def handleList(self, userList):
|
|
||||||
self._client.userlist.clearList()
|
def handleList(self, userList):
|
||||||
for room in userList.iteritems():
|
self._client.userlist.clearList()
|
||||||
roomName = room[0]
|
for room in userList.iteritems():
|
||||||
for user in room[1].iteritems():
|
roomName = room[0]
|
||||||
userName = user[0]
|
for user in room[1].iteritems():
|
||||||
file_ = user[1]['file'] if user[1]['file'] <> {} else None
|
userName = user[0]
|
||||||
position = user[1]['position']
|
file_ = user[1]['file'] if user[1]['file'] <> {} else None
|
||||||
self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True)
|
position = user[1]['position']
|
||||||
self._client.userlist.showUserList()
|
self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True)
|
||||||
|
self._client.userlist.showUserList()
|
||||||
def sendList(self):
|
|
||||||
self.sendMessage({"List": None})
|
def sendList(self):
|
||||||
|
self.sendMessage({"List": None})
|
||||||
def _extractStatePlaystateArguments(self, state):
|
|
||||||
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
|
def _extractStatePlaystateArguments(self, state):
|
||||||
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
|
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
|
||||||
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
|
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
|
||||||
setBy = state["playstate"]["setBy"] if state["playstate"].has_key("setBy") else None
|
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
|
||||||
return position, paused, doSeek, setBy
|
setBy = state["playstate"]["setBy"] if state["playstate"].has_key("setBy") else None
|
||||||
|
return position, paused, doSeek, setBy
|
||||||
def _handleStatePing(self, state):
|
|
||||||
yourLatency = state["ping"]["yourLatency"] if state["ping"].has_key("yourLatency") else 0
|
def _handleStatePing(self, state):
|
||||||
senderLatency = state["ping"]["senderLatency"] if state["ping"].has_key("senderLatency") else 0
|
yourLatency = state["ping"]["yourLatency"] if state["ping"].has_key("yourLatency") else 0
|
||||||
if (state["ping"].has_key("latencyCalculation")):
|
senderLatency = state["ping"]["senderLatency"] if state["ping"].has_key("senderLatency") else 0
|
||||||
latencyCalculation = state["ping"]["latencyCalculation"]
|
if (state["ping"].has_key("latencyCalculation")):
|
||||||
return yourLatency, senderLatency, latencyCalculation
|
latencyCalculation = state["ping"]["latencyCalculation"]
|
||||||
|
return yourLatency, senderLatency, latencyCalculation
|
||||||
def handleState(self, state):
|
|
||||||
position, paused, doSeek, setBy = None, None, None, None
|
def handleState(self, state):
|
||||||
yourLatency, senderLatency = 0, 0
|
position, paused, doSeek, setBy = None, None, None, None
|
||||||
if(state.has_key("ignoringOnTheFly")):
|
yourLatency, senderLatency = 0, 0
|
||||||
ignore = state["ignoringOnTheFly"]
|
if(state.has_key("ignoringOnTheFly")):
|
||||||
if(ignore.has_key("server")):
|
ignore = state["ignoringOnTheFly"]
|
||||||
self.serverIgnoringOnTheFly = ignore["server"]
|
if(ignore.has_key("server")):
|
||||||
self.clientIgnoringOnTheFly = 0
|
self.serverIgnoringOnTheFly = ignore["server"]
|
||||||
elif(ignore.has_key("client")):
|
self.clientIgnoringOnTheFly = 0
|
||||||
if(ignore['client']) == self.clientIgnoringOnTheFly:
|
elif(ignore.has_key("client")):
|
||||||
self.clientIgnoringOnTheFly = 0
|
if(ignore['client']) == self.clientIgnoringOnTheFly:
|
||||||
if(state.has_key("playstate")):
|
self.clientIgnoringOnTheFly = 0
|
||||||
position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state)
|
if(state.has_key("playstate")):
|
||||||
if(state.has_key("ping")):
|
position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state)
|
||||||
yourLatency, senderLatency, latencyCalculation = self._handleStatePing(state)
|
if(state.has_key("ping")):
|
||||||
if(position is not None and paused is not None and not self.clientIgnoringOnTheFly):
|
yourLatency, senderLatency, latencyCalculation = self._handleStatePing(state)
|
||||||
latency = yourLatency + senderLatency
|
if(position is not None and paused is not None and not self.clientIgnoringOnTheFly):
|
||||||
self._client.updateGlobalState(position, paused, doSeek, setBy, latency)
|
latency = yourLatency + senderLatency
|
||||||
position, paused, doSeek, stateChange = self._client.getLocalState()
|
self._client.updateGlobalState(position, paused, doSeek, setBy, latency)
|
||||||
self.sendState(position, paused, doSeek, latencyCalculation, stateChange)
|
position, paused, doSeek, stateChange = self._client.getLocalState()
|
||||||
|
self.sendState(position, paused, doSeek, latencyCalculation, stateChange)
|
||||||
def sendState(self, position, paused, doSeek, latencyCalculation, stateChange = False):
|
|
||||||
state = {}
|
def sendState(self, position, paused, doSeek, latencyCalculation, stateChange = False):
|
||||||
positionAndPausedIsSet = position is not None and paused is not None
|
state = {}
|
||||||
clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0
|
positionAndPausedIsSet = position is not None and paused is not None
|
||||||
if(clientIgnoreIsNotSet and positionAndPausedIsSet):
|
clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0
|
||||||
state["playstate"] = {}
|
if(clientIgnoreIsNotSet and positionAndPausedIsSet):
|
||||||
state["playstate"]["position"] = position
|
state["playstate"] = {}
|
||||||
state["playstate"]["paused"] = paused
|
state["playstate"]["position"] = position
|
||||||
if(doSeek): state["playstate"]["doSeek"] = doSeek
|
state["playstate"]["paused"] = paused
|
||||||
if(latencyCalculation):
|
if(doSeek): state["playstate"]["doSeek"] = doSeek
|
||||||
state["ping"] = {"latencyCalculation": latencyCalculation}
|
if(latencyCalculation):
|
||||||
if(stateChange):
|
state["ping"] = {"latencyCalculation": latencyCalculation}
|
||||||
self.clientIgnoringOnTheFly += 1
|
if(stateChange):
|
||||||
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
|
self.clientIgnoringOnTheFly += 1
|
||||||
state["ignoringOnTheFly"] = {}
|
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
|
||||||
if(self.serverIgnoringOnTheFly):
|
state["ignoringOnTheFly"] = {}
|
||||||
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
|
if(self.serverIgnoringOnTheFly):
|
||||||
self.serverIgnoringOnTheFly = 0
|
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
|
||||||
if(self.clientIgnoringOnTheFly):
|
self.serverIgnoringOnTheFly = 0
|
||||||
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
|
if(self.clientIgnoringOnTheFly):
|
||||||
self.sendMessage({"State": state})
|
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
|
||||||
|
self.sendMessage({"State": state})
|
||||||
def handleError(self, error):
|
|
||||||
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
|
def handleError(self, error):
|
||||||
|
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
|
||||||
def sendError(self, message):
|
|
||||||
self.sendMessage({"Error": {"message": message}})
|
def sendError(self, message):
|
||||||
|
self.sendMessage({"Error": {"message": message}})
|
||||||
|
|
||||||
class SyncServerProtocol(JSONCommandProtocol):
|
|
||||||
def __init__(self, factory):
|
class SyncServerProtocol(JSONCommandProtocol):
|
||||||
self._factory = factory
|
def __init__(self, factory):
|
||||||
self._logged = False
|
self._factory = factory
|
||||||
self.clientIgnoringOnTheFly = 0
|
self._logged = False
|
||||||
self.serverIgnoringOnTheFly = 0
|
self.clientIgnoringOnTheFly = 0
|
||||||
|
self.serverIgnoringOnTheFly = 0
|
||||||
def __hash__(self):
|
|
||||||
return hash('|'.join((
|
def __hash__(self):
|
||||||
self.transport.getPeer().host,
|
return hash('|'.join((
|
||||||
str(id(self)),
|
self.transport.getPeer().host,
|
||||||
)))
|
str(id(self)),
|
||||||
|
)))
|
||||||
def requireLogged(f): #@NoSelf
|
|
||||||
@wraps(f)
|
def requireLogged(f): #@NoSelf
|
||||||
def wrapper(self, *args, **kwds):
|
@wraps(f)
|
||||||
if(not self._logged):
|
def wrapper(self, *args, **kwds):
|
||||||
self.dropWithError("You must be known to server before sending this command")
|
if(not self._logged):
|
||||||
return f(self, *args, **kwds)
|
self.dropWithError(getMessage("en", "not-known-server-error"))
|
||||||
return wrapper
|
return f(self, *args, **kwds)
|
||||||
|
return wrapper
|
||||||
def dropWithError(self, error):
|
|
||||||
print "Client drop: %s -- %s" % (self.transport.getPeer().host, error)
|
def dropWithError(self, error):
|
||||||
self.sendError(error)
|
print getMessage("en", "client-drop-server-error") % (self.transport.getPeer().host, error)
|
||||||
self.drop()
|
self.sendError(error)
|
||||||
|
self.drop()
|
||||||
def connectionLost(self, reason):
|
|
||||||
self._factory.removeWatcher(self)
|
def connectionLost(self, reason):
|
||||||
|
self._factory.removeWatcher(self)
|
||||||
def _extractHelloArguments(self, hello):
|
|
||||||
roomName, roomPassword = None, None
|
def _extractHelloArguments(self, hello):
|
||||||
username = hello["username"] if hello.has_key("username") else None
|
roomName, roomPassword = None, None
|
||||||
username = username.strip()
|
username = hello["username"] if hello.has_key("username") else None
|
||||||
serverPassword = hello["password"] if hello.has_key("password") else None
|
username = username.strip()
|
||||||
room = hello["room"] if hello.has_key("room") else None
|
serverPassword = hello["password"] if hello.has_key("password") else None
|
||||||
if(room):
|
room = hello["room"] if hello.has_key("room") else None
|
||||||
roomName = room["name"] if room.has_key("name") else None
|
if(room):
|
||||||
roomName = roomName.strip()
|
roomName = room["name"] if room.has_key("name") else None
|
||||||
roomPassword = room["password"] if room.has_key("password") else None
|
roomName = roomName.strip()
|
||||||
version = hello["version"] if hello.has_key("version") else None
|
roomPassword = room["password"] if room.has_key("password") else None
|
||||||
return username, serverPassword, roomName, roomPassword, version
|
version = hello["version"] if hello.has_key("version") else None
|
||||||
|
return username, serverPassword, roomName, roomPassword, version
|
||||||
def _checkPassword(self, serverPassword):
|
|
||||||
if(self._factory.password):
|
def _checkPassword(self, serverPassword):
|
||||||
if(not serverPassword):
|
if(self._factory.password):
|
||||||
self.dropWithError("Password required")
|
if(not serverPassword):
|
||||||
return False
|
self.dropWithError(getMessage("en", "password-required-server-error"))
|
||||||
if(serverPassword != self._factory.password):
|
return False
|
||||||
self.dropWithError("Wrong password supplied")
|
if(serverPassword != self._factory.password):
|
||||||
return False
|
self.dropWithError(getMessage("en", "wrong-password-server-error"))
|
||||||
return True
|
return False
|
||||||
|
return True
|
||||||
def handleHello(self, hello):
|
|
||||||
username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello)
|
def handleHello(self, hello):
|
||||||
if(not username or not roomName or not version):
|
username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello)
|
||||||
self.dropWithError("Not enough Hello arguments")
|
if(not username or not roomName or not version):
|
||||||
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
|
self.dropWithError(getMessage("en", "hello-server-error"))
|
||||||
self.dropWithError("Mismatch between versions of client and server")
|
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
|
||||||
else:
|
self.dropWithError(getMessage("en", "version-mismatch-server-error"))
|
||||||
if(not self._checkPassword(serverPassword)):
|
else:
|
||||||
return
|
if(not self._checkPassword(serverPassword)):
|
||||||
self._factory.addWatcher(self, username, roomName, roomPassword)
|
return
|
||||||
self._logged = True
|
self._factory.addWatcher(self, username, roomName, roomPassword)
|
||||||
self.sendHello()
|
self._logged = True
|
||||||
|
self.sendHello()
|
||||||
def sendHello(self):
|
|
||||||
hello = {}
|
def sendHello(self):
|
||||||
hello["username"] = self._factory.watcherGetUsername(self)
|
hello = {}
|
||||||
room = self._factory.watcherGetRoom(self)
|
hello["username"] = self._factory.watcherGetUsername(self)
|
||||||
if(room): hello["room"] = {"name": room}
|
room = self._factory.watcherGetRoom(self)
|
||||||
hello["version"] = syncplay.version
|
if(room): hello["room"] = {"name": room}
|
||||||
self.sendMessage({"Hello": hello})
|
hello["version"] = syncplay.version
|
||||||
|
self.sendMessage({"Hello": hello})
|
||||||
@requireLogged
|
|
||||||
def handleSet(self, settings):
|
@requireLogged
|
||||||
for set_ in settings.iteritems():
|
def handleSet(self, settings):
|
||||||
command = set_[0]
|
for set_ in settings.iteritems():
|
||||||
if command == "room":
|
command = set_[0]
|
||||||
roomName = set_[1]["name"] if set_[1].has_key("name") else None
|
if command == "room":
|
||||||
self._factory.watcherSetRoom(self, roomName)
|
roomName = set_[1]["name"] if set_[1].has_key("name") else None
|
||||||
elif command == "file":
|
self._factory.watcherSetRoom(self, roomName)
|
||||||
self._factory.watcherSetFile(self, set_[1])
|
elif command == "file":
|
||||||
|
self._factory.watcherSetFile(self, set_[1])
|
||||||
def sendSet(self, setting):
|
|
||||||
self.sendMessage({"Set": setting})
|
def sendSet(self, setting):
|
||||||
|
self.sendMessage({"Set": setting})
|
||||||
def sendRoomSetting(self, roomName):
|
|
||||||
self.sendSet({"room": {"name": roomName}})
|
def sendRoomSetting(self, roomName):
|
||||||
|
self.sendSet({"room": {"name": roomName}})
|
||||||
def sendUserSetting(self, username, roomName, file_, event):
|
|
||||||
room = {"name": roomName}
|
def sendUserSetting(self, username, roomName, file_, event):
|
||||||
user = {}
|
room = {"name": roomName}
|
||||||
user[username] = {}
|
user = {}
|
||||||
user[username]["room"] = room
|
user[username] = {}
|
||||||
if(file_):
|
user[username]["room"] = room
|
||||||
user[username]["file"] = file_
|
if(file_):
|
||||||
if(event):
|
user[username]["file"] = file_
|
||||||
user[username]["event"] = event
|
if(event):
|
||||||
self.sendSet({"user": user})
|
user[username]["event"] = event
|
||||||
|
self.sendSet({"user": user})
|
||||||
def _addUserOnList(self, userlist, roomPositions, watcher):
|
|
||||||
if (not userlist.has_key(watcher.room)):
|
def _addUserOnList(self, userlist, roomPositions, watcher):
|
||||||
userlist[watcher.room] = {}
|
if (not userlist.has_key(watcher.room)):
|
||||||
roomPositions[watcher.room] = watcher.getRoomPosition()
|
userlist[watcher.room] = {}
|
||||||
userlist[watcher.room][watcher.name] = {
|
roomPositions[watcher.room] = watcher.getRoomPosition()
|
||||||
"file": watcher.file if watcher.file else {},
|
userlist[watcher.room][watcher.name] = {
|
||||||
"position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0
|
"file": watcher.file if watcher.file else {},
|
||||||
}
|
"position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0
|
||||||
def sendList(self):
|
}
|
||||||
userlist = {}
|
def sendList(self):
|
||||||
roomPositions = {}
|
userlist = {}
|
||||||
watchers = self._factory.getAllWatchers(self)
|
roomPositions = {}
|
||||||
for watcher in watchers.itervalues():
|
watchers = self._factory.getAllWatchers(self)
|
||||||
self._addUserOnList(userlist, roomPositions, watcher)
|
for watcher in watchers.itervalues():
|
||||||
self.sendMessage({"List": userlist})
|
self._addUserOnList(userlist, roomPositions, watcher)
|
||||||
|
self.sendMessage({"List": userlist})
|
||||||
@requireLogged
|
|
||||||
def handleList(self, _):
|
@requireLogged
|
||||||
self.sendList()
|
def handleList(self, _):
|
||||||
|
self.sendList()
|
||||||
def sendState(self, position, paused, doSeek, setBy, senderLatency, watcherLatency, forced = False):
|
|
||||||
playstate = {
|
def sendState(self, position, paused, doSeek, setBy, senderLatency, watcherLatency, forced = False):
|
||||||
"position": position,
|
playstate = {
|
||||||
"paused": paused,
|
"position": position,
|
||||||
"doSeek": doSeek,
|
"paused": paused,
|
||||||
"setBy": setBy
|
"doSeek": doSeek,
|
||||||
}
|
"setBy": setBy
|
||||||
ping = {
|
}
|
||||||
"yourLatency": watcherLatency,
|
ping = {
|
||||||
"senderLatency": senderLatency,
|
"yourLatency": watcherLatency,
|
||||||
"latencyCalculation": time.time()
|
"senderLatency": senderLatency,
|
||||||
}
|
"latencyCalculation": time.time()
|
||||||
state = {
|
}
|
||||||
"ping": ping,
|
state = {
|
||||||
"playstate": playstate,
|
"ping": ping,
|
||||||
}
|
"playstate": playstate,
|
||||||
if(forced):
|
}
|
||||||
self.serverIgnoringOnTheFly += 1
|
if(forced):
|
||||||
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
|
self.serverIgnoringOnTheFly += 1
|
||||||
state["ignoringOnTheFly"] = {}
|
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
|
||||||
if(self.serverIgnoringOnTheFly):
|
state["ignoringOnTheFly"] = {}
|
||||||
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
|
if(self.serverIgnoringOnTheFly):
|
||||||
if(self.clientIgnoringOnTheFly):
|
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
|
||||||
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
|
if(self.clientIgnoringOnTheFly):
|
||||||
self.clientIgnoringOnTheFly = 0
|
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
|
||||||
if(self.serverIgnoringOnTheFly == 0 or forced):
|
self.clientIgnoringOnTheFly = 0
|
||||||
self.sendMessage({"State": state})
|
if(self.serverIgnoringOnTheFly == 0 or forced):
|
||||||
|
self.sendMessage({"State": state})
|
||||||
|
|
||||||
def _extractStatePlaystateArguments(self, state):
|
|
||||||
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
|
def _extractStatePlaystateArguments(self, state):
|
||||||
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
|
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
|
||||||
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
|
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
|
||||||
return position, paused, doSeek
|
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
|
||||||
|
return position, paused, doSeek
|
||||||
@requireLogged
|
|
||||||
def handleState(self, state):
|
@requireLogged
|
||||||
position, paused, doSeek, latencyCalculation = None, None, None, None
|
def handleState(self, state):
|
||||||
if(state.has_key("ignoringOnTheFly")):
|
position, paused, doSeek, latencyCalculation = None, None, None, None
|
||||||
ignore = state["ignoringOnTheFly"]
|
if(state.has_key("ignoringOnTheFly")):
|
||||||
if(ignore.has_key("server")):
|
ignore = state["ignoringOnTheFly"]
|
||||||
if(self.serverIgnoringOnTheFly == ignore["server"]):
|
if(ignore.has_key("server")):
|
||||||
self.serverIgnoringOnTheFly = 0
|
if(self.serverIgnoringOnTheFly == ignore["server"]):
|
||||||
if(ignore.has_key("client")):
|
self.serverIgnoringOnTheFly = 0
|
||||||
self.clientIgnoringOnTheFly = ignore["client"]
|
if(ignore.has_key("client")):
|
||||||
if(state.has_key("playstate")):
|
self.clientIgnoringOnTheFly = ignore["client"]
|
||||||
position, paused, doSeek = self._extractStatePlaystateArguments(state)
|
if(state.has_key("playstate")):
|
||||||
if(state.has_key("ping")):
|
position, paused, doSeek = self._extractStatePlaystateArguments(state)
|
||||||
latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else None
|
if(state.has_key("ping")):
|
||||||
if(self.serverIgnoringOnTheFly == 0):
|
latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else None
|
||||||
self._factory.updateWatcherState(self, position, paused, doSeek, latencyCalculation)
|
if(self.serverIgnoringOnTheFly == 0):
|
||||||
|
self._factory.updateWatcherState(self, position, paused, doSeek, latencyCalculation)
|
||||||
def handleError(self, error):
|
|
||||||
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
|
def handleError(self, error):
|
||||||
|
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
|
||||||
def sendError(self, message):
|
|
||||||
self.sendMessage({"Error": {"message": message}})
|
def sendError(self, message):
|
||||||
|
self.sendMessage({"Error": {"message": message}})
|
||||||
|
|
||||||
|
|||||||
@ -1,256 +1,257 @@
|
|||||||
#coding:utf8
|
#coding:utf8
|
||||||
#TODO: #12, #13, #8;
|
#TODO: #12, #13, #8;
|
||||||
import hashlib
|
import hashlib
|
||||||
from twisted.internet import task, reactor
|
from twisted.internet import task, reactor
|
||||||
from twisted.internet.protocol import Factory
|
from twisted.internet.protocol import Factory
|
||||||
import syncplay
|
import syncplay
|
||||||
from syncplay.protocols import SyncServerProtocol
|
from syncplay.protocols import SyncServerProtocol
|
||||||
import time
|
import time
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
import threading
|
import threading
|
||||||
|
from syncplay.messages import getMessage
|
||||||
class SyncFactory(Factory):
|
|
||||||
def __init__(self, password = ''):
|
class SyncFactory(Factory):
|
||||||
print "Welcome to Syncplay server, ver. {0}".format(syncplay.version)
|
def __init__(self, password = ''):
|
||||||
if(password):
|
print getMessage("en", "welcome-server-notification").format(syncplay.version)
|
||||||
password = hashlib.md5(password).hexdigest()
|
if(password):
|
||||||
self.password = password
|
password = hashlib.md5(password).hexdigest()
|
||||||
self._rooms = {}
|
self.password = password
|
||||||
self._roomStates = {}
|
self._rooms = {}
|
||||||
self._roomUpdate = threading.RLock()
|
self._roomStates = {}
|
||||||
|
self._roomUpdate = threading.RLock()
|
||||||
def buildProtocol(self, addr):
|
|
||||||
return SyncServerProtocol(self)
|
def buildProtocol(self, addr):
|
||||||
|
return SyncServerProtocol(self)
|
||||||
def _createRoomIfDoesntExist(self, roomName):
|
|
||||||
if (not self._rooms.has_key(roomName)):
|
def _createRoomIfDoesntExist(self, roomName):
|
||||||
with self._roomUpdate:
|
if (not self._rooms.has_key(roomName)):
|
||||||
self._rooms[roomName] = {}
|
with self._roomUpdate:
|
||||||
self._roomStates[roomName] = {
|
self._rooms[roomName] = {}
|
||||||
"position": 0.0,
|
self._roomStates[roomName] = {
|
||||||
"paused": True,
|
"position": 0.0,
|
||||||
"setBy": None,
|
"paused": True,
|
||||||
"lastUpdate": time.time()
|
"setBy": None,
|
||||||
}
|
"lastUpdate": time.time()
|
||||||
|
}
|
||||||
def addWatcher(self, watcherProtocol, username, roomName, roomPassword):
|
|
||||||
allnames = []
|
def addWatcher(self, watcherProtocol, username, roomName, roomPassword):
|
||||||
for room in self._rooms.itervalues():
|
allnames = []
|
||||||
for watcher in room.itervalues():
|
for room in self._rooms.itervalues():
|
||||||
allnames.append(watcher.name.lower())
|
for watcher in room.itervalues():
|
||||||
while username.lower() in allnames:
|
allnames.append(watcher.name.lower())
|
||||||
username += '_'
|
while username.lower() in allnames:
|
||||||
self._createRoomIfDoesntExist(roomName)
|
username += '_'
|
||||||
watcher = Watcher(self, watcherProtocol, username, roomName)
|
self._createRoomIfDoesntExist(roomName)
|
||||||
with self._roomUpdate:
|
watcher = Watcher(self, watcherProtocol, username, roomName)
|
||||||
self._rooms[roomName][watcherProtocol] = watcher
|
with self._roomUpdate:
|
||||||
print "{0}({2}) connected to room '{1}'".format(username, roomName, watcherProtocol.transport.getPeer().host)
|
self._rooms[roomName][watcherProtocol] = watcher
|
||||||
reactor.callLater(0.1, watcher.scheduleSendState)
|
print getMessage("en", "client-connected-room-server-notification").format(username, roomName, watcherProtocol.transport.getPeer().host)
|
||||||
l = lambda w: w.sendUserSetting(username, roomName, None, {"joined": True})
|
reactor.callLater(0.1, watcher.scheduleSendState)
|
||||||
self.broadcast(watcherProtocol, l)
|
l = lambda w: w.sendUserSetting(username, roomName, None, {"joined": True})
|
||||||
|
self.broadcast(watcherProtocol, l)
|
||||||
def getWatcher(self, watcherProtocol):
|
|
||||||
for room in self._rooms.itervalues():
|
def getWatcher(self, watcherProtocol):
|
||||||
if(room.has_key(watcherProtocol)):
|
for room in self._rooms.itervalues():
|
||||||
return room[watcherProtocol]
|
if(room.has_key(watcherProtocol)):
|
||||||
|
return room[watcherProtocol]
|
||||||
def getAllWatchers(self, watcherProtocol): #TODO: Optimize me
|
|
||||||
watchers = {}
|
def getAllWatchers(self, watcherProtocol): #TODO: Optimize me
|
||||||
for room in self._rooms.itervalues():
|
watchers = {}
|
||||||
for watcher in room.itervalues():
|
for room in self._rooms.itervalues():
|
||||||
watchers[watcher.watcherProtocol] = watcher
|
for watcher in room.itervalues():
|
||||||
return watchers
|
watchers[watcher.watcherProtocol] = watcher
|
||||||
|
return watchers
|
||||||
def _removeWatcherFromTheRoom(self, watcherProtocol):
|
|
||||||
for room in self._rooms.itervalues():
|
def _removeWatcherFromTheRoom(self, watcherProtocol):
|
||||||
with self._roomUpdate:
|
for room in self._rooms.itervalues():
|
||||||
watcher = room.pop(watcherProtocol, None)
|
with self._roomUpdate:
|
||||||
if(watcher):
|
watcher = room.pop(watcherProtocol, None)
|
||||||
return watcher
|
if(watcher):
|
||||||
|
return watcher
|
||||||
def _deleteRoomIfEmpty(self, room):
|
|
||||||
if (self._rooms[room] == {}):
|
def _deleteRoomIfEmpty(self, room):
|
||||||
with self._roomUpdate:
|
if (self._rooms[room] == {}):
|
||||||
self._rooms.pop(room)
|
with self._roomUpdate:
|
||||||
self._roomStates.pop(room)
|
self._rooms.pop(room)
|
||||||
|
self._roomStates.pop(room)
|
||||||
def getRoomPausedAndPosition(self, room):
|
|
||||||
position = self._roomStates[room]["position"]
|
def getRoomPausedAndPosition(self, room):
|
||||||
paused = self._roomStates[room]["paused"]
|
position = self._roomStates[room]["position"]
|
||||||
if (not paused):
|
paused = self._roomStates[room]["paused"]
|
||||||
timePassedSinceSet = time.time() - self._roomStates[room]["lastUpdate"]
|
if (not paused):
|
||||||
position += timePassedSinceSet
|
timePassedSinceSet = time.time() - self._roomStates[room]["lastUpdate"]
|
||||||
return paused, position
|
position += timePassedSinceSet
|
||||||
|
return paused, position
|
||||||
def sendState(self, watcherProtocol, doSeek = False, senderLatency = 0, forcedUpdate = False):
|
|
||||||
watcher = self.getWatcher(watcherProtocol)
|
def sendState(self, watcherProtocol, doSeek = False, senderLatency = 0, forcedUpdate = False):
|
||||||
if(not watcher):
|
watcher = self.getWatcher(watcherProtocol)
|
||||||
return
|
if(not watcher):
|
||||||
room = watcher.room
|
return
|
||||||
paused, position = self.getRoomPausedAndPosition(room)
|
room = watcher.room
|
||||||
setBy = self._roomStates[room]["setBy"]
|
paused, position = self.getRoomPausedAndPosition(room)
|
||||||
watcher.paused = paused
|
setBy = self._roomStates[room]["setBy"]
|
||||||
watcher.position = position
|
watcher.paused = paused
|
||||||
watcherProtocol.sendState(position, paused, doSeek, setBy, senderLatency, watcher.latency, forcedUpdate)
|
watcher.position = position
|
||||||
if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT):
|
watcherProtocol.sendState(position, paused, doSeek, setBy, senderLatency, watcher.latency, forcedUpdate)
|
||||||
watcherProtocol.drop()
|
if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT):
|
||||||
self.removeWatcher(watcherProtocol)
|
watcherProtocol.drop()
|
||||||
|
self.removeWatcher(watcherProtocol)
|
||||||
def __updateWatcherPing(self, latencyCalculation, watcher):
|
|
||||||
if (latencyCalculation):
|
def __updateWatcherPing(self, latencyCalculation, watcher):
|
||||||
ping = (time.time() - latencyCalculation) / 2
|
if (latencyCalculation):
|
||||||
if (watcher.latency):
|
ping = (time.time() - latencyCalculation) / 2
|
||||||
watcher.latency = watcher.latency * (constants.PING_MOVING_AVERAGE_WEIGHT) + ping * (1-constants.PING_MOVING_AVERAGE_WEIGHT) #Exponential moving average
|
if (watcher.latency):
|
||||||
else:
|
watcher.latency = watcher.latency * (constants.PING_MOVING_AVERAGE_WEIGHT) + ping * (1-constants.PING_MOVING_AVERAGE_WEIGHT) #Exponential moving average
|
||||||
watcher.latency = ping
|
else:
|
||||||
|
watcher.latency = ping
|
||||||
def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek):
|
|
||||||
return doSeek or pauseChanged
|
def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek):
|
||||||
|
return doSeek or pauseChanged
|
||||||
def __updatePausedState(self, paused, watcher):
|
|
||||||
watcher.paused = paused
|
def __updatePausedState(self, paused, watcher):
|
||||||
if(self._roomStates[watcher.room]["paused"] <> paused):
|
watcher.paused = paused
|
||||||
self._roomStates[watcher.room]["setBy"] = watcher.name
|
if(self._roomStates[watcher.room]["paused"] <> paused):
|
||||||
self._roomStates[watcher.room]["paused"] = paused
|
self._roomStates[watcher.room]["setBy"] = watcher.name
|
||||||
self._roomStates[watcher.room]["lastUpdate"] = time.time()
|
self._roomStates[watcher.room]["paused"] = paused
|
||||||
return True
|
self._roomStates[watcher.room]["lastUpdate"] = time.time()
|
||||||
|
return True
|
||||||
def __updatePositionState(self, position, doSeek, watcher):
|
|
||||||
watcher.position = position
|
def __updatePositionState(self, position, doSeek, watcher):
|
||||||
if (doSeek):
|
watcher.position = position
|
||||||
self._roomStates[watcher.room]["position"] = position
|
if (doSeek):
|
||||||
self._roomStates[watcher.room]["setBy"] = watcher.name
|
self._roomStates[watcher.room]["position"] = position
|
||||||
self._roomStates[watcher.room]["lastUpdate"] = time.time()
|
self._roomStates[watcher.room]["setBy"] = watcher.name
|
||||||
else:
|
self._roomStates[watcher.room]["lastUpdate"] = time.time()
|
||||||
setter = min(self._rooms[watcher.room].values())
|
else:
|
||||||
self._roomStates[watcher.room]["position"] = setter.position
|
setter = min(self._rooms[watcher.room].values())
|
||||||
self._roomStates[watcher.room]["setBy"] = setter.name
|
self._roomStates[watcher.room]["position"] = setter.position
|
||||||
self._roomStates[watcher.room]["lastUpdate"] = setter.lastUpdate
|
self._roomStates[watcher.room]["setBy"] = setter.name
|
||||||
|
self._roomStates[watcher.room]["lastUpdate"] = setter.lastUpdate
|
||||||
def updateWatcherState(self, watcherProtocol, position, paused, doSeek, latencyCalculation):
|
|
||||||
watcher = self.getWatcher(watcherProtocol)
|
def updateWatcherState(self, watcherProtocol, position, paused, doSeek, latencyCalculation):
|
||||||
self.__updateWatcherPing(latencyCalculation, watcher)
|
watcher = self.getWatcher(watcherProtocol)
|
||||||
watcher.lastUpdate = time.time()
|
self.__updateWatcherPing(latencyCalculation, watcher)
|
||||||
if(watcher.file):
|
watcher.lastUpdate = time.time()
|
||||||
if(position is not None):
|
if(watcher.file):
|
||||||
self.__updatePositionState(position, doSeek, watcher)
|
if(position is not None):
|
||||||
pauseChanged = False
|
self.__updatePositionState(position, doSeek, watcher)
|
||||||
if(paused is not None):
|
pauseChanged = False
|
||||||
pauseChanged = self.__updatePausedState(paused, watcher)
|
if(paused is not None):
|
||||||
forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek)
|
pauseChanged = self.__updatePausedState(paused, watcher)
|
||||||
if(forceUpdate):
|
forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek)
|
||||||
l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate)
|
if(forceUpdate):
|
||||||
self.broadcastRoom(watcher.watcherProtocol, l)
|
l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate)
|
||||||
|
self.broadcastRoom(watcher.watcherProtocol, l)
|
||||||
def removeWatcher(self, watcherProtocol):
|
|
||||||
watcher = self.getWatcher(watcherProtocol)
|
def removeWatcher(self, watcherProtocol):
|
||||||
if(not watcher):
|
watcher = self.getWatcher(watcherProtocol)
|
||||||
return
|
if(not watcher):
|
||||||
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, None, {"left": True})
|
return
|
||||||
self.broadcast(watcherProtocol, l)
|
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, None, {"left": True})
|
||||||
self._removeWatcherFromTheRoom(watcherProtocol)
|
self.broadcast(watcherProtocol, l)
|
||||||
watcher.deactivate()
|
self._removeWatcherFromTheRoom(watcherProtocol)
|
||||||
self._deleteRoomIfEmpty(watcher.room)
|
watcher.deactivate()
|
||||||
print "{0} left server".format(watcher.name)
|
self._deleteRoomIfEmpty(watcher.room)
|
||||||
|
print getMessage("en", "client-left-server-notification").format(watcher.name)
|
||||||
def watcherGetUsername(self, watcherProtocol):
|
|
||||||
return self.getWatcher(watcherProtocol).name
|
def watcherGetUsername(self, watcherProtocol):
|
||||||
|
return self.getWatcher(watcherProtocol).name
|
||||||
def watcherGetRoom(self, watcherProtocol):
|
|
||||||
return self.getWatcher(watcherProtocol).room
|
def watcherGetRoom(self, watcherProtocol):
|
||||||
|
return self.getWatcher(watcherProtocol).room
|
||||||
def watcherSetRoom(self, watcherProtocol, room):
|
|
||||||
watcher = self._removeWatcherFromTheRoom(watcherProtocol)
|
def watcherSetRoom(self, watcherProtocol, room):
|
||||||
if(not watcher):
|
watcher = self._removeWatcherFromTheRoom(watcherProtocol)
|
||||||
return
|
if(not watcher):
|
||||||
watcher.resetStateTimer()
|
return
|
||||||
oldRoom = watcher.room
|
watcher.resetStateTimer()
|
||||||
self._createRoomIfDoesntExist(room)
|
oldRoom = watcher.room
|
||||||
with self._roomUpdate:
|
self._createRoomIfDoesntExist(room)
|
||||||
self._rooms[room][watcherProtocol] = watcher
|
with self._roomUpdate:
|
||||||
self._roomStates[room]["position"] = watcher.position
|
self._rooms[room][watcherProtocol] = watcher
|
||||||
self._roomStates[room]["setBy"] = watcher.name
|
self._roomStates[room]["position"] = watcher.position
|
||||||
self._roomStates[room]["lastUpdate"] = time.time()
|
self._roomStates[room]["setBy"] = watcher.name
|
||||||
self._deleteRoomIfEmpty(oldRoom)
|
self._roomStates[room]["lastUpdate"] = time.time()
|
||||||
watcher.room = room
|
self._deleteRoomIfEmpty(oldRoom)
|
||||||
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None)
|
watcher.room = room
|
||||||
self.broadcast(watcherProtocol, l)
|
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None)
|
||||||
|
self.broadcast(watcherProtocol, l)
|
||||||
def watcherSetFile(self, watcherProtocol, file_):
|
|
||||||
watcher = self.getWatcher(watcherProtocol)
|
def watcherSetFile(self, watcherProtocol, file_):
|
||||||
watcher.file = file_
|
watcher = self.getWatcher(watcherProtocol)
|
||||||
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None)
|
watcher.file = file_
|
||||||
self.broadcast(watcherProtocol, l)
|
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None)
|
||||||
|
self.broadcast(watcherProtocol, l)
|
||||||
def broadcastRoom(self, sender, what):
|
|
||||||
room = self._rooms[self.watcherGetRoom(sender)]
|
def broadcastRoom(self, sender, what):
|
||||||
if(room):
|
room = self._rooms[self.watcherGetRoom(sender)]
|
||||||
with self._roomUpdate:
|
if(room):
|
||||||
for receiver in room:
|
with self._roomUpdate:
|
||||||
what(receiver)
|
for receiver in room:
|
||||||
|
what(receiver)
|
||||||
def broadcast(self, sender, what):
|
|
||||||
with self._roomUpdate:
|
def broadcast(self, sender, what):
|
||||||
for room in self._rooms.itervalues():
|
with self._roomUpdate:
|
||||||
for receiver in room:
|
for room in self._rooms.itervalues():
|
||||||
what(receiver)
|
for receiver in room:
|
||||||
|
what(receiver)
|
||||||
class SyncIsolatedFactory(SyncFactory):
|
|
||||||
def broadcast(self, sender, what):
|
class SyncIsolatedFactory(SyncFactory):
|
||||||
self.broadcastRoom(sender, what)
|
def broadcast(self, sender, what):
|
||||||
|
self.broadcastRoom(sender, what)
|
||||||
def getAllWatchers(self, watcherProtocol):
|
|
||||||
room = self.getWatcher(watcherProtocol).room
|
def getAllWatchers(self, watcherProtocol):
|
||||||
if(self._rooms.has_key(room)):
|
room = self.getWatcher(watcherProtocol).room
|
||||||
return self._rooms[room]
|
if(self._rooms.has_key(room)):
|
||||||
else:
|
return self._rooms[room]
|
||||||
return {}
|
else:
|
||||||
|
return {}
|
||||||
def watcherSetRoom(self, watcherProtocol, room):
|
|
||||||
watcher = self.getWatcher(watcherProtocol)
|
def watcherSetRoom(self, watcherProtocol, room):
|
||||||
oldRoom = watcher.room
|
watcher = self.getWatcher(watcherProtocol)
|
||||||
l = lambda w: w.sendUserSetting(watcher.name, oldRoom, None, {"left": True})
|
oldRoom = watcher.room
|
||||||
self.broadcast(watcherProtocol, l)
|
l = lambda w: w.sendUserSetting(watcher.name, oldRoom, None, {"left": True})
|
||||||
SyncFactory.watcherSetRoom(self, watcherProtocol, room)
|
self.broadcast(watcherProtocol, l)
|
||||||
|
SyncFactory.watcherSetRoom(self, watcherProtocol, room)
|
||||||
|
|
||||||
class Watcher(object):
|
|
||||||
def __init__(self, factory, watcherProtocol, name, room):
|
class Watcher(object):
|
||||||
self.factory = factory
|
def __init__(self, factory, watcherProtocol, name, room):
|
||||||
self.watcherProtocol = watcherProtocol
|
self.factory = factory
|
||||||
self.name = name
|
self.watcherProtocol = watcherProtocol
|
||||||
self.room = room
|
self.name = name
|
||||||
self.file = None
|
self.room = room
|
||||||
self._sendStateTimer = None
|
self.file = None
|
||||||
self.position = None
|
self._sendStateTimer = None
|
||||||
self.latency = 0
|
self.position = None
|
||||||
self.lastUpdate = time.time()
|
self.latency = 0
|
||||||
|
self.lastUpdate = time.time()
|
||||||
def __lt__(self, b):
|
|
||||||
if(self.position is None):
|
def __lt__(self, b):
|
||||||
return False
|
if(self.position is None):
|
||||||
elif(b.position is None):
|
return False
|
||||||
return True
|
elif(b.position is None):
|
||||||
else:
|
return True
|
||||||
return self.position < b.position
|
else:
|
||||||
|
return self.position < b.position
|
||||||
def getRoomPosition(self):
|
|
||||||
_, position = self.factory.getRoomPausedAndPosition(self.room)
|
def getRoomPosition(self):
|
||||||
return position
|
_, position = self.factory.getRoomPausedAndPosition(self.room)
|
||||||
|
return position
|
||||||
def scheduleSendState(self):
|
|
||||||
self._sendStateTimer = task.LoopingCall(self.sendState)
|
def scheduleSendState(self):
|
||||||
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL, True)
|
self._sendStateTimer = task.LoopingCall(self.sendState)
|
||||||
|
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL, True)
|
||||||
def sendState(self):
|
|
||||||
self.factory.sendState(self.watcherProtocol)
|
def sendState(self):
|
||||||
|
self.factory.sendState(self.watcherProtocol)
|
||||||
def resetStateTimer(self):
|
|
||||||
if(self._sendStateTimer):
|
def resetStateTimer(self):
|
||||||
self._sendStateTimer.stop()
|
if(self._sendStateTimer):
|
||||||
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL)
|
self._sendStateTimer.stop()
|
||||||
|
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL)
|
||||||
def deactivate(self):
|
|
||||||
if(self._sendStateTimer):
|
def deactivate(self):
|
||||||
self._sendStateTimer.stop()
|
if(self._sendStateTimer):
|
||||||
|
self._sendStateTimer.stop()
|
||||||
|
|
||||||
|
|||||||
@ -1,235 +1,236 @@
|
|||||||
from ConfigParser import SafeConfigParser
|
from ConfigParser import SafeConfigParser
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
try:
|
from syncplay.messages import getMessage
|
||||||
from syncplay.ui.GuiConfiguration import GuiConfiguration
|
try:
|
||||||
except ImportError:
|
from syncplay.ui.GuiConfiguration import GuiConfiguration
|
||||||
GuiConfiguration = None
|
except ImportError:
|
||||||
|
GuiConfiguration = None
|
||||||
class InvalidConfigValue(Exception):
|
|
||||||
def __init__(self, message):
|
class InvalidConfigValue(Exception):
|
||||||
Exception.__init__(self, message)
|
def __init__(self, message):
|
||||||
|
Exception.__init__(self, message)
|
||||||
class ConfigurationGetter(object):
|
|
||||||
def __init__(self):
|
class ConfigurationGetter(object):
|
||||||
self._config = {
|
def __init__(self):
|
||||||
"host": None,
|
self._config = {
|
||||||
"port": constants.DEFAULT_PORT,
|
"host": None,
|
||||||
"name": None,
|
"port": constants.DEFAULT_PORT,
|
||||||
"debug": False,
|
"name": None,
|
||||||
"forceGuiPrompt": False,
|
"debug": False,
|
||||||
"noGui": False,
|
"forceGuiPrompt": False,
|
||||||
"noStore": False,
|
"noGui": False,
|
||||||
"room": "",
|
"noStore": False,
|
||||||
"password": None,
|
"room": "",
|
||||||
"playerPath": None,
|
"password": None,
|
||||||
"file": None,
|
"playerPath": None,
|
||||||
"playerArgs": [],
|
"file": None,
|
||||||
"playerType": None,
|
"playerArgs": [],
|
||||||
}
|
"playerType": None,
|
||||||
|
}
|
||||||
#
|
|
||||||
#Custom validation in self._validateArguments
|
#
|
||||||
#
|
#Custom validation in self._validateArguments
|
||||||
self._required = [
|
#
|
||||||
"host",
|
self._required = [
|
||||||
"port",
|
"host",
|
||||||
"name",
|
"port",
|
||||||
"playerPath",
|
"name",
|
||||||
"playerType",
|
"playerPath",
|
||||||
]
|
"playerType",
|
||||||
|
]
|
||||||
self._iniStructure = {
|
|
||||||
"server_data": ["host", "port", "password"],
|
self._iniStructure = {
|
||||||
"client_settings": ["name", "room", "playerPath"]
|
"server_data": ["host", "port", "password"],
|
||||||
}
|
"client_settings": ["name", "room", "playerPath"]
|
||||||
|
}
|
||||||
#
|
|
||||||
#Watch out for the method self._overrideConfigWithArgs when you're adding custom multi-word command line arguments
|
#
|
||||||
#
|
#Watch out for the method self._overrideConfigWithArgs when you're adding custom multi-word command line arguments
|
||||||
self._argparser = argparse.ArgumentParser(description='Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
|
#
|
||||||
epilog='If no options supplied values from configuration file will be used')
|
self._argparser = argparse.ArgumentParser(description=getMessage("en", "argument-description"),
|
||||||
self._argparser.add_argument('--no-gui', action='store_true', help='show no GUI')
|
epilog='If no options supplied values from configuration file will be used')
|
||||||
self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help='server\'s address')
|
self._argparser.add_argument('--no-gui', action='store_true', help=getMessage("en", "nogui-argument"))
|
||||||
self._argparser.add_argument('-n', '--name', metavar='username', type=str, help='desired username')
|
self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help=getMessage("en", "host-argument"))
|
||||||
self._argparser.add_argument('-d', '--debug', action='store_true', help='debug mode')
|
self._argparser.add_argument('-n', '--name', metavar='username', type=str, help=getMessage("en", "name-argument"))
|
||||||
self._argparser.add_argument('-g', '--force-gui-prompt', action='store_true', help='make configuration prompt appear')
|
self._argparser.add_argument('-d', '--debug', action='store_true', help=getMessage("en", "debug-argument"))
|
||||||
self._argparser.add_argument('--no-store', action='store_true', help='don\'t store values in .syncplay')
|
self._argparser.add_argument('-g', '--force-gui-prompt', action='store_true', help=getMessage("en", "force-gui-prompt-argument"))
|
||||||
self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help='default room')
|
self._argparser.add_argument('--no-store', action='store_true', help=getMessage("en", "no-store-argument"))
|
||||||
self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help='server password')
|
self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help=getMessage("en", "room-argument"))
|
||||||
self._argparser.add_argument('--player-path', metavar='path', type=str, help='path to your player executable')
|
self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("en", "password-argument"))
|
||||||
self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help='file to play')
|
self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("en", "player-path-argument"))
|
||||||
self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help='player options, if you need to pass options starting with - prepend them with single \'--\' argument')
|
self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help=getMessage("en", "file-argument"))
|
||||||
|
self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("en", "args-argument"))
|
||||||
def _validateArguments(self):
|
|
||||||
for key in self._required:
|
def _validateArguments(self):
|
||||||
if(key == "playerPath"):
|
for key in self._required:
|
||||||
if(self._isPlayerMPCAndValid(self._config["playerPath"])):
|
if(key == "playerPath"):
|
||||||
self._config["playerType"] = "mpc"
|
if(self._isPlayerMPCAndValid(self._config["playerPath"])):
|
||||||
self.__addSpecialMPCFlags()
|
self._config["playerType"] = "mpc"
|
||||||
elif(self._isMplayerPathAndValid(self._config["playerPath"])):
|
self.__addSpecialMPCFlags()
|
||||||
self._config["playerType"] = "mplayer"
|
elif(self._isMplayerPathAndValid(self._config["playerPath"])):
|
||||||
else:
|
self._config["playerType"] = "mplayer"
|
||||||
raise InvalidConfigValue("Player path is not set properly")
|
else:
|
||||||
elif(key == "host"):
|
raise InvalidConfigValue(getMessage("en", "player-path-error"))
|
||||||
self._config["host"], self._config["port"] = self._splitPortAndHost(self._config["host"])
|
elif(key == "host"):
|
||||||
hostNotValid = (self._config["host"] == "" or self._config["host"] is None)
|
self._config["host"], self._config["port"] = self._splitPortAndHost(self._config["host"])
|
||||||
portNotValid = (self._config["port"] == "" or self._config["port"] is None)
|
hostNotValid = (self._config["host"] == "" or self._config["host"] is None)
|
||||||
if(hostNotValid or portNotValid):
|
portNotValid = (self._config["port"] == "" or self._config["port"] is None)
|
||||||
raise InvalidConfigValue("Hostname can't be empty")
|
if(hostNotValid or portNotValid):
|
||||||
elif(self._config[key] == "" or self._config[key] is None):
|
raise InvalidConfigValue(getMessage("en", "hostname-empty-error"))
|
||||||
raise InvalidConfigValue("{} can't be empty".format(key))
|
elif(self._config[key] == "" or self._config[key] is None):
|
||||||
|
raise InvalidConfigValue(getMessage("en", "empty-error").format(key))
|
||||||
def _overrideConfigWithArgs(self, args):
|
|
||||||
for key, val in vars(args).items():
|
def _overrideConfigWithArgs(self, args):
|
||||||
if(val):
|
for key, val in vars(args).items():
|
||||||
if(key == "force_gui_prompt"):
|
if(val):
|
||||||
key = "forceGuiPrompt"
|
if(key == "force_gui_prompt"):
|
||||||
if(key == "no_store"):
|
key = "forceGuiPrompt"
|
||||||
key = "noStore"
|
if(key == "no_store"):
|
||||||
if(key == "player_path"):
|
key = "noStore"
|
||||||
key = "playerPath"
|
if(key == "player_path"):
|
||||||
if(key == "_args"):
|
key = "playerPath"
|
||||||
key = "playerArgs"
|
if(key == "_args"):
|
||||||
if(key == "no_gui"):
|
key = "playerArgs"
|
||||||
key = "noGui"
|
if(key == "no_gui"):
|
||||||
self._config[key] = val
|
key = "noGui"
|
||||||
|
self._config[key] = val
|
||||||
def _isPlayerMPCAndValid(self, path):
|
|
||||||
if(os.path.isfile(path)):
|
def _isPlayerMPCAndValid(self, path):
|
||||||
if(path[-10:] == 'mpc-hc.exe' or path[-12:] == 'mpc-hc64.exe'):
|
if(os.path.isfile(path)):
|
||||||
return True
|
if(path[-10:] == 'mpc-hc.exe' or path[-12:] == 'mpc-hc64.exe'):
|
||||||
if(os.path.isfile(path + "\\mpc-hc.exe")):
|
return True
|
||||||
path += "\\mpc-hc.exe"
|
if(os.path.isfile(path + "\\mpc-hc.exe")):
|
||||||
return True
|
path += "\\mpc-hc.exe"
|
||||||
if(os.path.isfile(path + "\\mpc-hc64.exe")):
|
return True
|
||||||
path += "\\mpc-hc64.exe"
|
if(os.path.isfile(path + "\\mpc-hc64.exe")):
|
||||||
return True
|
path += "\\mpc-hc64.exe"
|
||||||
return False
|
return True
|
||||||
|
return False
|
||||||
def __addSpecialMPCFlags(self):
|
|
||||||
self._config['playerArgs'].extend(['/open', '/new'])
|
def __addSpecialMPCFlags(self):
|
||||||
|
self._config['playerArgs'].extend(['/open', '/new'])
|
||||||
def _isMplayerPathAndValid(self, playerPath):
|
|
||||||
if("mplayer" in playerPath):
|
def _isMplayerPathAndValid(self, playerPath):
|
||||||
if os.access(playerPath, os.X_OK):
|
if("mplayer" in playerPath):
|
||||||
return True
|
if os.access(playerPath, os.X_OK):
|
||||||
for path in os.environ['PATH'].split(':'):
|
return True
|
||||||
path = os.path.join(os.path.realpath(path), playerPath)
|
for path in os.environ['PATH'].split(':'):
|
||||||
if os.access(path, os.X_OK):
|
path = os.path.join(os.path.realpath(path), playerPath)
|
||||||
self._config['playerPath'] = path
|
if os.access(path, os.X_OK):
|
||||||
return True
|
self._config['playerPath'] = path
|
||||||
return False
|
return True
|
||||||
|
return False
|
||||||
def _splitPortAndHost(self, host):
|
|
||||||
port = constants.DEFAULT_PORT if not self._config["port"] else self._config["port"]
|
def _splitPortAndHost(self, host):
|
||||||
if(host):
|
port = constants.DEFAULT_PORT if not self._config["port"] else self._config["port"]
|
||||||
if ':' in host:
|
if(host):
|
||||||
host, port = host.split(':', 1)
|
if ':' in host:
|
||||||
return host, int(port)
|
host, port = host.split(':', 1)
|
||||||
|
return host, int(port)
|
||||||
def _findWorkingDir(self):
|
|
||||||
frozen = getattr(sys, 'frozen', '')
|
def _findWorkingDir(self):
|
||||||
if not frozen:
|
frozen = getattr(sys, 'frozen', '')
|
||||||
path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
if not frozen:
|
||||||
elif frozen in ('dll', 'console_exe', 'windows_exe'):
|
path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||||
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
elif frozen in ('dll', 'console_exe', 'windows_exe'):
|
||||||
else:
|
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
||||||
path = ""
|
else:
|
||||||
return path
|
path = ""
|
||||||
|
return path
|
||||||
def _checkForPortableFile(self):
|
|
||||||
path = self._findWorkingDir()
|
def _checkForPortableFile(self):
|
||||||
if(os.path.isfile(os.path.join(path, constants.DEFAULT_CONFIG_NAME))):
|
path = self._findWorkingDir()
|
||||||
return os.path.join(path, constants.DEFAULT_CONFIG_NAME)
|
if(os.path.isfile(os.path.join(path, constants.DEFAULT_CONFIG_NAME))):
|
||||||
|
return os.path.join(path, constants.DEFAULT_CONFIG_NAME)
|
||||||
def _getConfigurationFilePath(self):
|
|
||||||
configFile = self._checkForPortableFile()
|
def _getConfigurationFilePath(self):
|
||||||
if(not configFile):
|
configFile = self._checkForPortableFile()
|
||||||
if(os.name <> 'nt'):
|
if(not configFile):
|
||||||
configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME)
|
if(os.name <> 'nt'):
|
||||||
else:
|
configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME)
|
||||||
configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME)
|
else:
|
||||||
return configFile
|
configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME)
|
||||||
|
return configFile
|
||||||
def _parseConfigFile(self, iniPath):
|
|
||||||
parser = SafeConfigParser()
|
def _parseConfigFile(self, iniPath):
|
||||||
parser.read(iniPath)
|
parser = SafeConfigParser()
|
||||||
for section, options in self._iniStructure.items():
|
parser.read(iniPath)
|
||||||
if(parser.has_section(section)):
|
for section, options in self._iniStructure.items():
|
||||||
for option in options:
|
if(parser.has_section(section)):
|
||||||
if(parser.has_option(section, option)):
|
for option in options:
|
||||||
self._config[option] = parser.get(section, option)
|
if(parser.has_option(section, option)):
|
||||||
|
self._config[option] = parser.get(section, option)
|
||||||
def _checkConfig(self):
|
|
||||||
try:
|
def _checkConfig(self):
|
||||||
self._validateArguments()
|
try:
|
||||||
except InvalidConfigValue:
|
self._validateArguments()
|
||||||
try:
|
except InvalidConfigValue:
|
||||||
for key, value in self._promptForMissingArguments().items():
|
try:
|
||||||
self._config[key] = value
|
for key, value in self._promptForMissingArguments().items():
|
||||||
self._checkConfig()
|
self._config[key] = value
|
||||||
except:
|
self._checkConfig()
|
||||||
sys.exit()
|
except:
|
||||||
|
sys.exit()
|
||||||
def _promptForMissingArguments(self):
|
|
||||||
if(self._config['noGui']):
|
def _promptForMissingArguments(self):
|
||||||
print "Some necessary arguments are missing, refer to --help"
|
if(self._config['noGui']):
|
||||||
sys.exit()
|
print getMessage("en", "arguments-missing-error")
|
||||||
elif(GuiConfiguration):
|
sys.exit()
|
||||||
return GuiConfiguration(self._config).getProcessedConfiguration()
|
elif(GuiConfiguration):
|
||||||
|
return GuiConfiguration(self._config).getProcessedConfiguration()
|
||||||
def __wasOptionChanged(self, parser, section, option):
|
|
||||||
if (parser.has_option(section, option)):
|
def __wasOptionChanged(self, parser, section, option):
|
||||||
if (parser.get(section, option) != str(self._config[option])):
|
if (parser.has_option(section, option)):
|
||||||
return True
|
if (parser.get(section, option) != str(self._config[option])):
|
||||||
else:
|
return True
|
||||||
return True
|
else:
|
||||||
|
return True
|
||||||
def _saveConfig(self, iniPath):
|
|
||||||
changed = False
|
def _saveConfig(self, iniPath):
|
||||||
if(self._config['noStore']):
|
changed = False
|
||||||
return
|
if(self._config['noStore']):
|
||||||
parser = SafeConfigParser()
|
return
|
||||||
parser.read(iniPath)
|
parser = SafeConfigParser()
|
||||||
for section, options in self._iniStructure.items():
|
parser.read(iniPath)
|
||||||
if(not parser.has_section(section)):
|
for section, options in self._iniStructure.items():
|
||||||
parser.add_section(section)
|
if(not parser.has_section(section)):
|
||||||
changed = True
|
parser.add_section(section)
|
||||||
for option in options:
|
changed = True
|
||||||
if(self.__wasOptionChanged(parser, section, option)):
|
for option in options:
|
||||||
changed = True
|
if(self.__wasOptionChanged(parser, section, option)):
|
||||||
parser.set(section, option, str(self._config[option]))
|
changed = True
|
||||||
if(changed):
|
parser.set(section, option, str(self._config[option]))
|
||||||
parser.write(file(iniPath, "w"))
|
if(changed):
|
||||||
|
parser.write(file(iniPath, "w"))
|
||||||
def getConfiguration(self):
|
|
||||||
iniPath = self._getConfigurationFilePath()
|
def getConfiguration(self):
|
||||||
self._parseConfigFile(iniPath)
|
iniPath = self._getConfigurationFilePath()
|
||||||
args = self._argparser.parse_args()
|
self._parseConfigFile(iniPath)
|
||||||
self._overrideConfigWithArgs(args)
|
args = self._argparser.parse_args()
|
||||||
if(self._config['forceGuiPrompt']):
|
self._overrideConfigWithArgs(args)
|
||||||
try:
|
if(self._config['forceGuiPrompt']):
|
||||||
self._promptForMissingArguments()
|
try:
|
||||||
except:
|
self._promptForMissingArguments()
|
||||||
sys.exit()
|
except:
|
||||||
self._checkConfig()
|
sys.exit()
|
||||||
self._saveConfig(iniPath)
|
self._checkConfig()
|
||||||
return self._config
|
self._saveConfig(iniPath)
|
||||||
|
return self._config
|
||||||
class ServerConfigurationGetter(object):
|
|
||||||
def getConfiguration(self):
|
class ServerConfigurationGetter(object):
|
||||||
self._prepareArgParser()
|
def getConfiguration(self):
|
||||||
self._args = self._argparser.parse_args()
|
self._prepareArgParser()
|
||||||
if(self._args.port == None):
|
self._args = self._argparser.parse_args()
|
||||||
self._args.port = constants.DEFAULT_PORT
|
if(self._args.port == None):
|
||||||
return self._args
|
self._args.port = constants.DEFAULT_PORT
|
||||||
|
return self._args
|
||||||
def _prepareArgParser(self):
|
|
||||||
self._argparser = argparse.ArgumentParser(description='Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network. Server instance',
|
def _prepareArgParser(self):
|
||||||
epilog='If no options supplied _config values will be used')
|
self._argparser = argparse.ArgumentParser(description=getMessage("en", "server-argument-description"),
|
||||||
self._argparser.add_argument('--port', metavar='port', type=str, nargs='?', help='server TCP port')
|
epilog=getMessage("en", "argument-epilog"))
|
||||||
self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help='server password')
|
self._argparser.add_argument('--port', metavar='port', type=str, nargs='?', help=getMessage("en", "server-port-argument"))
|
||||||
self._argparser.add_argument('--isolate-rooms', action='store_true', help='should rooms be isolated?')
|
self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help=getMessage("en", "password-argument"))
|
||||||
|
self._argparser.add_argument('--isolate-rooms', action='store_true', help=getMessage("en", "server-isolate-room-argument"))
|
||||||
|
|||||||
@ -1,100 +1,101 @@
|
|||||||
import pygtk
|
import pygtk
|
||||||
import os
|
import os
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
pygtk.require('2.0')
|
from syncplay.messages import getMessage
|
||||||
import gtk
|
pygtk.require('2.0')
|
||||||
gtk.set_interactive(False)
|
import gtk
|
||||||
import cairo, gio, pango, atk, pangocairo, gobject #@UnusedImport
|
gtk.set_interactive(False)
|
||||||
|
import cairo, gio, pango, atk, pangocairo, gobject #@UnusedImport
|
||||||
class GuiConfiguration:
|
|
||||||
def __init__(self, config):
|
class GuiConfiguration:
|
||||||
self.config = config
|
def __init__(self, config):
|
||||||
self.closedAndNotSaved = False
|
self.config = config
|
||||||
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
self.closedAndNotSaved = False
|
||||||
self.window.set_title("Syncplay Configuration")
|
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
self.window.connect("delete_event", lambda w, e: self._windowClosed())
|
self.window.set_title("Syncplay Configuration")
|
||||||
vbox = gtk.VBox(False, 0)
|
self.window.connect("delete_event", lambda w, e: self._windowClosed())
|
||||||
self.window.add(vbox)
|
vbox = gtk.VBox(False, 0)
|
||||||
vbox.show()
|
self.window.add(vbox)
|
||||||
self._addLabeledEntries(config, vbox)
|
vbox.show()
|
||||||
|
self._addLabeledEntries(config, vbox)
|
||||||
self.hostEntry.select_region(0, len(self.hostEntry.get_text()))
|
|
||||||
button = gtk.Button(stock=gtk.STOCK_SAVE)
|
self.hostEntry.select_region(0, len(self.hostEntry.get_text()))
|
||||||
button.connect("clicked", lambda w: self._saveDataAndLeave())
|
button = gtk.Button(stock=gtk.STOCK_SAVE)
|
||||||
vbox.pack_start(button, True, True, 0)
|
button.connect("clicked", lambda w: self._saveDataAndLeave())
|
||||||
button.set_flags(gtk.CAN_DEFAULT)
|
vbox.pack_start(button, True, True, 0)
|
||||||
button.grab_default()
|
button.set_flags(gtk.CAN_DEFAULT)
|
||||||
button.show()
|
button.grab_default()
|
||||||
self.window.show()
|
button.show()
|
||||||
gtk.main()
|
self.window.show()
|
||||||
|
gtk.main()
|
||||||
def _windowClosed(self):
|
|
||||||
self.window.destroy()
|
def _windowClosed(self):
|
||||||
gtk.main_quit()
|
self.window.destroy()
|
||||||
self.closedAndNotSaved = True
|
gtk.main_quit()
|
||||||
|
self.closedAndNotSaved = True
|
||||||
def _addLabeledEntries(self, config, vbox):
|
|
||||||
if(config['host'] == None):
|
def _addLabeledEntries(self, config, vbox):
|
||||||
host = ""
|
if(config['host'] == None):
|
||||||
elif(":" in config['host']):
|
host = ""
|
||||||
host = config['host']
|
elif(":" in config['host']):
|
||||||
else:
|
host = config['host']
|
||||||
host = config['host']+":"+str(config['port'])
|
else:
|
||||||
|
host = config['host']+":"+str(config['port'])
|
||||||
self.hostEntry = self._addLabeledEntryToVbox('Host: ', host, vbox, lambda __, _: self._saveDataAndLeave())
|
|
||||||
self.userEntry = self._addLabeledEntryToVbox('Username: ', config['name'], vbox, lambda __, _: self._saveDataAndLeave())
|
self.hostEntry = self._addLabeledEntryToVbox(getMessage("en", "host-label"), host, vbox, lambda __, _: self._saveDataAndLeave())
|
||||||
self.roomEntry = self._addLabeledEntryToVbox('Default room (optional): ', config['room'], vbox, lambda __, _: self._saveDataAndLeave())
|
self.userEntry = self._addLabeledEntryToVbox(getMessage("en", "username-label"), config['name'], vbox, lambda __, _: self._saveDataAndLeave())
|
||||||
self.passEntry = self._addLabeledEntryToVbox('Server password (optional): ', config['password'], vbox, lambda __, _: self._saveDataAndLeave())
|
self.roomEntry = self._addLabeledEntryToVbox(getMessage("en", "room-label"), config['room'], vbox, lambda __, _: self._saveDataAndLeave())
|
||||||
self.mpcEntry = self._addLabeledEntryToVbox('Path to player executable: ', self._tryToFillUpMpcPath(config['playerPath']), vbox, lambda __, _: self._saveDataAndLeave())
|
self.passEntry = self._addLabeledEntryToVbox(getMessage("en", "password-label"), config['password'], vbox, lambda __, _: self._saveDataAndLeave())
|
||||||
|
self.mpcEntry = self._addLabeledEntryToVbox(getMessage("en", "path-label"), self._tryToFillUpMpcPath(config['playerPath']), vbox, lambda __, _: self._saveDataAndLeave())
|
||||||
def _tryToFillUpMpcPath(self, playerPath):
|
|
||||||
if(playerPath == None):
|
def _tryToFillUpMpcPath(self, playerPath):
|
||||||
for path in constants.MPC_PATHS:
|
if(playerPath == None):
|
||||||
if(os.path.isfile(path)):
|
for path in constants.MPC_PATHS:
|
||||||
return path
|
if(os.path.isfile(path)):
|
||||||
return playerPath
|
return path
|
||||||
|
return playerPath
|
||||||
def getProcessedConfiguration(self):
|
|
||||||
if(self.closedAndNotSaved):
|
def getProcessedConfiguration(self):
|
||||||
raise self.WindowClosed
|
if(self.closedAndNotSaved):
|
||||||
return self.config
|
raise self.WindowClosed
|
||||||
|
return self.config
|
||||||
def _saveDataAndLeave(self):
|
|
||||||
self.config['host'] = self.hostEntry.get_text()
|
def _saveDataAndLeave(self):
|
||||||
self.config['name'] = self.userEntry.get_text()
|
self.config['host'] = self.hostEntry.get_text()
|
||||||
self.config['room'] = self.roomEntry.get_text()
|
self.config['name'] = self.userEntry.get_text()
|
||||||
self.config['password'] = self.passEntry.get_text()
|
self.config['room'] = self.roomEntry.get_text()
|
||||||
self.config['playerPath'] = self.mpcEntry.get_text()
|
self.config['password'] = self.passEntry.get_text()
|
||||||
self.window.destroy()
|
self.config['playerPath'] = self.mpcEntry.get_text()
|
||||||
gtk.main_quit()
|
self.window.destroy()
|
||||||
|
gtk.main_quit()
|
||||||
def _addLabeledEntryToVbox(self, label, initialEntryValue, vbox, callback):
|
|
||||||
hbox = gtk.HBox(False, 0)
|
def _addLabeledEntryToVbox(self, label, initialEntryValue, vbox, callback):
|
||||||
hbox.set_border_width(3)
|
hbox = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(hbox, False, False, 0)
|
hbox.set_border_width(3)
|
||||||
hbox.show()
|
vbox.pack_start(hbox, False, False, 0)
|
||||||
label_ = gtk.Label()
|
hbox.show()
|
||||||
label_.set_text(label)
|
label_ = gtk.Label()
|
||||||
label_.set_alignment(xalign=0, yalign=0.5)
|
label_.set_text(label)
|
||||||
hbox.pack_start(label_, False, False, 0)
|
label_.set_alignment(xalign=0, yalign=0.5)
|
||||||
label_.show()
|
hbox.pack_start(label_, False, False, 0)
|
||||||
entry = gtk.Entry()
|
label_.show()
|
||||||
entry.connect("activate", callback, entry)
|
entry = gtk.Entry()
|
||||||
if(initialEntryValue == None):
|
entry.connect("activate", callback, entry)
|
||||||
initialEntryValue = ""
|
if(initialEntryValue == None):
|
||||||
entry.set_text(initialEntryValue)
|
initialEntryValue = ""
|
||||||
hbox.pack_end(entry, False, False, 0)
|
entry.set_text(initialEntryValue)
|
||||||
entry.set_usize(200, -1)
|
hbox.pack_end(entry, False, False, 0)
|
||||||
entry.show()
|
entry.set_usize(200, -1)
|
||||||
hbox = gtk.HBox(False, 0)
|
entry.show()
|
||||||
vbox.add(hbox)
|
hbox = gtk.HBox(False, 0)
|
||||||
hbox.show()
|
vbox.add(hbox)
|
||||||
return entry
|
hbox.show()
|
||||||
|
return entry
|
||||||
class WindowClosed(Exception):
|
|
||||||
def __init__(self):
|
class WindowClosed(Exception):
|
||||||
Exception.__init__(self)
|
def __init__(self):
|
||||||
|
Exception.__init__(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,122 +1,123 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import syncplay
|
import syncplay
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from syncplay import utils
|
from syncplay import utils
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
class ConsoleUI(threading.Thread):
|
from syncplay.messages import getMessage
|
||||||
def __init__(self):
|
class ConsoleUI(threading.Thread):
|
||||||
self.promptMode = threading.Event()
|
def __init__(self):
|
||||||
self.PromptResult = ""
|
self.promptMode = threading.Event()
|
||||||
self.promptMode.set()
|
self.PromptResult = ""
|
||||||
self._syncplayClient = None
|
self.promptMode.set()
|
||||||
threading.Thread.__init__(self, name="ConsoleUI")
|
self._syncplayClient = None
|
||||||
|
threading.Thread.__init__(self, name="ConsoleUI")
|
||||||
def addClient(self, client):
|
|
||||||
self._syncplayClient = client
|
def addClient(self, client):
|
||||||
|
self._syncplayClient = client
|
||||||
def run(self):
|
|
||||||
while True:
|
def run(self):
|
||||||
data = raw_input()
|
while True:
|
||||||
data = data.rstrip('\n\r')
|
data = raw_input()
|
||||||
if(not self.promptMode.isSet()):
|
data = data.rstrip('\n\r')
|
||||||
self.PromptResult = data
|
if(not self.promptMode.isSet()):
|
||||||
self.promptMode.set()
|
self.PromptResult = data
|
||||||
elif(self._syncplayClient):
|
self.promptMode.set()
|
||||||
self._executeCommand(data)
|
elif(self._syncplayClient):
|
||||||
|
self._executeCommand(data)
|
||||||
def promptFor(self, prompt=">", message=""):
|
|
||||||
if message <> "":
|
def promptFor(self, prompt=">", message=""):
|
||||||
print(message)
|
if message <> "":
|
||||||
self.promptMode.clear()
|
print(message)
|
||||||
print(prompt, end='')
|
self.promptMode.clear()
|
||||||
self.promptMode.wait()
|
print(prompt, end='')
|
||||||
return self.PromptResult
|
self.promptMode.wait()
|
||||||
|
return self.PromptResult
|
||||||
def showMessage(self, message, noTimestamp=False):
|
|
||||||
if(os.name == "nt"):
|
def showMessage(self, message, noTimestamp=False):
|
||||||
message = message.encode('ascii', 'replace')
|
if(os.name == "nt"):
|
||||||
if(noTimestamp):
|
message = message.encode('ascii', 'replace')
|
||||||
print(message)
|
if(noTimestamp):
|
||||||
else:
|
print(message)
|
||||||
print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message)
|
else:
|
||||||
|
print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message)
|
||||||
def showDebugMessage(self, message):
|
|
||||||
print(message)
|
def showDebugMessage(self, message):
|
||||||
|
print(message)
|
||||||
def showErrorMessage(self, message):
|
|
||||||
print("ERROR:\t" + message)
|
def showErrorMessage(self, message):
|
||||||
|
print("ERROR:\t" + message)
|
||||||
def _extractSign(self, m):
|
|
||||||
if(m):
|
def _extractSign(self, m):
|
||||||
if(m == "-"):
|
if(m):
|
||||||
return -1
|
if(m == "-"):
|
||||||
else:
|
return -1
|
||||||
return 1
|
else:
|
||||||
else:
|
return 1
|
||||||
return None
|
else:
|
||||||
|
return None
|
||||||
def _tryAdvancedCommands(self, data):
|
|
||||||
o = re.match(constants.UI_OFFSET_REGEX, data)
|
def _tryAdvancedCommands(self, data):
|
||||||
s = re.match(constants.UI_SEEK_REGEX, data)
|
o = re.match(constants.UI_OFFSET_REGEX, data)
|
||||||
if(o):
|
s = re.match(constants.UI_SEEK_REGEX, data)
|
||||||
sign = self._extractSign(o.group('sign'))
|
if(o):
|
||||||
t = utils.parseTime(o.group('time'))
|
sign = self._extractSign(o.group('sign'))
|
||||||
if(t is None):
|
t = utils.parseTime(o.group('time'))
|
||||||
return
|
if(t is None):
|
||||||
if (o.group('sign') == "/"):
|
return
|
||||||
t = self._syncplayClient.getPlayerPosition() - t
|
if (o.group('sign') == "/"):
|
||||||
elif(sign):
|
t = self._syncplayClient.getPlayerPosition() - t
|
||||||
t = self._syncplayClient.getUserOffset() + sign * t
|
elif(sign):
|
||||||
self._syncplayClient.setUserOffset(t)
|
t = self._syncplayClient.getUserOffset() + sign * t
|
||||||
return True
|
self._syncplayClient.setUserOffset(t)
|
||||||
elif s:
|
return True
|
||||||
sign = self._extractSign(s.group('sign'))
|
elif s:
|
||||||
t = utils.parseTime(s.group('time'))
|
sign = self._extractSign(s.group('sign'))
|
||||||
if(t is None):
|
t = utils.parseTime(s.group('time'))
|
||||||
return
|
if(t is None):
|
||||||
if(sign):
|
return
|
||||||
t = self._syncplayClient.getGlobalPosition() + sign * t
|
if(sign):
|
||||||
self._syncplayClient.setPosition(t)
|
t = self._syncplayClient.getGlobalPosition() + sign * t
|
||||||
return True
|
self._syncplayClient.setPosition(t)
|
||||||
return False
|
return True
|
||||||
|
return False
|
||||||
def _executeCommand(self, data):
|
|
||||||
command = re.match(constants.UI_COMMAND_REGEX, data)
|
def _executeCommand(self, data):
|
||||||
if(not command):
|
command = re.match(constants.UI_COMMAND_REGEX, data)
|
||||||
return
|
if(not command):
|
||||||
if(command.group('command') in constants.COMMANDS_UNDO):
|
return
|
||||||
tmp_pos = self._syncplayClient.getPlayerPosition()
|
if(command.group('command') in constants.COMMANDS_UNDO):
|
||||||
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
|
tmp_pos = self._syncplayClient.getPlayerPosition()
|
||||||
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
|
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
|
||||||
elif (command.group('command') in constants.COMMANDS_LIST):
|
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
|
||||||
self._syncplayClient.getUserList()
|
elif (command.group('command') in constants.COMMANDS_LIST):
|
||||||
elif (command.group('command') in constants.COMMANDS_PAUSE):
|
self._syncplayClient.getUserList()
|
||||||
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
|
elif (command.group('command') in constants.COMMANDS_PAUSE):
|
||||||
elif (command.group('command') in constants.COMMANDS_ROOM):
|
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
|
||||||
room = command.group('parameter')
|
elif (command.group('command') in constants.COMMANDS_ROOM):
|
||||||
if room == None:
|
room = command.group('parameter')
|
||||||
if self._syncplayClient.userlist.currentUser.file:
|
if room == None:
|
||||||
room = self._syncplayClient.userlist.currentUser.file["name"]
|
if self._syncplayClient.userlist.currentUser.file:
|
||||||
else:
|
room = self._syncplayClient.userlist.currentUser.file["name"]
|
||||||
room = self._syncplayClient.defaultRoom
|
else:
|
||||||
|
room = self._syncplayClient.defaultRoom
|
||||||
self._syncplayClient.setRoom(room)
|
|
||||||
self._syncplayClient.sendRoom()
|
self._syncplayClient.setRoom(room)
|
||||||
else:
|
self._syncplayClient.sendRoom()
|
||||||
if(self._tryAdvancedCommands(data)):
|
else:
|
||||||
return
|
if(self._tryAdvancedCommands(data)):
|
||||||
if (command.group('command') not in constants.COMMANDS_HELP):
|
return
|
||||||
self.showMessage("Unrecognized command")
|
if (command.group('command') not in constants.COMMANDS_HELP):
|
||||||
self.showMessage("Available commands:", True)
|
self.showMessage(getMessage("en", "unrecognized-command-notification"))
|
||||||
self.showMessage("\tr [name] - change room", True)
|
self.showMessage(getMessage("en", "commandlist-notification"), True)
|
||||||
self.showMessage("\tl - show user list", True)
|
self.showMessage(getMessage("en", "commandlist-notification/room"), True)
|
||||||
self.showMessage("\tu - undo last seek", True)
|
self.showMessage(getMessage("en", "commandlist-notification/list"), True)
|
||||||
self.showMessage("\tp - toggle pause", True)
|
self.showMessage(getMessage("en", "commandlist-notification/undo"), True)
|
||||||
self.showMessage("\t[s][+-]time - seek to the given value of time, if + or - is not specified it's absolute time in seconds or min:sec", True)
|
self.showMessage(getMessage("en", "commandlist-notification/pause"), True)
|
||||||
self.showMessage("\th - this help", True)
|
self.showMessage(getMessage("en", "commandlist-notification/seek"), True)
|
||||||
self.showMessage("Syncplay version: {}".format(syncplay.version), True)
|
self.showMessage(getMessage("en", "commandlist-notification/help"), True)
|
||||||
self.showMessage("More info available at: {}".format(syncplay.projectURL), True)
|
self.showMessage("Syncplay version: {}".format(syncplay.version), True)
|
||||||
|
self.showMessage("More info available at: {}".format(syncplay.projectURL), True)
|
||||||
|
|
||||||
|
|||||||
@ -1,76 +1,77 @@
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import datetime
|
import datetime
|
||||||
from syncplay import constants
|
from syncplay import constants
|
||||||
|
from syncplay.messages import getMessage
|
||||||
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
|
||||||
"""Retry calling the decorated function using an exponential backoff.
|
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
|
||||||
|
"""Retry calling the decorated function using an exponential backoff.
|
||||||
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
|
|
||||||
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
|
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
|
||||||
|
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
|
||||||
:param ExceptionToCheck: the exception to check. may be a tuple of
|
|
||||||
excpetions to check
|
:param ExceptionToCheck: the exception to check. may be a tuple of
|
||||||
:type ExceptionToCheck: Exception or tuple
|
excpetions to check
|
||||||
:param tries: number of times to try (not retry) before giving up
|
:type ExceptionToCheck: Exception or tuple
|
||||||
:type tries: int
|
:param tries: number of times to try (not retry) before giving up
|
||||||
:param delay: initial delay between retries in seconds
|
:type tries: int
|
||||||
:type delay: int
|
:param delay: initial delay between retries in seconds
|
||||||
:param backoff: backoff multiplier e.g. value of 2 will double the delay
|
:type delay: int
|
||||||
each retry
|
:param backoff: backoff multiplier e.g. value of 2 will double the delay
|
||||||
:type backoff: int
|
each retry
|
||||||
:param logger: logger to use. If None, print
|
:type backoff: int
|
||||||
:type logger: logging.Logger instance
|
:param logger: logger to use. If None, print
|
||||||
"""
|
:type logger: logging.Logger instance
|
||||||
def deco_retry(f):
|
"""
|
||||||
def f_retry(*args, **kwargs):
|
def deco_retry(f):
|
||||||
mtries, mdelay = tries, delay
|
def f_retry(*args, **kwargs):
|
||||||
try_one_last_time = True
|
mtries, mdelay = tries, delay
|
||||||
while mtries > 1:
|
try_one_last_time = True
|
||||||
try:
|
while mtries > 1:
|
||||||
return f(*args, **kwargs)
|
try:
|
||||||
try_one_last_time = False
|
return f(*args, **kwargs)
|
||||||
break
|
try_one_last_time = False
|
||||||
except ExceptionToCheck, e:
|
break
|
||||||
if logger:
|
except ExceptionToCheck, e:
|
||||||
msg = "%s, Retrying in %d seconds..." % (str(e), mdelay)
|
if logger:
|
||||||
logger.warning(msg)
|
msg = getMessage("en", "retrying-notification") % (str(e), mdelay)
|
||||||
time.sleep(mdelay)
|
logger.warning(msg)
|
||||||
mtries -= 1
|
time.sleep(mdelay)
|
||||||
mdelay *= backoff
|
mtries -= 1
|
||||||
if try_one_last_time:
|
mdelay *= backoff
|
||||||
return f(*args, **kwargs)
|
if try_one_last_time:
|
||||||
return
|
return f(*args, **kwargs)
|
||||||
return f_retry # true decorator
|
return
|
||||||
return deco_retry
|
return f_retry # true decorator
|
||||||
|
return deco_retry
|
||||||
def parseTime(timeStr):
|
|
||||||
regex = re.compile(constants.PARSE_TIME_REGEX)
|
def parseTime(timeStr):
|
||||||
parts = regex.match(timeStr)
|
regex = re.compile(constants.PARSE_TIME_REGEX)
|
||||||
if not parts:
|
parts = regex.match(timeStr)
|
||||||
return
|
if not parts:
|
||||||
parts = parts.groupdict()
|
return
|
||||||
time_params = {}
|
parts = parts.groupdict()
|
||||||
for (name, param) in parts.iteritems():
|
time_params = {}
|
||||||
if param:
|
for (name, param) in parts.iteritems():
|
||||||
if(name == "miliseconds"):
|
if param:
|
||||||
time_params["microseconds"] = int(param) * 1000
|
if(name == "miliseconds"):
|
||||||
else:
|
time_params["microseconds"] = int(param) * 1000
|
||||||
time_params[name] = int(param)
|
else:
|
||||||
return datetime.timedelta(**time_params).total_seconds()
|
time_params[name] = int(param)
|
||||||
|
return datetime.timedelta(**time_params).total_seconds()
|
||||||
def formatTime(timeInSeconds):
|
|
||||||
timeInSeconds = round(timeInSeconds)
|
def formatTime(timeInSeconds):
|
||||||
weeks = timeInSeconds // 604800
|
timeInSeconds = round(timeInSeconds)
|
||||||
days = (timeInSeconds % 604800) // 86400
|
weeks = timeInSeconds // 604800
|
||||||
hours = (timeInSeconds % 86400) // 3600
|
days = (timeInSeconds % 604800) // 86400
|
||||||
minutes = (timeInSeconds % 3600) // 60
|
hours = (timeInSeconds % 86400) // 3600
|
||||||
seconds = timeInSeconds % 60
|
minutes = (timeInSeconds % 3600) // 60
|
||||||
if(weeks > 0):
|
seconds = timeInSeconds % 60
|
||||||
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
|
if(weeks > 0):
|
||||||
elif(days > 0):
|
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
|
||||||
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
|
elif(days > 0):
|
||||||
elif(hours > 0):
|
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
|
||||||
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
|
elif(hours > 0):
|
||||||
else:
|
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
|
||||||
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)
|
else:
|
||||||
|
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user