Use getMessage/EN where possible

This commit is contained in:
Et0h 2013-01-01 19:04:13 +00:00
parent 10c65aea7f
commit 35f3c3e6b3
10 changed files with 1845 additions and 1836 deletions

View File

@ -1,26 +1,27 @@
from syncplay.client import SyncplayClient
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
from syncplay import ui
try:
from syncplay.players.mpc import MPCHCAPIPlayer
except ImportError:
MPCHCAPIPlayer = None
from syncplay.players.mplayer import MplayerPlayer
class SyncplayClientManager(object):
def run(self):
config = ConfigurationGetter().getConfiguration()
interface = ui.getUi(graphical=not config["noGui"])
syncplayClient = None
if(config['playerType'] == "mpc"):
syncplayClient = SyncplayClient(MPCHCAPIPlayer, interface, config)
elif(config['playerType'] == "mplayer"):
syncplayClient = SyncplayClient(MplayerPlayer, interface, config)
if(syncplayClient):
interface.addClient(syncplayClient)
syncplayClient.start(config['host'], config['port'])
else:
interface.showErrorMessage("Unable to start client")
from syncplay.client import SyncplayClient
from syncplay.ui.ConfigurationGetter import ConfigurationGetter
from syncplay import ui
from syncplay.messages import getMessage
try:
from syncplay.players.mpc import MPCHCAPIPlayer
except ImportError:
MPCHCAPIPlayer = None
from syncplay.players.mplayer import MplayerPlayer
class SyncplayClientManager(object):
def run(self):
config = ConfigurationGetter().getConfiguration()
interface = ui.getUi(graphical=not config["noGui"])
syncplayClient = None
if(config['playerType'] == "mpc"):
syncplayClient = SyncplayClient(MPCHCAPIPlayer, interface, config)
elif(config['playerType'] == "mplayer"):
syncplayClient = SyncplayClient(MplayerPlayer, interface, config)
if(syncplayClient):
interface.addClient(syncplayClient)
syncplayClient.start(config['host'], config['port'])
else:
interface.showErrorMessage(getMessage("en", "unable-to-start-client-error"))

View File

