Update python-mpv-jsonipc to v1.1.11

This commit is contained in:
et0h 2020-05-13 22:46:20 +01:00
parent 956ba07e0a
commit 532a991692
3 changed files with 61 additions and 28 deletions

View File

@ -31,7 +31,7 @@ function will be called for each inbound message.
#### \_\_init\_\_ #### \_\_init\_\_
``` python ``` python
| __init__(ipc_socket, callback=None) | __init__(ipc_socket, callback=None, quit_callback=None)
``` ```
Create the wrapper. Create the wrapper.
@ -87,7 +87,7 @@ function will be called for each inbound message.
#### \_\_init\_\_ #### \_\_init\_\_
``` python ``` python
| __init__(ipc_socket, callback=None) | __init__(ipc_socket, callback=None, quit_callback=None)
``` ```
Create the wrapper. Create the wrapper.
@ -175,7 +175,7 @@ Low-level interface to MPV. Does NOT manage an mpv process. (Internal)
#### \_\_init\_\_ #### \_\_init\_\_
``` python ``` python
| __init__(ipc_socket, callback=None) | __init__(ipc_socket, callback=None, quit_callback=None)
``` ```
Create the wrapper. Create the wrapper.
@ -293,7 +293,7 @@ list is used. Not all commands may actually work when this fallback is used.
#### \_\_init\_\_ #### \_\_init\_\_
``` python ``` python
| __init__(start_mpv=True, ipc_socket=None, mpv_location=None, log_handler=None, loglevel=None, ****kwargs) | __init__(start_mpv=True, ipc_socket=None, mpv_location=None, log_handler=None, loglevel=None, quit_callback=None, ****kwargs)
``` ```
Create the interface to MPV and process instance. Create the interface to MPV and process instance.
@ -407,7 +407,7 @@ Remove callback to an MPV property change.
Decorator to bind a callback to an MPV property change. Decorator to bind a callback to an MPV property change.
@on\_key\_press(property\_name) @property\_observer(property\_name)
def my\_callback(name, data): def my\_callback(name, data):
pass pass
@ -441,7 +441,7 @@ Play the specified URL. An alias to loadfile().
| terminate() | terminate()
``` ```
Terminate the connection to MPV and process (if started by this module). Terminate the connection to MPV and process (if **start\_mpv** is used).
<a name=".python_mpv_jsonipc.MPV.command"></a> <a name=".python_mpv_jsonipc.MPV.command"></a>

View File

