Merge 35f3c3e6b3a827df86c8ce611265a4985b99b992 into d4bd954ad1d0371b21685217f1f10a52c92dffb1

This commit is contained in:
Etoh 2013-01-01 11:05:34 -08:00
commit 34c5de941e
10 changed files with 1920 additions and 1835 deletions

View File

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

View File

@ -7,6 +7,8 @@ en = {
"reconnection-attempt-notification" : "Connection with server lost, attempting to reconnect", "reconnection-attempt-notification" : "Connection with server lost, attempting to reconnect",
"disconnection-notification" : "Disconnected from server", "disconnection-notification" : "Disconnected from server",
"connection-failed-notification" : "Connection with server failed", "connection-failed-notification" : "Connection with server failed",
"connected-successful-notification" : "Successfully connected to server",
"retrying-notification" : "%s, Retrying in %d seconds...", #Seconds
"rewind-notification" : "Rewinded due to time difference with <{}>", #User "rewind-notification" : "Rewinded due to time difference with <{}>", #User
"slowdown-notification" : "Slowing down due to time difference with <{}>", #User "slowdown-notification" : "Slowing down due to time difference with <{}>", #User
@ -31,11 +33,85 @@ en = {
"notplaying-notification" : "People who are not playing any file:", "notplaying-notification" : "People who are not playing any file:",
"userlist-room-notification" : "In room '{}':", #Room "userlist-room-notification" : "In room '{}':", #Room
"mplayer-file-required-notification" : "Syncplay using mplayer requires you to provide file when starting",
"mplayer-file-required-notification/example" : "Usage example: syncplay [options] [url|path/]filename",
"unrecognized-command-notification" : "Unrecognized command",
"commandlist-notification" : "Available commands:",
"commandlist-notification/room" : "\tr [name] - change room",
"commandlist-notification/list" : "\tl - show user list",
"commandlist-notification/undo" : "\tu - undo last seek",
"commandlist-notification/pause" : "\tp - toggle pause",
"commandlist-notification/seek" : "\t[s][+-]time - seek to the given value of time, if + or - is not specified it's absolute time in seconds or min:sec",
"commandlist-notification/help" : "\th - this help",
"syncplay-version-notification" : "Syncplay version: {}", #syncplay.version
"more-info-notification" : "More info available at: {}", #projectURL
# Client prompts # Client prompts
"enter-to-exit-prompt" : "Press enter to exit\n", "enter-to-exit-prompt" : "Press enter to exit\n",
# Client errors # Client errors
"server-timeout-error" : "Connection with server timed out" "server-timeout-error" : "Connection with server timed out",
"mpc-slave-error" : "Unable to start MPC in slave mode!",
"mpc-version-insufficient-error" : "MPC version not sufficient, please use `mpc-hc` >= `1.6.4`",
"player-file-open-error" : "Player failed opening file",
"player-path-error" : "Player path is not set properly",
"hostname-empty-error" : "Hostname can't be empty",
"empty-error" : "{} can't be empty", #Configuration
"arguments-missing-error" : "Some necessary arguments are missing, refer to --help",
"unable-to-start-client-error" : "Unable to start client",
"not-json-error" : "Not a json encoded string\n",
"hello-arguments-error" : "Not enough Hello arguments\n",
"version-mismatch-error" : "Mismatch between versions of client and server\n",
# Client arguments
"argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
"argument-epilog" : 'If no options supplied _config values will be used',
"nogui-argument" : 'show no GUI',
"host-argument" : 'server\'s address',
"name-argument" : 'desired username',
"debug-argument" : 'debug mode',
"force-gui-prompt-argument" : 'make configuration prompt appear',
"no-store-argument" : 'don\'t store values in .syncplay',
"room-argument" : 'default room',
"password-argument" : 'server password',
"player-path-argument" : 'path to your player executable',
"file-argument" : 'file to play',
"args-argument" : 'player options, if you need to pass options starting with - prepend them with single \'--\' argument',
# Client labels
"host-label" : 'Host: ',
"username-label" : 'Username: ',
"room-label" : 'Default room (optional): ',
"password-label" : 'Server password (optional): ',
"path-label" : 'Path to player executable: ',
# Server notifications
"welcome-server-notification" : "Welcome to Syncplay server, ver. {0}", #version
"client-connected-room-server-notification" : "{0}({2}) connected to room '{1}'", #username, host, room
"client-left-server-notification" : "{0} left server", #name
#Server arguments
"server-argument-description" : 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network. Server instance',
"server-argument-epilog" : 'If no options supplied _config values will be used',
"server-port-argument" : 'server TCP port',
"server-password-argument" : 'server password',
"server-isolate-room-argument" : 'should rooms be isolated?',
#Server errors
"not-known-server-error" : "You must be known to server before sending this command",
"client-drop-server-error" : "Client drop: %s -- %s", #host, error
"password-required-server-error" : "Password required",
"wrong-password-server-error" : "Wrong password supplied",
"hello-server-error" : "Not enough Hello arguments",
"version-mismatch-server-error" : "Mismatch between versions of client and server",
"wrong-password-server-error" : "Wrong password supplied"
} }
pl = { pl = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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