@ -45,7 +45,7 @@ en = {
"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)
"more-info-notification" : "More info available at: {}", #projectURL
# Client prompts
"enter-to-exit-prompt" : "Press enter to exit\n",
@ -90,7 +90,7 @@ en = {
"path-label" : 'Path to player executable: ',
# Server notifications
"-welcome-server-notification" : "Welcome to Syncplay server, ver. {0}", #version
"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

View File

@ -1,454 +1,455 @@
#coding:utf8
import time
import threading
import thread
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
from functools import wraps
from syncplay.players.basePlayer import BasePlayer
import re
from syncplay.utils import retry
from syncplay import constants
class MpcHcApi:
def __init__(self):
self.callbacks = self.__Callbacks()
self.loadState = None
self.playState = None
self.filePlaying = None
self.fileDuration = None
self.filePath = None
self.lastFilePosition = None
self.version = None
self.__playpause_warden = False
self.__locks = self.__Locks()
self.__mpcExistenceChecking = threading.Thread(target=self.__mpcReadyInSlaveMode, name="Check MPC window")
self.__mpcExistenceChecking.setDaemon(True)
self.__listener = self.__Listener(self, self.__locks)
self.__listener.setDaemon(True)
self.__listener.start()
self.__locks.listenerStart.wait()
def waitForFileStateReady(f): #@NoSelf
@wraps(f)
def wrapper(self, *args, **kwds):
if(not self.__locks.fileReady.wait(constants.MPC_LOCK_WAIT_TIME)):
raise self.PlayerNotReadyException()
return f(self, *args, **kwds)
return wrapper
def startMpc(self, path, args=()):
args = "%s /slave %s" % (" ".join(args), str(self.__listener.hwnd))
win32api.ShellExecute(0, "open", path, args, None, 1)
if(not self.__locks.mpcStart.wait(constants.MPC_OPEN_MAX_WAIT_TIME)):
raise self.NoSlaveDetectedException("Unable to start MPC in slave mode!")
self.__mpcExistenceChecking.start()
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 askForVersion(self):
self.__listener.SendCommand(self.CMD_GETVERSION)
@waitForFileStateReady
def pause(self):
self.__listener.SendCommand(self.CMD_PAUSE)
@waitForFileStateReady
def playPause(self):
self.__listener.SendCommand(self.CMD_PLAYPAUSE)
@waitForFileStateReady
def unpause(self):
self.__listener.SendCommand(self.CMD_PLAY)
@waitForFileStateReady
def askForCurrentPosition(self):
self.__listener.SendCommand(self.CMD_GETCURRENTPOSITION)
@waitForFileStateReady
def seek(self, position):
self.__listener.SendCommand(self.CMD_SETPOSITION, unicode(position))
@waitForFileStateReady
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):
_fields_ = [
('nMsgPos', ctypes.c_int32),
('nDurationMS', ctypes.c_int32),
('strMsg', ctypes.c_wchar * (len(message) + 1))
]
cmessage = __OSDDATASTRUCT()
cmessage.nMsgPos = MsgPos
cmessage.nDurationMS = DurationMs
cmessage.strMsg = message
self.__listener.SendCommand(self.CMD_OSDSHOWMESSAGE, cmessage)
def sendRawCommand(self, cmd, value):
self.__listener.SendCommand(cmd, value)
def handleCommand(self, cmd, value):
if (cmd == self.CMD_CONNECT):
self.__listener.mpcHandle = int(value)
self.__locks.mpcStart.set()
if(self.callbacks.onConnected):
thread.start_new_thread(self.callbacks.onConnected, ())
elif(cmd == self.CMD_STATE):
self.loadState = int(value)
fileNotReady = self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or self.loadState == self.__MPC_LOADSTATE.MLS_LOADING
if(fileNotReady):
self.playState = None
self.__locks.fileReady.clear()
else:
self.__locks.fileReady.set()
if(self.callbacks.onFileStateChange):
thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,))
elif(cmd == self.CMD_PLAYMODE):
self.playState = int(value)
if(self.callbacks.onUpdatePlaystate):
thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,))
elif(cmd == self.CMD_NOWPLAYING):
value = re.split(r'(?<!\\)\|', value)
self.filePath = value[3]
self.filePlaying = value[3].split('\\').pop()
self.fileDuration = float(value[4])
if(self.callbacks.onUpdatePath):
thread.start_new_thread(self.callbacks.onUpdatePath, (self.onUpdatePath,))
if(self.callbacks.onUpdateFilename):
thread.start_new_thread(self.callbacks.onUpdateFilename, (self.filePlaying,))
if(self.callbacks.onUpdateFileDuration):
thread.start_new_thread(self.callbacks.onUpdateFileDuration, (self.fileDuration,))
elif(cmd == self.CMD_CURRENTPOSITION):
self.lastFilePosition = float(value)
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
self.lastFilePosition = float(value)
if(self.callbacks.onSeek):
thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,))
elif(cmd == self.CMD_DISCONNECT):
if(self.callbacks.onMpcClosed):
thread.start_new_thread(self.callbacks.onMpcClosed, (None,))
elif(cmd == self.CMD_VERSION):
if(self.callbacks.onVersion):
self.version = value
thread.start_new_thread(self.callbacks.onVersion, (value,))
class PlayerNotReadyException(Exception):
pass
class __Callbacks:
def __init__(self):
self.onConnected = None
self.onSeek = None
self.onUpdatePath = None
self.onUpdateFilename = None
self.onUpdateFileDuration = None
self.onGetCurrentPosition = None
self.onUpdatePlaystate = None
self.onFileStateChange = None
self.onMpcClosed = None
self.onVersion = None
class __Locks:
def __init__(self):
self.listenerStart = threading.Event()
self.mpcStart = threading.Event()
self.fileReady = threading.Event()
def __mpcReadyInSlaveMode(self):
while(True):
time.sleep(10)
if not win32gui.IsWindow(self.__listener.mpcHandle):
if(self.callbacks.onMpcClosed):
self.callbacks.onMpcClosed(None)
break
CMD_CONNECT = 0x50000000
CMD_STATE = 0x50000001
CMD_PLAYMODE = 0x50000002
CMD_NOWPLAYING = 0x50000003
CMD_LISTSUBTITLETRACKS = 0x50000004
CMD_LISTAUDIOTRACKS = 0x50000005
CMD_CURRENTPOSITION = 0x50000007
CMD_NOTIFYSEEK = 0x50000008
CMD_NOTIFYENDOFSTREAM = 0x50000009
CMD_PLAYLIST = 0x50000006
CMD_OPENFILE = 0xA0000000
CMD_STOP = 0xA0000001
CMD_CLOSEFILE = 0xA0000002
CMD_PLAYPAUSE = 0xA0000003
CMD_ADDTOPLAYLIST = 0xA0001000
CMD_CLEARPLAYLIST = 0xA0001001
CMD_STARTPLAYLIST = 0xA0001002
CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO
CMD_SETPOSITION = 0xA0002000
CMD_SETAUDIODELAY = 0xA0002001
CMD_SETSUBTITLEDELAY = 0xA0002002
CMD_SETINDEXPLAYLIST = 0xA0002003 # DOESNT WORK
CMD_SETAUDIOTRACK = 0xA0002004
CMD_SETSUBTITLETRACK = 0xA0002005
CMD_GETSUBTITLETRACKS = 0xA0003000
CMD_GETCURRENTPOSITION = 0xA0003004
CMD_JUMPOFNSECONDS = 0xA0003005
CMD_GETAUDIOTRACKS = 0xA0003001
CMD_GETNOWPLAYING = 0xA0003002
CMD_GETPLAYLIST = 0xA0003003
CMD_TOGGLEFULLSCREEN = 0xA0004000
CMD_JUMPFORWARDMED = 0xA0004001
CMD_JUMPBACKWARDMED = 0xA0004002
CMD_INCREASEVOLUME = 0xA0004003
CMD_DECREASEVOLUME = 0xA0004004
CMD_SHADER_TOGGLE = 0xA0004005
CMD_CLOSEAPP = 0xA0004006
CMD_OSDSHOWMESSAGE = 0xA0005000
CMD_VERSION = 0x5000000A
CMD_DISCONNECT = 0x5000000B
CMD_PLAY = 0xA0000004
CMD_PAUSE = 0xA0000005
CMD_GETVERSION = 0xA0003006
CMD_SETSPEED = 0xA0004008
class __MPC_LOADSTATE:
MLS_CLOSED = 0
MLS_LOADING = 1
MLS_LOADED = 2
MLS_CLOSING = 3
class __MPC_PLAYSTATE:
PS_PLAY = 0
PS_PAUSE = 1
PS_STOP = 2
PS_UNUSED = 3
class __Listener(threading.Thread):
def __init__(self, mpcApi, locks):
self.__mpcApi = mpcApi
self.locks = locks
self.mpcHandle = None
self.hwnd = None
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT)
threading.Thread.__init__(self, name="MPC Listener")
def run(self):
message_map = {
win32con.WM_COPYDATA: self.OnCopyData
}
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = message_map
wc.lpszClassName = 'MPCApiListener'
hinst = wc.hInstance = win32api.GetModuleHandle(None)
classAtom = win32gui.RegisterClass(wc)
self.hwnd = win32gui.CreateWindow (
classAtom,
"ListenerGUI",
0,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None
)
self.locks.listenerStart.set()
win32gui.PumpMessages()
def OnCopyData(self, hwnd, msg, wparam, lparam):
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
#print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
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
if not win32gui.IsWindow(self.mpcHandle):
if(self.__mpcApi.callbacks.onMpcClosed):
self.__mpcApi.callbacks.onMpcClosed(None)
cs = self.__COPYDATASTRUCT()
cs.dwData = cmd;
if(isinstance(message, (unicode, str))):
message = ctypes.create_unicode_buffer(message, len(message) + 1)
elif(isinstance(message, ctypes.Structure)):
pass
else:
raise TypeError
cs.lpData = ctypes.addressof(message)
cs.cbData = ctypes.sizeof(message)
ptr = ctypes.addressof(cs)
win32api.SendMessage(self.mpcHandle, win32con.WM_COPYDATA, self.hwnd, ptr)
class __COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_void_p)
]
class MPCHCAPIPlayer(BasePlayer):
speedSupported = False
def __init__(self, client):
self.__client = client
self._mpcApi = MpcHcApi()
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing()
self._mpcApi.callbacks.onMpcClosed = lambda _: self.__client.stop(False)
self._mpcApi.callbacks.onFileStateChange = lambda _: self.__lockAsking()
self._mpcApi.callbacks.onUpdatePlaystate = lambda _: self.__unlockAsking()
self._mpcApi.callbacks.onGetCurrentPosition = lambda _: self.__onGetPosition()
self._mpcApi.callbacks.onVersion = lambda _: self.__versionUpdate.set()
self.__switchPauseCalls = False
self.__preventAsking = threading.Event()
self.__positionUpdate = threading.Event()
self.__versionUpdate = threading.Event()
self.__fileUpdate = threading.RLock()
self.__versionUpdate.clear()
def drop(self):
self.__preventAsking.set()
self.__positionUpdate.set()
self.__versionUpdate.set()
self._mpcApi.sendRawCommand(MpcHcApi.CMD_CLOSEAPP, "")
@staticmethod
def run(client, playerPath, filePath, args):
mpc = MPCHCAPIPlayer(client)
mpc._mpcApi.callbacks.onConnected = lambda: mpc.initPlayer(filePath if(filePath) else None)
mpc._mpcApi.startMpc(playerPath, args)
return mpc
def __lockAsking(self):
self.__preventAsking.clear()
def __unlockAsking(self):
self.__preventAsking.set()
def __onGetPosition(self):
self.__positionUpdate.set()
def setSpeed(self, value):
try:
self._mpcApi.setSpeed(value)
except MpcHcApi.PlayerNotReadyException:
self.setSpeed(value)
def __dropIfNotSufficientVersion(self):
self._mpcApi.askForVersion()
if(not self.__versionUpdate.wait(0.1) or not self._mpcApi.version):
self.__mpcError("MPC version not sufficient, please use `mpc-hc` >= `1.6.4`")
self.__client.stop(True)
def __testMpcReady(self):
if(not self.__preventAsking.wait(10)):
raise Exception("Player failed opening file")
def __makePing(self):
try:
self.__testMpcReady()
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__handleUpdatedFilename()
self.__client.initPlayer(self)
self.__handleUpdatedFilename()
self.askForStatus()
except Exception, err:
self.__client.ui.showErrorMessage(err.message)
self.__client.stop()
def initPlayer(self, filePath):
self.__dropIfNotSufficientVersion()
if(not self._mpcApi.version):
return
self.__mpcVersion = self._mpcApi.version.split('.')
if(self.__mpcVersion[0:3] == ['1', '6', '4']):
self.__switchPauseCalls = True
if(self.__mpcVersion[0:3] >= ['1', '6', '5']):
self.speedSupported = True
if(filePath):
self._mpcApi.openFile(filePath)
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):
if self.__switchPauseCalls:
value = not value
if value:
self._mpcApi.pause()
else:
self._mpcApi.unpause()
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
def setPosition(self, value):
self._mpcApi.seek(value)
def __getPosition(self):
self.__positionUpdate.clear()
self._mpcApi.askForCurrentPosition()
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):
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
self.__fileUpdate.release()
position = self.__getPosition()
paused = self._mpcApi.isPaused()
position = float(position)
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
self.__client.updatePlayerStatus(paused, position)
self.__fileUpdate.release()
return
self.__echoGlobalStatus()
def __echoGlobalStatus(self):
self.__client.updatePlayerStatus(self.__client.getGlobalPaused(), self.__client.getGlobalPosition())
def __forcePause(self):
for _ in xrange(constants.MPC_MAX_RETRIES):
self.setPaused(True)
time.sleep(constants.MPC_RETRY_WAIT_TIME)
def __refreshMpcPlayState(self):
for _ in xrange(2):
self._mpcApi.playPause()
time.sleep(constants.MPC_PAUSE_TOGGLE_DELAY)
def _setPausedAccordinglyToServer(self):
self.__forcePause()
self.setPaused(self.__client.getGlobalPaused())
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
self.__refreshMpcPlayState()
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):
self._setPausedAccordinglyToServer()
self._mpcApi.seek(self.__client.getGlobalPosition())
def __handleUpdatedFilename(self):
with self.__fileUpdate:
self.__setUpStateForNewlyOpenedFile()
self.__client.updateFile(self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath)
def __mpcError(self, err=""):
self.__client.ui.showErrorMessage(err)
self.__client.stop()
def sendCustomCommand(self, cmd, val):
self._mpcApi.sendRawCommand(cmd, val)
#coding:utf8
import time
import threading
import thread
import win32con, win32api, win32gui, ctypes, ctypes.wintypes #@UnresolvedImport @UnusedImport
from functools import wraps
from syncplay.players.basePlayer import BasePlayer
import re
from syncplay.utils import retry
from syncplay import constants
from syncplay.messages import getMessage
class MpcHcApi:
def __init__(self):
self.callbacks = self.__Callbacks()
self.loadState = None
self.playState = None
self.filePlaying = None
self.fileDuration = None
self.filePath = None
self.lastFilePosition = None
self.version = None
self.__playpause_warden = False
self.__locks = self.__Locks()
self.__mpcExistenceChecking = threading.Thread(target=self.__mpcReadyInSlaveMode, name="Check MPC window")
self.__mpcExistenceChecking.setDaemon(True)
self.__listener = self.__Listener(self, self.__locks)
self.__listener.setDaemon(True)
self.__listener.start()
self.__locks.listenerStart.wait()
def waitForFileStateReady(f): #@NoSelf
@wraps(f)
def wrapper(self, *args, **kwds):
if(not self.__locks.fileReady.wait(constants.MPC_LOCK_WAIT_TIME)):
raise self.PlayerNotReadyException()
return f(self, *args, **kwds)
return wrapper
def startMpc(self, path, args=()):
args = "%s /slave %s" % (" ".join(args), str(self.__listener.hwnd))
win32api.ShellExecute(0, "open", path, args, None, 1)
if(not self.__locks.mpcStart.wait(constants.MPC_OPEN_MAX_WAIT_TIME)):
raise self.NoSlaveDetectedException(getMessage("en", "mpc-slave-error"))
self.__mpcExistenceChecking.start()
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 askForVersion(self):
self.__listener.SendCommand(self.CMD_GETVERSION)
@waitForFileStateReady
def pause(self):
self.__listener.SendCommand(self.CMD_PAUSE)
@waitForFileStateReady
def playPause(self):
self.__listener.SendCommand(self.CMD_PLAYPAUSE)
@waitForFileStateReady
def unpause(self):
self.__listener.SendCommand(self.CMD_PLAY)
@waitForFileStateReady
def askForCurrentPosition(self):
self.__listener.SendCommand(self.CMD_GETCURRENTPOSITION)
@waitForFileStateReady
def seek(self, position):
self.__listener.SendCommand(self.CMD_SETPOSITION, unicode(position))
@waitForFileStateReady
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):
_fields_ = [
('nMsgPos', ctypes.c_int32),
('nDurationMS', ctypes.c_int32),
('strMsg', ctypes.c_wchar * (len(message) + 1))
]
cmessage = __OSDDATASTRUCT()
cmessage.nMsgPos = MsgPos
cmessage.nDurationMS = DurationMs
cmessage.strMsg = message
self.__listener.SendCommand(self.CMD_OSDSHOWMESSAGE, cmessage)
def sendRawCommand(self, cmd, value):
self.__listener.SendCommand(cmd, value)
def handleCommand(self, cmd, value):
if (cmd == self.CMD_CONNECT):
self.__listener.mpcHandle = int(value)
self.__locks.mpcStart.set()
if(self.callbacks.onConnected):
thread.start_new_thread(self.callbacks.onConnected, ())
elif(cmd == self.CMD_STATE):
self.loadState = int(value)
fileNotReady = self.loadState == self.__MPC_LOADSTATE.MLS_CLOSING or self.loadState == self.__MPC_LOADSTATE.MLS_LOADING
if(fileNotReady):
self.playState = None
self.__locks.fileReady.clear()
else:
self.__locks.fileReady.set()
if(self.callbacks.onFileStateChange):
thread.start_new_thread(self.callbacks.onFileStateChange, (self.loadState,))
elif(cmd == self.CMD_PLAYMODE):
self.playState = int(value)
if(self.callbacks.onUpdatePlaystate):
thread.start_new_thread(self.callbacks.onUpdatePlaystate, (self.playState,))
elif(cmd == self.CMD_NOWPLAYING):
value = re.split(r'(?<!\\)\|', value)
self.filePath = value[3]
self.filePlaying = value[3].split('\\').pop()
self.fileDuration = float(value[4])
if(self.callbacks.onUpdatePath):
thread.start_new_thread(self.callbacks.onUpdatePath, (self.onUpdatePath,))
if(self.callbacks.onUpdateFilename):
thread.start_new_thread(self.callbacks.onUpdateFilename, (self.filePlaying,))
if(self.callbacks.onUpdateFileDuration):
thread.start_new_thread(self.callbacks.onUpdateFileDuration, (self.fileDuration,))
elif(cmd == self.CMD_CURRENTPOSITION):
self.lastFilePosition = float(value)
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
self.lastFilePosition = float(value)
if(self.callbacks.onSeek):
thread.start_new_thread(self.callbacks.onSeek, (self.lastFilePosition,))
elif(cmd == self.CMD_DISCONNECT):
if(self.callbacks.onMpcClosed):
thread.start_new_thread(self.callbacks.onMpcClosed, (None,))
elif(cmd == self.CMD_VERSION):
if(self.callbacks.onVersion):
self.version = value
thread.start_new_thread(self.callbacks.onVersion, (value,))
class PlayerNotReadyException(Exception):
pass
class __Callbacks:
def __init__(self):
self.onConnected = None
self.onSeek = None
self.onUpdatePath = None
self.onUpdateFilename = None
self.onUpdateFileDuration = None
self.onGetCurrentPosition = None
self.onUpdatePlaystate = None
self.onFileStateChange = None
self.onMpcClosed = None
self.onVersion = None
class __Locks:
def __init__(self):
self.listenerStart = threading.Event()
self.mpcStart = threading.Event()
self.fileReady = threading.Event()
def __mpcReadyInSlaveMode(self):
while(True):
time.sleep(10)
if not win32gui.IsWindow(self.__listener.mpcHandle):
if(self.callbacks.onMpcClosed):
self.callbacks.onMpcClosed(None)
break
CMD_CONNECT = 0x50000000
CMD_STATE = 0x50000001
CMD_PLAYMODE = 0x50000002
CMD_NOWPLAYING = 0x50000003
CMD_LISTSUBTITLETRACKS = 0x50000004
CMD_LISTAUDIOTRACKS = 0x50000005
CMD_CURRENTPOSITION = 0x50000007
CMD_NOTIFYSEEK = 0x50000008
CMD_NOTIFYENDOFSTREAM = 0x50000009
CMD_PLAYLIST = 0x50000006
CMD_OPENFILE = 0xA0000000
CMD_STOP = 0xA0000001
CMD_CLOSEFILE = 0xA0000002
CMD_PLAYPAUSE = 0xA0000003
CMD_ADDTOPLAYLIST = 0xA0001000
CMD_CLEARPLAYLIST = 0xA0001001
CMD_STARTPLAYLIST = 0xA0001002
CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO
CMD_SETPOSITION = 0xA0002000
CMD_SETAUDIODELAY = 0xA0002001
CMD_SETSUBTITLEDELAY = 0xA0002002
CMD_SETINDEXPLAYLIST = 0xA0002003 # DOESNT WORK
CMD_SETAUDIOTRACK = 0xA0002004
CMD_SETSUBTITLETRACK = 0xA0002005
CMD_GETSUBTITLETRACKS = 0xA0003000
CMD_GETCURRENTPOSITION = 0xA0003004
CMD_JUMPOFNSECONDS = 0xA0003005
CMD_GETAUDIOTRACKS = 0xA0003001
CMD_GETNOWPLAYING = 0xA0003002
CMD_GETPLAYLIST = 0xA0003003
CMD_TOGGLEFULLSCREEN = 0xA0004000
CMD_JUMPFORWARDMED = 0xA0004001
CMD_JUMPBACKWARDMED = 0xA0004002
CMD_INCREASEVOLUME = 0xA0004003
CMD_DECREASEVOLUME = 0xA0004004
CMD_SHADER_TOGGLE = 0xA0004005
CMD_CLOSEAPP = 0xA0004006
CMD_OSDSHOWMESSAGE = 0xA0005000
CMD_VERSION = 0x5000000A
CMD_DISCONNECT = 0x5000000B
CMD_PLAY = 0xA0000004
CMD_PAUSE = 0xA0000005
CMD_GETVERSION = 0xA0003006
CMD_SETSPEED = 0xA0004008
class __MPC_LOADSTATE:
MLS_CLOSED = 0
MLS_LOADING = 1
MLS_LOADED = 2
MLS_CLOSING = 3
class __MPC_PLAYSTATE:
PS_PLAY = 0
PS_PAUSE = 1
PS_STOP = 2
PS_UNUSED = 3
class __Listener(threading.Thread):
def __init__(self, mpcApi, locks):
self.__mpcApi = mpcApi
self.locks = locks
self.mpcHandle = None
self.hwnd = None
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT)
threading.Thread.__init__(self, name="MPC Listener")
def run(self):
message_map = {
win32con.WM_COPYDATA: self.OnCopyData
}
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = message_map
wc.lpszClassName = 'MPCApiListener'
hinst = wc.hInstance = win32api.GetModuleHandle(None)
classAtom = win32gui.RegisterClass(wc)
self.hwnd = win32gui.CreateWindow (
classAtom,
"ListenerGUI",
0,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None
)
self.locks.listenerStart.set()
win32gui.PumpMessages()
def OnCopyData(self, hwnd, msg, wparam, lparam):
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
#print "API:\tin>\t 0x%X\t" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
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
if not win32gui.IsWindow(self.mpcHandle):
if(self.__mpcApi.callbacks.onMpcClosed):
self.__mpcApi.callbacks.onMpcClosed(None)
cs = self.__COPYDATASTRUCT()
cs.dwData = cmd;
if(isinstance(message, (unicode, str))):
message = ctypes.create_unicode_buffer(message, len(message) + 1)
elif(isinstance(message, ctypes.Structure)):
pass
else:
raise TypeError
cs.lpData = ctypes.addressof(message)
cs.cbData = ctypes.sizeof(message)
ptr = ctypes.addressof(cs)
win32api.SendMessage(self.mpcHandle, win32con.WM_COPYDATA, self.hwnd, ptr)
class __COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_void_p)
]
class MPCHCAPIPlayer(BasePlayer):
speedSupported = False
def __init__(self, client):
self.__client = client
self._mpcApi = MpcHcApi()
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__makePing()
self._mpcApi.callbacks.onMpcClosed = lambda _: self.__client.stop(False)
self._mpcApi.callbacks.onFileStateChange = lambda _: self.__lockAsking()
self._mpcApi.callbacks.onUpdatePlaystate = lambda _: self.__unlockAsking()
self._mpcApi.callbacks.onGetCurrentPosition = lambda _: self.__onGetPosition()
self._mpcApi.callbacks.onVersion = lambda _: self.__versionUpdate.set()
self.__switchPauseCalls = False
self.__preventAsking = threading.Event()
self.__positionUpdate = threading.Event()
self.__versionUpdate = threading.Event()
self.__fileUpdate = threading.RLock()
self.__versionUpdate.clear()
def drop(self):
self.__preventAsking.set()
self.__positionUpdate.set()
self.__versionUpdate.set()
self._mpcApi.sendRawCommand(MpcHcApi.CMD_CLOSEAPP, "")
@staticmethod
def run(client, playerPath, filePath, args):
mpc = MPCHCAPIPlayer(client)
mpc._mpcApi.callbacks.onConnected = lambda: mpc.initPlayer(filePath if(filePath) else None)
mpc._mpcApi.startMpc(playerPath, args)
return mpc
def __lockAsking(self):
self.__preventAsking.clear()
def __unlockAsking(self):
self.__preventAsking.set()
def __onGetPosition(self):
self.__positionUpdate.set()
def setSpeed(self, value):
try:
self._mpcApi.setSpeed(value)
except MpcHcApi.PlayerNotReadyException:
self.setSpeed(value)
def __dropIfNotSufficientVersion(self):
self._mpcApi.askForVersion()
if(not self.__versionUpdate.wait(0.1) or not self._mpcApi.version):
self.__mpcError(getMessage("en", "mpc-version-insufficient-error"))
self.__client.stop(True)
def __testMpcReady(self):
if(not self.__preventAsking.wait(10)):
raise Exception(getMessage("en", "player-file-open-error"))
def __makePing(self):
try:
self.__testMpcReady()
self._mpcApi.callbacks.onUpdateFilename = lambda _: self.__handleUpdatedFilename()
self.__client.initPlayer(self)
self.__handleUpdatedFilename()
self.askForStatus()
except Exception, err:
self.__client.ui.showErrorMessage(err.message)
self.__client.stop()
def initPlayer(self, filePath):
self.__dropIfNotSufficientVersion()
if(not self._mpcApi.version):
return
self.__mpcVersion = self._mpcApi.version.split('.')
if(self.__mpcVersion[0:3] == ['1', '6', '4']):
self.__switchPauseCalls = True
if(self.__mpcVersion[0:3] >= ['1', '6', '5']):
self.speedSupported = True
if(filePath):
self._mpcApi.openFile(filePath)
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):
if self.__switchPauseCalls:
value = not value
if value:
self._mpcApi.pause()
else:
self._mpcApi.unpause()
@retry(MpcHcApi.PlayerNotReadyException, constants.MPC_MAX_RETRIES, constants.MPC_RETRY_WAIT_TIME, 1)
def setPosition(self, value):
self._mpcApi.seek(value)
def __getPosition(self):
self.__positionUpdate.clear()
self._mpcApi.askForCurrentPosition()
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):
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
self.__fileUpdate.release()
position = self.__getPosition()
paused = self._mpcApi.isPaused()
position = float(position)
if(self.__preventAsking.wait(0) and self.__fileUpdate.acquire(0)):
self.__client.updatePlayerStatus(paused, position)
self.__fileUpdate.release()
return
self.__echoGlobalStatus()
def __echoGlobalStatus(self):
self.__client.updatePlayerStatus(self.__client.getGlobalPaused(), self.__client.getGlobalPosition())
def __forcePause(self):
for _ in xrange(constants.MPC_MAX_RETRIES):
self.setPaused(True)
time.sleep(constants.MPC_RETRY_WAIT_TIME)
def __refreshMpcPlayState(self):
for _ in xrange(2):
self._mpcApi.playPause()
time.sleep(constants.MPC_PAUSE_TOGGLE_DELAY)
def _setPausedAccordinglyToServer(self):
self.__forcePause()
self.setPaused(self.__client.getGlobalPaused())
if(self._mpcApi.isPaused() <> self.__client.getGlobalPaused()):
self.__refreshMpcPlayState()
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):
self._setPausedAccordinglyToServer()
self._mpcApi.seek(self.__client.getGlobalPosition())
def __handleUpdatedFilename(self):
with self.__fileUpdate:
self.__setUpStateForNewlyOpenedFile()
self.__client.updateFile(self._mpcApi.filePlaying, self._mpcApi.fileDuration, self._mpcApi.filePath)
def __mpcError(self, err=""):
self.__client.ui.showErrorMessage(err)
self.__client.stop()
def sendCustomCommand(self, cmd, val):
self._mpcApi.sendRawCommand(cmd, val)

