Completly new setup

This commit is contained in:
Uriziel 2012-07-11 10:41:23 +02:00
parent 321fb5a3a5
commit aba2117344
8 changed files with 126 additions and 113 deletions

View File

@ -4,10 +4,10 @@ from setuptools import find_packages
common_info = dict(
name = 'SyncPlay',
version = '0.7',
version = '1.9',
author = 'Tomasz Kowalczyk, Uriziel',
author_email = 'code@fluxid.pl, urizieli@gmail.com',
description = 'Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
description = 'Syncplay',
packages = find_packages(exclude=['venv']),
install_requires = ['Twisted>=11.1'],
)

View File

@ -45,7 +45,7 @@ class SyncClientProtocol(CommandProtocol):
print message
self._syncplayClient.name = args[0]
self._syncplayClient.protocol.sender.send_list()
self._syncplayClient.schedule_send_status()
self._syncplayClient.scheduleSendStatus()
@argumentCount(2, 3)
def present(self, args):
@ -71,7 +71,7 @@ class SyncClientProtocol(CommandProtocol):
counter, ctime, paused, position, name = args
self._syncplayClient.update_global_state(counter, ctime, paused, position, name)
self._syncplayClient.updateGlobalState(counter, ctime, paused, position, name)
@argumentCount(3)
def seek(self, args):
@ -152,7 +152,7 @@ class SyncClientProtocol(CommandProtocol):
def send_state(self, counter, ctime, paused, position):
self._protocol.sendMessage('state', counter, int(ctime*1000), ('paused' if paused else 'playing'), int(position*1000))
def send_seek(self, counter, ctime, position):
def sendSeek(self, counter, ctime, position):
self._protocol.sendMessage('seek', counter, int(ctime*1000), int(position*1000))
def send_room(self, where):
@ -246,7 +246,7 @@ class SyncplayClientManager(object):
reactor.callLater(0.1, reactor.stop)
def get_player_position(self):
def getPlayerPosition(self):
if not self.last_player_update:
return 0.0
position = self.player_position
@ -254,7 +254,7 @@ class SyncplayClientManager(object):
position += time.time() - self.last_player_update
return position
def get_global_position(self):
def getGlobalPosition(self):
if not self.last_global_update:
return 0.0
position = self.global_position
@ -263,12 +263,12 @@ class SyncplayClientManager(object):
return position
def init_player(self, player):
def initPlayer(self, player):
self.player = player
if self.last_global_update:
self.player.set_position(self.get_global_position())
self.player.set_position(self.getGlobalPosition())
self.player.set_paused(True)
self.schedule_ask_player()
self.scheduleAskPlayer()
def initProtocol(self, protocol):
self.protocol = protocol
@ -281,30 +281,30 @@ class SyncplayClientManager(object):
self.protocol.drop()
self.protocol = None
def schedule_ask_player(self, when=0.2):
def scheduleAskPlayer(self, when=0.2):
if self.ask_delayed and self.ask_delayed.active():
self.ask_delayed.reset(when)
else:
self.ask_delayed = reactor.callLater(when, self.ask_player)
self.ask_delayed = reactor.callLater(when, self.askPlayer)
def ask_player(self):
def askPlayer(self):
if not self.running:
return
if self.player:
self.status_ask_sent += 1
self.player.ask_for_status()
self.schedule_ask_player()
self.scheduleAskPlayer()
def schedule_send_status(self, when=1):
def scheduleSendStatus(self, when=1):
if self.send_delayed and self.send_delayed.active():
self.send_delayed.reset(when)
else:
self.send_delayed = reactor.callLater(when, self.send_status)
self.send_delayed = reactor.callLater(when, self.sendStatus)
def send_status(self, force = False):
def sendStatus(self, force = False):
if not (self.running and self.protocol):
return
self.schedule_send_status()
self.scheduleSendStatus()
if self.counter > self.counter_recv and not force:
return
self.counter += 1
@ -315,20 +315,20 @@ class SyncplayClientManager(object):
if self.protocol:
self.protocol.sender.send_state(self.counter, curtime, self.player_paused, self.player_position)
def send_seek(self):
def sendSeek(self):
if not (self.running and self.protocol):
return
self.counter += 10
self.protocol.sender.send_seek(self.counter, time.time(), self.player_position)
self.protocol.sender.sendSeek(self.counter, time.time(), self.player_position)
message = self.name +' seeked to ' + format_time(self.player_position)
print message
self.player.display_message(message)
def send_filename(self):
def sendFilename(self):
if self.protocol and self.player_filename:
self.protocol.sender.send_playing(self.player_filename)
def __exectue_seek_cmd(self, seek_type, minutes, seconds):
def __exectueSeekCmd(self, seek_type, minutes, seconds):
self.player_position_before_last_seek = self.player_position
if seek_type == 's':
seconds = int(seconds) if seconds <> None else 0
@ -339,13 +339,13 @@ class SyncplayClientManager(object):
seconds += int(minutes) * 60 if minutes <> None else 60
self.player.set_position(self.player_position+seconds)
def execute_command(self, data):
def executeCommand(self, data):
RE_SEEK = re.compile("^(s[+s]?) ?(-?\d+)?([^0-9](\d+))?$")
RE_ROOM = re.compile("^room( (\w+))?")
matched_seek = RE_SEEK.match(data)
matched_room = RE_ROOM.match(data)
if matched_seek :
self.__exectue_seek_cmd(matched_seek.group(1), matched_seek.group(2), matched_seek.group(4))
self.__exectueSeekCmd(matched_seek.group(1), matched_seek.group(2), matched_seek.group(4))
elif matched_room:
room = matched_room.group(2)
if room == None:
@ -366,7 +366,7 @@ class SyncplayClientManager(object):
print "\tp - toggle pause"
print "\troom [room] - change room, if no supplied go to default"
def update_player_status(self, paused, position):
def updatePlayerStatus(self, paused, position):
self.status_ask_received += 1
if self.status_ask_received < self.status_ask_sent:
return
@ -374,18 +374,18 @@ class SyncplayClientManager(object):
self.player_paused = paused
self.player_position = position
self.last_player_update = time.time()
diff = position - self.get_global_position()
diff = position - self.getGlobalPosition()
if old_paused and not paused:
self.player_paused_at = None
if old_paused != paused and self.global_paused != paused:
self.send_status(True)
self.sendStatus(True)
if paused:
message = '%s paused' % self.name
print message
self.player.display_message(message)
if(diff > 0):
self.player.set_position(self.get_global_position())
self.ask_player()
self.player.set_position(self.getGlobalPosition())
self.askPlayer()
else:
message = '%s unpaused' % self.name
print message
@ -402,19 +402,19 @@ class SyncplayClientManager(object):
self.player.set_speed(1)
self.player_speed_fix = False
if abs(diff) > 8:# and not self.seek_sent_wait:
self.send_seek()
self.sendSeek()
self.seek_sent_wait = True
if not paused and self.player_paused_at is not None and position >= self.player_paused_at:
#print 'Pausing %0.2fs after pause point' % (position - self.player_paused_at)
self.player.set_paused(True)
self.ask_player()
self.askPlayer()
def update_filename(self, filename):
def updateFilename(self, filename):
filename = unicode(filename, errors='replace')
self.player_filename = filename.encode('ascii','replace')
self.send_filename()
self.sendFilename()
def update_global_state(self, counter, ctime, paused, position, name):
def updateGlobalState(self, counter, ctime, paused, position, name):
self.counter_recv = max(self.counter_recv, counter)
counter_valid = self.counter and counter >= self.counter
@ -443,7 +443,7 @@ class SyncplayClientManager(object):
changed = True
if counter_valid:
diff = self.get_player_position() - position
diff = self.getPlayerPosition() - position
if abs(diff) > 4:
self.player.set_position(position)
#self.player.set_paused(True)
@ -467,15 +467,15 @@ class SyncplayClientManager(object):
print message
self.player.display_message(message)
if(diff > 0):
self.player.set_position(self.get_global_position())
self.ask_player()
self.player.set_position(self.getGlobalPosition())
self.askPlayer()
if diff < 0:
self.player.set_paused(True)
self.global_noted_pause_change = paused
changed = True
if changed:
self.ask_player()
self.askPlayer()
def seek(self, ctime, position, who):
@ -486,7 +486,7 @@ class SyncplayClientManager(object):
if self.player:
self.player_position_before_last_seek = self.player_position
self.player.set_position(position)
self.ask_player()
self.askPlayer()
message = who + ' seeked to ' + format_time(position)
print message
self.player.display_message(message)

View File

@ -5,6 +5,7 @@ import win32con, win32api, win32gui, ctypes, ctypes.wintypes
class MPC_API:
def __init__(self, enforce_custom_handler = False):
self.enforce_custom_handler = enforce_custom_handler
'''
List of callbacks that can be set
on_connected (0 args)
@ -15,8 +16,7 @@ class MPC_API:
on_update_playstate (playstate)
on_file_ready (filename)
custom_handler (cmd, value)
'''
self.enforce_custom_handler = enforce_custom_handler
'''
self.callbacks = self.__CALLBACKS()
self.loadstate = None
self.playstate = None
@ -50,7 +50,6 @@ class MPC_API:
'''
Checks if api is ready to receive commands
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def is_api_ready(self):
file_state_ok = self.loadstate == self.__MPC_LOADSTATE.MLS_CLOSED or self.loadstate == self.__MPC_LOADSTATE.MLS_LOADED or self.loadstate == None
@ -59,28 +58,28 @@ class MPC_API:
'''
Checks if file is loaded in player
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def is_file_ready(self):
return (self.loadstate == self.__MPC_LOADSTATE.MLS_LOADED and self.fileplaying and self.playstate <> None)
'''
Opens a file given in an argument in player
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def open_file(self, file_path):
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_OPENFILE, file_path)
'''
Is player paused (Stop is considered pause)
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def is_paused(self):
return (self.playstate <> self.__MPC_PLAYSTATE.PS_PLAY and self.playstate <> None)
'''
Pause playing file
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def pause(self):
if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
@ -88,7 +87,7 @@ class MPC_API:
self.playpause()
'''
Play paused file
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def unpause(self):
if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
@ -96,7 +95,7 @@ class MPC_API:
self.playpause()
'''
Toggle play/pause
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def playpause(self):
if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
@ -112,7 +111,7 @@ class MPC_API:
'''
Asks mpc for it's current file position, if on_update_position callback is set
developers should rather rely on that rather than on a return value
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def ask_for_current_position(self):
if(not self.is_file_ready()):
@ -125,7 +124,7 @@ class MPC_API:
return self.lastfileposition
'''
Given a position in seconds will ask client to seek there
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def seek(self, position):
self.__locks.seek.clear()
@ -136,7 +135,7 @@ class MPC_API:
@param message: unicode string to display in player
@param MsgPos: Either 1, left top corner or 2, right top corner, defaults to 2
@param DurationMs: Duration of osd display, defaults to 3000
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def send_osd(self, message, MsgPos = 2, DurationMs = 3000):
class __OSDDATASTRUCT(ctypes.Structure):
@ -155,7 +154,7 @@ class MPC_API:
Send raw cmd and value to mpc
Commands are available in MPC_API_COMMANDS class
Value has to be either ctype.Structure or unicode string
Throws MPC_API.NoSlaveDetectedException if mpc window is not found
'''
def send_raw_command(self, cmd, value):
self.__listener.SendCommand(cmd, value)
@ -178,6 +177,7 @@ class MPC_API:
elif(cmd == MPC_API_COMMANDS.CMD_STATE):
self.loadstate = int(value)
if(self.callbacks.on_fileStateChange): thread.start_new_thread(self.callbacks.on_fileStateChange, (self.loadstate,))
elif(cmd == MPC_API_COMMANDS.CMD_PLAYMODE):
self.playstate = int(value)
@ -188,7 +188,6 @@ class MPC_API:
if(self.callbacks.on_update_filename): thread.start_new_thread(self.callbacks.on_update_filename,(self.fileplaying,))
self.fileduration = int(value.split('|')[4])
if(self.callbacks.on_update_file_duration): thread.start_new_thread(self.callbacks.on_update_file_duration,(self.fileplaying,))
if(self.callbacks.on_file_ready): thread.start_new_thread(self.callbacks.on_file_ready, ())
elif(cmd == MPC_API_COMMANDS.CMD_CURRENTPOSITION):
self.lastfileposition = float(value)
@ -211,7 +210,6 @@ class MPC_API:
def __init__(self, message):
Exception.__init__(self, message)
class __CALLBACKS:
def __init__(self):
self.on_connected = None
@ -220,7 +218,7 @@ class MPC_API:
self.on_update_file_duration = None
self.on_update_position = None
self.on_update_playstate = None
self.on_file_ready = None
self.on_fileStateChange = None
self.custom_handler = None
self.on_mpc_closed = None
@ -295,12 +293,11 @@ class MPC_API:
def OnCopyData(self, hwnd, msg, wparam, lparam):
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
# print ">>> 0x%X" % int(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.__mpc_api.handle_command(pCDS.contents.dwData, ctypes.wstring_at(pCDS.contents.lpData))
def SendCommand(self, cmd, message = u''):
# print "<<< 0x%X" % int(cmd), message
#print "API:\t<out\t 0x%X\t" % int(cmd), message
if not win32gui.IsWindow(self.mpc_handle):
raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected")
cs = self.__COPYDATASTRUCT()
@ -316,7 +313,7 @@ class MPC_API:
cs.cbData = ctypes.sizeof(message)
ptr= ctypes.addressof(cs)
win32api.SendMessage(self.mpc_handle, win32con.WM_COPYDATA, self.hwnd, ptr)
class __COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),

View File

@ -2,6 +2,7 @@
from ..mpc_api import MPC_API
import time
import threading
class MPCHCAPIPlayer(object):
def __init__(self, manager):
@ -9,17 +10,25 @@ class MPCHCAPIPlayer(object):
self.mpc_api = MPC_API()
self.pinged = False
self.tmp_filename = None
self.tmp_position = None
self.mpc_api.callbacks.on_file_ready = lambda: self.make_ping()
self.mpc_api.callbacks.on_update_filename = lambda _: self.make_ping()
self.mpc_api.callbacks.on_mpc_closed = lambda: self.mpc_error("MPC closed")
self.semaphore_filename = False
self.mpc_api.callbacks.on_fileStateChange = self.lockAsking
self.mpc_api.callbacks.on_update_playstate = self.unlockAsking
self.askSemaphore = False
self.askLock = threading.RLock()
def drop(self):
pass
def lockAsking(self, state):
self.askSemaphore = True
def unlockAsking(self, state):
self.askSemaphore = False
def set_speed(self, value):
pass
@ -32,9 +41,10 @@ class MPCHCAPIPlayer(object):
return
def make_ping(self):
self.mpc_api.callbacks.on_file_ready = None
self.test_mpc_ready()
self._syncplayClient.init_player(self)
self.mpc_api.callbacks.on_update_filename = self.handle_updated_filename
self._syncplayClient.initPlayer(self)
self.handle_updated_filename(self.mpc_api.fileplaying)
self.pinged = True
self.ask_for_status()
@ -66,55 +76,58 @@ class MPCHCAPIPlayer(object):
except Exception, err:
self.mpc_error(err)
def ask_for_status(self):
position = self.tmp_position if self.tmp_position else 0
paused = None
def __askForPositionUntilPlayerReady(self):
try:
if(self.mpc_api.is_file_ready() and not self.semaphore_filename):
try:
position = self.mpc_api.ask_for_current_position()
except MPC_API.PlayerNotReadyException:
time.sleep(0.1)
self.ask_for_status()
return
if(self.tmp_filename <> self.mpc_api.fileplaying):
self.handle_updated_filename(self.mpc_api.fileplaying)
return
return self.mpc_api.ask_for_current_position()
except MPC_API.PlayerNotReadyException:
time.sleep(0.1)
return self.__askForPositionUntilPlayerReady()
def ask_for_status(self):
try:
self.askLock.acquire()
if(self.mpc_api.is_file_ready() and not self.askSemaphore):
position = self.__askForPositionUntilPlayerReady()
paused = self.mpc_api.is_paused()
position = float(position)
self.tmp_position = position
self._syncplayClient.update_player_status(paused, position)
else:
self._syncplayClient.update_player_status(True, self._syncplayClient.get_global_position())
if(not self.askSemaphore):
self._syncplayClient.updatePlayerStatus(paused, position)
return
self._syncplayClient.updatePlayerStatus(True, self._syncplayClient.getGlobalPosition())
except Exception, err:
self.mpc_error(err)
def __force_pause(self, filename, position):
self.set_paused(True)
finally:
self.askLock.release()
def __pauseChangeCheckLoop(self, i, changeFrom):
time.sleep(0.1)
if (not self.mpc_api.is_paused()):
self.__set_up_newly_opened_file(filename, position)
if(i < 10):
if(self.mpc_api.is_paused() <> changeFrom):
return
else:
self.__pauseChangeCheckLoop(i+1, True)
def __force_pause(self):
self.__pauseChangeCheckLoop(0, True)
self.set_paused(True)
def __set_up_newly_opened_file(self, filename, position):
self.test_mpc_ready()
try:
self.__force_pause()
self.mpc_api.seek(position)
except MPC_API.PlayerNotReadyException:
time.sleep(0.1)
self.__set_up_newly_opened_file(filename, position)
self.__force_pause(filename, position)
def handle_updated_filename(self, filename):
position = self._syncplayClient.get_global_position()
if(self.semaphore_filename):
self._syncplayClient.update_player_status(True, position)
return
self.semaphore_filename = True
self.__set_up_newly_opened_file(filename, position)
self.tmp_filename = filename
self._syncplayClient.update_filename(str(self.tmp_filename))
self._syncplayClient.update_player_status(True, position)
self.semaphore_filename = False
try:
self.askLock.acquire()
position = self._syncplayClient.getGlobalPosition()
self.__set_up_newly_opened_file(filename, position)
self._syncplayClient.updateFilename(str(filename))
finally:
self.askLock.release()
def mpc_error(self, err=""):
print "ERROR:", str(err) + ',', "desu"

View File

@ -106,8 +106,8 @@ class MplayerProtocol(LineProcessProtocol):
self.send_get_property('filename')
def mplayer_answer_filename(self, value):
self._syncplayClient.init_player(self)
self._syncplayClient.update_filename(value)
self._syncplayClient.initPlayer(self)
self._syncplayClient.updateFilename(value)
def set_paused(self, value):
@ -130,7 +130,7 @@ class MplayerProtocol(LineProcessProtocol):
def mplayer_answer_time_pos(self, value):
value = float(value)
self._syncplayClient.update_player_status(self.tmp_paused, value)
self._syncplayClient.updatePlayerStatus(self.tmp_paused, value)
def set_speed(self, value):

View File

@ -29,7 +29,7 @@ class ConsoleUI(threading.Thread):
self.PromptResult = data
self.promptMode.set()
elif(self._syncplayClient):
self._syncplayClient.execute_command(data)
self._syncplayClient.executeCommand(data)
except:
raise

View File

@ -75,7 +75,7 @@ def stdin_thread(manager):
data = os.read(fd, 1024)
if not data:
break
manager.execute_command(data.rstrip('\n\r'))
manager.executeCommand(data.rstrip('\n\r'))
except:
pass
@ -97,7 +97,7 @@ class ConfigurationGetter(object):
raise Exception('Working dir not found')
def _prepareArgParser(self):
self._parser = argparse.ArgumentParser(description='Syncplay',
self._parser = argparse.ArgumentParser(description='Solution to synchronize playback of multiple MPlayer and MPC-HC instances over the network.',
epilog='If no options supplied _config values will be used')
self._parser.add_argument('--no-gui', action='store_true', help='show no GUI')
self._parser.add_argument('--host', metavar='hostname', type=str, help='server\'s address')
@ -126,6 +126,7 @@ class ConfigurationGetter(object):
self._config.write(configfile)
def _setUpValuesToSave(self, section_name):
self._splitPortAndHost()
self._config.set(section_name, 'host', self._args.host)
self._config.set(section_name, 'name', self._args.name)
@ -146,11 +147,12 @@ class ConfigurationGetter(object):
self._args.name = self._config.get(section_name, 'name')
def _splitPortAndHost(self):
if ':' in self._args.host:
self._args.host, port = self._args.host.split(':', 1)
self._args.port = int(port)
else:
self._args.port = 8999
if(self._args.host):
if ':' in self._args.host:
self._args.host, port = self._args.host.split(':', 1)
self._args.port = int(port)
else:
self._args.port = 8999
def prepareClientConfiguration(self):
self._findWorkingDirectory()

View File

@ -7,7 +7,8 @@ from syncplay import utils
class SyncplayMPC:
def runClient(self):
self._prepareArguments()
self.interface = ui.getUi(graphical = not self.args.no_gui)
# self.interface = ui.getUi(graphical = not self.args.no_gui)
self.interface = ui.getUi(graphical = False) #TODO: add gui
self._promptForMissingArguments()
syncplayClient = client.SyncplayClientManager(self.args.name, lambda m: mpc.run_mpc(m, self.args.mpc_path, self.args.file, self.args._args), self.interface, self.args.debug)
self.interface.addClient(syncplayClient)