From aba2117344c227b5be55ce29508bcbe0fb232bba Mon Sep 17 00:00:00 2001 From: Uriziel Date: Wed, 11 Jul 2012 10:41:23 +0200 Subject: [PATCH] Completly new setup --- setup_common.py | 4 +- syncplay/client.py | 74 ++++++++++++++-------------- syncplay/mpc_api.py | 37 +++++++------- syncplay/players/mpc.py | 97 +++++++++++++++++++++---------------- syncplay/players/mplayer.py | 6 +-- syncplay/ui/consoleUI.py | 2 +- syncplay/utils.py | 16 +++--- syncplay_mpc.py | 3 +- 8 files changed, 126 insertions(+), 113 deletions(-) diff --git a/setup_common.py b/setup_common.py index ab117ee..9e0e548 100644 --- a/setup_common.py +++ b/setup_common.py @@ -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'], ) diff --git a/syncplay/client.py b/syncplay/client.py index 871cd8a..284c156 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -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) diff --git a/syncplay/mpc_api.py b/syncplay/mpc_api.py index b0274af..dde6c90 100644 --- a/syncplay/mpc_api.py +++ b/syncplay/mpc_api.py @@ -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 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" diff --git a/syncplay/players/mplayer.py b/syncplay/players/mplayer.py index 8a72b0f..0ef777c 100644 --- a/syncplay/players/mplayer.py +++ b/syncplay/players/mplayer.py @@ -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): diff --git a/syncplay/ui/consoleUI.py b/syncplay/ui/consoleUI.py index 826eac1..63b147e 100644 --- a/syncplay/ui/consoleUI.py +++ b/syncplay/ui/consoleUI.py @@ -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 diff --git a/syncplay/utils.py b/syncplay/utils.py index 44be4f3..f7b4887 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -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() diff --git a/syncplay_mpc.py b/syncplay_mpc.py index 52fbcd2..8fe7bc5 100644 --- a/syncplay_mpc.py +++ b/syncplay_mpc.py @@ -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)