View File

@ -1,170 +1,171 @@
import subprocess
import re
import threading
from syncplay.players.basePlayer import BasePlayer
from syncplay import constants
class MplayerPlayer(BasePlayer):
speedSupported = True
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
def __init__(self, client, playerPath, filePath, args):
self._client = client
self._paused = None
self._duration = None
self._filename = None
self._filepath = None
try:
self._listener = self.__Listener(self, playerPath, filePath, args)
except ValueError:
self._client.ui.showMessage("Syncplay using mplayer requires you to provide file when starting")
self._client.ui.showMessage("Usage example: syncplay [options] [url|path/]filename")
self._client.stop(True)
return
self._listener.setDaemon(True)
self._listener.start()
self._durationAsk = threading.Event()
self._filenameAsk = threading.Event()
self._pathAsk = threading.Event()
self._positionAsk = threading.Event()
self._pausedAsk = threading.Event()
self._preparePlayer()
def _fileUpdateClearEvents(self):
self._durationAsk.clear()
self._filenameAsk.clear()
self._pathAsk.clear()
def _fileUpdateWaitEvents(self):
self._durationAsk.wait()
self._filenameAsk.wait()
self._pathAsk.wait()
def _onFileUpdate(self):
self._fileUpdateClearEvents()
self._getFilename()
self._getLength()
self._getFilepath()
self._fileUpdateWaitEvents()
self._client.updateFile(self._filename, self._duration, self._filepath)
def _preparePlayer(self):
self.setPaused(self._client.getGlobalPaused())
self.setPosition(self._client.getGlobalPosition())
self._client.initPlayer(self)
self._onFileUpdate()
def askForStatus(self):
self._positionAsk.clear()
self._pausedAsk.clear()
self._getPaused()
self._getPosition()
self._positionAsk.wait()
self._pausedAsk.wait()
self._client.updatePlayerStatus(self._paused, self._position)
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 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 setPosition(self, value):
self._position = value
self._setProperty('time_pos', "{}".format(value))
def setPaused(self, value):
self._paused = value
self._setProperty('pause', 'yes' if value else 'no')
def _getFilename(self):
self._getProperty('filename')
def _getLength(self):
self._getProperty('length')
def _getFilepath(self):
self._getProperty('path')
def _getPaused(self):
self._getProperty('pause')
def _getPosition(self):
self._getProperty('time_pos')
def lineReceived(self, line):
match = self.RE_ANSWER.match(line)
if not match:
return
name, value = match.group(1).lower(), match.group(2)
if(name == "time_pos"):
self._position = float(value)
self._positionAsk.set()
elif(name == "pause"):
self._paused = bool(value == 'yes')
self._pausedAsk.set()
elif(name == "length"):
self._duration = float(value)
self._durationAsk.set()
elif(name == "path"):
self._filepath = value
self._pathAsk.set()
elif(name == "filename"):
self._filename = value
self._filenameAsk.set()
@staticmethod
def run(client, playerPath, filePath, args):
mplayer = MplayerPlayer(client, playerPath, filePath, args)
return mplayer
def drop(self):
self._listener.sendLine('quit')
self._durationAsk.set()
self._filenameAsk.set()
self._pathAsk.set()
self._positionAsk.set()
self._pausedAsk.set()
self._client.stop(False)
for line in self._listener.readStderrLine():
self._client.ui.showMessage(line, True, True)
class __Listener(threading.Thread):
def __init__(self, playerController, playerPath, filePath, args):
self.__playerController = playerController
if(not filePath):
raise ValueError
call = [playerPath, filePath]
call.extend(constants.MPLAYER_SLAVE_ARGS)
if(args):
call.extend(args)
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):
line = self.__process.stdout.readline()
line = line.rstrip("\r\n")
self.__playerController.lineReceived(line)
self.__playerController.drop()
def sendLine(self, line):
try:
self.__process.stdin.write(line + "\n")
except IOError:
pass
def readStderrLine(self):
for line in self.__process.stderr.readlines():
yield line
import subprocess
import re
import threading
from syncplay.players.basePlayer import BasePlayer
from syncplay import constants
from syncplay.messages import getMessage
class MplayerPlayer(BasePlayer):
speedSupported = True
RE_ANSWER = re.compile(constants.MPLAYER_ANSWER_REGEX)
def __init__(self, client, playerPath, filePath, args):
self._client = client
self._paused = None
self._duration = None
self._filename = None
self._filepath = None
try:
self._listener = self.__Listener(self, playerPath, filePath, args)
except ValueError:
self._client.ui.showMessage(getMessage("en", "mplayer-file-required-notification"))
self._client.ui.showMessage(getMessage("en", "mplayer-file-required-notification/example"))
self._client.stop(True)
return
self._listener.setDaemon(True)
self._listener.start()
self._durationAsk = threading.Event()
self._filenameAsk = threading.Event()
self._pathAsk = threading.Event()
self._positionAsk = threading.Event()
self._pausedAsk = threading.Event()
self._preparePlayer()
def _fileUpdateClearEvents(self):
self._durationAsk.clear()
self._filenameAsk.clear()
self._pathAsk.clear()
def _fileUpdateWaitEvents(self):
self._durationAsk.wait()
self._filenameAsk.wait()
self._pathAsk.wait()
def _onFileUpdate(self):
self._fileUpdateClearEvents()
self._getFilename()
self._getLength()
self._getFilepath()
self._fileUpdateWaitEvents()
self._client.updateFile(self._filename, self._duration, self._filepath)
def _preparePlayer(self):
self.setPaused(self._client.getGlobalPaused())
self.setPosition(self._client.getGlobalPosition())
self._client.initPlayer(self)
self._onFileUpdate()
def askForStatus(self):
self._positionAsk.clear()
self._pausedAsk.clear()
self._getPaused()
self._getPosition()
self._positionAsk.wait()
self._pausedAsk.wait()
self._client.updatePlayerStatus(self._paused, self._position)
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 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 setPosition(self, value):
self._position = value
self._setProperty('time_pos', "{}".format(value))
def setPaused(self, value):
self._paused = value
self._setProperty('pause', 'yes' if value else 'no')
def _getFilename(self):
self._getProperty('filename')
def _getLength(self):
self._getProperty('length')
def _getFilepath(self):
self._getProperty('path')
def _getPaused(self):
self._getProperty('pause')
def _getPosition(self):
self._getProperty('time_pos')
def lineReceived(self, line):
match = self.RE_ANSWER.match(line)
if not match:
return
name, value = match.group(1).lower(), match.group(2)
if(name == "time_pos"):
self._position = float(value)
self._positionAsk.set()
elif(name == "pause"):
self._paused = bool(value == 'yes')
self._pausedAsk.set()
elif(name == "length"):
self._duration = float(value)
self._durationAsk.set()
elif(name == "path"):
self._filepath = value
self._pathAsk.set()
elif(name == "filename"):
self._filename = value
self._filenameAsk.set()
@staticmethod
def run(client, playerPath, filePath, args):
mplayer = MplayerPlayer(client, playerPath, filePath, args)
return mplayer
def drop(self):
self._listener.sendLine('quit')
self._durationAsk.set()
self._filenameAsk.set()
self._pathAsk.set()
self._positionAsk.set()
self._pausedAsk.set()
self._client.stop(False)
for line in self._listener.readStderrLine():
self._client.ui.showMessage(line, True, True)
class __Listener(threading.Thread):
def __init__(self, playerController, playerPath, filePath, args):
self.__playerController = playerController
if(not filePath):
raise ValueError
call = [playerPath, filePath]
call.extend(constants.MPLAYER_SLAVE_ARGS)
if(args):
call.extend(args)
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):
line = self.__process.stdout.readline()
line = line.rstrip("\r\n")
self.__playerController.lineReceived(line)
self.__playerController.drop()
def sendLine(self, line):
try:
self.__process.stdin.write(line + "\n")
except IOError:
pass
def readStderrLine(self):
for line in self.__process.stderr.readlines():
yield line

View File

@ -1,395 +1,396 @@
#coding:utf8
from twisted.protocols.basic import LineReceiver
import json
import syncplay
from functools import wraps
import time
class JSONCommandProtocol(LineReceiver):
def handleMessages(self, messages):
for message in messages.iteritems():
command = message[0]
if command == "Hello":
self.handleHello(message[1])
elif command == "Set":
self.handleSet(message[1])
elif command == "List":
self.handleList(message[1])
elif command == "State":
self.handleState(message[1])
elif command == "Error":
self.handleError(message[1])
else:
self.dropWithError("Unknown Command\n" + message[1]) #TODO: log, not drop
def printReceived(self, line): #TODO: remove
#print ">>i", line
pass
def printSent(self, line):
#print "o<<", line
pass
def lineReceived(self, line):
line = line.strip()
if not line:
return
self.printReceived(line)
try:
messages = json.loads(line)
except:
self.dropWithError("Not a json encoded string\n" + line)
return
self.handleMessages(messages)
def sendMessage(self, dict_):
line = json.dumps(dict_)
self.printSent(line)
self.sendLine(line)
def drop(self):
self.transport.loseConnection()
def dropWithError(self, error):
raise NotImplementedError()
class SyncClientProtocol(JSONCommandProtocol):
def __init__(self, client):
self._client = client
self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0
self.logged = False
def connectionMade(self):
self._client.initProtocol(self)
self.sendHello()
def connectionLost(self, reason):
self._client.destroyProtocol()
def dropWithError(self, error):
self._client.ui.showErrorMessage(error)
self._client.protocolFactory.stopRetrying()
self.drop()
def _extractHelloArguments(self, hello):
username = hello["username"] if hello.has_key("username") else None
roomName = hello["room"]["name"] if hello.has_key("room") else None
version = hello["version"] if hello.has_key("version") else None
return username, roomName, version
def handleHello(self, hello):
username, roomName, version = self._extractHelloArguments(hello)
if(not username or not roomName or not version):
self.dropWithError("Not enough Hello arguments\n" + hello)
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
self.dropWithError("Mismatch between versions of client and server\n" + hello)
else:
self._client.setUsername(username)
self._client.setRoom(roomName)
self.logged = True
self._client.ui.showMessage("Successfully connected to server")
self._client.sendFile()
def sendHello(self):
hello = {}
hello["username"] = self._client.getUsername()
password = self._client.getPassword()
if(password): hello["password"] = password
room = self._client.getRoom()
if(room): hello["room"] = {"name" :room}
hello["version"] = syncplay.version
self.sendMessage({"Hello": hello})
def _SetUser(self, users):
for user in users.iteritems():
username = user[0]
settings = user[1]
room = settings["room"]["name"] if settings.has_key("room") else None
file_ = settings["file"] if settings.has_key("file") else None
if(settings.has_key("event")):
if(settings["event"].has_key("joined")):
self._client.userlist.addUser(username, room, file_)
elif(settings["event"].has_key("left")):
self._client.userlist.removeUser(username)
else:
self._client.userlist.modUser(username, room, file_)
def handleSet(self, settings):
for set_ in settings.iteritems():
command = set_[0]
if command == "room":
roomName = set_[1]["name"] if set_[1].has_key("name") else None
self._client.setRoom(roomName)
elif command == "user":
self._SetUser(set_[1])
def sendSet(self, setting):
self.sendMessage({"Set": setting})
def sendRoomSetting(self, roomName, password=None):
setting = {}
setting["name"] = roomName
if(password): setting["password"] = password
self.sendSet({"room": setting})
def sendFileSetting(self, file_):
self.sendSet({"file": file_})
self.sendList()
def handleList(self, userList):
self._client.userlist.clearList()
for room in userList.iteritems():
roomName = room[0]
for user in room[1].iteritems():
userName = user[0]
file_ = user[1]['file'] if user[1]['file'] <> {} else None
position = user[1]['position']
self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True)
self._client.userlist.showUserList()
def sendList(self):
self.sendMessage({"List": None})
def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
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
senderLatency = state["ping"]["senderLatency"] if state["ping"].has_key("senderLatency") else 0
if (state["ping"].has_key("latencyCalculation")):
latencyCalculation = state["ping"]["latencyCalculation"]
return yourLatency, senderLatency, latencyCalculation
def handleState(self, state):
position, paused, doSeek, setBy = None, None, None, None
yourLatency, senderLatency = 0, 0
if(state.has_key("ignoringOnTheFly")):
ignore = state["ignoringOnTheFly"]
if(ignore.has_key("server")):
self.serverIgnoringOnTheFly = ignore["server"]
self.clientIgnoringOnTheFly = 0
elif(ignore.has_key("client")):
if(ignore['client']) == self.clientIgnoringOnTheFly:
self.clientIgnoringOnTheFly = 0
if(state.has_key("playstate")):
position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")):
yourLatency, senderLatency, latencyCalculation = self._handleStatePing(state)
if(position is not None and paused is not None and not self.clientIgnoringOnTheFly):
latency = yourLatency + senderLatency
self._client.updateGlobalState(position, paused, doSeek, setBy, latency)
position, paused, doSeek, stateChange = self._client.getLocalState()
self.sendState(position, paused, doSeek, latencyCalculation, stateChange)
def sendState(self, position, paused, doSeek, latencyCalculation, stateChange = False):
state = {}
positionAndPausedIsSet = position is not None and paused is not None
clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0
if(clientIgnoreIsNotSet and positionAndPausedIsSet):
state["playstate"] = {}
state["playstate"]["position"] = position
state["playstate"]["paused"] = paused
if(doSeek): state["playstate"]["doSeek"] = doSeek
if(latencyCalculation):
state["ping"] = {"latencyCalculation": latencyCalculation}
if(stateChange):
self.clientIgnoringOnTheFly += 1
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"] = {}
if(self.serverIgnoringOnTheFly):
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
self.serverIgnoringOnTheFly = 0
if(self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
self.sendMessage({"State": state})
def handleError(self, error):
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
def sendError(self, message):
self.sendMessage({"Error": {"message": message}})
class SyncServerProtocol(JSONCommandProtocol):
def __init__(self, factory):
self._factory = factory
self._logged = False
self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0
def __hash__(self):
return hash('|'.join((
self.transport.getPeer().host,
str(id(self)),
)))
def requireLogged(f): #@NoSelf
@wraps(f)
def wrapper(self, *args, **kwds):
if(not self._logged):
self.dropWithError("You must be known to server before sending this command")
return f(self, *args, **kwds)
return wrapper
def dropWithError(self, error):
print "Client drop: %s -- %s" % (self.transport.getPeer().host, error)
self.sendError(error)
self.drop()
def connectionLost(self, reason):
self._factory.removeWatcher(self)
def _extractHelloArguments(self, hello):
roomName, roomPassword = None, None
username = hello["username"] if hello.has_key("username") else None
username = username.strip()
serverPassword = hello["password"] if hello.has_key("password") else None
room = hello["room"] if hello.has_key("room") else None
if(room):
roomName = room["name"] if room.has_key("name") else None
roomName = roomName.strip()
roomPassword = room["password"] if room.has_key("password") else None
version = hello["version"] if hello.has_key("version") else None
return username, serverPassword, roomName, roomPassword, version
def _checkPassword(self, serverPassword):
if(self._factory.password):
if(not serverPassword):
self.dropWithError("Password required")
return False
if(serverPassword != self._factory.password):
self.dropWithError("Wrong password supplied")
return False
return True
def handleHello(self, hello):
username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello)
if(not username or not roomName or not version):
self.dropWithError("Not enough Hello arguments")
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
self.dropWithError("Mismatch between versions of client and server")
else:
if(not self._checkPassword(serverPassword)):
return
self._factory.addWatcher(self, username, roomName, roomPassword)
self._logged = True
self.sendHello()
def sendHello(self):
hello = {}
hello["username"] = self._factory.watcherGetUsername(self)
room = self._factory.watcherGetRoom(self)
if(room): hello["room"] = {"name": room}
hello["version"] = syncplay.version
self.sendMessage({"Hello": hello})
@requireLogged
def handleSet(self, settings):
for set_ in settings.iteritems():
command = set_[0]
if command == "room":
roomName = set_[1]["name"] if set_[1].has_key("name") else None
self._factory.watcherSetRoom(self, roomName)
elif command == "file":
self._factory.watcherSetFile(self, set_[1])
def sendSet(self, setting):
self.sendMessage({"Set": setting})
def sendRoomSetting(self, roomName):
self.sendSet({"room": {"name": roomName}})
def sendUserSetting(self, username, roomName, file_, event):
room = {"name": roomName}
user = {}
user[username] = {}
user[username]["room"] = room
if(file_):
user[username]["file"] = file_
if(event):
user[username]["event"] = event
self.sendSet({"user": user})
def _addUserOnList(self, userlist, roomPositions, watcher):
if (not userlist.has_key(watcher.room)):
userlist[watcher.room] = {}
roomPositions[watcher.room] = watcher.getRoomPosition()
userlist[watcher.room][watcher.name] = {
"file": watcher.file if watcher.file else {},
"position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0
}
def sendList(self):
userlist = {}
roomPositions = {}
watchers = self._factory.getAllWatchers(self)
for watcher in watchers.itervalues():
self._addUserOnList(userlist, roomPositions, watcher)
self.sendMessage({"List": userlist})
@requireLogged
def handleList(self, _):
self.sendList()
def sendState(self, position, paused, doSeek, setBy, senderLatency, watcherLatency, forced = False):
playstate = {
"position": position,
"paused": paused,
"doSeek": doSeek,
"setBy": setBy
}
ping = {
"yourLatency": watcherLatency,
"senderLatency": senderLatency,
"latencyCalculation": time.time()
}
state = {
"ping": ping,
"playstate": playstate,
}
if(forced):
self.serverIgnoringOnTheFly += 1
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"] = {}
if(self.serverIgnoringOnTheFly):
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
if(self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
self.clientIgnoringOnTheFly = 0
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
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
return position, paused, doSeek
@requireLogged
def handleState(self, state):
position, paused, doSeek, latencyCalculation = None, None, None, None
if(state.has_key("ignoringOnTheFly")):
ignore = state["ignoringOnTheFly"]
if(ignore.has_key("server")):
if(self.serverIgnoringOnTheFly == ignore["server"]):
self.serverIgnoringOnTheFly = 0
if(ignore.has_key("client")):
self.clientIgnoringOnTheFly = ignore["client"]
if(state.has_key("playstate")):
position, paused, doSeek = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")):
latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else None
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 sendError(self, message):
self.sendMessage({"Error": {"message": message}})
#coding:utf8
from twisted.protocols.basic import LineReceiver
import json
import syncplay
from functools import wraps
import time
from syncplay.messages import getMessage
class JSONCommandProtocol(LineReceiver):
def handleMessages(self, messages):
for message in messages.iteritems():
command = message[0]
if command == "Hello":
self.handleHello(message[1])
elif command == "Set":
self.handleSet(message[1])
elif command == "List":
self.handleList(message[1])
elif command == "State":
self.handleState(message[1])
elif command == "Error":
self.handleError(message[1])
else:
self.dropWithError("Unknown Command\n" + message[1]) #TODO: log, not drop
def printReceived(self, line): #TODO: remove
#print ">>i", line
pass
def printSent(self, line):
#print "o<<", line
pass
def lineReceived(self, line):
line = line.strip()
if not line:
return
self.printReceived(line)
try:
messages = json.loads(line)
except:
self.dropWithError(getMessage("en", "not-json-error") + line)
return
self.handleMessages(messages)
def sendMessage(self, dict_):
line = json.dumps(dict_)
self.printSent(line)
self.sendLine(line)
def drop(self):
self.transport.loseConnection()
def dropWithError(self, error):
raise NotImplementedError()
class SyncClientProtocol(JSONCommandProtocol):
def __init__(self, client):
self._client = client
self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0
self.logged = False
def connectionMade(self):
self._client.initProtocol(self)
self.sendHello()
def connectionLost(self, reason):
self._client.destroyProtocol()
def dropWithError(self, error):
self._client.ui.showErrorMessage(error)
self._client.protocolFactory.stopRetrying()
self.drop()
def _extractHelloArguments(self, hello):
username = hello["username"] if hello.has_key("username") else None
roomName = hello["room"]["name"] if hello.has_key("room") else None
version = hello["version"] if hello.has_key("version") else None
return username, roomName, version
def handleHello(self, hello):
username, roomName, version = self._extractHelloArguments(hello)
if(not username or not roomName or not version):
self.dropWithError(getMessage("en", "hello-arguments-error") + hello)
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
self.dropWithError(getMessage("en", "version-mismatch-error") + hello)
else:
self._client.setUsername(username)
self._client.setRoom(roomName)
self.logged = True
self._client.ui.showMessage(getMessage("en", "connected-successful-notification"))
self._client.sendFile()
def sendHello(self):
hello = {}
hello["username"] = self._client.getUsername()
password = self._client.getPassword()
if(password): hello["password"] = password
room = self._client.getRoom()
if(room): hello["room"] = {"name" :room}
hello["version"] = syncplay.version
self.sendMessage({"Hello": hello})
def _SetUser(self, users):
for user in users.iteritems():
username = user[0]
settings = user[1]
room = settings["room"]["name"] if settings.has_key("room") else None
file_ = settings["file"] if settings.has_key("file") else None
if(settings.has_key("event")):
if(settings["event"].has_key("joined")):
self._client.userlist.addUser(username, room, file_)
elif(settings["event"].has_key("left")):
self._client.userlist.removeUser(username)
else:
self._client.userlist.modUser(username, room, file_)
def handleSet(self, settings):
for set_ in settings.iteritems():
command = set_[0]
if command == "room":
roomName = set_[1]["name"] if set_[1].has_key("name") else None
self._client.setRoom(roomName)
elif command == "user":
self._SetUser(set_[1])
def sendSet(self, setting):
self.sendMessage({"Set": setting})
def sendRoomSetting(self, roomName, password=None):
setting = {}
setting["name"] = roomName
if(password): setting["password"] = password
self.sendSet({"room": setting})
def sendFileSetting(self, file_):
self.sendSet({"file": file_})
self.sendList()
def handleList(self, userList):
self._client.userlist.clearList()
for room in userList.iteritems():
roomName = room[0]
for user in room[1].iteritems():
userName = user[0]
file_ = user[1]['file'] if user[1]['file'] <> {} else None
position = user[1]['position']
self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True)
self._client.userlist.showUserList()
def sendList(self):
self.sendMessage({"List": None})
def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
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
senderLatency = state["ping"]["senderLatency"] if state["ping"].has_key("senderLatency") else 0
if (state["ping"].has_key("latencyCalculation")):
latencyCalculation = state["ping"]["latencyCalculation"]
return yourLatency, senderLatency, latencyCalculation
def handleState(self, state):
position, paused, doSeek, setBy = None, None, None, None
yourLatency, senderLatency = 0, 0
if(state.has_key("ignoringOnTheFly")):
ignore = state["ignoringOnTheFly"]
if(ignore.has_key("server")):
self.serverIgnoringOnTheFly = ignore["server"]
self.clientIgnoringOnTheFly = 0
elif(ignore.has_key("client")):
if(ignore['client']) == self.clientIgnoringOnTheFly:
self.clientIgnoringOnTheFly = 0
if(state.has_key("playstate")):
position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")):
yourLatency, senderLatency, latencyCalculation = self._handleStatePing(state)
if(position is not None and paused is not None and not self.clientIgnoringOnTheFly):
latency = yourLatency + senderLatency
self._client.updateGlobalState(position, paused, doSeek, setBy, latency)
position, paused, doSeek, stateChange = self._client.getLocalState()
self.sendState(position, paused, doSeek, latencyCalculation, stateChange)
def sendState(self, position, paused, doSeek, latencyCalculation, stateChange = False):
state = {}
positionAndPausedIsSet = position is not None and paused is not None
clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0
if(clientIgnoreIsNotSet and positionAndPausedIsSet):
state["playstate"] = {}
state["playstate"]["position"] = position
state["playstate"]["paused"] = paused
if(doSeek): state["playstate"]["doSeek"] = doSeek
if(latencyCalculation):
state["ping"] = {"latencyCalculation": latencyCalculation}
if(stateChange):
self.clientIgnoringOnTheFly += 1
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"] = {}
if(self.serverIgnoringOnTheFly):
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
self.serverIgnoringOnTheFly = 0
if(self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
self.sendMessage({"State": state})
def handleError(self, error):
self.dropWithError(error["message"]) #TODO: more processing and fallbacking
def sendError(self, message):
self.sendMessage({"Error": {"message": message}})
class SyncServerProtocol(JSONCommandProtocol):
def __init__(self, factory):
self._factory = factory
self._logged = False
self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0
def __hash__(self):
return hash('|'.join((
self.transport.getPeer().host,
str(id(self)),
)))
def requireLogged(f): #@NoSelf
@wraps(f)
def wrapper(self, *args, **kwds):
if(not self._logged):
self.dropWithError(getMessage("en", "not-known-server-error"))
return f(self, *args, **kwds)
return wrapper
def dropWithError(self, error):
print getMessage("en", "client-drop-server-error") % (self.transport.getPeer().host, error)
self.sendError(error)
self.drop()
def connectionLost(self, reason):
self._factory.removeWatcher(self)
def _extractHelloArguments(self, hello):
roomName, roomPassword = None, None
username = hello["username"] if hello.has_key("username") else None
username = username.strip()
serverPassword = hello["password"] if hello.has_key("password") else None
room = hello["room"] if hello.has_key("room") else None
if(room):
roomName = room["name"] if room.has_key("name") else None
roomName = roomName.strip()
roomPassword = room["password"] if room.has_key("password") else None
version = hello["version"] if hello.has_key("version") else None
return username, serverPassword, roomName, roomPassword, version
def _checkPassword(self, serverPassword):
if(self._factory.password):
if(not serverPassword):
self.dropWithError(getMessage("en", "password-required-server-error"))
return False
if(serverPassword != self._factory.password):
self.dropWithError(getMessage("en", "wrong-password-server-error"))
return False
return True
def handleHello(self, hello):
username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello)
if(not username or not roomName or not version):
self.dropWithError(getMessage("en", "hello-server-error"))
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
self.dropWithError(getMessage("en", "version-mismatch-server-error"))
else:
if(not self._checkPassword(serverPassword)):
return
self._factory.addWatcher(self, username, roomName, roomPassword)
self._logged = True
self.sendHello()
def sendHello(self):
hello = {}
hello["username"] = self._factory.watcherGetUsername(self)
room = self._factory.watcherGetRoom(self)
if(room): hello["room"] = {"name": room}
hello["version"] = syncplay.version
self.sendMessage({"Hello": hello})
@requireLogged
def handleSet(self, settings):
for set_ in settings.iteritems():
command = set_[0]
if command == "room":
roomName = set_[1]["name"] if set_[1].has_key("name") else None
self._factory.watcherSetRoom(self, roomName)
elif command == "file":
self._factory.watcherSetFile(self, set_[1])
def sendSet(self, setting):
self.sendMessage({"Set": setting})
def sendRoomSetting(self, roomName):
self.sendSet({"room": {"name": roomName}})
def sendUserSetting(self, username, roomName, file_, event):
room = {"name": roomName}
user = {}
user[username] = {}
user[username]["room"] = room
if(file_):
user[username]["file"] = file_
if(event):
user[username]["event"] = event
self.sendSet({"user": user})
def _addUserOnList(self, userlist, roomPositions, watcher):
if (not userlist.has_key(watcher.room)):
userlist[watcher.room] = {}
roomPositions[watcher.room] = watcher.getRoomPosition()
userlist[watcher.room][watcher.name] = {
"file": watcher.file if watcher.file else {},
"position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0
}
def sendList(self):
userlist = {}
roomPositions = {}
watchers = self._factory.getAllWatchers(self)
for watcher in watchers.itervalues():
self._addUserOnList(userlist, roomPositions, watcher)
self.sendMessage({"List": userlist})
@requireLogged
def handleList(self, _):
self.sendList()
def sendState(self, position, paused, doSeek, setBy, senderLatency, watcherLatency, forced = False):
playstate = {
"position": position,
"paused": paused,
"doSeek": doSeek,
"setBy": setBy
}
ping = {
"yourLatency": watcherLatency,
"senderLatency": senderLatency,
"latencyCalculation": time.time()
}
state = {
"ping": ping,
"playstate": playstate,
}
if(forced):
self.serverIgnoringOnTheFly += 1
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"] = {}
if(self.serverIgnoringOnTheFly):
state["ignoringOnTheFly"]["server"] = self.serverIgnoringOnTheFly
if(self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
self.clientIgnoringOnTheFly = 0
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
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
doSeek = state["playstate"]["doSeek"] if state["playstate"].has_key("doSeek") else None
return position, paused, doSeek
@requireLogged
def handleState(self, state):
position, paused, doSeek, latencyCalculation = None, None, None, None
if(state.has_key("ignoringOnTheFly")):
ignore = state["ignoringOnTheFly"]
if(ignore.has_key("server")):
if(self.serverIgnoringOnTheFly == ignore["server"]):
self.serverIgnoringOnTheFly = 0
if(ignore.has_key("client")):
self.clientIgnoringOnTheFly = ignore["client"]
if(state.has_key("playstate")):
position, paused, doSeek = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")):
latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else None
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 sendError(self, message):
self.sendMessage({"Error": {"message": message}})

View File

@ -1,256 +1,257 @@
#coding:utf8
#TODO: #12, #13, #8;
import hashlib
from twisted.internet import task, reactor
from twisted.internet.protocol import Factory
import syncplay
from syncplay.protocols import SyncServerProtocol
import time
from syncplay import constants
import threading
class SyncFactory(Factory):
def __init__(self, password = ''):
print "Welcome to Syncplay server, ver. {0}".format(syncplay.version)
if(password):
password = hashlib.md5(password).hexdigest()
self.password = password
self._rooms = {}
self._roomStates = {}
self._roomUpdate = threading.RLock()
def buildProtocol(self, addr):
return SyncServerProtocol(self)
def _createRoomIfDoesntExist(self, roomName):
if (not self._rooms.has_key(roomName)):
with self._roomUpdate:
self._rooms[roomName] = {}
self._roomStates[roomName] = {
"position": 0.0,
"paused": True,
"setBy": None,
"lastUpdate": time.time()
}
def addWatcher(self, watcherProtocol, username, roomName, roomPassword):
allnames = []
for room in self._rooms.itervalues():
for watcher in room.itervalues():
allnames.append(watcher.name.lower())
while username.lower() in allnames:
username += '_'
self._createRoomIfDoesntExist(roomName)
watcher = Watcher(self, watcherProtocol, username, roomName)
with self._roomUpdate:
self._rooms[roomName][watcherProtocol] = watcher
print "{0}({2}) connected to room '{1}'".format(username, roomName, watcherProtocol.transport.getPeer().host)
reactor.callLater(0.1, watcher.scheduleSendState)
l = lambda w: w.sendUserSetting(username, roomName, None, {"joined": True})
self.broadcast(watcherProtocol, l)
def getWatcher(self, watcherProtocol):
for room in self._rooms.itervalues():
if(room.has_key(watcherProtocol)):
return room[watcherProtocol]
def getAllWatchers(self, watcherProtocol): #TODO: Optimize me
watchers = {}
for room in self._rooms.itervalues():
for watcher in room.itervalues():
watchers[watcher.watcherProtocol] = watcher
return watchers
def _removeWatcherFromTheRoom(self, watcherProtocol):
for room in self._rooms.itervalues():
with self._roomUpdate:
watcher = room.pop(watcherProtocol, None)
if(watcher):
return watcher
def _deleteRoomIfEmpty(self, room):
if (self._rooms[room] == {}):
with self._roomUpdate:
self._rooms.pop(room)
self._roomStates.pop(room)
def getRoomPausedAndPosition(self, room):
position = self._roomStates[room]["position"]
paused = self._roomStates[room]["paused"]
if (not paused):
timePassedSinceSet = time.time() - self._roomStates[room]["lastUpdate"]
position += timePassedSinceSet
return paused, position
def sendState(self, watcherProtocol, doSeek = False, senderLatency = 0, forcedUpdate = False):
watcher = self.getWatcher(watcherProtocol)
if(not watcher):
return
room = watcher.room
paused, position = self.getRoomPausedAndPosition(room)
setBy = self._roomStates[room]["setBy"]
watcher.paused = paused
watcher.position = position
watcherProtocol.sendState(position, paused, doSeek, setBy, senderLatency, watcher.latency, forcedUpdate)
if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT):
watcherProtocol.drop()
self.removeWatcher(watcherProtocol)
def __updateWatcherPing(self, latencyCalculation, watcher):
if (latencyCalculation):
ping = (time.time() - latencyCalculation) / 2
if (watcher.latency):
watcher.latency = watcher.latency * (constants.PING_MOVING_AVERAGE_WEIGHT) + ping * (1-constants.PING_MOVING_AVERAGE_WEIGHT) #Exponential moving average
else:
watcher.latency = ping
def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek):
return doSeek or pauseChanged
def __updatePausedState(self, paused, watcher):
watcher.paused = paused
if(self._roomStates[watcher.room]["paused"] <> paused):
self._roomStates[watcher.room]["setBy"] = watcher.name
self._roomStates[watcher.room]["paused"] = paused
self._roomStates[watcher.room]["lastUpdate"] = time.time()
return True
def __updatePositionState(self, position, doSeek, watcher):
watcher.position = position
if (doSeek):
self._roomStates[watcher.room]["position"] = position
self._roomStates[watcher.room]["setBy"] = watcher.name
self._roomStates[watcher.room]["lastUpdate"] = time.time()
else:
setter = min(self._rooms[watcher.room].values())
self._roomStates[watcher.room]["position"] = setter.position
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)
self.__updateWatcherPing(latencyCalculation, watcher)
watcher.lastUpdate = time.time()
if(watcher.file):
if(position is not None):
self.__updatePositionState(position, doSeek, watcher)
pauseChanged = False
if(paused is not None):
pauseChanged = self.__updatePausedState(paused, watcher)
forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek)
if(forceUpdate):
l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate)
self.broadcastRoom(watcher.watcherProtocol, l)
def removeWatcher(self, watcherProtocol):
watcher = self.getWatcher(watcherProtocol)
if(not watcher):
return
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, None, {"left": True})
self.broadcast(watcherProtocol, l)
self._removeWatcherFromTheRoom(watcherProtocol)
watcher.deactivate()
self._deleteRoomIfEmpty(watcher.room)
print "{0} left server".format(watcher.name)
def watcherGetUsername(self, watcherProtocol):
return self.getWatcher(watcherProtocol).name
def watcherGetRoom(self, watcherProtocol):
return self.getWatcher(watcherProtocol).room
def watcherSetRoom(self, watcherProtocol, room):
watcher = self._removeWatcherFromTheRoom(watcherProtocol)
if(not watcher):
return
watcher.resetStateTimer()
oldRoom = watcher.room
self._createRoomIfDoesntExist(room)
with self._roomUpdate:
self._rooms[room][watcherProtocol] = watcher
self._roomStates[room]["position"] = watcher.position
self._roomStates[room]["setBy"] = watcher.name
self._roomStates[room]["lastUpdate"] = time.time()
self._deleteRoomIfEmpty(oldRoom)
watcher.room = room
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)
watcher.file = file_
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)]
if(room):
with self._roomUpdate:
for receiver in room:
what(receiver)
def broadcast(self, sender, what):
with self._roomUpdate:
for room in self._rooms.itervalues():
for receiver in room:
what(receiver)
class SyncIsolatedFactory(SyncFactory):
def broadcast(self, sender, what):
self.broadcastRoom(sender, what)
def getAllWatchers(self, watcherProtocol):
room = self.getWatcher(watcherProtocol).room
if(self._rooms.has_key(room)):
return self._rooms[room]
else:
return {}
def watcherSetRoom(self, watcherProtocol, room):
watcher = self.getWatcher(watcherProtocol)
oldRoom = watcher.room
l = lambda w: w.sendUserSetting(watcher.name, oldRoom, None, {"left": True})
self.broadcast(watcherProtocol, l)
SyncFactory.watcherSetRoom(self, watcherProtocol, room)
class Watcher(object):
def __init__(self, factory, watcherProtocol, name, room):
self.factory = factory
self.watcherProtocol = watcherProtocol
self.name = name
self.room = room
self.file = None
self._sendStateTimer = None
self.position = None
self.latency = 0
self.lastUpdate = time.time()
def __lt__(self, b):
if(self.position is None):
return False
elif(b.position is None):
return True
else:
return self.position < b.position
def getRoomPosition(self):
_, position = self.factory.getRoomPausedAndPosition(self.room)
return position
def scheduleSendState(self):
self._sendStateTimer = task.LoopingCall(self.sendState)
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL, True)
def sendState(self):
self.factory.sendState(self.watcherProtocol)
def resetStateTimer(self):
if(self._sendStateTimer):
self._sendStateTimer.stop()
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL)
def deactivate(self):
if(self._sendStateTimer):
self._sendStateTimer.stop()
#coding:utf8
#TODO: #12, #13, #8;
import hashlib
from twisted.internet import task, reactor
from twisted.internet.protocol import Factory
import syncplay
from syncplay.protocols import SyncServerProtocol
import time
from syncplay import constants
import threading
from syncplay.messages import getMessage
class SyncFactory(Factory):
def __init__(self, password = ''):
print getMessage("en", "welcome-server-notification").format(syncplay.version)
if(password):
password = hashlib.md5(password).hexdigest()
self.password = password
self._rooms = {}
self._roomStates = {}
self._roomUpdate = threading.RLock()
def buildProtocol(self, addr):
return SyncServerProtocol(self)
def _createRoomIfDoesntExist(self, roomName):
if (not self._rooms.has_key(roomName)):
with self._roomUpdate:
self._rooms[roomName] = {}
self._roomStates[roomName] = {
"position": 0.0,
"paused": True,
"setBy": None,
"lastUpdate": time.time()
}
def addWatcher(self, watcherProtocol, username, roomName, roomPassword):
allnames = []
for room in self._rooms.itervalues():
for watcher in room.itervalues():
allnames.append(watcher.name.lower())
while username.lower() in allnames:
username += '_'
self._createRoomIfDoesntExist(roomName)
watcher = Watcher(self, watcherProtocol, username, roomName)
with self._roomUpdate:
self._rooms[roomName][watcherProtocol] = watcher
print getMessage("en", "client-connected-room-server-notification").format(username, roomName, watcherProtocol.transport.getPeer().host)
reactor.callLater(0.1, watcher.scheduleSendState)
l = lambda w: w.sendUserSetting(username, roomName, None, {"joined": True})
self.broadcast(watcherProtocol, l)
def getWatcher(self, watcherProtocol):
for room in self._rooms.itervalues():
if(room.has_key(watcherProtocol)):
return room[watcherProtocol]
def getAllWatchers(self, watcherProtocol): #TODO: Optimize me
watchers = {}
for room in self._rooms.itervalues():
for watcher in room.itervalues():
watchers[watcher.watcherProtocol] = watcher
return watchers
def _removeWatcherFromTheRoom(self, watcherProtocol):
for room in self._rooms.itervalues():
with self._roomUpdate:
watcher = room.pop(watcherProtocol, None)
if(watcher):
return watcher
def _deleteRoomIfEmpty(self, room):
if (self._rooms[room] == {}):
with self._roomUpdate:
self._rooms.pop(room)
self._roomStates.pop(room)
def getRoomPausedAndPosition(self, room):
position = self._roomStates[room]["position"]
paused = self._roomStates[room]["paused"]
if (not paused):
timePassedSinceSet = time.time() - self._roomStates[room]["lastUpdate"]
position += timePassedSinceSet
return paused, position
def sendState(self, watcherProtocol, doSeek = False, senderLatency = 0, forcedUpdate = False):
watcher = self.getWatcher(watcherProtocol)
if(not watcher):
return
room = watcher.room
paused, position = self.getRoomPausedAndPosition(room)
setBy = self._roomStates[room]["setBy"]
watcher.paused = paused
watcher.position = position
watcherProtocol.sendState(position, paused, doSeek, setBy, senderLatency, watcher.latency, forcedUpdate)
if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT):
watcherProtocol.drop()
self.removeWatcher(watcherProtocol)
def __updateWatcherPing(self, latencyCalculation, watcher):
if (latencyCalculation):
ping = (time.time() - latencyCalculation) / 2
if (watcher.latency):
watcher.latency = watcher.latency * (constants.PING_MOVING_AVERAGE_WEIGHT) + ping * (1-constants.PING_MOVING_AVERAGE_WEIGHT) #Exponential moving average
else:
watcher.latency = ping
def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek):
return doSeek or pauseChanged
def __updatePausedState(self, paused, watcher):
watcher.paused = paused
if(self._roomStates[watcher.room]["paused"] <> paused):
self._roomStates[watcher.room]["setBy"] = watcher.name
self._roomStates[watcher.room]["paused"] = paused
self._roomStates[watcher.room]["lastUpdate"] = time.time()
return True
def __updatePositionState(self, position, doSeek, watcher):
watcher.position = position
if (doSeek):
self._roomStates[watcher.room]["position"] = position
self._roomStates[watcher.room]["setBy"] = watcher.name
self._roomStates[watcher.room]["lastUpdate"] = time.time()
else:
setter = min(self._rooms[watcher.room].values())
self._roomStates[watcher.room]["position"] = setter.position
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)
self.__updateWatcherPing(latencyCalculation, watcher)
watcher.lastUpdate = time.time()
if(watcher.file):
if(position is not None):
self.__updatePositionState(position, doSeek, watcher)
pauseChanged = False
if(paused is not None):
pauseChanged = self.__updatePausedState(paused, watcher)
forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek)
if(forceUpdate):
l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate)
self.broadcastRoom(watcher.watcherProtocol, l)
def removeWatcher(self, watcherProtocol):
watcher = self.getWatcher(watcherProtocol)
if(not watcher):
return
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, None, {"left": True})
self.broadcast(watcherProtocol, l)
self._removeWatcherFromTheRoom(watcherProtocol)
watcher.deactivate()
self._deleteRoomIfEmpty(watcher.room)
print getMessage("en", "client-left-server-notification").format(watcher.name)
def watcherGetUsername(self, watcherProtocol):
return self.getWatcher(watcherProtocol).name
def watcherGetRoom(self, watcherProtocol):
return self.getWatcher(watcherProtocol).room
def watcherSetRoom(self, watcherProtocol, room):
watcher = self._removeWatcherFromTheRoom(watcherProtocol)
if(not watcher):
return
watcher.resetStateTimer()
oldRoom = watcher.room
self._createRoomIfDoesntExist(room)
with self._roomUpdate:
self._rooms[room][watcherProtocol] = watcher
self._roomStates[room]["position"] = watcher.position
self._roomStates[room]["setBy"] = watcher.name
self._roomStates[room]["lastUpdate"] = time.time()
self._deleteRoomIfEmpty(oldRoom)
watcher.room = room
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)
watcher.file = file_
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)]
if(room):
with self._roomUpdate:
for receiver in room:
what(receiver)
def broadcast(self, sender, what):
with self._roomUpdate:
for room in self._rooms.itervalues():
for receiver in room:
what(receiver)
class SyncIsolatedFactory(SyncFactory):
def broadcast(self, sender, what):
self.broadcastRoom(sender, what)
def getAllWatchers(self, watcherProtocol):
room = self.getWatcher(watcherProtocol).room
if(self._rooms.has_key(room)):
return self._rooms[room]
else:
return {}
def watcherSetRoom(self, watcherProtocol, room):
watcher = self.getWatcher(watcherProtocol)
oldRoom = watcher.room
l = lambda w: w.sendUserSetting(watcher.name, oldRoom, None, {"left": True})
self.broadcast(watcherProtocol, l)
SyncFactory.watcherSetRoom(self, watcherProtocol, room)
class Watcher(object):
def __init__(self, factory, watcherProtocol, name, room):
self.factory = factory
self.watcherProtocol = watcherProtocol
self.name = name
self.room = room
self.file = None
self._sendStateTimer = None
self.position = None
self.latency = 0
self.lastUpdate = time.time()
def __lt__(self, b):
if(self.position is None):
return False
elif(b.position is None):
return True
else:
return self.position < b.position
def getRoomPosition(self):
_, position = self.factory.getRoomPausedAndPosition(self.room)
return position
def scheduleSendState(self):
self._sendStateTimer = task.LoopingCall(self.sendState)
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL, True)
def sendState(self):
self.factory.sendState(self.watcherProtocol)
def resetStateTimer(self):
if(self._sendStateTimer):
self._sendStateTimer.stop()
self._sendStateTimer.start(constants.SERVER_STATE_INTERVAL)
def deactivate(self):
if(self._sendStateTimer):
self._sendStateTimer.stop()

