mpv.py: add support for asynchronous commands
This commit is contained in:
parent
e8c8736a1a
commit
107c563f8d
1 changed files with 48 additions and 4 deletions
52
mpv.py
52
mpv.py
|
|
@ -390,6 +390,7 @@ class MpvEvent(Structure):
|
|||
|
||||
def as_dict(self, decoder=identity_decoder):
|
||||
dtype = {MpvEventID.END_FILE: MpvEventEndFile,
|
||||
MpvEventID.COMMAND_REPLY: MpvEventCommand,
|
||||
MpvEventID.PROPERTY_CHANGE: MpvEventProperty,
|
||||
MpvEventID.GET_PROPERTY_REPLY: MpvEventProperty,
|
||||
MpvEventID.LOG_MESSAGE: MpvEventLogMessage,
|
||||
|
|
@ -455,6 +456,14 @@ class MpvEventClientMessage(Structure):
|
|||
def as_dict(self, decoder=identity_decoder):
|
||||
return { 'args': [ self.args[i].decode('utf-8') for i in range(self.num_args) ] }
|
||||
|
||||
|
||||
class MpvEventCommand(Structure):
|
||||
_fields_ = [('result', MpvNode)]
|
||||
|
||||
def as_dict(self, decoder=identity_decoder):
|
||||
return {'result': self.result.node_value(decoder)}
|
||||
|
||||
|
||||
StreamReadFn = CFUNCTYPE(c_int64, c_void_p, POINTER(c_char), c_uint64)
|
||||
StreamSeekFn = CFUNCTYPE(c_int64, c_void_p, c_int64)
|
||||
StreamSizeFn = CFUNCTYPE(c_int64, c_void_p)
|
||||
|
|
@ -544,7 +553,6 @@ _handle_func('mpv_command', [POINTER(c_char_p)],
|
|||
_handle_func('mpv_command_string', [c_char_p, c_char_p], c_int, ec_errcheck)
|
||||
_handle_func('mpv_command_async', [c_ulonglong, POINTER(c_char_p)], c_int, ec_errcheck)
|
||||
_handle_func('mpv_command_node', [POINTER(MpvNode), POINTER(MpvNode)], c_int, ec_errcheck)
|
||||
_handle_func('mpv_command_async', [c_ulonglong, POINTER(MpvNode)], c_int, ec_errcheck)
|
||||
|
||||
_handle_func('mpv_set_property', [c_char_p, MpvFormat, c_void_p], c_int, ec_errcheck)
|
||||
_handle_func('mpv_set_property_string', [c_char_p, c_char_p], c_int, ec_errcheck)
|
||||
|
|
@ -641,6 +649,12 @@ def _event_generator(handle):
|
|||
yield event
|
||||
|
||||
|
||||
def _create_null_term_cmd_arg_array(name, args):
|
||||
args = [name.encode('utf-8')] + [(arg if type(arg) is bytes else str(arg).encode('utf-8'))
|
||||
for arg in args if arg is not None] + [None]
|
||||
return (c_char_p * len(args))(*args)
|
||||
|
||||
|
||||
_py_to_mpv = lambda name: name.replace('_', '-')
|
||||
_mpv_to_py = lambda name: name.replace('-', '_')
|
||||
|
||||
|
|
@ -805,6 +819,9 @@ class MPV(object):
|
|||
|
||||
To make your program not barf hard the first time its used on a weird file system **always** access properties
|
||||
containing file names or file tags through ``MPV.raw``. """
|
||||
|
||||
_UINT_64_MAX = 2 ** 64 - 1
|
||||
|
||||
def __init__(self, *extra_mpv_flags, log_handler=None, start_event_thread=True, loglevel=None, **extra_mpv_opts):
|
||||
"""Create an MPV instance.
|
||||
|
||||
|
|
@ -832,6 +849,9 @@ class MPV(object):
|
|||
self.lazy = _DecoderPropertyProxy(self, lazy_decoder)
|
||||
|
||||
self._event_callbacks = []
|
||||
self._event_async_callbacks = {}
|
||||
self._event_async_callback_counter = 0
|
||||
self._event_async_callback_counter_lock = threading.Lock()
|
||||
self._event_handler_lock = threading.Lock()
|
||||
self._property_handlers = collections.defaultdict(lambda: [])
|
||||
self._quit_handlers = set()
|
||||
|
|
@ -884,6 +904,15 @@ class MPV(object):
|
|||
if target in self._message_handlers:
|
||||
self._message_handlers[target](*args)
|
||||
|
||||
if eid == MpvEventID.COMMAND_REPLY:
|
||||
key = devent['reply_userdata']
|
||||
err = devent['error']
|
||||
callback = self._event_async_callbacks.pop(key, None)
|
||||
if callback:
|
||||
callback(err, devent['event']['result'])
|
||||
elif err:
|
||||
warn('Error while executing asynchronous command')
|
||||
|
||||
if eid == MpvEventID.SHUTDOWN:
|
||||
_mpv_destroy(self._event_handle)
|
||||
return
|
||||
|
|
@ -1072,9 +1101,17 @@ class MPV(object):
|
|||
|
||||
def string_command(self, name, *args):
|
||||
"""Execute a raw command."""
|
||||
args = [name.encode('utf-8')] + [ (arg if type(arg) is bytes else str(arg).encode('utf-8'))
|
||||
for arg in args if arg is not None ] + [None]
|
||||
_mpv_command(self.handle, (c_char_p*len(args))(*args))
|
||||
args = _create_null_term_cmd_arg_array(name, args)
|
||||
_mpv_command(self.handle, args)
|
||||
|
||||
def command_async(self, name, *args, callback=None):
|
||||
"""Same as mpv_command, but run the command asynchronously. Once the command ran, the callback will be invoked,
|
||||
if provided. The first argument of the callback will be a boolean value. It will be set to True, if there was an
|
||||
error, False else. The second argument of the callback depends on the command.
|
||||
"""
|
||||
args = _create_null_term_cmd_arg_array(name, args)
|
||||
key = self._register_async_callback(callback)
|
||||
_mpv_command_async(self._event_handle, key, args)
|
||||
|
||||
def node_command(self, name, *args, decoder=strict_decoder):
|
||||
self.command(name, *args, decoder=decoder)
|
||||
|
|
@ -1853,6 +1890,13 @@ class MPV(object):
|
|||
except AttributeError:
|
||||
return None
|
||||
|
||||
def _register_async_callback(self, callback):
|
||||
with self._event_async_callback_counter_lock:
|
||||
key = self._event_async_callback_counter = (self._event_async_callback_counter + 1) % MPV._UINT_64_MAX
|
||||
self._event_async_callbacks[key] = callback
|
||||
return key
|
||||
|
||||
|
||||
class MpvRenderContext:
|
||||
def __init__(self, mpv, api_type, **kwargs):
|
||||
self._mpv = mpv
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue