From c90a5f692f255b9ee5bd51be65dc00a554ebf88e Mon Sep 17 00:00:00 2001 From: jaseg Date: Fri, 22 Apr 2022 23:01:13 +0200 Subject: Continue reworking event infrastrucutre. Note: We do not decode property values by default anymore. --- mpv.py | 83 ++++++++++++++++++++++++++++++++++++++----------------- tests/test_mpv.py | 16 +++++------ 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/mpv.py b/mpv.py index c78e187..5811d70 100644 --- a/mpv.py +++ b/mpv.py @@ -282,14 +282,14 @@ class MpvEventID(c_int): SEEK = 20 PLAYBACK_RESTART = 21 PROPERTY_CHANGE = 22 - EVENT_QUEUE_OVERFLOW = 24 - EVENT_HOOK = 25 + QUEUE_OVERFLOW = 24 + HOOK = 25 ANY = ( SHUTDOWN, LOG_MESSAGE, GET_PROPERTY_REPLY, SET_PROPERTY_REPLY, COMMAND_REPLY, START_FILE, END_FILE, FILE_LOADED, CLIENT_MESSAGE, VIDEO_RECONFIG, AUDIO_RECONFIG, SEEK, PLAYBACK_RESTART, PROPERTY_CHANGE) def __repr__(self): - return _mpv_event_name(self.value).decode() + return f'' @classmethod def from_str(kls, s): @@ -383,15 +383,15 @@ class MpvEvent(Structure): def data(self): dtype = { MpvEventID.GET_PROPERTY_REPLY: MpvEventProperty, - MpvEventID.PROPERTY_CHANGE: MpvEventProperty + MpvEventID.PROPERTY_CHANGE: MpvEventProperty, MpvEventID.LOG_MESSAGE: MpvEventLogMessage, MpvEventID.CLIENT_MESSAGE: MpvEventClientMessage, MpvEventID.START_FILE: MpvEventStartFile, MpvEventID.END_FILE: MpvEventEndFile, MpvEventID.HOOK: MpvEventHook, - MpvEventID.COMMAND_REPLY* MpvEventCommand, - }.get(self.event_id) - return cast(self.data, POINTER(dtype)).contents.as_dict(decoder=decoder) + MpvEventID.COMMAND_REPLY: MpvEventCommand, + }.get(self.event_id.value) + return cast(self._data, POINTER(dtype)).contents if dtype else None def as_dict(self, decoder=identity_decoder): out = cast(create_string_buffer(sizeof(MpvNode)), POINTER(MpvNode)) @@ -400,15 +400,39 @@ class MpvEvent(Structure): _mpv_free_node_contents(out) return rv + def __str__(self): + d = self.data + return f'<{type(d).__name__} ({self.event_id.value}) err={self.error} p={self.reply_userdata:016x} d={self.as_dict()}>' + class MpvEventProperty(Structure): - _fields_ = [('name', c_char_p), + _fields_ = [('_name', c_char_p), ('format', MpvFormat), ('data', MpvNodeUnion)] + @property + def name(self): + return self._name.decode("utf-8") + + @property + def value(self): + return MpvNode.node_cast_value(self.data, self.format.value) + class MpvEventLogMessage(Structure): - _fields_ = [('prefix', c_char_p), - ('level', c_char_p), - ('text', c_char_p)] + _fields_ = [('_prefix', c_char_p), + ('_level', c_char_p), + ('_text', c_char_p)] + + @property + def prefix(self): + return self._prefix.decode("utf-8") + + @property + def level(self): + return self._level.decode("utf-8") + + @property + def text(self): + return self._text class MpvEventEndFile(Structure): _fields_ = [('reason', c_int), @@ -421,29 +445,31 @@ class MpvEventEndFile(Structure): ERROR = 4 REDIRECT = 5 - # For backwards-compatibility - @property - def value(self): - return self.reason - class MpvEventStartFile(Structure): _fields_ = [('playlist_entry_id', c_ulonglong),] -class MpvEventScriptInputDispatch(Structure): - _fields_ = [('arg0', c_int), - ('type', c_char_p)] - class MpvEventClientMessage(Structure): - _fields_ = [('num_args', c_int), - ('args', POINTER(c_char_p))] + _fields_ = [('_num_args', c_int), + ('_args', POINTER(c_char_p))] + + @property + def args(self): + return [ self._args[i] for i in range(self._num_args) ] class MpvEventCommand(Structure): - _fields_ = [('result', MpvNode)] + _fields_ = [('_result', MpvNode)] + + def result(self): + return self._result.node_value() class MpvEventHook(Structure): - _fields_ = [('name', c_char_p), + _fields_ = [('_name', c_char_p), ('id', c_ulonglong),] + + @property + def name(self): + return self._name.decode("utf-8") StreamReadFn = CFUNCTYPE(c_int64, c_void_p, POINTER(c_char), c_uint64) StreamSeekFn = CFUNCTYPE(c_int64, c_void_p, c_int64) @@ -856,13 +882,15 @@ class MPV(object): for event in _event_generator(self._event_handle): try: eid = event.event_id.value + if not eid == MpvEventID.LOG_MESSAGE: + print(event) with self._event_handler_lock: if eid == MpvEventID.SHUTDOWN: self._core_shutdown = True for callback in self._event_callbacks: - callback(devent) + callback(event) if eid == MpvEventID.PROPERTY_CHANGE: pc = event.data @@ -877,6 +905,7 @@ class MPV(object): if eid == MpvEventID.CLIENT_MESSAGE: # {'event': {'args': ['key-binding', 'foo', 'u-', 'g']}, 'reply_userdata': 0, 'error': 0, 'event_id': 16} target, *args = event.data.args + target = target.decode("utf-8") if target in self._message_handlers: self._message_handlers[target](*args) @@ -1659,6 +1688,10 @@ class MPV(object): self.command('enable-section', binding_name, 'allow-hide-cursor+allow-vo-dragging') def _handle_key_binding_message(self, binding_name, key_state, key_name=None, key_char=None): + binding_name = binding_name.decode('utf-8') + key_state = key_state.decode('utf-8') + key_name = key_name.decode('utf-8') if key_name is not None else None + key_char = key_char.decode('utf-8') if key_char is not None else None self._key_binding_handlers[binding_name](key_state, key_name, key_char) def unregister_key_binding(self, keydef): diff --git a/tests/test_mpv.py b/tests/test_mpv.py index 0830e23..03cab06 100755 --- a/tests/test_mpv.py +++ b/tests/test_mpv.py @@ -243,7 +243,7 @@ class ObservePropertyTest(MpvTestCase): time.sleep(0.1) #couple frames m.terminate() # needed for synchronization of event thread - handler.assert_has_calls([mock.call('vid', 'auto')]) + handler.assert_has_calls([mock.call('vid', b'auto')]) def test_property_observer_decorator(self): handler = mock.Mock() @@ -380,7 +380,7 @@ class KeyBindingTest(MpvTestCase): def test_wait_for_property_error_forwarding(self): def run(): nonlocal self - self.m.wait_until_playing() + self.m.wait_until_playing(timeout=2) self.m.mute = True t = threading.Thread(target=run, daemon=True) t.start() @@ -396,7 +396,7 @@ class KeyBindingTest(MpvTestCase): def test_register_simple_decorator_fun_chaining(self): self.m.loop = 'inf' self.m.play(TESTVID) - self.m.wait_until_playing() + self.m.wait_until_playing(timeout=2) handler1, handler2 = mock.Mock(), mock.Mock() @@ -412,7 +412,7 @@ class KeyBindingTest(MpvTestCase): self.assertEqual(reg_test_fun.mpv_key_bindings, ['b', 'a']) def keypress_and_sync(key): - with self.m.prepare_and_wait_for_event('client_message'): + with self.m.prepare_and_wait_for_event('client_message', timeout=2): self.m.keypress(key) keypress_and_sync('a') @@ -610,7 +610,7 @@ class TestLifecycle(unittest.TestCase): handler() t = threading.Thread(target=run, daemon=True) t.start() - m.wait_until_playing() + m.wait_until_playing(timeout=2) m.mute = True t.join() m.terminate() @@ -682,7 +682,7 @@ class TestLifecycle(unittest.TestCase): m = mpv.MPV(vo=testvo, log_handler=handler) m.play(TESTVID) # Wait for playback to start - m.wait_until_playing() + m.wait_until_playing(timeout=2) m.command("print-text", 'This is a python-mpv test') m.wait_for_playback() m.terminate() @@ -713,7 +713,7 @@ class CommandTests(MpvTestCase): time.sleep(0.5) self.m.loadfile(TESTVID) - self.m.wait_until_playing() + self.m.wait_until_playing(timeout=2) self.m.sub_add(TESTSRT) self.m.wait_for_playback() @@ -727,7 +727,7 @@ class CommandTests(MpvTestCase): time.sleep(0.5) self.m.loadfile(TESTVID) - self.m.wait_until_playing() + self.m.wait_until_playing(timeout=2) self.m.command_async('sub_add', TESTSRT, callback=callback) reply = self.m.command_async('expand-text', 'test ${mute}') assert reply.result() == 'test no' -- cgit