Small beautifications
This commit is contained in:
parent
df80c10799
commit
28b761627f
894
mpv.py
894
mpv.py
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue