Fixes on mpc-api

This commit is contained in:
Uriziel 2012-06-29 21:59:32 +02:00
parent 777a3a66f0
commit 4fda98492b
4 changed files with 165 additions and 199 deletions

View File

@ -15,7 +15,10 @@ def prepare_args(args):
args.args.extend(['/open', '/new']) args.args.extend(['/open', '/new'])
if __name__ == '__main__': if __name__ == '__main__':
args = utils.get_configuration() try:
prepare_args(args) args = utils.get_configuration()
manager = client.Manager(args.host, args.port, args.name, lambda m: mpc_using_api.run_mpc(m, args.mpc_path, args.file, args.args)) prepare_args(args)
manager.start() manager = client.Manager(args.host, args.port, args.name, lambda m: mpc_using_api.run_mpc(m, args.mpc_path, args.file, args.args))
manager.start()
finally:
manager.stop()

View File

@ -212,6 +212,7 @@ class Manager(object):
command_handler = threading.Thread(target = utils.stdin_thread, args = (self,), name = "Command handler") command_handler = threading.Thread(target = utils.stdin_thread, args = (self,), name = "Command handler")
command_handler.setDaemon(True) command_handler.setDaemon(True)
command_handler.start() command_handler.start()
def start(self): def start(self):
if self.running: if self.running:
return return

View File

@ -1,29 +1,22 @@
#coding:utf8 #coding:utf8
import threading,time,thread
import win32con, win32api, win32gui, ctypes, ctypes.wintypes import win32con, win32api, win32gui, ctypes, ctypes.wintypes
import os, thread
class MPC_API: class MPC_API:
def __init__(self, enforce_custom_handler = False): 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
self.__position_request_warden = False
self.__seek_warden = False
self.__playpause_warden = False
''' '''
List of callbacks that can be set List of callbacks that can be set
Each callback by default receives a tuple argument, can be empty though on_connected (0 args)
on_connected on_seek (0 args)
on_seek on_update_filename (filename)
on_update_filename on_update_file_duration (duration)
on_update_file_duration on_update_position (positon)
on_update_position on_update_playstate (playstate)
on_update_playstate on_file_ready (filename)
on_file_ready custom_handler (cmd, value)
custom_handler
''' '''
self.enforce_custom_handler = enforce_custom_handler
self.callbacks = self.__CALLBACKS() self.callbacks = self.__CALLBACKS()
self.loadstate = None self.loadstate = None
self.playstate = None self.playstate = None
@ -33,13 +26,15 @@ class MPC_API:
Most likely won't be up to date unless you ask API to refresh it Most likely won't be up to date unless you ask API to refresh it
''' '''
self.lastfileposition = None self.lastfileposition = None
self.__playpause_warden = False
self.__locks = self.__LOCKS()
self.__mpc_ready_checking = threading.Thread(target = self.__mpc_ready_in_slave_mode, name ="Check MPC window")
self.__mpc_ready_checking.setDaemon(True)
self.__listener = self.__Listener(self, self.__locks)
self.__listener.setDaemon(True)
self.__listener.start()
self.__locks.listner_start.wait()
'''
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
''' '''
@ -47,16 +42,17 @@ class MPC_API:
will start mpc-hc in a slave mode will start mpc-hc in a slave mode
''' '''
def start_mpc(self, path, args = ()): 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 args = "%s /slave %s" % (" ".join(args), str(self.__listener.hwnd))
while(self.__listener.mpc_handle == None and is_starting): continue win32api.ShellExecute(0, "open", path, args, None, 1)
if(not self.__locks.mpc_start.wait(10)):
raise MPC_API.NoSlaveDetectedException("Unable to start MPC in slave mode!")
self.__mpc_ready_checking.start()
''' '''
Checks if api is ready to receive commands Checks if api is ready to receive commands
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def is_api_ready(self): 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 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) return (file_state_ok and listener_ok)
@ -66,7 +62,6 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def is_file_ready(self): 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) return (self.loadstate == self.__MPC_LOADSTATE.MLS_LOADED and self.fileplaying and self.playstate <> None)
''' '''
@ -74,7 +69,6 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def open_file(self, file_path): def open_file(self, file_path):
self.__mpc_ready_in_slave_mode()
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_OPENFILE, file_path) self.__listener.SendCommand(MPC_API_COMMANDS.CMD_OPENFILE, file_path)
''' '''
@ -82,7 +76,6 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def is_paused(self): def is_paused(self):
self.__mpc_ready_in_slave_mode()
return (self.playstate <> self.__MPC_PLAYSTATE.PS_PLAY and self.playstate <> None) return (self.playstate <> self.__MPC_PLAYSTATE.PS_PLAY and self.playstate <> None)
''' '''
@ -90,39 +83,31 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def pause(self): def pause(self):
self.__mpc_ready_in_slave_mode() if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
while(self.playstate == None):self.__mpc_ready_in_slave_mode() if(not self.is_paused()):
if(not self.is_paused() and self.__playpause_warden == False): self.playpause()
self.__playpause_warden = True
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE)
while(not self.is_paused()): self.__mpc_ready_in_slave_mode()
self.__playpause_warden = False
''' '''
Play paused file Play paused file
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def unpause(self): def unpause(self):
self.__mpc_ready_in_slave_mode() if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
while(self.playstate == None):self.__mpc_ready_in_slave_mode() if(self.is_paused()):
if(self.is_paused() and self.__playpause_warden == False): self.playpause()
self.__playpause_warden = True
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE)
while(self.is_paused()): self.__mpc_ready_in_slave_mode()
self.__playpause_warden = False
''' '''
Toggle play/pause Toggle play/pause
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def playpause(self): def playpause(self):
self.__mpc_ready_in_slave_mode() if(not self.is_file_ready()): raise MPC_API.PlayerNotReadyException("Playstate change on no file")
tmp = self.playstate tmp = self.playstate
while(self.playstate == None):self.__mpc_ready_in_slave_mode()
if(self.__playpause_warden == False): if(self.__playpause_warden == False):
self.__playpause_warden = True self.__playpause_warden = True
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE) self.__listener.SendCommand(MPC_API_COMMANDS.CMD_PLAYPAUSE)
while(tmp == self.playstate): self.__mpc_ready_in_slave_mode() while(tmp == self.playstate and self.__playpause_warden): continue
self.__playpause_warden = False self.__playpause_warden = False
if(tmp == self.playstate): self.playpause() #playstate changed manualy after issuing a command
''' '''
Asks mpc for it's current file position, if on_update_position callback is set Asks mpc for it's current file position, if on_update_position callback is set
@ -130,22 +115,23 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def ask_for_current_position(self): def ask_for_current_position(self):
self.__mpc_ready_in_slave_mode() if(not self.is_file_ready()):
if(not self.__position_request_warden): raise MPC_API.PlayerNotReadyException("File not yet ready")
self.__position_request_warden = True self.__locks.positionget.clear()
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_GETCURRENTPOSITION) 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() if(not self.callbacks.on_update_position):
return self.lastfileposition if(not self.__locks.positionget.wait(0.2)):
raise MPC_API.PlayerNotReadyException("Position get fail")
return self.lastfileposition
''' '''
Given a position in seconds will ask client to seek there Given a position in seconds will ask client to seek there
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def seek(self, position): def seek(self, position):
self.__mpc_ready_in_slave_mode() self.__locks.seek.clear()
self.__seek_warden = True
self.__listener.SendCommand(MPC_API_COMMANDS.CMD_SETPOSITION, unicode(position)) self.__listener.SendCommand(MPC_API_COMMANDS.CMD_SETPOSITION, unicode(position))
while(self.__seek_warden): self.__mpc_ready_in_slave_mode() if(not self.__locks.seek.wait(0.2)):
raise MPC_API.PlayerNotReadyException("Seek fail")
''' '''
@param message: unicode string to display in player @param message: unicode string to display in player
@param MsgPos: Either 1, left top corner or 2, right top corner, defaults to 2 @param MsgPos: Either 1, left top corner or 2, right top corner, defaults to 2
@ -153,7 +139,6 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def send_osd(self, message, MsgPos = 2, DurationMs = 3000): def send_osd(self, message, MsgPos = 2, DurationMs = 3000):
self.__mpc_ready_in_slave_mode()
class __OSDDATASTRUCT(ctypes.Structure): class __OSDDATASTRUCT(ctypes.Structure):
_fields_ = [ _fields_ = [
('nMsgPos', ctypes.c_int32), ('nMsgPos', ctypes.c_int32),
@ -173,7 +158,6 @@ class MPC_API:
Throws MPC_API.NoSlaveDetectedException if mpc window is not found Throws MPC_API.NoSlaveDetectedException if mpc window is not found
''' '''
def send_raw_command(self, cmd, value): def send_raw_command(self, cmd, value):
self.__mpc_ready_in_slave_mode()
self.__listener.SendCommand(cmd, value) self.__listener.SendCommand(cmd, value)
''' '''
@ -184,44 +168,49 @@ class MPC_API:
''' '''
def handle_command(self,cmd, value, enforce_custom_handler = False): 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): if((self.enforce_custom_handler or enforce_custom_handler) and self.callbacks.custom_handler <> None):
self.callbacks.custom_handler((cmd, value,)) thread.start_new_thread(self.callbacks.custom_handler,(cmd, value,))
else: else:
if (cmd == MPC_API_COMMANDS.CMD_CONNECT): if (cmd == MPC_API_COMMANDS.CMD_CONNECT):
self.__listener.mpc_handle = int(value) self.__listener.mpc_handle = int(value)
self.__locks.mpc_start.set()
if(self.callbacks.on_connected): if(self.callbacks.on_connected):
self.callbacks.on_connected(()) thread.start_new_thread(self.callbacks.on_connected, ())
elif(cmd == MPC_API_COMMANDS.CMD_STATE): elif(cmd == MPC_API_COMMANDS.CMD_STATE):
self.loadstate = int(value) self.loadstate = int(value)
elif(cmd == MPC_API_COMMANDS.CMD_PLAYMODE): elif(cmd == MPC_API_COMMANDS.CMD_PLAYMODE):
self.playstate = int(value) self.playstate = int(value)
if(self.callbacks.on_update_playstate): self.callbacks.on_update_playstate((self.playstate,)) if(self.callbacks.on_update_playstate): thread.start_new_thread(self.callbacks.on_update_playstate,(self.playstate,))
elif(cmd == MPC_API_COMMANDS.CMD_NOWPLAYING): 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() self.fileplaying = value.split('|')[3].split('\\').pop()
if(self.callbacks.on_update_filename): self.callbacks.on_update_filename((self.fileplaying,)) if(self.callbacks.on_update_filename): thread.start_new_thread(self.callbacks.on_update_filename,(self.fileplaying,))
self.fileduration = int(value.split('|')[4]) self.fileduration = int(value.split('|')[4])
if(self.callbacks.on_update_file_duration): self.callbacks.on_update_file_duration((self.fileplaying,)) 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): elif(cmd == MPC_API_COMMANDS.CMD_CURRENTPOSITION):
self.lastfileposition = float(value) self.lastfileposition = float(value)
self.__position_request_warden = False self.__locks.positionget.set()
if(self.callbacks.on_update_position): self.callbacks.on_update_position((self.lastfileposition,)) if(self.callbacks.on_update_position): thread.start_new_thread(self.callbacks.on_update_position,(self.lastfileposition,))
elif(cmd == MPC_API_COMMANDS.CMD_NOTIFYSEEK): elif(cmd == MPC_API_COMMANDS.CMD_NOTIFYSEEK):
self.__seek_warden = False self.__locks.seek.set()
if(self.lastfileposition <> float(value)): #Notify seek is sometimes sent twice if(self.lastfileposition <> float(value)): #Notify seek is sometimes sent twice
self.lastfileposition = float(value) self.lastfileposition = float(value)
if(self.callbacks.on_seek): self.callbacks.on_seek((self.lastfileposition,)) if(self.callbacks.on_seek): thread.start_new_thread(self.callbacks.on_seek,(self.lastfileposition,))
else: else:
if(self.callbacks.custom_handler <> None): if(self.callbacks.custom_handler <> None):
self.callbacks.custom_handler((cmd, value,)) thread.start_new_thread(self.callbacks.custom_handler,(cmd, value,))
class NoSlaveDetectedException(Exception): class NoSlaveDetectedException(Exception):
def __init__(self, message): def __init__(self, message):
Exception.__init__(self, message) Exception.__init__(self, message)
class PlayerNotReadyException(Exception):
def __init__(self, message):
Exception.__init__(self, message)
class __CALLBACKS: class __CALLBACKS:
def __init__(self): def __init__(self):
@ -233,11 +222,21 @@ class MPC_API:
self.on_update_playstate = None self.on_update_playstate = None
self.on_file_ready = None self.on_file_ready = None
self.custom_handler = None self.custom_handler = None
self.on_mpc_closed = None
class __LOCKS:
def __init__(self):
self.listner_start = threading.Event()
self.mpc_start = threading.Event()
self.positionget = threading.Event()
self.seek = threading.Event()
def __mpc_ready_in_slave_mode(self): def __mpc_ready_in_slave_mode(self):
if not win32gui.IsWindow(self.__listener.mpc_handle): while(True):
raise MPC_API.NoSlaveDetectedException("MPC Slave Window not detected") time.sleep(1)
if not win32gui.IsWindow(self.__listener.mpc_handle):
self.callbacks.on_mpc_closed()
break
class __MPC_LOADSTATE: class __MPC_LOADSTATE:
MLS_CLOSED = 0 MLS_CLOSED = 0
@ -259,9 +258,16 @@ class MPC_API:
''' '''
class __Listener: class __Listener(threading.Thread):
def __init__(self, mpc_api): def __init__(self, mpc_api, locks):
self.__mpc_api = mpc_api
self.locks = locks
self.mpc_handle = None self.mpc_handle = None
self.hwnd = None
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT)
threading.Thread.__init__(self, name="MPC Listener")
def run(self):
message_map = { message_map = {
win32con.WM_COPYDATA: self.OnCopyData win32con.WM_COPYDATA: self.OnCopyData
} }
@ -283,12 +289,10 @@ class MPC_API:
hinst, hinst,
None None
) )
self.__PCOPYDATASTRUCT = ctypes.POINTER(self.__COPYDATASTRUCT) self.locks.listner_start.set()
self.__mpc_api = mpc_api
mpc_api.register_listener(self)
win32gui.PumpMessages() win32gui.PumpMessages()
def OnCopyData(self, hwnd, msg, wparam, lparam): def OnCopyData(self, hwnd, msg, wparam, lparam):
pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT) pCDS = ctypes.cast(lparam, self.__PCOPYDATASTRUCT)
# print ">>> 0x%X" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData) # print ">>> 0x%X" % int(pCDS.contents.dwData), ctypes.wstring_at(pCDS.contents.lpData)
@ -321,130 +325,44 @@ class MPC_API:
] ]
class MPC_API_COMMANDS(): class MPC_API_COMMANDS():
# Send after connection
# Par 1 : MPC window handle (command should be send to this HWnd)
CMD_CONNECT = 0x50000000 CMD_CONNECT = 0x50000000
# Send when opening or closing file
# Par 1 : current state (see MPC_LOADSTATE enum)
CMD_STATE = 0x50000001 CMD_STATE = 0x50000001
# Send when playing, pausing or closing file
# Par 1 : current play mode (see MPC_PLAYSTATE enum)
CMD_PLAYMODE = 0x50000002 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 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 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 CMD_LISTAUDIOTRACKS = 0x50000005
# Send current playback position in responce
# of CMD_GETCURRENTPOSITION.
# Par 1 : current position in seconds
CMD_CURRENTPOSITION = 0x50000007 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 CMD_NOTIFYSEEK = 0x50000008
# Notify the end of current playback
# (Automatically sent).
# Par 1 : none.
CMD_NOTIFYENDOFSTREAM = 0x50000009 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 CMD_PLAYLIST = 0x50000006
# ==== Commands from host to MPC
# Open new file
# Par 1 : file path
CMD_OPENFILE = 0xA0000000 CMD_OPENFILE = 0xA0000000
# Stop playback, but keep file / playlist
CMD_STOP = 0xA0000001 CMD_STOP = 0xA0000001
# Stop playback and close file / playlist
CMD_CLOSEFILE = 0xA0000002 CMD_CLOSEFILE = 0xA0000002
# Pause or restart playback
CMD_PLAYPAUSE = 0xA0000003 CMD_PLAYPAUSE = 0xA0000003
# Add a new file to playlist (did not start playing)
# Par 1 : file path
CMD_ADDTOPLAYLIST = 0xA0001000 CMD_ADDTOPLAYLIST = 0xA0001000
# Remove all files from playlist
CMD_CLEARPLAYLIST = 0xA0001001 CMD_CLEARPLAYLIST = 0xA0001001
# Start playing playlist
CMD_STARTPLAYLIST = 0xA0001002 CMD_STARTPLAYLIST = 0xA0001002
CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO CMD_REMOVEFROMPLAYLIST = 0xA0001003 # TODO
# Cue current file to specific position
# Par 1 : new position in seconds
CMD_SETPOSITION = 0xA0002000 CMD_SETPOSITION = 0xA0002000
# Set the audio delay
# Par 1 : new audio delay in ms
CMD_SETAUDIODELAY = 0xA0002001 CMD_SETAUDIODELAY = 0xA0002001
# Set the subtitle delay
# Par 1 : new subtitle delay in ms
CMD_SETSUBTITLEDELAY = 0xA0002002 CMD_SETSUBTITLEDELAY = 0xA0002002
# Set the active file in the playlist CMD_SETINDEXPLAYLIST = 0xA0002003 # DOESNT WORK
# 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 CMD_SETAUDIOTRACK = 0xA0002004
# Set the subtitle track
# Par 1 : index of the subtitle track, -1 for disabling subtitles
CMD_SETSUBTITLETRACK = 0xA0002005 CMD_SETSUBTITLETRACK = 0xA0002005
# Ask for a list of the subtitles tracks of the file
# return a CMD_LISTSUBTITLETRACKS
CMD_GETSUBTITLETRACKS = 0xA0003000 CMD_GETSUBTITLETRACKS = 0xA0003000
# Ask for the current playback position,
# see CMD_CURRENTPOSITION.
# Par 1 : current position in seconds
CMD_GETCURRENTPOSITION = 0xA0003004 CMD_GETCURRENTPOSITION = 0xA0003004
# Jump forward/backward of N seconds,
# Par 1 : seconds (negative values for backward)
CMD_JUMPOFNSECONDS = 0xA0003005 CMD_JUMPOFNSECONDS = 0xA0003005
# Ask for a list of the audio tracks of the file
# return a CMD_LISTAUDIOTRACKS
CMD_GETAUDIOTRACKS = 0xA0003001 CMD_GETAUDIOTRACKS = 0xA0003001
# Ask for the properties of the current loaded file
# return a CMD_NOWPLAYING
CMD_GETNOWPLAYING = 0xA0003002 CMD_GETNOWPLAYING = 0xA0003002
# Ask for the current playlist
# return a CMD_PLAYLIST
CMD_GETPLAYLIST = 0xA0003003 CMD_GETPLAYLIST = 0xA0003003
# Toggle FullScreen
CMD_TOGGLEFULLSCREEN = 0xA0004000 CMD_TOGGLEFULLSCREEN = 0xA0004000
# Jump forward(medium)
CMD_JUMPFORWARDMED = 0xA0004001 CMD_JUMPFORWARDMED = 0xA0004001
# Jump backward(medium)
CMD_JUMPBACKWARDMED = 0xA0004002 CMD_JUMPBACKWARDMED = 0xA0004002
# Increase Volume
CMD_INCREASEVOLUME = 0xA0004003 CMD_INCREASEVOLUME = 0xA0004003
# Decrease volume
CMD_DECREASEVOLUME = 0xA0004004 CMD_DECREASEVOLUME = 0xA0004004
# Shader toggle
CMD_SHADER_TOGGLE = 0xA0004005 CMD_SHADER_TOGGLE = 0xA0004005
# Close App
CMD_CLOSEAPP = 0xA0004006 CMD_CLOSEAPP = 0xA0004006
# show host defined OSD message string
CMD_OSDSHOWMESSAGE = 0xA0005000 CMD_OSDSHOWMESSAGE = 0xA0005000

View File

@ -1,6 +1,7 @@
#coding:utf8 #coding:utf8
from ..mpc_api import MPC_API from ..mpc_api import MPC_API
from twisted.internet import reactor import time
class MPCHCAPIPlayer(object): class MPCHCAPIPlayer(object):
def __init__(self, manager): def __init__(self, manager):
@ -11,24 +12,37 @@ class MPCHCAPIPlayer(object):
self.tmp_filename = None self.tmp_filename = None
self.tmp_position = None self.tmp_position = None
self.mpc_api.callbacks.on_file_ready = lambda _: reactor.callLater(0.7,self.make_ping) self.mpc_api.callbacks.on_file_ready = lambda: self.make_ping()
self.mpc_api.callbacks.on_mpc_closed = lambda: self.mpc_error("MPC closed")
self.semaphore_filename = False
def drop(self): def drop(self):
pass pass
def set_speed(self, value): def set_speed(self, value):
pass pass
def test_mpc_ready(self):
try:
self.mpc_api.ask_for_current_position()
except MPC_API.PlayerNotReadyException:
time.sleep(0.1)
self.test_mpc_ready()
return
def make_ping(self): def make_ping(self):
self.mpc_api.callbacks.on_file_ready = None
self.test_mpc_ready()
self.manager.init_player(self) self.manager.init_player(self)
self.pinged = True self.pinged = True
self.ask_for_status() self.ask_for_status()
self.mpc_api.callbacks.on_file_ready = None
def display_message(self, message): def display_message(self, message):
try: try:
self.mpc_api.send_osd(message) self.mpc_api.send_osd(message)
except: except Exception, err:
self.mpc_error() self.mpc_error(err)
def set_paused(self, value): def set_paused(self, value):
try: try:
@ -36,49 +50,79 @@ class MPCHCAPIPlayer(object):
self.mpc_api.pause() self.mpc_api.pause()
else: else:
self.mpc_api.unpause() self.mpc_api.unpause()
except: except MPC_API.PlayerNotReadyException:
self.mpc_error() time.sleep(0.2)
self.set_paused(value)
return
except Exception, err:
self.mpc_error(err)
def set_position(self, value): def set_position(self, value):
try: try:
self.mpc_api.seek(value) self.mpc_api.seek(value)
except: except MPC_API.PlayerNotReadyException:
self.mpc_error() self.set_position(value)
return
except Exception, err:
self.mpc_error(err)
def ask_for_status(self): def ask_for_status(self):
position = self.tmp_position if self.tmp_position else 0 position = self.tmp_position if self.tmp_position else 0
paused = None paused = None
try: try:
if(self.mpc_api.is_file_ready()): 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): if(self.tmp_filename <> self.mpc_api.fileplaying):
self.handle_updated_filename(self.mpc_api.fileplaying) self.handle_updated_filename(self.mpc_api.fileplaying)
return return
position = self.mpc_api.ask_for_current_position()
paused = self.mpc_api.is_paused() paused = self.mpc_api.is_paused()
position = float(position) position = float(position)
self.tmp_position = position self.tmp_position = position
self.manager.update_player_status(paused, position) self.manager.update_player_status(paused, position)
else: else:
self.manager.update_player_status(True, self.manager.get_global_position()) self.manager.update_player_status(True, self.manager.get_global_position())
except MPC_API.NoSlaveDetectedException: except Exception, err:
self.mpc_error() self.mpc_error(err)
def handle_updated_filename(self,filename): def __force_pause(self, filename, position):
self.set_paused(True)
time.sleep(0.1)
if (not self.mpc_api.is_paused()):
self.__set_up_newly_opened_file(filename, position)
def __set_up_newly_opened_file(self, filename, position):
self.test_mpc_ready()
try:
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.manager.get_global_position() position = self.manager.get_global_position()
if(self.semaphore_filename):
self.manager.update_player_status(True, position)
return
self.semaphore_filename = True
self.__set_up_newly_opened_file(filename, position)
self.tmp_filename = filename self.tmp_filename = filename
self.manager.update_filename(str(self.tmp_filename)) self.manager.update_filename(str(self.tmp_filename))
self.mpc_api.seek(position)
reactor.callLater(0.7, self.set_paused, True)
self.manager.update_player_status(True, position) self.manager.update_player_status(True, position)
self.semaphore_filename = False
def mpc_error(self): def mpc_error(self, err=""):
print "ERROR:", str(err) + ',', "desu"
if self.manager.running: if self.manager.running:
print 'Failed to connect to MPC-HC API!' print 'Failed to connect to MPC-HC API!'
self.manager.stop() self.manager.stop()
def run_mpc(manager, mpc_path, file_path, args): def run_mpc(manager, mpc_path, file_path, args):
mpc = MPCHCAPIPlayer(manager) mpc = MPCHCAPIPlayer(manager)
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) mpc.mpc_api.start_mpc(mpc_path, args)