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