Wrap all docstrings to 120 char line width

This is to stay consistent with everything else.
This commit is contained in:
jaseg 2017-09-14 12:03:50 +02:00
parent fa125c6604
commit dcf75e604c

229
mpv.py
View file

@ -4,18 +4,16 @@
# Python MPV library module # Python MPV library module
# Copyright (C) 2017 Sebastian Götte <code@jaseg.net> # Copyright (C) 2017 Sebastian Götte <code@jaseg.net>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# it under the terms of the GNU Affero General Public License as published by # Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
# the Free Software Foundation, either version 3 of the License, or # later version.
# (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# but WITHOUT ANY WARRANTY; without even the implied warranty of # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # details.
# GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# along with this program. If not, see <http://www.gnu.org/licenses/>. # <http://www.gnu.org/licenses/>.
# #
from ctypes import * from ctypes import *
@ -377,9 +375,7 @@ _handle_gl_func('mpv_opengl_cb_uninit_gl', [],
def _mpv_coax_proptype(value, proptype=str): def _mpv_coax_proptype(value, proptype=str):
"""Intelligently coax the given python value into something that can """Intelligently coax the given python value into something that can be understood as a proptype property."""
be understood as a proptype property.
"""
if type(value) is bytes: if type(value) is bytes:
return value; return value;
elif type(value) is bool: elif type(value) is bool:
@ -390,11 +386,9 @@ def _mpv_coax_proptype(value, proptype=str):
raise TypeError('Cannot coax value of type {} into property type {}'.format(type(value), proptype)) raise TypeError('Cannot coax value of type {} into property type {}'.format(type(value), proptype))
def _make_node_str_list(l): def _make_node_str_list(l):
"""Take a list of python objects and make a MPV string node array """Take a list of python objects and make a MPV string node array from it.
from it.
As an example, the python list ``l = [ "foo", 23, false ]`` will As an example, the python list ``l = [ "foo", 23, false ]`` will result in the following MPV node object::
result in the following MPV node object::
struct mpv_node { struct mpv_node {
.format = MPV_NODE_ARRAY, .format = MPV_NODE_ARRAY,
@ -501,32 +495,24 @@ class _DecoderPropertyProxy(_PropertyProxy):
setattr(self.mpv, _py_to_mpv(name), value) setattr(self.mpv, _py_to_mpv(name), value)
class MPV(object): class MPV(object):
"""See man mpv(1) for the details of the implemented commands. All """See man mpv(1) for the details of the implemented commands. All mpv properties can be accessed as
mpv properties can be accessed as ``my_mpv.some_property`` and all ``my_mpv.some_property`` and all mpv options can be accessed as ``my_mpv['some-option']``.
mpv options can be accessed as ``my_mpv['some-option']``.
By default, properties are returned as decoded ``str`` and an error By default, properties are returned as decoded ``str`` and an error is thrown if the value does not contain valid
is thrown if the value does not contain valid utf-8. To get a utf-8. To get a decoded ``str`` if possibly but ``bytes`` instead of an error if not, use
decoded ``str`` if possibly but ``bytes`` instead of an error if ``my_mpv.lazy.some_property``. To always get raw ``bytes``, use ``my_mpv.raw.some_property``. To access a
not, use ``my_mpv.lazy.some_property``. To always get raw ``bytes``, property's decoded OSD value, use ``my_mpv.osd.some_property``.
use ``my_mpv.raw.some_property``. To access a property's decoded
OSD value, use ``my_mpv.osd.some_property``.
To get API information on an option, use To get API information on an option, use ``my_mpv.option_info('option-name')``. To get API information on a
``my_mpv.option_info('option-name')``. To get API information on a property, use ``my_mpv.properties['property-name']``. Take care to use mpv's dashed-names instead of the
property, use ``my_mpv.properties['property-name']``. Take care to underscore_names exposed on the python object.
use mpv's dashed-names instead of the underscore_names exposed on
the python object.
To make your program not barf hard the first time its used on a To make your program not barf hard the first time its used on a weird file system **always** access properties
weird file system **always** access properties containing file names containing file names or file tags through ``MPV.raw``. """
or file tags through ``MPV.raw``.
"""
def __init__(self, *extra_mpv_flags, log_handler=None, start_event_thread=True, loglevel=None, **extra_mpv_opts): def __init__(self, *extra_mpv_flags, log_handler=None, start_event_thread=True, loglevel=None, **extra_mpv_opts):
"""Create an MPV instance. """Create an MPV instance.
Extra arguments and extra keyword arguments will be passed to Extra arguments and extra keyword arguments will be passed to mpv as options.
mpv as options.
""" """
self.handle = _mpv_create() self.handle = _mpv_create()
@ -571,10 +557,8 @@ class MPV(object):
self._playback_cond.wait() self._playback_cond.wait()
def wait_for_property(self, name, cond=lambda val: val, level_sensitive=True): def wait_for_property(self, name, cond=lambda val: val, level_sensitive=True):
"""Waits until ``cond`` evaluates to a truthy value on the named """Waits until ``cond`` evaluates to a truthy value on the named property. This can be used to wait for
property. This can be used to wait for properties such as properties such as ``idle_active`` indicating the player is done with regular playback and just idling around
``idle_active`` indicating the player is done with regular
playback and just idling around
""" """
sema = threading.Semaphore(value=0) sema = threading.Semaphore(value=0)
def observer(name, val): def observer(name, val):
@ -590,9 +574,8 @@ class MPV(object):
self.terminate() self.terminate()
def terminate(self): def terminate(self):
"""Properly terminates this player instance. Preferably use this """Properly terminates this player instance. Preferably use this instead of relying on python's garbage
instead of relying on python's garbage collector to cause this collector to cause this to be called from the object's destructor.
to be called from the object's destructor.
""" """
self.handle, handle = None, self.handle self.handle, handle = None, self.handle
if threading.current_thread() is self._event_thread: if threading.current_thread() is self._event_thread:
@ -606,15 +589,12 @@ class MPV(object):
self._event_thread.join() self._event_thread.join()
def set_loglevel(self, level): def set_loglevel(self, level):
"""Set MPV's log level. This adjusts which output will be sent """Set MPV's log level. This adjusts which output will be sent to this object's log handlers. If you just want
to this object's log handlers. If you just want mpv's regular mpv's regular terminal output, you don't need to adjust this but just need to pass a log handler to the MPV
terminal output, you don't need to adjust this but just need to constructur such as ``MPV(log_handler=print)``.
pass a log handler to the MPV constructur such as
``MPV(log_handler=print)``.
Valid log levels are "no", "fatal", "error", "warn", "info", "v" Valid log levels are "no", "fatal", "error", "warn", "info", "v" "debug" and "trace". For details see your mpv's
"debug" and "trace". For details see your mpv's client.h header client.h header file.
file.
""" """
_mpv_request_log_messages(self._event_handle, level.encode('utf-8')) _mpv_request_log_messages(self._event_handle, level.encode('utf-8'))
@ -651,17 +631,14 @@ class MPV(object):
self.command('frame_back_step') self.command('frame_back_step')
def _add_property(self, name, value=1): def _add_property(self, name, value=1):
"""Add the given value to the property. On overflow or """Add the given value to the property. On overflow or underflow, clamp the property to the maximum. If
underflow, clamp the property to the maximum. If ``value`` is ``value`` is omitted, assume ``1``.
omitted, assume ``1``.
""" """
self.command('add', name, value) self.command('add', name, value)
def _cycle_property(self, name, direction='up'): def _cycle_property(self, name, direction='up'):
"""Cycle the given property. ``up`` and ``down`` set the cycle """Cycle the given property. ``up`` and ``down`` set the cycle direction. On overflow, set the property back to
direction. On overflow, set the property back to the minimum, on the minimum, on underflow set it to the maximum. If ``up`` or ``down`` is omitted, assume ``up``.
underflow set it to the maximum. If ``up`` or ``down`` is
omitted, assume ``up``.
""" """
self.command('cycle', name, direction) self.command('cycle', name, direction)
@ -678,9 +655,7 @@ class MPV(object):
self.command('screenshot_to_file', filename.encode(fs_enc), includes) self.command('screenshot_to_file', filename.encode(fs_enc), includes)
def screenshot_raw(self, includes='subtitles'): def screenshot_raw(self, includes='subtitles'):
"""Mapped mpv screenshot_raw command, see man mpv(1). Returns a """Mapped mpv screenshot_raw command, see man mpv(1). Returns a pillow Image object."""
pillow Image object.
"""
from PIL import Image from PIL import Image
res = self.node_command('screenshot-raw', includes) res = self.node_command('screenshot-raw', includes)
if res['format'] != 'bgr0': if res['format'] != 'bgr0':
@ -791,17 +766,13 @@ class MPV(object):
self.command('script_message_to', target, *args) self.command('script_message_to', target, *args)
def observe_property(self, name, handler): def observe_property(self, name, handler):
"""Register an observer on the named property. An observer is a """Register an observer on the named property. An observer is a function that is called with the new property
function that is called with the new property value every time value every time the property's value is changed. The basic function signature is ``fun(property_name,
the property's value is changed. The basic function signature is new_value)`` with new_value being the decoded property value as a python object. This function can be used as a
``fun(property_name, new_value)`` with new_value being the function decorator if no handler is given.
decoded property value as a python object. This function can be
used as a function decorator if no handler is given.
To unregister the observer, call either of To unregister the observer, call either of ``mpv.unobserve_property(name, handler)``,
``mpv.unobserve_property(name, handler)``, ``mpv.unobserve_all_properties(handler)`` or the handler's ``unregister_mpv_properties`` attribute::
``mpv.unobserve_all_properties(handler)`` or the handler's
``unregister_mpv_properties`` attribute::
@player.observe_property('volume') @player.observe_property('volume')
def my_handler(new_volume, *): def my_handler(new_volume, *):
@ -813,9 +784,7 @@ class MPV(object):
_mpv_observe_property(self._event_handle, hash(name)&0xffffffffffffffff, name.encode('utf-8'), MpvFormat.NODE) _mpv_observe_property(self._event_handle, hash(name)&0xffffffffffffffff, name.encode('utf-8'), MpvFormat.NODE)
def property_observer(self, name): def property_observer(self, name):
"""Function decorator to register a property observer. See """Function decorator to register a property observer. See ``MPV.observe_property`` for details."""
``MPV.observe_property`` for details.
"""
def wrapper(fun): def wrapper(fun):
self.observe_property(name, fun) self.observe_property(name, fun)
fun.unobserve_mpv_properties = lambda: self.unobserve_property(name, fun) fun.unobserve_mpv_properties = lambda: self.unobserve_property(name, fun)
@ -823,34 +792,26 @@ class MPV(object):
return wrapper return wrapper
def unobserve_property(self, name, handler): def unobserve_property(self, name, handler):
"""Unregister a property observer. This requires both the """Unregister a property observer. This requires both the observed property's name and the handler function that
observed property's name and the handler function that was was originally registered as one handler could be registered for several properties. To unregister a handler
originally registered as one handler could be registered for from *all* observed properties see ``unobserve_all_properties``.
several properties. To unregister a handler from *all* observed
properties see ``unobserve_all_properties``.
""" """
self._property_handlers[name].remove(handler) self._property_handlers[name].remove(handler)
if not self._property_handlers[name]: if not self._property_handlers[name]:
_mpv_unobserve_property(self._event_handle, hash(name)&0xffffffffffffffff) _mpv_unobserve_property(self._event_handle, hash(name)&0xffffffffffffffff)
def unobserve_all_properties(self, handler): def unobserve_all_properties(self, handler):
"""Unregister a property observer from *all* observed """Unregister a property observer from *all* observed properties."""
properties.
"""
for name in self._property_handlers: for name in self._property_handlers:
self.unobserve_property(name, handler) self.unobserve_property(name, handler)
def register_message_handler(self, target, handler=None): def register_message_handler(self, target, handler=None):
"""Register a mpv script message handler. This can be used to """Register a mpv script message handler. This can be used to communicate with embedded lua scripts. Pass the
communicate with embedded lua scripts. Pass the script message script message target name this handler should be listening to and the handler function.
target name this handler should be listening to and the handler
function.
WARNING: Only one handler can be registered at a time for any WARNING: Only one handler can be registered at a time for any given target.
given target.
To unregister the message handler, call its To unregister the message handler, call its ``unregister_mpv_messages`` function::
``unregister_mpv_messages`` function::
player = mpv.MPV() player = mpv.MPV()
@player.message_handler('foo') @player.message_handler('foo')
@ -865,11 +826,10 @@ class MPV(object):
self._message_handlers[target] = handler self._message_handlers[target] = handler
def unregister_message_handler(self, target_or_handler): def unregister_message_handler(self, target_or_handler):
"""Unregister a mpv script message handler for the given script """Unregister a mpv script message handler for the given script message target name.
message target name.
You can also call the ``unregister_mpv_messages`` function You can also call the ``unregister_mpv_messages`` function attribute set on the handler function when it is
attribute set on the handler function when it is registered. registered.
""" """
if isinstance(target_or_handler, str): if isinstance(target_or_handler, str):
del self._message_handlers[target_or_handler] del self._message_handlers[target_or_handler]
@ -881,11 +841,9 @@ class MPV(object):
def message_handler(self, target): def message_handler(self, target):
"""Decorator to register a mpv script message handler. """Decorator to register a mpv script message handler.
WARNING: Only one handler can be registered at a time for any WARNING: Only one handler can be registered at a time for any given target.
given target.
To unregister the message handler, call its To unregister the message handler, call its ``unregister_mpv_messages`` function::
``unregister_mpv_messages`` function::
player = mpv.MPV() player = mpv.MPV()
@player.message_handler('foo') @player.message_handler('foo')
@ -903,8 +861,7 @@ class MPV(object):
def register_event_callback(self, callback): def register_event_callback(self, callback):
"""Register a blanket event callback receiving all event types. """Register a blanket event callback receiving all event types.
To unregister the event callback, call its To unregister the event callback, call its ``unregister_mpv_events`` function::
``unregister_mpv_events`` function::
player = mpv.MPV() player = mpv.MPV()
@player.event_callback('shutdown') @player.event_callback('shutdown')
@ -920,15 +877,12 @@ class MPV(object):
self._event_callbacks.remove(callback) self._event_callbacks.remove(callback)
def event_callback(self, *event_types): def event_callback(self, *event_types):
"""Function decorator to register a blanket event callback for """Function decorator to register a blanket event callback for the given event types. Event types can be given
the given event types. Event types can be given as str (e.g. as str (e.g. 'start-file'), integer or MpvEventID object.
'start-file'), integer or MpvEventID object.
WARNING: Due to the way this is filtering events, this decorator WARNING: Due to the way this is filtering events, this decorator cannot be chained with itself.
cannot be chained with itself.
To unregister the event callback, call its To unregister the event callback, call its ``unregister_mpv_events`` function::
``unregister_mpv_events`` function::
player = mpv.MPV() player = mpv.MPV()
@player.event_callback('shutdown') @player.event_callback('shutdown')
@ -953,11 +907,10 @@ class MPV(object):
return 'py_kb_{:016x}'.format(hash(callback_or_cmd)&0xffffffffffffffff) return 'py_kb_{:016x}'.format(hash(callback_or_cmd)&0xffffffffffffffff)
def on_key_press(self, keydef, mode='force'): def on_key_press(self, keydef, mode='force'):
"""Function decorator to register a simplified key binding. The """Function decorator to register a simplified key binding. The callback is called whenever the key given is
callback is called whenever the key given is *pressed*. *pressed*.
To unregister the callback function, you can call its To unregister the callback function, you can call its ``unregister_mpv_key_bindings`` attribute::
``unregister_mpv_key_bindings`` attribute::
player = mpv.MPV() player = mpv.MPV()
@player.on_key_press('Q') @player.on_key_press('Q')
@ -966,13 +919,11 @@ class MPV(object):
binding.unregister_mpv_key_bindings() binding.unregister_mpv_key_bindings()
WARNING: For a single keydef only a single callback/command can WARNING: For a single keydef only a single callback/command can be registered at the same time. If you register
be registered at the same time. If you register a binding a binding multiple times older bindings will be overwritten and there is a possibility of references leaking. So
multiple times older bindings will be overwritten and there is a don't do that.
possibility of references leaking. So don't do that.
The BIG FAT WARNING regarding untrusted keydefs from the The BIG FAT WARNING regarding untrusted keydefs from the key_binding method applies here as well.
key_binding method applies here as well.
""" """
def register(fun): def register(fun):
@self.key_binding(keydef, mode) @self.key_binding(keydef, mode)
@ -986,17 +937,13 @@ class MPV(object):
def key_binding(self, keydef, mode='force'): def key_binding(self, keydef, mode='force'):
"""Function decorator to register a low-level key binding. """Function decorator to register a low-level key binding.
The callback function signature is ``fun(key_state, key_name)`` The callback function signature is ``fun(key_state, key_name)`` where ``key_state`` is either ``'U'`` for "key
where ``key_state`` is either ``'U'`` for "key up" or ``'D'`` up" or ``'D'`` for "key down".
for "key down".
The keydef format is: ``[Shift+][Ctrl+][Alt+][Meta+]<key>`` The keydef format is: ``[Shift+][Ctrl+][Alt+][Meta+]<key>`` where ``<key>`` is either the literal character the
where ``<key>`` is either the literal character the key produces key produces (ASCII or Unicode character), or a symbolic name (as printed by ``mpv --input-keylist``).
(ASCII or Unicode character), or a symbolic name (as printed by
``mpv --input-keylist``).
To unregister the callback function, you can call its To unregister the callback function, you can call its ``unregister_mpv_key_bindings`` attribute::
``unregister_mpv_key_bindings`` attribute::
player = mpv.MPV() player = mpv.MPV()
@player.key_binding('Q') @player.key_binding('Q')
@ -1005,19 +952,15 @@ class MPV(object):
binding.unregister_mpv_key_bindings() binding.unregister_mpv_key_bindings()
WARNING: For a single keydef only a single callback/command can WARNING: For a single keydef only a single callback/command can be registered at the same time. If you register
be registered at the same time. If you register a binding a binding multiple times older bindings will be overwritten and there is a possibility of references leaking. So
multiple times older bindings will be overwritten and there is a don't do that.
possibility of references leaking. So don't do that.
BIG FAT WARNING: mpv's key binding mechanism is pretty powerful. BIG FAT WARNING: mpv's key binding mechanism is pretty powerful. This means, you essentially get arbitrary code
This means, you essentially get arbitrary code exectution exectution through key bindings. This interface makes some limited effort to sanitize the keydef given in the
through key bindings. This interface makes some limited effort first parameter, but YOU SHOULD NOT RELY ON THIS IN FOR SECURITY. If your input comes from config files, this is
to sanitize the keydef given in the first parameter, but YOU completely fine--but, if you are about to pass untrusted input into this parameter, better double-check whether
SHOULD NOT RELY ON THIS IN FOR SECURITY. If your input comes this is secure in your case.
from config files, this is completely fine--but, if you are
about to pass untrusted input into this parameter, better
double-check whether this is secure in your case.
""" """
def register(fun): def register(fun):
fun.mpv_key_bindings = getattr(fun, 'mpv_key_bindings', []) + [keydef] fun.mpv_key_bindings = getattr(fun, 'mpv_key_bindings', []) + [keydef]
@ -1031,9 +974,8 @@ class MPV(object):
return register return register
def register_key_binding(self, keydef, callback_or_cmd, mode='force'): def register_key_binding(self, keydef, callback_or_cmd, mode='force'):
"""Register a key binding. This takes an mpv keydef and either """Register a key binding. This takes an mpv keydef and either a string containing a mpv command or a python
a string containing a mpv command or a python callback function. callback function. See ``MPV.key_binding`` for details.
See ``MPV.key_binding`` for details.
""" """
if not re.match(r'(Shift+)?(Ctrl+)?(Alt+)?(Meta+)?(.|\w+)', keydef): if not re.match(r'(Shift+)?(Ctrl+)?(Alt+)?(Meta+)?(.|\w+)', keydef):
raise ValueError('Invalid keydef. Expected format: [Shift+][Ctrl+][Alt+][Meta+]<key>\n' raise ValueError('Invalid keydef. Expected format: [Shift+][Ctrl+][Alt+][Meta+]<key>\n'
@ -1075,8 +1017,7 @@ class MPV(object):
return [element['filename'] for element in self.playlist] return [element['filename'] for element in self.playlist]
def playlist_append(self, filename, **options): def playlist_append(self, filename, **options):
"""Append a path or URL to the playlist. This does not start """Append a path or URL to the playlist. This does not start playing the file automatically. To do that, use
playing the file automatically. To do that, use
``MPV.loadfile(filename, 'append-play')``.""" ``MPV.loadfile(filename, 'append-play')``."""
self.loadfile(filename, 'append', **options) self.loadfile(filename, 'append', **options)