View File

@ -1,235 +1,236 @@
from ConfigParser import SafeConfigParser
import argparse
import os
import sys
from syncplay import constants
try:
from syncplay.ui.GuiConfiguration import GuiConfiguration
except ImportError:
GuiConfiguration = None
class InvalidConfigValue(Exception):
def __init__(self, message):
Exception.__init__(self, message)
class ConfigurationGetter(object):
def __init__(self):
self._config = {
"host": None,
"port": constants.DEFAULT_PORT,
"name": None,
"debug": False,
"forceGuiPrompt": False,
"noGui": False,
"noStore": False,
"room": "",
"password": None,
"playerPath": None,
"file": None,
"playerArgs": [],
"playerType": None,
}
#
#Custom validation in self._validateArguments
#
self._required = [
"host",
"port",
"name",
"playerPath",
"playerType",
]
self._iniStructure = {
"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
#
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.add_argument('--no-gui', action='store_true', help='show no GUI')
self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help='server\'s address')
self._argparser.add_argument('-n', '--name', metavar='username', type=str, help='desired username')
self._argparser.add_argument('-d', '--debug', action='store_true', help='debug mode')
self._argparser.add_argument('-g', '--force-gui-prompt', action='store_true', help='make configuration prompt appear')
self._argparser.add_argument('--no-store', action='store_true', help='don\'t store values in .syncplay')
self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help='default room')
self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help='server password')
self._argparser.add_argument('--player-path', metavar='path', type=str, help='path to your player executable')
self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help='file to play')
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')
def _validateArguments(self):
for key in self._required:
if(key == "playerPath"):
if(self._isPlayerMPCAndValid(self._config["playerPath"])):
self._config["playerType"] = "mpc"
self.__addSpecialMPCFlags()
elif(self._isMplayerPathAndValid(self._config["playerPath"])):
self._config["playerType"] = "mplayer"
else:
raise InvalidConfigValue("Player path is not set properly")
elif(key == "host"):
self._config["host"], self._config["port"] = self._splitPortAndHost(self._config["host"])
hostNotValid = (self._config["host"] == "" or self._config["host"] is None)
portNotValid = (self._config["port"] == "" or self._config["port"] is None)
if(hostNotValid or portNotValid):
raise InvalidConfigValue("Hostname can't be empty")
elif(self._config[key] == "" or self._config[key] is None):
raise InvalidConfigValue("{} can't be empty".format(key))
def _overrideConfigWithArgs(self, args):
for key, val in vars(args).items():
if(val):
if(key == "force_gui_prompt"):
key = "forceGuiPrompt"
if(key == "no_store"):
key = "noStore"
if(key == "player_path"):
key = "playerPath"
if(key == "_args"):
key = "playerArgs"
if(key == "no_gui"):
key = "noGui"
self._config[key] = val
def _isPlayerMPCAndValid(self, path):
if(os.path.isfile(path)):
if(path[-10:] == 'mpc-hc.exe' or path[-12:] == 'mpc-hc64.exe'):
return True
if(os.path.isfile(path + "\\mpc-hc.exe")):
path += "\\mpc-hc.exe"
return True
if(os.path.isfile(path + "\\mpc-hc64.exe")):
path += "\\mpc-hc64.exe"
return True
return False
def __addSpecialMPCFlags(self):
self._config['playerArgs'].extend(['/open', '/new'])
def _isMplayerPathAndValid(self, playerPath):
if("mplayer" in playerPath):
if os.access(playerPath, os.X_OK):
return True
for path in os.environ['PATH'].split(':'):
path = os.path.join(os.path.realpath(path), playerPath)
if os.access(path, os.X_OK):
self._config['playerPath'] = path
return True
return False
def _splitPortAndHost(self, host):
port = constants.DEFAULT_PORT if not self._config["port"] else self._config["port"]
if(host):
if ':' in host:
host, port = host.split(':', 1)
return host, int(port)
def _findWorkingDir(self):
frozen = getattr(sys, 'frozen', '')
if not frozen:
path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
elif frozen in ('dll', 'console_exe', 'windows_exe'):
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
else:
path = ""
return path
def _checkForPortableFile(self):
path = self._findWorkingDir()
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()
if(not configFile):
if(os.name <> 'nt'):
configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME)
else:
configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME)
return configFile
def _parseConfigFile(self, iniPath):
parser = SafeConfigParser()
parser.read(iniPath)
for section, options in self._iniStructure.items():
if(parser.has_section(section)):
for option in options:
if(parser.has_option(section, option)):
self._config[option] = parser.get(section, option)
def _checkConfig(self):
try:
self._validateArguments()
except InvalidConfigValue:
try:
for key, value in self._promptForMissingArguments().items():
self._config[key] = value
self._checkConfig()
except:
sys.exit()
def _promptForMissingArguments(self):
if(self._config['noGui']):
print "Some necessary arguments are missing, refer to --help"
sys.exit()
elif(GuiConfiguration):
return GuiConfiguration(self._config).getProcessedConfiguration()
def __wasOptionChanged(self, parser, section, option):
if (parser.has_option(section, option)):
if (parser.get(section, option) != str(self._config[option])):
return True
else:
return True
def _saveConfig(self, iniPath):
changed = False
if(self._config['noStore']):
return
parser = SafeConfigParser()
parser.read(iniPath)
for section, options in self._iniStructure.items():
if(not parser.has_section(section)):
parser.add_section(section)
changed = True
for option in options:
if(self.__wasOptionChanged(parser, section, option)):
changed = True
parser.set(section, option, str(self._config[option]))
if(changed):
parser.write(file(iniPath, "w"))
def getConfiguration(self):
iniPath = self._getConfigurationFilePath()
self._parseConfigFile(iniPath)
args = self._argparser.parse_args()
self._overrideConfigWithArgs(args)
if(self._config['forceGuiPrompt']):
try:
self._promptForMissingArguments()
except:
sys.exit()
self._checkConfig()
self._saveConfig(iniPath)
return self._config
class ServerConfigurationGetter(object):
def getConfiguration(self):
self._prepareArgParser()
self._args = self._argparser.parse_args()
if(self._args.port == None):
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',
epilog='If no options supplied _config values will be used')
self._argparser.add_argument('--port', metavar='port', type=str, nargs='?', help='server TCP port')
self._argparser.add_argument('--password', metavar='password', type=str, nargs='?', help='server password')
self._argparser.add_argument('--isolate-rooms', action='store_true', help='should rooms be isolated?')
from ConfigParser import SafeConfigParser
import argparse
import os
import sys
from syncplay import constants
from syncplay.messages import getMessage
try:
from syncplay.ui.GuiConfiguration import GuiConfiguration
except ImportError:
GuiConfiguration = None
class InvalidConfigValue(Exception):
def __init__(self, message):
Exception.__init__(self, message)
class ConfigurationGetter(object):
def __init__(self):
self._config = {
"host": None,
"port": constants.DEFAULT_PORT,
"name": None,
"debug": False,
"forceGuiPrompt": False,
"noGui": False,
"noStore": False,
"room": "",
"password": None,
"playerPath": None,
"file": None,
"playerArgs": [],
"playerType": None,
}
#
#Custom validation in self._validateArguments
#
self._required = [
"host",
"port",
"name",
"playerPath",
"playerType",
]
self._iniStructure = {
"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
#
self._argparser = argparse.ArgumentParser(description=getMessage("en", "argument-description"),
epilog='If no options supplied values from configuration file will be used')
self._argparser.add_argument('--no-gui', action='store_true', help=getMessage("en", "nogui-argument"))
self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help=getMessage("en", "host-argument"))
self._argparser.add_argument('-n', '--name', metavar='username', type=str, help=getMessage("en", "name-argument"))
self._argparser.add_argument('-d', '--debug', action='store_true', help=getMessage("en", "debug-argument"))
self._argparser.add_argument('-g', '--force-gui-prompt', action='store_true', help=getMessage("en", "force-gui-prompt-argument"))
self._argparser.add_argument('--no-store', action='store_true', help=getMessage("en", "no-store-argument"))
self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help=getMessage("en", "room-argument"))
self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("en", "password-argument"))
self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("en", "player-path-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:
if(key == "playerPath"):
if(self._isPlayerMPCAndValid(self._config["playerPath"])):
self._config["playerType"] = "mpc"
self.__addSpecialMPCFlags()
elif(self._isMplayerPathAndValid(self._config["playerPath"])):
self._config["playerType"] = "mplayer"
else:
raise InvalidConfigValue(getMessage("en", "player-path-error"))
elif(key == "host"):
self._config["host"], self._config["port"] = self._splitPortAndHost(self._config["host"])
hostNotValid = (self._config["host"] == "" or self._config["host"] is None)
portNotValid = (self._config["port"] == "" or self._config["port"] is None)
if(hostNotValid or portNotValid):
raise InvalidConfigValue(getMessage("en", "hostname-empty-error"))
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():
if(val):
if(key == "force_gui_prompt"):
key = "forceGuiPrompt"
if(key == "no_store"):
key = "noStore"
if(key == "player_path"):
key = "playerPath"
if(key == "_args"):
key = "playerArgs"
if(key == "no_gui"):
key = "noGui"
self._config[key] = val
def _isPlayerMPCAndValid(self, path):
if(os.path.isfile(path)):
if(path[-10:] == 'mpc-hc.exe' or path[-12:] == 'mpc-hc64.exe'):
return True
if(os.path.isfile(path + "\\mpc-hc.exe")):
path += "\\mpc-hc.exe"
return True
if(os.path.isfile(path + "\\mpc-hc64.exe")):
path += "\\mpc-hc64.exe"
return True
return False
def __addSpecialMPCFlags(self):
self._config['playerArgs'].extend(['/open', '/new'])
def _isMplayerPathAndValid(self, playerPath):
if("mplayer" in playerPath):
if os.access(playerPath, os.X_OK):
return True
for path in os.environ['PATH'].split(':'):
path = os.path.join(os.path.realpath(path), playerPath)
if os.access(path, os.X_OK):
self._config['playerPath'] = path
return True
return False
def _splitPortAndHost(self, host):
port = constants.DEFAULT_PORT if not self._config["port"] else self._config["port"]
if(host):
if ':' in host:
host, port = host.split(':', 1)
return host, int(port)
def _findWorkingDir(self):
frozen = getattr(sys, 'frozen', '')
if not frozen:
path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
elif frozen in ('dll', 'console_exe', 'windows_exe'):
path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
else:
path = ""
return path
def _checkForPortableFile(self):
path = self._findWorkingDir()
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()
if(not configFile):
if(os.name <> 'nt'):
configFile = os.path.join(os.getenv('HOME', '.'), constants.DEFAULT_CONFIG_NAME)
else:
configFile = os.path.join(os.getenv('APPDATA', '.'), constants.DEFAULT_CONFIG_NAME)
return configFile
def _parseConfigFile(self, iniPath):
parser = SafeConfigParser()
parser.read(iniPath)
for section, options in self._iniStructure.items():
if(parser.has_section(section)):
for option in options:
if(parser.has_option(section, option)):
self._config[option] = parser.get(section, option)
def _checkConfig(self):
try:
self._validateArguments()
except InvalidConfigValue:
try:
for key, value in self._promptForMissingArguments().items():
self._config[key] = value
self._checkConfig()
except:
sys.exit()
def _promptForMissingArguments(self):
if(self._config['noGui']):
print getMessage("en", "arguments-missing-error")
sys.exit()
elif(GuiConfiguration):
return GuiConfiguration(self._config).getProcessedConfiguration()
def __wasOptionChanged(self, parser, section, option):
if (parser.has_option(section, option)):
if (parser.get(section, option) != str(self._config[option])):
return True
else:
return True
def _saveConfig(self, iniPath):
changed = False
if(self._config['noStore']):
return
parser = SafeConfigParser()
parser.read(iniPath)
for section, options in self._iniStructure.items():
if(not parser.has_section(section)):
parser.add_section(section)
changed = True
for option in options:
if(self.__wasOptionChanged(parser, section, option)):
changed = True
parser.set(section, option, str(self._config[option]))
if(changed):
parser.write(file(iniPath, "w"))
def getConfiguration(self):
iniPath = self._getConfigurationFilePath()
self._parseConfigFile(iniPath)
args = self._argparser.parse_args()
self._overrideConfigWithArgs(args)
if(self._config['forceGuiPrompt']):
try:
self._promptForMissingArguments()
except:
sys.exit()
self._checkConfig()
self._saveConfig(iniPath)
return self._config
class ServerConfigurationGetter(object):
def getConfiguration(self):
self._prepareArgParser()
self._args = self._argparser.parse_args()
if(self._args.port == None):
self._args.port = constants.DEFAULT_PORT
return self._args
def _prepareArgParser(self):
self._argparser = argparse.ArgumentParser(description=getMessage("en", "server-argument-description"),
epilog=getMessage("en", "argument-epilog"))
self._argparser.add_argument('--port', metavar='port', type=str, nargs='?', help=getMessage("en", "server-port-argument"))
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"))

View File

@ -1,100 +1,101 @@
import pygtk
import os
from syncplay import constants
pygtk.require('2.0')
import gtk
gtk.set_interactive(False)
import cairo, gio, pango, atk, pangocairo, gobject #@UnusedImport
class GuiConfiguration:
def __init__(self, config):
self.config = config
self.closedAndNotSaved = False
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Syncplay Configuration")
self.window.connect("delete_event", lambda w, e: self._windowClosed())
vbox = gtk.VBox(False, 0)
self.window.add(vbox)
vbox.show()
self._addLabeledEntries(config, vbox)
self.hostEntry.select_region(0, len(self.hostEntry.get_text()))
button = gtk.Button(stock=gtk.STOCK_SAVE)
button.connect("clicked", lambda w: self._saveDataAndLeave())
vbox.pack_start(button, True, True, 0)
button.set_flags(gtk.CAN_DEFAULT)
button.grab_default()
button.show()
self.window.show()
gtk.main()
def _windowClosed(self):
self.window.destroy()
gtk.main_quit()
self.closedAndNotSaved = True
def _addLabeledEntries(self, config, vbox):
if(config['host'] == None):
host = ""
elif(":" in config['host']):
host = config['host']
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.roomEntry = self._addLabeledEntryToVbox('Default room (optional): ', config['room'], vbox, lambda __, _: self._saveDataAndLeave())
self.passEntry = self._addLabeledEntryToVbox('Server password (optional): ', config['password'], vbox, lambda __, _: self._saveDataAndLeave())
self.mpcEntry = self._addLabeledEntryToVbox('Path to player executable: ', self._tryToFillUpMpcPath(config['playerPath']), vbox, lambda __, _: self._saveDataAndLeave())
def _tryToFillUpMpcPath(self, playerPath):
if(playerPath == None):
for path in constants.MPC_PATHS:
if(os.path.isfile(path)):
return path
return playerPath
def getProcessedConfiguration(self):
if(self.closedAndNotSaved):
raise self.WindowClosed
return self.config
def _saveDataAndLeave(self):
self.config['host'] = self.hostEntry.get_text()
self.config['name'] = self.userEntry.get_text()
self.config['room'] = self.roomEntry.get_text()
self.config['password'] = self.passEntry.get_text()
self.config['playerPath'] = self.mpcEntry.get_text()
self.window.destroy()
gtk.main_quit()
def _addLabeledEntryToVbox(self, label, initialEntryValue, vbox, callback):
hbox = gtk.HBox(False, 0)
hbox.set_border_width(3)
vbox.pack_start(hbox, False, False, 0)
hbox.show()
label_ = gtk.Label()
label_.set_text(label)
label_.set_alignment(xalign=0, yalign=0.5)
hbox.pack_start(label_, False, False, 0)
label_.show()
entry = gtk.Entry()
entry.connect("activate", callback, entry)
if(initialEntryValue == None):
initialEntryValue = ""
entry.set_text(initialEntryValue)
hbox.pack_end(entry, False, False, 0)
entry.set_usize(200, -1)
entry.show()
hbox = gtk.HBox(False, 0)
vbox.add(hbox)
hbox.show()
return entry
class WindowClosed(Exception):
def __init__(self):
Exception.__init__(self)
import pygtk
import os
from syncplay import constants
from syncplay.messages import getMessage
pygtk.require('2.0')
import gtk
gtk.set_interactive(False)
import cairo, gio, pango, atk, pangocairo, gobject #@UnusedImport
class GuiConfiguration:
def __init__(self, config):
self.config = config
self.closedAndNotSaved = False
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Syncplay Configuration")
self.window.connect("delete_event", lambda w, e: self._windowClosed())
vbox = gtk.VBox(False, 0)
self.window.add(vbox)
vbox.show()
self._addLabeledEntries(config, vbox)
self.hostEntry.select_region(0, len(self.hostEntry.get_text()))
button = gtk.Button(stock=gtk.STOCK_SAVE)
button.connect("clicked", lambda w: self._saveDataAndLeave())
vbox.pack_start(button, True, True, 0)
button.set_flags(gtk.CAN_DEFAULT)
button.grab_default()
button.show()
self.window.show()
gtk.main()
def _windowClosed(self):
self.window.destroy()
gtk.main_quit()
self.closedAndNotSaved = True
def _addLabeledEntries(self, config, vbox):
if(config['host'] == None):
host = ""
elif(":" in config['host']):
host = config['host']
else:
host = config['host']+":"+str(config['port'])
self.hostEntry = self._addLabeledEntryToVbox(getMessage("en", "host-label"), host, vbox, lambda __, _: self._saveDataAndLeave())
self.userEntry = self._addLabeledEntryToVbox(getMessage("en", "username-label"), config['name'], vbox, lambda __, _: self._saveDataAndLeave())
self.roomEntry = self._addLabeledEntryToVbox(getMessage("en", "room-label"), config['room'], 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):
for path in constants.MPC_PATHS:
if(os.path.isfile(path)):
return path
return playerPath
def getProcessedConfiguration(self):
if(self.closedAndNotSaved):
raise self.WindowClosed
return self.config
def _saveDataAndLeave(self):
self.config['host'] = self.hostEntry.get_text()
self.config['name'] = self.userEntry.get_text()
self.config['room'] = self.roomEntry.get_text()
self.config['password'] = self.passEntry.get_text()
self.config['playerPath'] = self.mpcEntry.get_text()
self.window.destroy()
gtk.main_quit()
def _addLabeledEntryToVbox(self, label, initialEntryValue, vbox, callback):
hbox = gtk.HBox(False, 0)
hbox.set_border_width(3)
vbox.pack_start(hbox, False, False, 0)
hbox.show()
label_ = gtk.Label()
label_.set_text(label)
label_.set_alignment(xalign=0, yalign=0.5)
hbox.pack_start(label_, False, False, 0)
label_.show()
entry = gtk.Entry()
entry.connect("activate", callback, entry)
if(initialEntryValue == None):
initialEntryValue = ""
entry.set_text(initialEntryValue)
hbox.pack_end(entry, False, False, 0)
entry.set_usize(200, -1)
entry.show()
hbox = gtk.HBox(False, 0)
vbox.add(hbox)
hbox.show()
return entry
class WindowClosed(Exception):
def __init__(self):
Exception.__init__(self)

View File

@ -1,122 +1,123 @@
from __future__ import print_function
import threading
import time
import syncplay
import os
import re
from syncplay import utils
from syncplay import constants
class ConsoleUI(threading.Thread):
def __init__(self):
self.promptMode = threading.Event()
self.PromptResult = ""
self.promptMode.set()
self._syncplayClient = None
threading.Thread.__init__(self, name="ConsoleUI")
def addClient(self, client):
self._syncplayClient = client
def run(self):
while True:
data = raw_input()
data = data.rstrip('\n\r')
if(not self.promptMode.isSet()):
self.PromptResult = data
self.promptMode.set()
elif(self._syncplayClient):
self._executeCommand(data)
def promptFor(self, prompt=">", message=""):
if message <> "":
print(message)
self.promptMode.clear()
print(prompt, end='')
self.promptMode.wait()
return self.PromptResult
def showMessage(self, message, noTimestamp=False):
if(os.name == "nt"):
message = message.encode('ascii', 'replace')
if(noTimestamp):
print(message)
else:
print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message)
def showDebugMessage(self, message):
print(message)
def showErrorMessage(self, message):
print("ERROR:\t" + message)
def _extractSign(self, m):
if(m):
if(m == "-"):
return -1
else:
return 1
else:
return None
def _tryAdvancedCommands(self, data):
o = re.match(constants.UI_OFFSET_REGEX, data)
s = re.match(constants.UI_SEEK_REGEX, data)
if(o):
sign = self._extractSign(o.group('sign'))
t = utils.parseTime(o.group('time'))
if(t is None):
return
if (o.group('sign') == "/"):
t = self._syncplayClient.getPlayerPosition() - t
elif(sign):
t = self._syncplayClient.getUserOffset() + sign * t
self._syncplayClient.setUserOffset(t)
return True
elif s:
sign = self._extractSign(s.group('sign'))
t = utils.parseTime(s.group('time'))
if(t is None):
return
if(sign):
t = self._syncplayClient.getGlobalPosition() + sign * t
self._syncplayClient.setPosition(t)
return True
return False
def _executeCommand(self, data):
command = re.match(constants.UI_COMMAND_REGEX, data)
if(not command):
return
if(command.group('command') in constants.COMMANDS_UNDO):
tmp_pos = self._syncplayClient.getPlayerPosition()
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
elif (command.group('command') in constants.COMMANDS_LIST):
self._syncplayClient.getUserList()
elif (command.group('command') in constants.COMMANDS_PAUSE):
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
elif (command.group('command') in constants.COMMANDS_ROOM):
room = command.group('parameter')
if room == None:
if self._syncplayClient.userlist.currentUser.file:
room = self._syncplayClient.userlist.currentUser.file["name"]
else:
room = self._syncplayClient.defaultRoom
self._syncplayClient.setRoom(room)
self._syncplayClient.sendRoom()
else:
if(self._tryAdvancedCommands(data)):
return
if (command.group('command') not in constants.COMMANDS_HELP):
self.showMessage("Unrecognized command")
self.showMessage("Available commands:", True)
self.showMessage("\tr [name] - change room", True)
self.showMessage("\tl - show user list", True)
self.showMessage("\tu - undo last seek", True)
self.showMessage("\tp - toggle pause", 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("\th - this help", True)
self.showMessage("Syncplay version: {}".format(syncplay.version), True)
self.showMessage("More info available at: {}".format(syncplay.projectURL), True)
from __future__ import print_function
import threading
import time
import syncplay
import os
import re
from syncplay import utils
from syncplay import constants
from syncplay.messages import getMessage
class ConsoleUI(threading.Thread):
def __init__(self):
self.promptMode = threading.Event()
self.PromptResult = ""
self.promptMode.set()
self._syncplayClient = None
threading.Thread.__init__(self, name="ConsoleUI")
def addClient(self, client):
self._syncplayClient = client
def run(self):
while True:
data = raw_input()
data = data.rstrip('\n\r')
if(not self.promptMode.isSet()):
self.PromptResult = data
self.promptMode.set()
elif(self._syncplayClient):
self._executeCommand(data)
def promptFor(self, prompt=">", message=""):
if message <> "":
print(message)
self.promptMode.clear()
print(prompt, end='')
self.promptMode.wait()
return self.PromptResult
def showMessage(self, message, noTimestamp=False):
if(os.name == "nt"):
message = message.encode('ascii', 'replace')
if(noTimestamp):
print(message)
else:
print(time.strftime(constants.UI_TIME_FORMAT, time.localtime()) + message)
def showDebugMessage(self, message):
print(message)
def showErrorMessage(self, message):
print("ERROR:\t" + message)
def _extractSign(self, m):
if(m):
if(m == "-"):
return -1
else:
return 1
else:
return None
def _tryAdvancedCommands(self, data):
o = re.match(constants.UI_OFFSET_REGEX, data)
s = re.match(constants.UI_SEEK_REGEX, data)
if(o):
sign = self._extractSign(o.group('sign'))
t = utils.parseTime(o.group('time'))
if(t is None):
return
if (o.group('sign') == "/"):
t = self._syncplayClient.getPlayerPosition() - t
elif(sign):
t = self._syncplayClient.getUserOffset() + sign * t
self._syncplayClient.setUserOffset(t)
return True
elif s:
sign = self._extractSign(s.group('sign'))
t = utils.parseTime(s.group('time'))
if(t is None):
return
if(sign):
t = self._syncplayClient.getGlobalPosition() + sign * t
self._syncplayClient.setPosition(t)
return True
return False
def _executeCommand(self, data):
command = re.match(constants.UI_COMMAND_REGEX, data)
if(not command):
return
if(command.group('command') in constants.COMMANDS_UNDO):
tmp_pos = self._syncplayClient.getPlayerPosition()
self._syncplayClient.setPosition(self._syncplayClient.playerPositionBeforeLastSeek)
self._syncplayClient.playerPositionBeforeLastSeek = tmp_pos
elif (command.group('command') in constants.COMMANDS_LIST):
self._syncplayClient.getUserList()
elif (command.group('command') in constants.COMMANDS_PAUSE):
self._syncplayClient.setPaused(not self._syncplayClient.getPlayerPaused())
elif (command.group('command') in constants.COMMANDS_ROOM):
room = command.group('parameter')
if room == None:
if self._syncplayClient.userlist.currentUser.file:
room = self._syncplayClient.userlist.currentUser.file["name"]
else:
room = self._syncplayClient.defaultRoom
self._syncplayClient.setRoom(room)
self._syncplayClient.sendRoom()
else:
if(self._tryAdvancedCommands(data)):
return
if (command.group('command') not in constants.COMMANDS_HELP):
self.showMessage(getMessage("en", "unrecognized-command-notification"))
self.showMessage(getMessage("en", "commandlist-notification"), True)
self.showMessage(getMessage("en", "commandlist-notification/room"), True)
self.showMessage(getMessage("en", "commandlist-notification/list"), True)
self.showMessage(getMessage("en", "commandlist-notification/undo"), True)
self.showMessage(getMessage("en", "commandlist-notification/pause"), True)
self.showMessage(getMessage("en", "commandlist-notification/seek"), True)
self.showMessage(getMessage("en", "commandlist-notification/help"), True)
self.showMessage("Syncplay version: {}".format(syncplay.version), True)
self.showMessage("More info available at: {}".format(syncplay.projectURL), True)

View File

@ -1,76 +1,77 @@
import time
import re
import datetime
from syncplay import constants
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
:param ExceptionToCheck: the exception to check. may be a tuple of
excpetions to check
:type ExceptionToCheck: Exception or tuple
:param tries: number of times to try (not retry) before giving up
:type tries: int
:param delay: initial delay between retries in seconds
:type delay: int
:param backoff: backoff multiplier e.g. value of 2 will double the delay
each retry
:type backoff: int
:param logger: logger to use. If None, print
:type logger: logging.Logger instance
"""
def deco_retry(f):
def f_retry(*args, **kwargs):
mtries, mdelay = tries, delay
try_one_last_time = True
while mtries > 1:
try:
return f(*args, **kwargs)
try_one_last_time = False
break
except ExceptionToCheck, e:
if logger:
msg = "%s, Retrying in %d seconds..." % (str(e), mdelay)
logger.warning(msg)
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
if try_one_last_time:
return f(*args, **kwargs)
return
return f_retry # true decorator
return deco_retry
def parseTime(timeStr):
regex = re.compile(constants.PARSE_TIME_REGEX)
parts = regex.match(timeStr)
if not parts:
return
parts = parts.groupdict()
time_params = {}
for (name, param) in parts.iteritems():
if param:
if(name == "miliseconds"):
time_params["microseconds"] = int(param) * 1000
else:
time_params[name] = int(param)
return datetime.timedelta(**time_params).total_seconds()
def formatTime(timeInSeconds):
timeInSeconds = round(timeInSeconds)
weeks = timeInSeconds // 604800
days = (timeInSeconds % 604800) // 86400
hours = (timeInSeconds % 86400) // 3600
minutes = (timeInSeconds % 3600) // 60
seconds = timeInSeconds % 60
if(weeks > 0):
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
elif(days > 0):
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
elif(hours > 0):
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
else:
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)
import time
import re
import datetime
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.
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
:type ExceptionToCheck: Exception or tuple
:param tries: number of times to try (not retry) before giving up
:type tries: int
:param delay: initial delay between retries in seconds
:type delay: int
:param backoff: backoff multiplier e.g. value of 2 will double the delay
each retry
:type backoff: int
:param logger: logger to use. If None, print
:type logger: logging.Logger instance
"""
def deco_retry(f):
def f_retry(*args, **kwargs):
mtries, mdelay = tries, delay
try_one_last_time = True
while mtries > 1:
try:
return f(*args, **kwargs)
try_one_last_time = False
break
except ExceptionToCheck, e:
if logger:
msg = getMessage("en", "retrying-notification") % (str(e), mdelay)
logger.warning(msg)
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
if try_one_last_time:
return f(*args, **kwargs)
return
return f_retry # true decorator
return deco_retry
def parseTime(timeStr):
regex = re.compile(constants.PARSE_TIME_REGEX)
parts = regex.match(timeStr)
if not parts:
return
parts = parts.groupdict()
time_params = {}
for (name, param) in parts.iteritems():
if param:
if(name == "miliseconds"):
time_params["microseconds"] = int(param) * 1000
else:
time_params[name] = int(param)
return datetime.timedelta(**time_params).total_seconds()
def formatTime(timeInSeconds):
timeInSeconds = round(timeInSeconds)
weeks = timeInSeconds // 604800
days = (timeInSeconds % 604800) // 86400
hours = (timeInSeconds % 86400) // 3600
minutes = (timeInSeconds % 3600) // 60
seconds = timeInSeconds % 60
if(weeks > 0):
return '{0:.0f}w, {1:.0f}d, {2:02.0f}:{3:02.0f}:{4:02.0f}'.format(weeks, days, hours, minutes, seconds)
elif(days > 0):
return '{0:.0f}d, {1:02.0f}:{2:02.0f}:{3:02.0f}'.format(days, hours, minutes, seconds)
elif(hours > 0):
return '{0:02.0f}:{1:02.0f}:{2:02.0f}'.format(hours, minutes, seconds)
else:
return '{0:02.0f}:{1:02.0f}'.format(minutes, seconds)