summaryrefslogtreecommitdiff
path: root/mpv.py
diff options
context:
space:
mode:
authorjaseg <code@jaseg.net>2016-02-19 14:00:17 +0100
committerjaseg <code@jaseg.net>2016-02-19 14:06:59 +0100
commit51869a91843e2ecb67284d47c47ce38ccde4c007 (patch)
tree77cfb0d61a19d0eec40074df40062a0ae8d62e8a /mpv.py
parent1dd8329a426aa83c274c266c4f54062784b9dbb0 (diff)
downloadpython-mpv-51869a91843e2ecb67284d47c47ce38ccde4c007.tar.gz
python-mpv-51869a91843e2ecb67284d47c47ce38ccde4c007.tar.bz2
python-mpv-51869a91843e2ecb67284d47c47ce38ccde4c007.zip
Properly release resources after use
Namely, let the event handler thread terminate itself cleanly and remove reference leak so __del__ can do its job.
Diffstat (limited to 'mpv.py')
-rw-r--r--mpv.py60
1 files changed, 38 insertions, 22 deletions
diff --git a/mpv.py b/mpv.py
index d7733e4..d07503f 100644
--- a/mpv.py
+++ b/mpv.py
@@ -4,6 +4,7 @@ import threading
import os
import asyncio
from warnings import warn
+from functools import partial
# vim: ts=4 sw=4
@@ -204,6 +205,7 @@ _mpv_free = backend.mpv_free
backend.mpv_create.restype = MpvHandle
_mpv_create = backend.mpv_create
+_handle_func('mpv_create_client', [c_char_p], MpvHandle)
_handle_func('mpv_client_name', [], c_char_p)
_handle_func('mpv_initialize')
_handle_func('mpv_detach_destroy', [], c_int)
@@ -278,6 +280,25 @@ def load_lua():
youtube urls. """
CDLL('liblua.so', mode=RTLD_GLOBAL)
+
+def _event_loop(event_handle, _playback_cond, event_callbacks, _property_handlers):
+ for event in _event_generator(event_handle):
+ devent = event.as_dict() # copy data from ctypes
+ eid = devent['event_id']
+ if eid in (MpvEventID.SHUTDOWN, MpvEventID.END_FILE, MpvEventID.PAUSE):
+ with _playback_cond:
+ _playback_cond.notify_all()
+ if eid == MpvEventID.PROPERTY_CHANGE:
+ _property_handlers[devent['reply_userdata']](devent['event'])
+ if eid == MpvEventID.LOG_MESSAGE and log_handler is not None:
+ ev = devent['event']
+ log_handler('{}: {}: {}'.format(ev['level'], ev['prefix'], ev['text']))
+ for callback in event_callbacks:
+ callback.call(devent)
+ if eid == MpvEventID.SHUTDOWN:
+ _mpv_detach_destroy(event_handle)
+ return
+
class MPV:
""" See man mpv(1) for the details of the implemented commands. """
def __init__(self, log_handler=None, **kwargs):
@@ -287,41 +308,36 @@ class MPV:
self.handle = _mpv_create()
+ _mpv_set_option_string(self.handle, b'audio-display', b'no')
+ istr = lambda o: ('yes' if o else 'no') if type(o) is bool else str(o)
+ for k,v in kwargs.items():
+ _mpv_set_option_string(self.handle, k.replace('_', '-').encode(), istr(v).encode())
+ _mpv_initialize(self.handle)
+
self.event_callbacks = []
self._property_handlers = {}
self._playback_cond = threading.Condition()
- def event_loop():
- for event in _event_generator(self.handle):
- devent = event.as_dict() # copy data from ctypes
- if devent['event_id'] in (MpvEventID.SHUTDOWN, MpvEventID.END_FILE, MpvEventID.PAUSE):
- with self._playback_cond:
- self._playback_cond.notify_all()
- if devent['event_id'] == MpvEventID.PROPERTY_CHANGE:
- self._property_handlers[devent['reply_userdata']](devent['event'])
- if devent['event_id'] == MpvEventID.LOG_MESSAGE and log_handler is not None:
- ev = devent['event']
- log_handler('{}: {}: {}'.format(ev['level'], ev['prefix'], ev['text']))
- for callback in self.event_callbacks:
- callback.call()
- self._event_thread = threading.Thread(target=event_loop, daemon=True)
+ self._event_handle = _mpv_create_client(self.handle, b'mpv-python-event-handler-thread')
+ loop = partial(_event_loop, self._event_handle, self._playback_cond, self.event_callbacks, self._property_handlers)
+ self._event_thread = threading.Thread(target=loop, daemon=True, name='MPVEventHandlerThread')
self._event_thread.start()
if log_handler is not None:
self.set_loglevel('terminal-default')
-
- _mpv_set_option_string(self.handle, b'audio-display', b'no')
- istr = lambda o: ('yes' if o else 'no') if type(o) is bool else str(o)
- for k,v in kwargs.items():
- _mpv_set_option_string(self.handle, k.replace('_', '-').encode(), istr(v).encode())
- _mpv_initialize(self.handle)
def wait_for_playback(self):
""" Waits until playback of the current title is paused or done """
with self._playback_cond:
self._playback_cond.wait()
-# def __del__(self):
-# _mpv_terminate_destroy(self.handle)
+ def __del__(self):
+ if self.handle:
+ self.terminate()
+
+ def terminate(self):
+ self.handle, handle = None, self.handle
+ _mpv_terminate_destroy(handle)
+ self._event_thread.join()
def set_loglevel(self, level):
_mpv_request_log_messages(self.handle, level.encode())