Small beautifications

This commit is contained in:
jaseg 2015-05-10 17:55:06 +02:00
parent df80c10799
commit 28b761627f
1 changed files with 446 additions and 448 deletions

894
mpv.py
View File

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