Small beautifications

This commit is contained in:
jaseg 2015-05-10 17:55:06 +02:00
parent df80c10799
commit 28b761627f

894
mpv.py
View file

@ -12,172 +12,172 @@ backend = CDLL('libmpv.so')
class MpvHandle(c_void_p): class MpvHandle(c_void_p):
pass pass
class ErrorCode: class ErrorCode:
""" For documentation on these, see mpv's libmpv/client.h """ """ For documentation on these, see mpv's libmpv/client.h """
SUCCESS = 0 SUCCESS = 0
EVENT_QUEUE_FULL = -1 EVENT_QUEUE_FULL = -1
NOMEM = -2 NOMEM = -2
UNINITIALIZED = -3 UNINITIALIZED = -3
INVALID_PARAMETER = -4 INVALID_PARAMETER = -4
OPTION_NOT_FOUND = -5 OPTION_NOT_FOUND = -5
OPTION_FORMAT = -6 OPTION_FORMAT = -6
OPTION_ERROR = -7 OPTION_ERROR = -7
PROPERTY_NOT_FOUND = -8 PROPERTY_NOT_FOUND = -8
PROPERTY_FORMAT = -9 PROPERTY_FORMAT = -9
PROPERTY_UNAVAILABLE = -10 PROPERTY_UNAVAILABLE = -10
PROPERTY_ERROR = -11 PROPERTY_ERROR = -11
COMMAND = -12 COMMAND = -12
EXCEPTION_DICT = { EXCEPTION_DICT = {
0: None, 0: None,
-1: lambda *a: MemoryError('mpv event queue full', *a), -1: lambda *a: MemoryError('mpv event queue full', *a),
-2: lambda *a: MemoryError('mpv cannot allocate memory', *a), -2: lambda *a: MemoryError('mpv cannot allocate memory', *a),
-3: lambda *a: ValueError('Uninitialized mpv handle used', *a), -3: lambda *a: ValueError('Uninitialized mpv handle used', *a),
-4: lambda *a: ValueError('Invalid value for mpv parameter', *a), -4: lambda *a: ValueError('Invalid value for mpv parameter', *a),
-5: lambda *a: AttributeError('mpv option does not exist', *a), -5: lambda *a: AttributeError('mpv option does not exist', *a),
-6: lambda *a: TypeError('Tried to set mpv option using wrong format', *a), -6: lambda *a: TypeError('Tried to set mpv option using wrong format', *a),
-7: lambda *a: ValueError('Invalid value for mpv option', *a), -7: lambda *a: ValueError('Invalid value for mpv option', *a),
-8: lambda *a: AttributeError('mpv property does not exist', *a), -8: lambda *a: AttributeError('mpv property does not exist', *a),
-9: lambda *a: TypeError('Tried to set mpv property using wrong format', *a), -9: lambda *a: TypeError('Tried to set mpv property using wrong format', *a),
-10: lambda *a: AttributeError('mpv property is not available', *a), -10: lambda *a: AttributeError('mpv property is not available', *a),
-11: lambda *a: ValueError('Invalid value for mpv property', *a), -11: lambda *a: ValueError('Invalid value for mpv property', *a),
-12: lambda *a: SystemError('Error running mpv command', *a) -12: lambda *a: SystemError('Error running mpv command', *a)
} }
@classmethod @classmethod
def DEFAULT_ERROR_HANDLER(ec, *args): def DEFAULT_ERROR_HANDLER(ec, *args):
return ValueError(_mpv_error_string(ec).decode(), ec, *a) return ValueError(_mpv_error_string(ec).decode(), ec, *a)
@classmethod @classmethod
def raise_for_ec(kls, func, *args): def raise_for_ec(kls, func, *args):
ec = func(*args) ec = func(*args)
ex = kls.EXCEPTION_DICT.get(ec , kls.DEFAULT_ERROR_HANDLER) ex = kls.EXCEPTION_DICT.get(ec , kls.DEFAULT_ERROR_HANDLER)
if ex: if ex:
raise ex(ec, *args) raise ex(ec, *args)
class MpvFormat(c_int): class MpvFormat(c_int):
NONE = 0 NONE = 0
STRING = 1 STRING = 1
OSD_STRING = 2 OSD_STRING = 2
FLAG = 3 FLAG = 3
INT64 = 4 INT64 = 4
DOUBLE = 5 DOUBLE = 5
NODE = 6 NODE = 6
NODE_ARRAY = 7 NODE_ARRAY = 7
NODE_MAP = 8 NODE_MAP = 8
class MpvEventID(c_int): class MpvEventID(c_int):
NONE = 0 NONE = 0
SHUTDOWN = 1 SHUTDOWN = 1
LOG_MESSAGE = 2 LOG_MESSAGE = 2
GET_PROPERTY_REPLY = 3 GET_PROPERTY_REPLY = 3
SET_PROPERTY_REPLY = 4 SET_PROPERTY_REPLY = 4
COMMAND_REPLY = 5 COMMAND_REPLY = 5
START_FILE = 6 START_FILE = 6
END_FILE = 7 END_FILE = 7
FILE_LOADED = 8 FILE_LOADED = 8
TRACKS_CHANGED = 9 TRACKS_CHANGED = 9
TRACK_SWITCHED = 10 TRACK_SWITCHED = 10
IDLE = 11 IDLE = 11
PAUSE = 12 PAUSE = 12
UNPAUSE = 13 UNPAUSE = 13
TICK = 14 TICK = 14
SCRIPT_INPUT_DISPATCH = 15 SCRIPT_INPUT_DISPATCH = 15
CLIENT_MESSAGE = 16 CLIENT_MESSAGE = 16
VIDEO_RECONFIG = 17 VIDEO_RECONFIG = 17
AUDIO_RECONFIG = 18 AUDIO_RECONFIG = 18
METADATA_UPDATE = 19 METADATA_UPDATE = 19
SEEK = 20 SEEK = 20
PLAYBACK_RESTART = 21 PLAYBACK_RESTART = 21
PROPERTY_CHANGE = 22 PROPERTY_CHANGE = 22
CHAPTER_CHANGE = 23 CHAPTER_CHANGE = 23
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, TRACKS_CHANGED, TRACK_SWITCHED, IDLE, PAUSE, UNPAUSE, TICK, SCRIPT_INPUT_DISPATCH, FILE_LOADED, TRACKS_CHANGED, TRACK_SWITCHED, IDLE, PAUSE, UNPAUSE, TICK, SCRIPT_INPUT_DISPATCH,
CLIENT_MESSAGE, VIDEO_RECONFIG, AUDIO_RECONFIG, METADATA_UPDATE, SEEK, PLAYBACK_RESTART, PROPERTY_CHANGE, CLIENT_MESSAGE, VIDEO_RECONFIG, AUDIO_RECONFIG, METADATA_UPDATE, SEEK, PLAYBACK_RESTART, PROPERTY_CHANGE,
CHAPTER_CHANGE ) CHAPTER_CHANGE )
class MpvEvent(Structure): class MpvEvent(Structure):
_fields_ = [('event_id', MpvEventID), _fields_ = [('event_id', MpvEventID),
('error', c_int), ('error', c_int),
('reply_userdata', c_ulonglong), ('reply_userdata', c_ulonglong),
('data', c_void_p)] ('data', c_void_p)]
def as_dict(self): def as_dict(self):
dtype = {MpvEventID.END_FILE: MpvEventEndFile, dtype = {MpvEventID.END_FILE: MpvEventEndFile,
MpvEventID.PROPERTY_CHANGE: MpvEventProperty, MpvEventID.PROPERTY_CHANGE: MpvEventProperty,
MpvEventID.GET_PROPERTY_REPLY: MpvEventProperty, MpvEventID.GET_PROPERTY_REPLY: MpvEventProperty,
MpvEventID.LOG_MESSAGE: MpvEventLogMessage, MpvEventID.LOG_MESSAGE: MpvEventLogMessage,
MpvEventID.SCRIPT_INPUT_DISPATCH: MpvEventScriptInputDispatch, MpvEventID.SCRIPT_INPUT_DISPATCH: MpvEventScriptInputDispatch,
MpvEventID.CLIENT_MESSAGE: MpvEventClientMessage MpvEventID.CLIENT_MESSAGE: MpvEventClientMessage
}.get(self.event_id.value, None) }.get(self.event_id.value, None)
return {'event_id': self.event_id.value, return {'event_id': self.event_id.value,
'error': self.error, 'error': self.error,
'reply_userdata': self.reply_userdata, 'reply_userdata': self.reply_userdata,
'event': cast(self.data, POINTER(dtype)).contents.as_dict() if dtype else None} 'event': cast(self.data, POINTER(dtype)).contents.as_dict() if dtype else None}
class MpvEventProperty(Structure): class MpvEventProperty(Structure):
_fields_ = [('name', c_char_p), _fields_ = [('name', c_char_p),
('format', MpvFormat), ('format', MpvFormat),
('data', c_void_p)] ('data', c_void_p)]
def as_dict(): def as_dict():
pass # FIXME pass # FIXME
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)]
def as_dict(self): def as_dict(self):
return { name: getattr(self, name).value for name, _t in _fields_ } return { name: getattr(self, name).value for name, _t in _fields_ }
class MpvEventEndFile(c_int): class MpvEventEndFile(c_int):
EOF_OR_INIT_FAILURE = 0 EOF_OR_INIT_FAILURE = 0
RESTARTED = 1 RESTARTED = 1
ABORTED = 2 ABORTED = 2
QUIT = 3 QUIT = 3
def as_dict(self): def as_dict(self):
return {'reason': self.value} return {'reason': self.value}
class MpvEventScriptInputDispatch(Structure): class MpvEventScriptInputDispatch(Structure):
_fields_ = [('arg0', c_int), _fields_ = [('arg0', c_int),
('type', c_char_p)] ('type', c_char_p)]
def as_dict(self): def as_dict(self):
pass # TODO pass # TODO
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))]
def as_dict(self): def as_dict(self):
return { 'args': [ self.args[i].value for i in range(self.num_args.value) ] } return { 'args': [ self.args[i].value for i in range(self.num_args.value) ] }
WakeupCallback = CFUNCTYPE(None, c_void_p) WakeupCallback = CFUNCTYPE(None, c_void_p)
def _handle_func(name, args=[], res=None): def _handle_func(name, args=[], res=None):
func = getattr(backend, name) func = getattr(backend, name)
if res is not None: if res is not None:
func.restype = res func.restype = res
func.argtypes = [MpvHandle] + args func.argtypes = [MpvHandle] + args
def wrapper(*args): def wrapper(*args):
if res is not None: if res is not None:
return func(*args) return func(*args)
else: else:
ErrorCode.raise_for_ec(func, *args) ErrorCode.raise_for_ec(func, *args)
globals()['_'+name] = wrapper globals()['_'+name] = wrapper
backend.mpv_client_api_version.restype = c_ulong backend.mpv_client_api_version.restype = c_ulong
def _mpv_client_api_version(): def _mpv_client_api_version():
ver = backend.mpv_client_api_version() ver = backend.mpv_client_api_version()
return ver>>16, ver&0xFFFF return ver>>16, ver&0xFFFF
backend.mpv_free.argtypes = [c_void_p] backend.mpv_free.argtypes = [c_void_p]
_mpv_free = backend.mpv_free _mpv_free = backend.mpv_free
@ -228,380 +228,378 @@ _handle_func('mpv_get_wakeup_pipe', [], c_int)
class ynbool: class ynbool:
def __init__(self, val=False): def __init__(self, val=False):
if not val or val == b'no' or val == 'no': self.val = bool(val and val not in (b'no', 'no'))
self.val = False
else:
self.val = True
def __nonzero__(self): def __nonzero__(self):
return self.val return self.val
def __str__(self): def __str__(self):
return 'yes' if self.val else 'no' return 'yes' if self.val else 'no'
def __repr__(self): def __repr__(self):
return str(self.val) return str(self.val)
def _ensure_encoding(possibly_bytes): def _ensure_encoding(possibly_bytes):
return possibly_bytes.decode() if type(possibly_bytes) is bytes else possibly_bytes return possibly_bytes.decode() if type(possibly_bytes) is bytes else possibly_bytes
def _event_generator(handle): def _event_generator(handle):
while True: while True:
event = _mpv_wait_event(handle, 0).contents event = _mpv_wait_event(handle, 0).contents
if event.event_id.value == MpvEventID.NONE: if event.event_id.value == MpvEventID.NONE:
raise StopIteration() raise StopIteration()
yield event yield event
def load_lua(): def load_lua():
""" Use this function if you intend to use mpv's built-in lua interpreter. This is e.g. needed for playback of """ Use this function if you intend to use mpv's built-in lua interpreter. This is e.g. needed for playback of
youtube urls. """ youtube urls. """
CDLL('liblua.so', mode=RTLD_GLOBAL) CDLL('liblua.so', mode=RTLD_GLOBAL)
class MPV: class MPV:
""" See man mpv(1) for the details of the implemented commands. """ """ See man mpv(1) for the details of the implemented commands. """
def __init__(self, evloop=None, **kwargs): def __init__(self, evloop=None, **kwargs):
""" Create an MPV instance. You should pass in an asyncio event loop that will handle the mpv event queue via """ Create an MPV instance. You should pass in an asyncio event loop that will handle the mpv event queue via
the evloop argument. If you do not pass in one, one will be created for you and run in a freshly spawned thread the evloop argument. If you do not pass in one, one will be created for you and run in a freshly spawned thread
(works for prototypes but is likely not what you want). (works for prototypes but is likely not what you want).
Any kwargs given will be passed to mpv as options. """ Any kwargs given will be passed to mpv as options. """
self.handle = _mpv_create() self.handle = _mpv_create()
self.event_callbacks = [] self.event_callbacks = []
self._event_fd = _mpv_get_wakeup_pipe(self.handle) self._event_fd = _mpv_get_wakeup_pipe(self.handle)
self._playback_cond = threading.Condition() self._playback_cond = threading.Condition()
def mpv_event_extractor(): def mpv_event_extractor():
os.read(self._event_fd, 512) os.read(self._event_fd, 512)
for event in _event_generator(self.handle): for event in _event_generator(self.handle):
devent = event.as_dict() # copy data from ctypes devent = event.as_dict() # copy data from ctypes
if devent['event_id'] in (MpvEventID.SHUTDOWN, MpvEventID.END_FILE, MpvEventID.PAUSE): if devent['event_id'] in (MpvEventID.SHUTDOWN, MpvEventID.END_FILE, MpvEventID.PAUSE):
with self._playback_cond: with self._playback_cond:
self._playback_cond.notify_all() self._playback_cond.notify_all()
for callback in self.event_callbacks: for callback in self.event_callbacks:
callback.call() callback.call()
if evloop: if evloop:
evloop.add_reader(self._event_fd, mpv_event_extractor) evloop.add_reader(self._event_fd, mpv_event_extractor)
else: else:
def loop_runner(): def loop_runner():
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
loop.add_reader(self._event_fd, mpv_event_extractor) loop.add_reader(self._event_fd, mpv_event_extractor)
try: try:
loop.run_forever() loop.run_forever()
finally: finally:
loop.close() loop.close()
self._event_thread = threading.Thread(target=loop_runner, daemon=True) self._event_thread = threading.Thread(target=loop_runner, daemon=True)
self._event_thread.start() self._event_thread.start()
_mpv_set_option_string(self.handle, b'audio-display', b'no') _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) istr = lambda o: ('yes' if o else 'no') if type(o) is bool else str(o)
for k,v in kwargs.items(): for k,v in kwargs.items():
_mpv_set_option_string(self.handle, k.replace('_', '-').encode(), istr(v).encode()) _mpv_set_option_string(self.handle, k.replace('_', '-').encode(), istr(v).encode())
_mpv_initialize(self.handle) _mpv_initialize(self.handle)
def wait_for_playback(self): def wait_for_playback(self):
""" Waits until playback of the current title is paused or done """ """ Waits until playback of the current title is paused or done """
with self._playback_cond: with self._playback_cond:
self._playback_cond.wait() self._playback_cond.wait()
# def __del__(self): # def __del__(self):
# _mpv_terminate_destroy(self.handle) # _mpv_terminate_destroy(self.handle)
def command(self, name, *args): def command(self, name, *args):
""" Execute a raw command """ """ Execute a raw command """
args = [name.encode()] + [ str(arg).encode() for arg in args if arg is not None ] + [None] args = [name.encode()] + [ str(arg).encode() for arg in args if arg is not None ] + [None]
_mpv_command(self.handle, (c_char_p*len(args))(*args)) _mpv_command(self.handle, (c_char_p*len(args))(*args))
def seek(self, amount, reference="relative", precision="default-precise"): def seek(self, amount, reference="relative", precision="default-precise"):
self.command('seek', amount, reference, precision) self.command('seek', amount, reference, precision)
def revert_seek(self): def revert_seek(self):
self.command('revert_seek'); self.command('revert_seek');
def frame_step(self): def frame_step(self):
self.command('frame_step') self.command('frame_step')
def frame_back_step(self): def frame_back_step(self):
self.command('frame_back_step') self.command('frame_back_step')
def _set_property(self, name, value): def _set_property(self, name, value):
self.command('set_property', name, str(value)) self.command('set_property', name, str(value))
def _add_property(self, name, value=None): def _add_property(self, name, value=None):
self.command('add_property', name, value) self.command('add_property', name, value)
def _cycle_property(self, name, direction='up'): def _cycle_property(self, name, direction='up'):
self.command('cycle_property', name, direction) self.command('cycle_property', name, direction)
def _multiply_property(self, name, factor): def _multiply_property(self, name, factor):
self.command('multiply_property', name, factor) self.command('multiply_property', name, factor)
def screenshot(self, includes='subtitles', mode='single'): def screenshot(self, includes='subtitles', mode='single'):
self.command('screenshot', includes, mode) self.command('screenshot', includes, mode)
def screenshot_to_file(self, filename, includes='subtitles'): def screenshot_to_file(self, filename, includes='subtitles'):
self.command('screenshot_to_file', filename, includes) self.command('screenshot_to_file', filename, includes)
def playlist_next(self, mode='weak'): def playlist_next(self, mode='weak'):
self.command('playlist_next', mode) self.command('playlist_next', mode)
def playlist_prev(self, mode='weak'): def playlist_prev(self, mode='weak'):
self.command('playlist_prev', mode) self.command('playlist_prev', mode)
def loadfile(self, filename, mode='replace'): def loadfile(self, filename, mode='replace'):
self.command('loadfile', filename, mode) self.command('loadfile', filename, mode)
def loadlist(self, playlist, mode='replace'): def loadlist(self, playlist, mode='replace'):
self.command('loadlist', playlist, mode) self.command('loadlist', playlist, mode)
def playlist_clear(self): def playlist_clear(self):
self.command('playlist_clear') self.command('playlist_clear')
def playlist_remove(self, index='current'): def playlist_remove(self, index='current'):
self.command('playlist_remove', index) self.command('playlist_remove', index)
def playlist_move(self, index1, index2): def playlist_move(self, index1, index2):
self.command('playlist_move', index1, index2) self.command('playlist_move', index1, index2)
def run(self, command, *args): def run(self, command, *args):
self.command('run', command, *args) self.command('run', command, *args)
def quit(self, code=None): def quit(self, code=None):
self.command('quit', code) self.command('quit', code)
def quit_watch_later(self, code=None): def quit_watch_later(self, code=None):
self.command('quit_watch_later', code) self.command('quit_watch_later', code)
def sub_add(self, filename): def sub_add(self, filename):
self.command('sub_add', filename) self.command('sub_add', filename)
def sub_remove(self, sub_id=None): def sub_remove(self, sub_id=None):
self.command('sub_remove', sub_id) self.command('sub_remove', sub_id)
def sub_reload(self, sub_id=None): def sub_reload(self, sub_id=None):
self.command('sub_reload', sub_id) self.command('sub_reload', sub_id)
def sub_step(self, skip): def sub_step(self, skip):
self.command('sub_step', skip) self.command('sub_step', skip)
def sub_seek(self, skip): def sub_seek(self, skip):
self.command('sub_seek', skip) self.command('sub_seek', skip)
def toggle_osd(self): def toggle_osd(self):
self.command('osd') self.command('osd')
def show_text(self, string, duration='-', level=None): def show_text(self, string, duration='-', level=None):
self.command('show_text', string, duration, level) self.command('show_text', string, duration, level)
def show_progress(self): def show_progress(self):
self.command('show_progress') self.command('show_progress')
def discnav(self, command): def discnav(self, command):
self.command('discnav', command) self.command('discnav', command)
def write_watch_later_config(self): def write_watch_later_config(self):
self.command('write_watch_later_config') self.command('write_watch_later_config')
def overlay_add(self, overlay_id, x, y, file_or_fd, offset, fmt, w, h, stride): def overlay_add(self, overlay_id, x, y, file_or_fd, offset, fmt, w, h, stride):
self.command('overlay_add', overlay_id, x, y, file_or_fd, offset, fmt, w, h, stride) self.command('overlay_add', overlay_id, x, y, file_or_fd, offset, fmt, w, h, stride)
def overlay_remove(self, overlay_id): def overlay_remove(self, overlay_id):
self.command('overlay_remove', overlay_id) self.command('overlay_remove', overlay_id)
def script_message(self, *args): def script_message(self, *args):
self.command('script_message', *args) self.command('script_message', *args)
def script_message_to(self, target, *args): def script_message_to(self, target, *args):
self.command('script_message_to', target, *args) self.command('script_message_to', target, *args)
@property @property
def metadata(self): def metadata(self):
... raise NotImplementedError
def chapter_metadata(self):
...
def vf_metadata(self): def chapter_metadata(self):
... raise NotImplementedError
# Convenience functions
def play(self, filename):
self.loadfile(filename)
# Complex properties def vf_metadata(self):
raise NotImplementedError
# Convenience functions
def play(self, filename):
self.loadfile(filename)
_VIDEO_PARAMS_LIST = ( # Complex properties
('pixelformat', str),
('w', int),
('h', int),
('dw', int),
('dh', int),
('aspect', float),
('par', float),
('colormatrix', str),
('colorlevels', str),
('chroma-location', str),
('rotate', int))
@property _VIDEO_PARAMS_LIST = (
def video_params(self): ('pixelformat', str),
return self._get_dict('video-params/', _VIDEO_PARAMS_LIST) ('w', int),
('h', int),
('dw', int),
('dh', int),
('aspect', float),
('par', float),
('colormatrix', str),
('colorlevels', str),
('chroma-location', str),
('rotate', int))
@property @property
def video_out_params(self): def video_params(self):
return self._get_dict('video-out-params/', _VIDEO_PARAMS_LIST) return self._get_dict('video-params/', _VIDEO_PARAMS_LIST)
@property @property
def playlist(self): def video_out_params(self):
return self._get_list('playlist/', (('filename', str),)) return self._get_dict('video-out-params/', _VIDEO_PARAMS_LIST)
@property
def track_list(self):
return self._get_list('track-list/', (
('id', int),
('type', str),
('src-id', int),
('title', str),
('lang', str),
('albumart', ynbool),
('default', ynbool),
('external', ynbool),
('external-filename', str),
('codec', str),
('selected', ynbool)))
@property
def chapter_list(self):
return self._get_dict('chapter-list/', (('title', str), ('time', float)))
def _get_dict(self, prefix, props): @property
return { name: proptype(_ensure_encoding(_mpv_get_property_string(self.handle, (prefix+name).encode()))) for name, proptype in props } def playlist(self):
return self._get_list('playlist/', (('filename', str),))
@property
def track_list(self):
return self._get_list('track-list/', (
('id', int),
('type', str),
('src-id', int),
('title', str),
('lang', str),
('albumart', ynbool),
('default', ynbool),
('external', ynbool),
('external-filename', str),
('codec', str),
('selected', ynbool)))
@property
def chapter_list(self):
return self._get_dict('chapter-list/', (('title', str), ('time', float)))
def _get_list(self, prefix, props): def _get_dict(self, prefix, props):
count = int(_ensure_encoding(_mpv_get_property_string(self.handle, (prefix+'count').encode()))) return { name: proptype(_ensure_encoding(_mpv_get_property_string(self.handle, (prefix+name).encode()))) for name, proptype in props }
return [ self._get_dict(prefix+str(index)+'/', props) for index in range(count)]
# TODO: af, vf properties
# TODO: edition-list
# TODO property-mapped options
def _get_list(self, prefix, props):
count = int(_ensure_encoding(_mpv_get_property_string(self.handle, (prefix+'count').encode())))
return [ self._get_dict(prefix+str(index)+'/', props) for index in range(count)]
# TODO: af, vf properties
# TODO: edition-list
# TODO property-mapped options
def bindproperty(MPV, name, proptype, access): def bindproperty(MPV, name, proptype, access):
def getter(self): def getter(self):
return proptype(_ensure_encoding(_mpv_get_property_string(self.handle, name.encode()))) return proptype(_ensure_encoding(_mpv_get_property_string(self.handle, name.encode())))
def setter(self, value): def setter(self, value):
_mpv_set_property_string(self.handle, name.encode(), str(proptype(value)).encode()) _mpv_set_property_string(self.handle, name.encode(), str(proptype(value)).encode())
def barf(*args): def barf(*args):
raise NotImplementedError('Access denied') raise NotImplementedError('Access denied')
setattr(MPV, name.replace('-', '_'), property(getter if 'r' in access else barf, setter if 'w' in access else barf)) setattr(MPV, name.replace('-', '_'), property(getter if 'r' in access else barf, setter if 'w' in access else barf))
for name, proptype, access in ( for name, proptype, access in (
('osd-level', int, 'rw'), ('osd-level', int, 'rw'),
('osd-scale', float, 'rw'), ('osd-scale', float, 'rw'),
('loop', str, 'rw'), ('loop', str, 'rw'),
('loop-file', str, 'rw'), ('loop-file', str, 'rw'),
('speed', float, 'rw'), ('speed', float, 'rw'),
('filename', str, 'r'), ('filename', str, 'r'),
('file-size', int, 'r'), ('file-size', int, 'r'),
('path', str, 'r'), ('path', str, 'r'),
('media-title', str, 'r'), ('media-title', str, 'r'),
('stream-pos', int, 'rw'), ('stream-pos', int, 'rw'),
('stream-end', int, 'r'), ('stream-end', int, 'r'),
('length', float, 'r'), ('length', float, 'r'),
('avsync', float, 'r'), ('avsync', float, 'r'),
('total-avsync-change', float, 'r'), ('total-avsync-change', float, 'r'),
('drop-frame-count', int, 'r'), ('drop-frame-count', int, 'r'),
('percent-pos', int, 'rw'), ('percent-pos', int, 'rw'),
('ratio-pos', float, 'rw'), ('ratio-pos', float, 'rw'),
('time-pos', float, 'rw'), ('time-pos', float, 'rw'),
('time-start', float, 'r'), ('time-start', float, 'r'),
('time-remaining', float, 'r'), ('time-remaining', float, 'r'),
('playtime-remaining', float, 'r'), ('playtime-remaining', float, 'r'),
('chapter', int, 'rw'), ('chapter', int, 'rw'),
('edition', int, 'rw'), ('edition', int, 'rw'),
('disc-titles', int, 'r'), ('disc-titles', int, 'r'),
('disc-title', str, 'rw'), ('disc-title', str, 'rw'),
('disc-menu-active', ynbool, 'r'), ('disc-menu-active', ynbool, 'r'),
('chapters', int, 'r'), ('chapters', int, 'r'),
('editions', int, 'r'), ('editions', int, 'r'),
('angle', int, 'rw'), ('angle', int, 'rw'),
('pause', ynbool, 'rw'), ('pause', ynbool, 'rw'),
('core-idle', ynbool, 'r'), ('core-idle', ynbool, 'r'),
('cache', int, 'r'), ('cache', int, 'r'),
('cache-size' , int, 'rw'), ('cache-size' , int, 'rw'),
('pause-for-cache', ynbool, 'r'), ('pause-for-cache', ynbool, 'r'),
('eof-reached', ynbool, 'r'), ('eof-reached', ynbool, 'r'),
('pts-association-mode', str, 'rw'), ('pts-association-mode', str, 'rw'),
('hr-seek', ynbool, 'rw'), ('hr-seek', ynbool, 'rw'),
('volume', float, 'rw'), ('volume', float, 'rw'),
('mute', ynbool, 'rw'), ('mute', ynbool, 'rw'),
('audio-delay', float, 'rw'), ('audio-delay', float, 'rw'),
('audio-format', str, 'r'), ('audio-format', str, 'r'),
('audio-codec', str, 'r'), ('audio-codec', str, 'r'),
('audio-bitrate', float, 'r'), ('audio-bitrate', float, 'r'),
('audio-samplerate', int, 'r'), ('audio-samplerate', int, 'r'),
('audio-channels', str, 'r'), ('audio-channels', str, 'r'),
('aid', int, 'rw'), ('aid', int, 'rw'),
('audio', int, 'rw'), ('audio', int, 'rw'),
('balance', int, 'rw'), ('balance', int, 'rw'),
('fullscreen', ynbool, 'rw'), ('fullscreen', ynbool, 'rw'),
('deinterlace', str, 'rw'), ('deinterlace', str, 'rw'),
('colormatrix', str, 'rw'), ('colormatrix', str, 'rw'),
('colormatrix-input-range', str, 'rw'), ('colormatrix-input-range', str, 'rw'),
('colormatrix-output-range', str, 'rw'), ('colormatrix-output-range', str, 'rw'),
('colormatrix-primaries', str, 'rw'), ('colormatrix-primaries', str, 'rw'),
('ontop', ynbool, 'rw'), ('ontop', ynbool, 'rw'),
('border', ynbool, 'rw'), ('border', ynbool, 'rw'),
('framedrop', str, 'rw'), ('framedrop', str, 'rw'),
('gamma', float, 'rw'), ('gamma', float, 'rw'),
('brightness', int, 'rw'), ('brightness', int, 'rw'),
('contrast', int, 'rw'), ('contrast', int, 'rw'),
('saturation', int, 'rw'), ('saturation', int, 'rw'),
('hue', int, 'rw'), ('hue', int, 'rw'),
('hwdec', ynbool, 'rw'), ('hwdec', ynbool, 'rw'),
('panscan', float, 'rw'), ('panscan', float, 'rw'),
('video-format', str, 'r'), ('video-format', str, 'r'),
('video-codec', str, 'r'), ('video-codec', str, 'r'),
('video-bitrate', float, 'r'), ('video-bitrate', float, 'r'),
('width', int, 'r'), ('width', int, 'r'),
('height', int, 'r'), ('height', int, 'r'),
('dwidth', int, 'r'), ('dwidth', int, 'r'),
('dheight', int, 'r'), ('dheight', int, 'r'),
('fps', float, 'r'), ('fps', float, 'r'),
('estimated-vf-fps', float, 'r'), ('estimated-vf-fps', float, 'r'),
('window-scale', float, 'rw'), ('window-scale', float, 'rw'),
('video-aspect', str, 'rw'), ('video-aspect', str, 'rw'),
('osd-width', int, 'r'), ('osd-width', int, 'r'),
('osd-height', int, 'r'), ('osd-height', int, 'r'),
('osd-par', float, 'r'), ('osd-par', float, 'r'),
('vid', int, 'rw'), ('vid', int, 'rw'),
('video', int, 'rw'), ('video', int, 'rw'),
('video-align-x', float, 'rw'), ('video-align-x', float, 'rw'),
('video-align-y', float, 'rw'), ('video-align-y', float, 'rw'),
('video-pan-x', int, 'rw'), ('video-pan-x', int, 'rw'),
('video-pan-y', int, 'rw'), ('video-pan-y', int, 'rw'),
('video-zoom', float, 'rw'), ('video-zoom', float, 'rw'),
('video-unscaled', ynbool, 'w'), ('video-unscaled', ynbool, 'w'),
('program', int, 'w'), ('program', int, 'w'),
('sid', int, 'rw'), ('sid', int, 'rw'),
('secondary-sid', int, 'rw'), ('secondary-sid', int, 'rw'),
('sub', int, 'rw'), ('sub', int, 'rw'),
('sub-delay', float, 'rw'), ('sub-delay', float, 'rw'),
('sub-pos', int, 'rw'), ('sub-pos', int, 'rw'),
('sub-visibility', ynbool, 'rw'), ('sub-visibility', ynbool, 'rw'),
('sub-forced-only', ynbool, 'rw'), ('sub-forced-only', ynbool, 'rw'),
('sub-scale', float, 'rw'), ('sub-scale', float, 'rw'),
('ass-use-margins', ynbool, 'rw'), ('ass-use-margins', ynbool, 'rw'),
('ass-vsfilter-aspect-compat', ynbool, 'rw'), ('ass-vsfilter-aspect-compat', ynbool, 'rw'),
('ass-style-override', str, 'rw'), ('ass-style-override', str, 'rw'),
('stream-capture', str, 'rw'), ('stream-capture', str, 'rw'),
('tv-brightness', int, 'rw'), ('tv-brightness', int, 'rw'),
('tv-contrast', int, 'rw'), ('tv-contrast', int, 'rw'),
('tv-saturation', int, 'rw'), ('tv-saturation', int, 'rw'),
('tv-hue', int, 'rw'), ('tv-hue', int, 'rw'),
('playlist-pos', int, 'rw'), ('playlist-pos', int, 'rw'),
('playlist-count', int, 'r'), ('playlist-count', int, 'r'),
('quvi-format', str, 'rw'), ('quvi-format', str, 'rw'),
('seekable', ynbool, 'r')): ('seekable', ynbool, 'r')):
bindproperty(MPV, name, proptype, access) bindproperty(MPV, name, proptype, access)