summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElias Müller <elias.mueller@mailbox.org>2020-09-28 20:24:15 +0200
committerjaseg <git@jaseg.de>2022-04-17 22:51:06 +0200
commit107c563f8d71d1eda8098f797620996ac1e60eee (patch)
treed98d93846e21b32527f20c2ecde48abef21d9566
parente8c8736a1a0af284f97f7d43a92aaea6735e8a54 (diff)
downloadpython-mpv-107c563f8d71d1eda8098f797620996ac1e60eee.tar.gz
python-mpv-107c563f8d71d1eda8098f797620996ac1e60eee.tar.bz2
python-mpv-107c563f8d71d1eda8098f797620996ac1e60eee.zip
mpv.py: add support for asynchronous commands
-rw-r--r--mpv.py52
1 files changed, 48 insertions, 4 deletions
diff --git a/mpv.py b/mpv.py
index 7ffb198..82df90b 100644
--- a/mpv.py
+++ b/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