@ -42,14 +42,16 @@ class WindowsSocket(threading.Thread):
Data is automatically encoded and decoded as JSON. The callback Data is automatically encoded and decoded as JSON. The callback
function will be called for each inbound message. function will be called for each inbound message.
""" """
def __init__(self, ipc_socket, callback=None): def __init__(self, ipc_socket, callback=None, quit_callback=None):
"""Create the wrapper. """Create the wrapper.
*ipc_socket* is the pipe name. (Not including \\\\.\\pipe\\) *ipc_socket* is the pipe name. (Not including \\\\.\\pipe\\)
*callback(json_data)* is the function for recieving events. *callback(json_data)* is the function for recieving events.
*quit_callback* is called when the socket connection dies.
""" """
ipc_socket = "\\\\.\\pipe\\" + ipc_socket ipc_socket = "\\\\.\\pipe\\" + ipc_socket
self.callback = callback self.callback = callback
self.quit_callback = quit_callback
access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
limit = 5 # Connection may fail at first. Try 5 times. limit = 5 # Connection may fail at first. Try 5 times.
@ -71,15 +73,21 @@ class WindowsSocket(threading.Thread):
threading.Thread.__init__(self) threading.Thread.__init__(self)
def stop(self): def stop(self, join=True):
"""Terminate the thread.""" """Terminate the thread."""
if self.socket is not None: if self.socket is not None:
self.socket.close() self.socket.close()
self.join() if join:
self.join()
def send(self, data): def send(self, data):
"""Send *data* to the pipe, encoded as JSON.""" """Send *data* to the pipe, encoded as JSON."""
self.socket.send_bytes(json.dumps(data).encode('utf-8') + b'\n') try:
self.socket.send_bytes(json.dumps(data).encode('utf-8') + b'\n')
except OSError as ex:
if len(ex.args) == 1 and ex.args[0] == "handle is closed":
raise BrokenPipeError("handle is closed")
raise ex
def run(self): def run(self):
"""Process pipe events. Do not run this directly. Use *start*.""" """Process pipe events. Do not run this directly. Use *start*."""
@ -102,7 +110,8 @@ class WindowsSocket(threading.Thread):
self.callback(json_data) self.callback(json_data)
data = b'' data = b''
except EOFError: except EOFError:
pass if self.quit_callback:
self.quit_callback()
class UnixSocket(threading.Thread): class UnixSocket(threading.Thread):
""" """
@ -111,14 +120,16 @@ class UnixSocket(threading.Thread):
Data is automatically encoded and decoded as JSON. The callback Data is automatically encoded and decoded as JSON. The callback
function will be called for each inbound message. function will be called for each inbound message.
""" """
def __init__(self, ipc_socket, callback=None): def __init__(self, ipc_socket, callback=None, quit_callback=None):
"""Create the wrapper. """Create the wrapper.
*ipc_socket* is the path to the socket. *ipc_socket* is the path to the socket.
*callback(json_data)* is the function for recieving events. *callback(json_data)* is the function for recieving events.
*quit_callback* is called when the socket connection dies.
""" """
self.ipc_socket = ipc_socket self.ipc_socket = ipc_socket
self.callback = callback self.callback = callback
self.quit_callback = quit_callback
self.socket = socket.socket(socket.AF_UNIX) self.socket = socket.socket(socket.AF_UNIX)
self.socket.connect(self.ipc_socket) self.socket.connect(self.ipc_socket)
@ -127,15 +138,22 @@ class UnixSocket(threading.Thread):
threading.Thread.__init__(self) threading.Thread.__init__(self)
def stop(self): def stop(self, join=True):
"""Terminate the thread.""" """Terminate the thread."""
if self.socket is not None: if self.socket is not None:
self.socket.shutdown(socket.SHUT_WR) try:
self.socket.close() self.socket.shutdown(socket.SHUT_WR)
self.join() self.socket.close()
self.socket = None
except OSError:
pass # Ignore socket close failure.
if join:
self.join()
def send(self, data): def send(self, data):
"""Send *data* to the socket, encoded as JSON.""" """Send *data* to the socket, encoded as JSON."""
if self.socket is None:
raise BrokenPipeError("socket is closed")
self.socket.send(json.dumps(data).encode('utf-8') + b'\n') self.socket.send(json.dumps(data).encode('utf-8') + b'\n')
def run(self): def run(self):
@ -157,6 +175,8 @@ class UnixSocket(threading.Thread):
json_data = json.loads(item) json_data = json.loads(item)
self.callback(json_data) self.callback(json_data)
data = b'' data = b''
if self.quit_callback:
self.quit_callback()
class MPVProcess: class MPVProcess:
""" """
@ -236,21 +256,23 @@ class MPVInter:
""" """
Low-level interface to MPV. Does NOT manage an mpv process. (Internal) Low-level interface to MPV. Does NOT manage an mpv process. (Internal)
""" """
def __init__(self, ipc_socket, callback=None): def __init__(self, ipc_socket, callback=None, quit_callback=None):
"""Create the wrapper. """Create the wrapper.
*ipc_socket* is the path to the Unix/Linux socket or name of the Windows pipe. *ipc_socket* is the path to the Unix/Linux socket or name of the Windows pipe.
*callback(event_name, data)* is the function for recieving events. *callback(event_name, data)* is the function for recieving events.
*quit_callback* is called when the socket connection to MPV dies.
""" """
Socket = UnixSocket Socket = UnixSocket
if os.name == 'nt': if os.name == 'nt':
Socket = WindowsSocket Socket = WindowsSocket
self.callback = callback self.callback = callback
self.quit_callback = quit_callback
if self.callback is None: if self.callback is None:
self.callback = lambda event, data: None self.callback = lambda event, data: None
self.socket = Socket(ipc_socket, self.event_callback) self.socket = Socket(ipc_socket, self.event_callback, self.quit_callback)
self.socket.start() self.socket.start()
self.command_id = 1 self.command_id = 1
self.rid_lock = threading.Lock() self.rid_lock = threading.Lock()
@ -258,9 +280,9 @@ class MPVInter:
self.cid_result = {} self.cid_result = {}
self.cid_wait = {} self.cid_wait = {}
def stop(self): def stop(self, join=True):
"""Terminate the underlying connection.""" """Terminate the underlying connection."""
self.socket.stop() self.socket.stop(join)
def event_callback(self, data): def event_callback(self, data):
"""Internal callback for recieving events from MPV.""" """Internal callback for recieving events from MPV."""
@ -326,10 +348,10 @@ class EventHandler(threading.Thread):
""" """
self.queue.put((func, args)) self.queue.put((func, args))
def stop(self): def stop(self, join=True):
"""Terminate the thread.""" """Terminate the thread."""
self.queue.put("quit") self.queue.put("quit")
self.join() self.join(join)
def run(self): def run(self):
"""Process socket events. Do not run this directly. Use *start*.""" """Process socket events. Do not run this directly. Use *start*."""
@ -352,7 +374,8 @@ class MPV:
Please note that if you are using a really old MPV version, a fallback command Please note that if you are using a really old MPV version, a fallback command
list is used. Not all commands may actually work when this fallback is used. list is used. Not all commands may actually work when this fallback is used.
""" """
def __init__(self, start_mpv=True, ipc_socket=None, mpv_location=None, log_handler=None, loglevel=None, **kwargs): def __init__(self, start_mpv=True, ipc_socket=None, mpv_location=None,
log_handler=None, loglevel=None, quit_callback=None, **kwargs):
""" """
Create the interface to MPV and process instance. Create the interface to MPV and process instance.
@ -361,6 +384,7 @@ class MPV:
*mpv_location* is the location of MPV for *start_mpv*. (Default: Use MPV in PATH) *mpv_location* is the location of MPV for *start_mpv*. (Default: Use MPV in PATH)
*log_handler(level, prefix, text)* is an optional handler for log events. (Default: Disabled) *log_handler(level, prefix, text)* is an optional handler for log events. (Default: Disabled)
*loglevel* is the level for log messages. Levels are fatal, error, warn, info, v, debug, trace. (Default: Disabled) *loglevel* is the level for log messages. Levels are fatal, error, warn, info, v, debug, trace. (Default: Disabled)
*quit_callback* is called when the socket connection to MPV dies.
All other arguments are forwarded to MPV as command-line arguments if *start_mpv* is used. All other arguments are forwarded to MPV as command-line arguments if *start_mpv* is used.
""" """
@ -370,6 +394,7 @@ class MPV:
self.property_bindings = {} self.property_bindings = {}
self.mpv_process = None self.mpv_process = None
self.mpv_inter = None self.mpv_inter = None
self.quit_callback = quit_callback
self.event_handler = EventHandler() self.event_handler = EventHandler()
self.event_handler.start() self.event_handler.start()
if ipc_socket is None: if ipc_socket is None:
@ -391,7 +416,7 @@ class MPV:
else: else:
raise MPVError("MPV process retry limit reached.") raise MPVError("MPV process retry limit reached.")
self.mpv_inter = MPVInter(ipc_socket, self._callback) self.mpv_inter = MPVInter(ipc_socket, self._callback, self._quit_callback)
self.properties = set(x.replace("-", "_") for x in self.command("get_property", "property-list")) self.properties = set(x.replace("-", "_") for x in self.command("get_property", "property-list"))
try: try:
command_list = [x["name"] for x in self.command("get_property", "command-list")] command_list = [x["name"] for x in self.command("get_property", "command-list")]
@ -426,6 +451,14 @@ class MPV:
if len(args) == 2 and args[0] == "custom-bind": if len(args) == 2 and args[0] == "custom-bind":
self.event_handler.put_task(self.key_bindings[args[1]]) self.event_handler.put_task(self.key_bindings[args[1]])
def _quit_callback(self):
"""
Internal handler for quit events.
"""
if self.quit_callback:
self.quit_callback()
self.terminate(join=False)
def bind_event(self, name, callback): def bind_event(self, name, callback):
""" """
Bind a callback to an MPV event. Bind a callback to an MPV event.
@ -563,13 +596,13 @@ class MPV:
def __del__(self): def __del__(self):
self.terminate() self.terminate()
def terminate(self): def terminate(self, join=True):
"""Terminate the connection to MPV and process (if *start_mpv* is used).""" """Terminate the connection to MPV and process (if *start_mpv* is used)."""
if self.mpv_process: if self.mpv_process:
self.mpv_process.stop() self.mpv_process.stop()
if self.mpv_inter: if self.mpv_inter:
self.mpv_inter.stop() self.mpv_inter.stop(join)
self.event_handler.stop() self.event_handler.stop(join)
def command(self, command, *args): def command(self, command, *args):
""" """

View File

@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
setup( setup(
name='python-mpv-jsonipc', name='python-mpv-jsonipc',
version='1.1.10', version='1.1.11',
author="Ian Walton", author="Ian Walton",
author_email="iwalton3@gmail.com", author_email="iwalton3@gmail.com",
description="Python API to MPV using JSON IPC", description="Python API to MPV using JSON IPC",