diff --git a/syncplay/client.py b/syncplay/client.py index 314d379..cd08eed 100644 --- a/syncplay/client.py +++ b/syncplay/client.py @@ -34,7 +34,8 @@ class SyncClientProtocol(CommandProtocol): @arg_count(1) def handle_init_hello(self, args): - print 'Connected as', args[0] + message ='Connected as ' + args[0] + print message self.manager.name = args[0] self.change_state('connected') self.send_list() @@ -47,9 +48,13 @@ class SyncClientProtocol(CommandProtocol): else: who, where, what = args[0], args[1], None if what: - print '%s is present and is playing \'%s\' in the room: \'%s\'' % (who, what, where) + message = '%s is present and is playing \'%s\' in the room: \'%s\'' % (who, what, where) + print message + self.manager.player.display_message(message) else: - print '%s is present in the room: \'%s\'' % (who, where) + message = '%s is present in the room: \'%s\'' % (who, where) + print message + self.manager.player.display_message(message) @arg_count(4, 5) def handle_connected_state(self, args): @@ -83,19 +88,27 @@ class SyncClientProtocol(CommandProtocol): @arg_count(3) def handle_connected_playing(self, args): who, where, what = args - print '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where) + message = '%s is playing \'%s\' in the room: \'%s\'' % (who, what, where) + print message + self.manager.player.display_message(message) @arg_count(1) def handle_connected_joined(self, args): - print '%s joined' % args[0] + message = '%s joined' % args[0] + print message + self.manager.player.display_message(message) @arg_count(2) def handle_connected_room(self, args): - print '%s entered the room: \'%s\'' % (args[0], args[1]) + message = '%s entered the room: \'%s\'' % (args[0], args[1]) + print message + self.manager.player.display_message(message) @arg_count(1) def handle_connected_left(self, args): - print '%s left' % args[0] + message = '%s left' % args[0] + print message + self.manager.player.display_message(message) def send_list(self): self.send_message('list') @@ -147,9 +160,17 @@ class SyncClientFactory(ClientFactory): print 'Connection lost, reconnecting' reactor.callLater(0.1, connector.connect) else: - print 'Disconnected' + if(self.manager.player): + message = 'Disconnected' + print message + self.manager.player.display_message(message) def clientConnectionFailed(self, connector, reason): + if(self.manager.player): + message = 'Disconnected' + print message + self.manager.player.display_message(message) + print 'Connection failed' self.manager.stop() @@ -284,7 +305,10 @@ class Manager(object): return self.counter += 10 self.protocol.send_seek(self.counter, time.time(), self.player_position) - print self.name,'seeked to', format_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): if self.protocol and self.player_filename: @@ -346,12 +370,18 @@ class Manager(object): if old_paused != paused and self.global_paused != paused: self.send_status(True) if paused: - print '%s paused' % self.name + 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() else: - print '%s resumed' % self.name + message = '%s resumed' % self.name + print message + self.player.display_message(message) + if not (self.global_paused or self.seek_sent_wait): if (0.4 if self.player_speed_fix else 0.6) <= diff <= 4: @@ -412,17 +442,26 @@ class Manager(object): if abs(diff) > 4: self.player.set_position(position) #self.player.set_paused(True) - print "Rewinded due to time difference" + message = "Rewinded due to time difference" + print message + self.player.display_message(message) + if self.player_paused and not paused: self.player_paused_at = None self.player.set_paused(False) if self.global_noted_pause_change != paused: - print '%s unpaused' % name + message = '%s unpaused' % name + print message + self.player.display_message(message) + elif paused and not self.player_paused: self.player_paused_at = position if self.global_noted_pause_change != paused: - print '%s paused' % name + message = '%s paused' % name + print message + self.player.display_message(message) + if diff < 0: self.player.set_paused(True) self.global_noted_pause_change = paused @@ -440,6 +479,9 @@ class Manager(object): self.player_position_before_last_seek = self.player_position self.player.set_position(position) self.ask_player() - print who, 'seeked to', format_time(position) + 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 d578f29..36562bf 100644 --- a/syncplay/mpc_api.py +++ b/syncplay/mpc_api.py @@ -1,118 +1,218 @@ #coding:utf8 import win32con, win32api, win32gui, ctypes, ctypes.wintypes -import sys, os, thread +import os, thread class MPC_API: - def __init__(self): - self.listener = None + def __init__(self, enforce_custom_handler = False): + self.enforce_custom_handler = enforce_custom_handler + self.__listener = None thread.start_new_thread(self.__Listener, (self,)) - while(self.listener == None): continue - + while(self.__listener == None): continue + self.__position_request_warden = False + self.__seek_warden = False + + ''' + List of callbacks that can be set + on_connected + on_seek + on_update_filename + on_update_file_duration + on_update_position + on_update_playstate + on_file_ready + custom_handler + ''' + self.callbacks = self.__CALLBACKS() self.loadstate = None self.playstate = None self.fileplaying = None self.fileduration = None + ''' + Most likely won't be up to date unless you ask API to refresh it + ''' self.lastfileposition = None - self.position_request_warden = False - self.seek_warden = False - self.callbacks = self.__CALLBACKS() - + + ''' + This is called from another thread and if it could it would be a private method + Developers should not bother with it + ''' def register_listener(self, listener): - self.listener = listener + self.__listener = listener + + ''' + Given a path fo mpc-hc.exe and optional additional arguments in tuple + will start mpc-hc in a slave mode + ''' + def start_mpc(self, path, args = ()): + is_starting = os.spawnl(os.P_NOWAIT, path, ' ' + ' '.join(args),'/slave %s ' % str(self.__listener.hwnd)) #can be switched with win32api.ShellExecute + while(self.__listener.mpc_handle == None and is_starting): continue + + + ''' + Checks if api is ready to receive commands + Throws MPC_API.NoSlaveDetectedException if mpc window is not found + ''' def is_api_ready(self): self.__mpc_ready_in_slave_mode() file_state_ok = self.loadstate == self.__MPC_LOADSTATE.MLS_CLOSED or self.loadstate == self.__MPC_LOADSTATE.MLS_LOADED or self.loadstate == None - listener_ok = self.listener <> None and self.listener.mpc_handle <> None + listener_ok = self.__listener <> None and self.__listener.mpc_handle <> None return (file_state_ok and listener_ok) + ''' + Checks if file is loaded in player + Throws MPC_API.NoSlaveDetectedException if mpc window is not found + ''' def is_file_ready(self): self.__mpc_ready_in_slave_mode() return (self.loadstate == self.__MPC_LOADSTATE.MLS_LOADED and self.fileplaying and self.playstate <> None) - def start_mpc(self, path, args = ()): - is_starting = os.spawnl(os.P_NOWAIT, path, ' ' + ' '.join(args),' /open /new /slave %s ' % str(self.listener.hwnd)) #can be switched with win32api.ShellExecute - while(self.listener.mpc_handle == None and is_starting): continue - + ''' + 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.__mpc_ready_in_slave_mode() - self.listener.SendCommand(self.__API_COMMANDS.CMD_OPENFILE, 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): self.__mpc_ready_in_slave_mode() 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): self.__mpc_ready_in_slave_mode() if(not self.is_paused()): - self.listener.SendCommand(self.__API_COMMANDS.CMD_PLAYPAUSE) + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE) while(not self.is_paused()): self.__mpc_ready_in_slave_mode() + ''' + Play paused file + Throws MPC_API.NoSlaveDetectedException if mpc window is not found + ''' def unpause(self): self.__mpc_ready_in_slave_mode() if(self.is_paused()): - self.listener.SendCommand(self.__API_COMMANDS.CMD_PLAYPAUSE) + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE) while(self.is_paused()): self.__mpc_ready_in_slave_mode() + ''' + Toggle play/pause + Throws MPC_API.NoSlaveDetectedException if mpc window is not found + ''' def playpause(self): self.__mpc_ready_in_slave_mode() tmp = self.playstate - self.listener.SendCommand(self.__API_COMMANDS.CMD_PLAYPAUSE) + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE) while(tmp == self.playstate): self.__mpc_ready_in_slave_mode() + ''' + 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): self.__mpc_ready_in_slave_mode() - if(not self.position_request_warden): - self.position_request_warden = True - self.listener.SendCommand(self.__API_COMMANDS.CMD_GETCURRENTPOSITION) - while(self.position_request_warden and not self.callbacks.on_update_position): self.__mpc_ready_in_slave_mode() + if(not self.__position_request_warden): + self.__position_request_warden = True + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_GETCURRENTPOSITION) + while(self.__position_request_warden and not self.callbacks.on_update_position): self.__mpc_ready_in_slave_mode() 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.__mpc_ready_in_slave_mode() - self.seek_warden = True - self.listener.SendCommand(self.__API_COMMANDS.CMD_SETPOSITION, unicode(position)) - while(self.seek_warden): - continue - - def __mpc_ready_in_slave_mode(self): - if not win32gui.IsWindow(self.listener.mpc_handle): - raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected") - - def handle_command(self,cmd, value): - #print '>>>'+hex(cmd).upper(), value - if (cmd == self.__API_COMMANDS.CMD_CONNECT): - self.listener.mpc_handle = int(value) - if(self.callbacks.on_connected): - self.callbacks.on_connected() - elif(cmd == self.__API_COMMANDS.CMD_STATE): - self.loadstate = int(value) - elif(cmd == self.__API_COMMANDS.CMD_PLAYMODE): - self.playstate = int(value) - if(self.callbacks.on_update_playstate): self.callbacks.on_update_playstate(self.playstate) - elif(cmd == self.__API_COMMANDS.CMD_NOWPLAYING): - if(self.callbacks.on_file_ready): self.callbacks.on_file_ready() - self.fileplaying = value.split('|')[3].split('\\').pop() - if(self.callbacks.on_update_filename): self.callbacks.on_update_filename(self.fileplaying) - self.fileduration = int(value.split('|')[4]) - if(self.callbacks.on_update_file_duration): self.callbacks.on_update_file_duration(self.fileplaying) - elif(cmd == self.__API_COMMANDS.CMD_CURRENTPOSITION): - self.lastfileposition = float(value) - self.position_request_warden = False - if(self.callbacks.on_update_position): self.callbacks.on_update_position(self.lastfileposition) - elif(cmd == self.__API_COMMANDS.CMD_NOTIFYSEEK): - self.seek_warden = False - if(self.lastfileposition <> float(value)): #Notify seek is sometimes sent twice - self.lastfileposition = float(value) - if(self.callbacks.on_seek): self.callbacks.on_seek(self.lastfileposition) + self.__seek_warden = True + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_SETPOSITION, unicode(position)) + while(self.__seek_warden): continue + + ''' + @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): + self.__mpc_ready_in_slave_mode() + class __OSDDATASTRUCT(ctypes.Structure): + _fields_ = [ + ('nMsgPos', ctypes.c_int32), + ('nDurationMS', ctypes.c_int32), + ('strMsg', ctypes.c_wchar * (len(message)+1)) + ] + cmessage = __OSDDATASTRUCT() + cmessage.nMsgPos = MsgPos + cmessage.nDurationMS = DurationMs + cmessage.strMsg = message + self.__listener.SendCommand(MPC_API_COMMANDS.CMD_OSDSHOWMESSAGE, cmessage) + + ''' + 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.__mpc_ready_in_slave_mode() + self.__listener.SendCommand(cmd, value) + + ''' + Callback function to intercept commands sent by MPC + Handles only a handful of them + If callbacks.custom_handler is provided all not recognized commands will be redirected to it + If enforce_custom_handler is set it will redirect all the commands to it + ''' + def handle_command(self,cmd, value, enforce_custom_handler = False): + if((self.enforce_custom_handler or enforce_custom_handler) and self.callbacks.custom_handler <> None): + self.callbacks.custom_handler((cmd, value,)) else: - pass + if (cmd == MPC_API_COMMANDS.CMD_CONNECT): + self.__listener.mpc_handle = int(value) + if(self.callbacks.on_connected): + self.callbacks.on_connected(()) + + elif(cmd == MPC_API_COMMANDS.CMD_STATE): + self.loadstate = int(value) + + elif(cmd == MPC_API_COMMANDS.CMD_PLAYMODE): + self.playstate = int(value) + if(self.callbacks.on_update_playstate): self.callbacks.on_update_playstate((self.playstate,)) + + elif(cmd == MPC_API_COMMANDS.CMD_NOWPLAYING): + if(self.callbacks.on_file_ready): self.callbacks.on_file_ready() + self.fileplaying = value.split('|')[3].split('\\').pop() + if(self.callbacks.on_update_filename): self.callbacks.on_update_filename((self.fileplaying,)) + self.fileduration = int(value.split('|')[4]) + if(self.callbacks.on_update_file_duration): self.callbacks.on_update_file_duration((self.fileplaying,)) + + elif(cmd == MPC_API_COMMANDS.CMD_CURRENTPOSITION): + self.lastfileposition = float(value) + self.__position_request_warden = False + if(self.callbacks.on_update_position): self.callbacks.on_update_position((self.lastfileposition,)) + + elif(cmd == MPC_API_COMMANDS.CMD_NOTIFYSEEK): + self.__seek_warden = False + if(self.lastfileposition <> float(value)): #Notify seek is sometimes sent twice + self.lastfileposition = float(value) + if(self.callbacks.on_seek): self.callbacks.on_seek((self.lastfileposition,)) + else: + if(self.callbacks.custom_handler <> None): + self.callbacks.custom_handler((cmd, value,)) class NoSlaveDetectedException(Exception): def __init__(self, message): Exception.__init__(self, message) - + class __CALLBACKS: def __init__(self): self.on_connected = None @@ -122,7 +222,13 @@ class MPC_API: self.on_update_position = None self.on_update_playstate = None self.on_file_ready = None - + self.custom_handler = None + + + def __mpc_ready_in_slave_mode(self): + if not win32gui.IsWindow(self.__listener.mpc_handle): + raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected") + class __MPC_LOADSTATE: MLS_CLOSED = 0 MLS_LOADING = 1 @@ -136,158 +242,16 @@ class MPC_API: PS_UNUSED = 3 ''' - class MPC_OSDDATA(): - _fields_ = [ - ('nMsgPos'), - ('nDurationMS'), - ('strMsg') - ] + class __OSD_MESSAGEPOS: + OSD_NOMESSAGE = 0 + OSD_TOPLEFT = 1 + OSD_TOPRIGHT = 2 + ''' - class __API_COMMANDS(): - # Send after connection - # Par 1 : MPC window handle (command should be send to this HWnd) - CMD_CONNECT = 0x50000000 - # Send when opening or closing file - # Par 1 : current state (see MPC_LOADSTATE enum) - CMD_STATE = 0x50000001 - # Send when playing, pausing or closing file - # Par 1 : current play mode (see MPC_PLAYSTATE enum) - CMD_PLAYMODE = 0x50000002 - # Send after opening a new file - # Par 1 : title - # Par 2 : author - # Par 3 : description - # Par 4 : complete filename (path included) - # Par 5 : duration in seconds - CMD_NOWPLAYING = 0x50000003 - # List of subtitle tracks - # Par 1 : Subtitle track name 0 - # Par 2 : Subtitle track name 1 - # ... - # Par n : Active subtitle track, -1 if subtitles disabled - # - # if no subtitle track present, returns -1 - # if no file loaded, returns -2 - CMD_LISTSUBTITLETRACKS = 0x50000004 - # List of audio tracks - # Par 1 : Audio track name 0 - # Par 2 : Audio track name 1 - # ... - # Par n : Active audio track - # - # if no audio track present, returns -1 - # if no file loaded, returns -2 - CMD_LISTAUDIOTRACKS = 0x50000005 - # Send current playback position in responce - # of CMD_GETCURRENTPOSITION. - # Par 1 : current position in seconds - CMD_CURRENTPOSITION = 0x50000007 - # Send the current playback position after a jump. - # (Automatically sent after a seek event). - # Par 1 : new playback position (in seconds). - CMD_NOTIFYSEEK = 0x50000008 - # Notify the end of current playback - # (Automatically sent). - # Par 1 : none. - CMD_NOTIFYENDOFSTREAM = 0x50000009 - # List of files in the playlist - # Par 1 : file path 0 - # Par 2 : file path 1 - # ... - # Par n : active file, -1 if no active file - CMD_PLAYLIST = 0x50000006 - # ==== Commands from host to MPC - # Open new file - # Par 1 : file path - CMD_OPENFILE = 0xA0000000 - # Stop playback, but keep file / playlist - CMD_STOP = 0xA0000001 - # Stop playback and close file / playlist - CMD_CLOSEFILE = 0xA0000002 - # Pause or restart playback - CMD_PLAYPAUSE = 0xA0000003 - # Add a new file to playlist (did not start playing) - # Par 1 : file path - CMD_ADDTOPLAYLIST = 0xA0001000 - # Remove all files from playlist - CMD_CLEARPLAYLIST = 0xA0001001 - # Start playing playlist - CMD_STARTPLAYLIST = 0xA0001002 - CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO - # Cue current file to specific position - # Par 1 : new position in seconds - CMD_SETPOSITION = 0xA0002000 - # Set the audio delay - # Par 1 : new audio delay in ms - CMD_SETAUDIODELAY = 0xA0002001 - # Set the subtitle delay - # Par 1 : new subtitle delay in ms - CMD_SETSUBTITLEDELAY = 0xA0002002 - # Set the active file in the playlist - # Par 1 : index of the active file, -1 for no file selected - # DOESNT WORK - CMD_SETINDEXPLAYLIST = 0xA0002003 - # Set the audio track - # Par 1 : index of the audio track - CMD_SETAUDIOTRACK = 0xA0002004 - # Set the subtitle track - # Par 1 : index of the subtitle track, -1 for disabling subtitles - CMD_SETSUBTITLETRACK = 0xA0002005 - # Ask for a list of the subtitles tracks of the file - # return a CMD_LISTSUBTITLETRACKS - CMD_GETSUBTITLETRACKS = 0xA0003000 - # Ask for the current playback position, - # see CMD_CURRENTPOSITION. - # Par 1 : current position in seconds - CMD_GETCURRENTPOSITION = 0xA0003004 - # Jump forward/backward of N seconds, - # Par 1 : seconds (negative values for backward) - CMD_JUMPOFNSECONDS = 0xA0003005 - # Ask for a list of the audio tracks of the file - # return a CMD_LISTAUDIOTRACKS - CMD_GETAUDIOTRACKS = 0xA0003001 - # Ask for the properties of the current loaded file - # return a CMD_NOWPLAYING - CMD_GETNOWPLAYING = 0xA0003002 - # Ask for the current playlist - # return a CMD_PLAYLIST - CMD_GETPLAYLIST = 0xA0003003 - # Toggle FullScreen - CMD_TOGGLEFULLSCREEN = 0xA0004000 - # Jump forward(medium) - CMD_JUMPFORWARDMED = 0xA0004001 - # Jump backward(medium) - CMD_JUMPBACKWARDMED = 0xA0004002 - # Increase Volume - CMD_INCREASEVOLUME = 0xA0004003 - # Decrease volume - CMD_DECREASEVOLUME = 0xA0004004 - # Shader toggle - CMD_SHADER_TOGGLE = 0xA0004005 - # Close App - CMD_CLOSEAPP = 0xA0004006 - # show host defined OSD message string - CMD_OSDSHOWMESSAGE = 0xA0005000 class __Listener: - def SendCommand(self, cmd, message = ''): - #print "<<<" + (hex(cmd).upper()), str(message) - if not win32gui.IsWindow(self.mpc_handle): - raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected") - cs = self.__COPYDATASTRUCT() - cs.dwData = cmd; - cs.lpData = ctypes.cast(ctypes.c_wchar_p(message), ctypes.c_void_p) - utf_size_multiplier = 2 if sys.maxunicode < 65536 else 4 - cs.cbData = utf_size_multiplier*len(message)+1 - ptr= ctypes.addressof(cs) - win32api.SendMessage(self.mpc_handle, win32con.WM_COPYDATA, self.hwnd, ptr) - class __COPYDATASTRUCT(ctypes.Structure): - _fields_ = [ - ('dwData', ctypes.wintypes.LPARAM), - ('cbData', ctypes.wintypes.DWORD), - ('lpData', ctypes.c_void_p) - ] def __init__(self, mpc_api): + self.mpc_handle = None message_map = { win32con.WM_COPYDATA: self.OnCopyData } @@ -310,11 +274,165 @@ class MPC_API: None ) self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT) - self.mpc_api = mpc_api - self.mpc_handle = None + self.__mpc_api = mpc_api + mpc_api.register_listener(self) win32gui.PumpMessages() - + def OnCopyData(self, hwnd, msg, wparam, lparam): pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT) - self.mpc_api.handle_command(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''): + if not win32gui.IsWindow(self.mpc_handle): + raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected") + cs = self.__COPYDATASTRUCT() + cs.dwData = cmd; + + if(isinstance(message, (unicode, str))): + message = ctypes.create_unicode_buffer(message, len(message)+1) + elif(isinstance(message, ctypes.Structure)): + pass + else: + raise TypeError + cs.lpData = ctypes.addressof(message) + cs.cbData = ctypes.sizeof(message) + ptr= ctypes.addressof(cs) + win32api.SendMessage(self.mpc_handle, win32con.WM_COPYDATA, self.hwnd, ptr) + + class __COPYDATASTRUCT(ctypes.Structure): + _fields_ = [ + ('dwData', ctypes.wintypes.LPARAM), + ('cbData', ctypes.wintypes.DWORD), + ('lpData', ctypes.c_void_p) + ] + + + +class MPC_API_COMMANDS(): + # Send after connection + # Par 1 : MPC window handle (command should be send to this HWnd) + CMD_CONNECT = 0x50000000 + # Send when opening or closing file + # Par 1 : current state (see MPC_LOADSTATE enum) + CMD_STATE = 0x50000001 + # Send when playing, pausing or closing file + # Par 1 : current play mode (see MPC_PLAYSTATE enum) + CMD_PLAYMODE = 0x50000002 + # Send after opening a new file + # Par 1 : title + # Par 2 : author + # Par 3 : description + # Par 4 : complete filename (path included) + # Par 5 : duration in seconds + CMD_NOWPLAYING = 0x50000003 + # List of subtitle tracks + # Par 1 : Subtitle track name 0 + # Par 2 : Subtitle track name 1 + # ... + # Par n : Active subtitle track, -1 if subtitles disabled + # + # if no subtitle track present, returns -1 + # if no file loaded, returns -2 + CMD_LISTSUBTITLETRACKS = 0x50000004 + # List of audio tracks + # Par 1 : Audio track name 0 + # Par 2 : Audio track name 1 + # ... + # Par n : Active audio track + # + # if no audio track present, returns -1 + # if no file loaded, returns -2 + CMD_LISTAUDIOTRACKS = 0x50000005 + # Send current playback position in responce + # of CMD_GETCURRENTPOSITION. + # Par 1 : current position in seconds + CMD_CURRENTPOSITION = 0x50000007 + # Send the current playback position after a jump. + # (Automatically sent after a seek event). + # Par 1 : new playback position (in seconds). + CMD_NOTIFYSEEK = 0x50000008 + # Notify the end of current playback + # (Automatically sent). + # Par 1 : none. + CMD_NOTIFYENDOFSTREAM = 0x50000009 + # List of files in the playlist + # Par 1 : file path 0 + # Par 2 : file path 1 + # ... + # Par n : active file, -1 if no active file + CMD_PLAYLIST = 0x50000006 + # ==== Commands from host to MPC + # Open new file + # Par 1 : file path + CMD_OPENFILE = 0xA0000000 + # Stop playback, but keep file / playlist + CMD_STOP = 0xA0000001 + # Stop playback and close file / playlist + CMD_CLOSEFILE = 0xA0000002 + # Pause or restart playback + CMD_PLAYPAUSE = 0xA0000003 + # Add a new file to playlist (did not start playing) + # Par 1 : file path + CMD_ADDTOPLAYLIST = 0xA0001000 + # Remove all files from playlist + CMD_CLEARPLAYLIST = 0xA0001001 + # Start playing playlist + CMD_STARTPLAYLIST = 0xA0001002 + CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO + # Cue current file to specific position + # Par 1 : new position in seconds + CMD_SETPOSITION = 0xA0002000 + # Set the audio delay + # Par 1 : new audio delay in ms + CMD_SETAUDIODELAY = 0xA0002001 + # Set the subtitle delay + # Par 1 : new subtitle delay in ms + CMD_SETSUBTITLEDELAY = 0xA0002002 + # Set the active file in the playlist + # Par 1 : index of the active file, -1 for no file selected + # DOESNT WORK + CMD_SETINDEXPLAYLIST = 0xA0002003 + # Set the audio track + # Par 1 : index of the audio track + CMD_SETAUDIOTRACK = 0xA0002004 + # Set the subtitle track + # Par 1 : index of the subtitle track, -1 for disabling subtitles + CMD_SETSUBTITLETRACK = 0xA0002005 + # Ask for a list of the subtitles tracks of the file + # return a CMD_LISTSUBTITLETRACKS + CMD_GETSUBTITLETRACKS = 0xA0003000 + # Ask for the current playback position, + # see CMD_CURRENTPOSITION. + # Par 1 : current position in seconds + CMD_GETCURRENTPOSITION = 0xA0003004 + # Jump forward/backward of N seconds, + # Par 1 : seconds (negative values for backward) + CMD_JUMPOFNSECONDS = 0xA0003005 + # Ask for a list of the audio tracks of the file + # return a CMD_LISTAUDIOTRACKS + CMD_GETAUDIOTRACKS = 0xA0003001 + # Ask for the properties of the current loaded file + # return a CMD_NOWPLAYING + CMD_GETNOWPLAYING = 0xA0003002 + # Ask for the current playlist + # return a CMD_PLAYLIST + CMD_GETPLAYLIST = 0xA0003003 + # Toggle FullScreen + CMD_TOGGLEFULLSCREEN = 0xA0004000 + # Jump forward(medium) + CMD_JUMPFORWARDMED = 0xA0004001 + # Jump backward(medium) + CMD_JUMPBACKWARDMED = 0xA0004002 + # Increase Volume + CMD_INCREASEVOLUME = 0xA0004003 + # Decrease volume + CMD_DECREASEVOLUME = 0xA0004004 + # Shader toggle + CMD_SHADER_TOGGLE = 0xA0004005 + # Close App + CMD_CLOSEAPP = 0xA0004006 + # show host defined OSD message string + CMD_OSDSHOWMESSAGE = 0xA0005000 + \ No newline at end of file diff --git a/syncplay/players/mpc.py b/syncplay/players/mpc.py index ce49b8d..d22b389 100644 --- a/syncplay/players/mpc.py +++ b/syncplay/players/mpc.py @@ -82,6 +82,9 @@ class MPCHCPlayer(object): def set_speed(self, value): pass + def display_message(self, message): + pass + def make_ping(self): self.ask_for_status() diff --git a/syncplay/players/mpc_using_api.py b/syncplay/players/mpc_using_api.py index 5a5c8b5..9eb9f74 100644 --- a/syncplay/players/mpc_using_api.py +++ b/syncplay/players/mpc_using_api.py @@ -19,6 +19,12 @@ class MPCHCAPIPlayer(object): def make_ping(self): self.ask_for_status() + def display_message(self, message): + try: + self.mpc_api.send_osd(message) + except: + self.mpc_error() + def set_paused(self, value): try: if value: @@ -66,6 +72,6 @@ class MPCHCAPIPlayer(object): def run_mpc(manager, mpc_path, file_path, args): mpc = MPCHCAPIPlayer(manager) mpc.mpc_api.callbacks.on_file_ready = mpc.make_ping - mpc.mpc_api.callbacks.on_connected = lambda: mpc.mpc_api.open_file(file_path) if(file_path) else None + mpc.mpc_api.callbacks.on_connected = lambda _: mpc.mpc_api.open_file(file_path) if(file_path) else None mpc.mpc_api.start_mpc(mpc_path, args)