def __init__(self): super(MpvPlayer, self).__init__() self._mpv = MPV(ytdl=False, input_default_bindings=True, input_vo_keyboard=True) self._playlist = Playlist() self._playlist.song_changed.connect(self._on_song_changed)
def __init__(self): self.queue = [[ '_KhsQ3nn6Kw', 'Wankelmut & Emma Louise - My Head Is A Jungle (MK Remix)', '', True ]] self.current_track = ['vzYYW8V3Ibc', 'Nothing', 'Nothing', False] self.player = MPV(ytdl=True, video=False) self.youtube = YoutubeWrapper() sp_oauth = spotipy.oauth2.SpotifyOAuth(scope=config.SPOTIPY_SCOPE, cache_path=config.SPOTIPY_CACHE) self.spotify = spotipy.Spotify(auth_manager=sp_oauth) devices = self.spotify.devices() print(json.dumps(devices, sort_keys=True, indent=4)) device_id = devices['devices'][0]['id'] results = self.spotify.search(q='Airwaves', type='track') print( json.dumps(results['tracks']['items'][0], sort_keys=True, indent=4)) self.spotify.start_playback( device_id, uris=[results['tracks']['items'][0]['uri']])
def __init__(self, audio_device=b'auto', winid=None, *args, **kwargs): super(MpvPlayer, self).__init__(*args, **kwargs) # https://github.com/cosven/FeelUOwn/issues/246 locale.setlocale(locale.LC_NUMERIC, 'C') mpvkwargs = {} if winid is not None: mpvkwargs['wid'] = winid mpvkwargs['vo'] = 'opengl-cb' # set log_handler if you want to debug # mpvkwargs['log_handler'] = self.__log_handler # mpvkwargs['msg_level'] = 'all=v' logger.info('libmpv version %s', _mpv_client_api_version()) self._mpv = MPV(ytdl=False, input_default_bindings=True, input_vo_keyboard=True, **mpvkwargs) _mpv_set_property_string(self._mpv.handle, b'audio-device', audio_device) # old version libmpv(for example: (1, 20)) should set option by using # _mpv_set_option_string, while newer version can use _mpv_set_property_string _mpv_set_option_string(self._mpv.handle, b'user-agent', b'Mozilla/5.0 (Windows NT 10.0; Win64; x64)') #: if video_format changes to None, there is no video available self.video_format_changed = Signal()
def __init__(self, mpv_commands_async: bool, **properties): super().__init__(**properties) self.mpv_commands_async = mpv_commands_async self._proc_addr_wrapper = OpenGlCbGetProcAddrFn(get_process_address) self.ctx = None self.mpv = MPV(input_default_bindings=True, input_vo_keyboard=True, osc=True # log_handler=print, # loglevel='debug' ) self.connect("realize", self.on_realize) self.connect("render", self.on_render) self.connect("unrealize", self.on_unrealize) self.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) # self.add_events(Gdk.EventMask.KEY_PRESS_MASK) # self.add_events(Gdk.EventMask.STRUCTURE_MASK) # self.add_events(Gdk.EventMask.SCROLL_MASK) self.connect("motion-notify-event", self.on_mouse_move_event) self.connect("button-press-event", self.on_button_press_event) self.connect("button-release-event", self.on_button_release_event)
def __init__(self, filename): self.filename = filename self.mpv = MPV(input_default_bindings=True, input_vo_keyboard=True) self.mpv.pause = True self.mpv.play(self.filename) self.mpv.wait_for_property("length") self.length = self.mpv.length * 1000
def __init__(self, parent=None): super().__init__(parent=parent) self.mpv = MPV(vo='opengl-cb', ytdl=True) self.mpv_gl = _mpv_get_sub_api(self.mpv.handle, MpvSubApi.MPV_SUB_API_OPENGL_CB) self.on_update_c = OpenGlCbUpdateFn(self.on_update) self.on_update_fake_c = OpenGlCbUpdateFn(self.on_update_fake) self.get_proc_addr_c = OpenGlCbGetProcAddrFn(get_proc_addr) _mpv_opengl_cb_set_update_callback(self.mpv_gl, self.on_update_c, None) self.frameSwapped.connect(self.swapped, Qt.DirectConnection)
def __init__(self, audio_device=b'auto', *args, **kwargs): super(MpvPlayer, self).__init__() self._mpv = MPV(ytdl=False, input_default_bindings=True, input_vo_keyboard=True) _mpv_set_property_string(self._mpv.handle, b'audio-device', audio_device) self._playlist = Playlist() self._playlist.song_changed.connect(self._on_song_changed)
class MpvWidget(QOpenGLWidget): def __init__(self, parent=None): super().__init__(parent=parent) self.mpv = MPV(vo='opengl-cb', ytdl=True) self.mpv_gl = _mpv_get_sub_api(self.mpv.handle, MpvSubApi.MPV_SUB_API_OPENGL_CB) self.on_update_c = OpenGlCbUpdateFn(self.on_update) self.on_update_fake_c = OpenGlCbUpdateFn(self.on_update_fake) self.get_proc_addr_c = OpenGlCbGetProcAddrFn(get_proc_addr) _mpv_opengl_cb_set_update_callback(self.mpv_gl, self.on_update_c, None) self.frameSwapped.connect(self.swapped, Qt.DirectConnection) def initializeGL(self): _mpv_opengl_cb_init_gl(self.mpv_gl, None, self.get_proc_addr_c, None) def paintGL(self): # compatible with HiDPI display ratio = self.windowHandle().devicePixelRatio() w = int(self.width() * ratio) h = int(self.height() * ratio) _mpv_opengl_cb_draw(self.mpv_gl, self.defaultFramebufferObject(), w, -h) @pyqtSlot() def maybe_update(self): if self.window().isMinimized(): self.makeCurrent() self.paintGL() self.context().swapBuffers(self.context().surface()) self.swapped() self.doneCurrent() else: self.update() def on_update(self, ctx=None): # HELP: maybeUpdate 中的部分逻辑需要在主线程中执行, # 而 QMetaObject.invokeMethod 似乎正好可以达到这个目标。 # 我们将 maybe_update 标记为 pyqtSlot,这样才能正确的 invoke。 QMetaObject.invokeMethod(self, 'maybe_update') def on_update_fake(self, ctx=None): pass def swapped(self): _mpv_opengl_cb_report_flip(self.mpv_gl, 0) def closeEvent(self, _): self.makeCurrent() if self.mpv_gl: _mpv_opengl_cb_set_update_callback(self.mpv_gl, self.on_update_fake_c, None) _mpv_opengl_cb_uninit_gl(self.mpv_gl) self.mpv.terminate()
def __new__(cls, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf): self = super(Comp, cls).__new__(cls) self.play_backward, self.reading = False, False self.playing = -1 self.json_file, self.mode, self.vid = json_file, mode, mpv_vid self.entries, self.played = entries, [] self.playlist, self.search_res = iter(()), deque() self.mp = MPV(input_default_bindings=True, input_vo_keyboard=True, ytdl=True, ytdl_format=ytdlf) return self
class MpvPlayer(PlayerBase): def __init__(self, flags: Optional[str] = None) -> None: super().__init__() # Converting the flags passed by parameter (str) to a tuple flags = flags.split() if flags not in (None, '') else [] # The audio is always muted, which is needed because not all the # youtube-dl videos are silent. The keep-open flag stops mpv from # closing after the video is over. flags.extend(['mute', 'keep-open']) args = {} if logging.root.level <= logging.INFO: args['log_handler'] = print args['loglevel'] = 'info' args['wid'] = str(int(self.winId())) args['vo'] = 'gpu,libmpv,x11' args['input_default_bindings'] = False args['config'] = False self._mpv = MPV(*flags, **args) def __del__(self) -> None: try: self._mpv.terminate() except AttributeError: pass @property def pause(self) -> bool: return self._mpv.pause @pause.setter def pause(self, do_pause: bool) -> None: logging.info("Playing/Pausing video") self._mpv.pause = do_pause @property def position(self) -> int: time = self._mpv.playback_time return round(time * 1000) if time is not None else 0 @position.setter def position(self, ms: int) -> None: """ Mpv will throw an error if the position is changed before the video starts, so this waits for 'seekable' to be set to True. """ self._mpv.wait_for_property('seekable') logging.info("Position set to %d milliseconds", ms) self._mpv.seek(round(ms / 1000, 2), reference='absolute') def start_video(self, media: str, is_playing: bool = True) -> None: logging.info("Started new video") self._mpv.play(media) # Mpv starts automatically playing the video if not is_playing: self.pause = True
def __init__(self, flags: Optional[str] = None) -> None: super().__init__() # Converting the flags passed by parameter (str) to a tuple flags = flags.split() if flags not in (None, '') else [] flags.extend(self.DEFAULT_FLAGS) args = {} if logging.root.level <= logging.INFO: args['log_handler'] = print args['loglevel'] = 'info' args['wid'] = str(int(self.winId())) # sip.voidptr -> int -> str args.update(self.DEFAULT_ARGS) self._mpv = MPV(*flags, **args)
def __init__(self, filename): self.filename = filename self.open = True self.fbo = gl.glGenFramebuffers(1) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo) self.texture = gl.glGenTextures(1) gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.texture, 0) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, 100, 100, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, None) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) self.playbackPos = (0, ) self.volume = (0, ) self.loop = 'inf' self.mpv = MPV(log_handler=print, loglevel='debug') def get_process_address(_, name): print(name) address = glfw.get_proc_address(name.decode('utf8')) return ctypes.cast(address, ctypes.c_void_p).value proc_addr_wrapper = OpenGlCbGetProcAddrFn(get_process_address) self.ctx = MpvRenderContext( self.mpv, 'opengl', opengl_init_params={'get_proc_address': proc_addr_wrapper}) self.mpv.play(self.filename) self.mpv.volume = 0
class MPV(PlayerInterface, Logging): def __init__(self, args: tuple) -> None: self._player = Player(input_default_bindings=True, input_vo_keyboard=True, **args) def callback(name: str, handler: Callable) -> None: def wrap(*a: tuple, **kw: dict) -> None: handler(*a, **kw) self._player.event_callback(name)(wrap) callback('shutdown', self.event_shutdown) def stop(self): self._player.quit() self._player = None def pause(self): self._player.pause = not self._player.pause def osd(self, level): self._player.osd_level = level def osd_level(self): return self._player.osd_level @property def current_volume(self): return self._player.volume def volume(self, value): self._player.volume = value def __getattr__(self, name): try: return super().__getattr__(name) except TypeError: pass def event_shutdown(self, data, *a, **kw): pass @property def length(self): return self.duration def message(self, msg, duration): self._player.show_message(msg, duration) def show_progress(self): self._player.show_progress()
def __init__(self, args: tuple) -> None: self._player = Player(input_default_bindings=True, input_vo_keyboard=True, **args) def callback(name: str, handler: Callable) -> None: def wrap(*a: tuple, **kw: dict) -> None: handler(*a, **kw) self._player.event_callback(name)(wrap) callback('shutdown', self.event_shutdown)
def __init__(self, audio_device=b'auto', winid=None, *args, **kwargs): super(MpvPlayer, self).__init__(*args, **kwargs) # https://github.com/cosven/FeelUOwn/issues/246 locale.setlocale(locale.LC_NUMERIC, 'C') mpvkwargs = {} if winid is not None: mpvkwargs['wid'] = winid mpvkwargs['vo'] = 'opengl-cb' # set log_handler if you want to debug # mpvkwargs['log_handler'] = self.__log_handler # mpvkwargs['msg_level'] = 'all=v' # the default version of libmpv on Ubuntu 18.04 is (1, 25) self._version = _mpv_client_api_version() self._mpv = MPV(ytdl=False, input_default_bindings=True, input_vo_keyboard=True, **mpvkwargs) _mpv_set_property_string(self._mpv.handle, b'audio-device', audio_device) # old version libmpv(for example: (1, 20)) should set option by using # _mpv_set_option_string, while newer version can use _mpv_set_property_string _mpv_set_option_string(self._mpv.handle, b'user-agent', b'Mozilla/5.0 (Windows NT 10.0; Win64; x64)') #: if video_format changes to None, there is no video available self.video_format_changed = Signal() self._mpv.observe_property( 'time-pos', lambda name, position: self._on_position_changed(position)) self._mpv.observe_property( 'duration', lambda name, duration: self._on_duration_changed(duration)) self._mpv.observe_property( 'video-format', lambda name, vformat: self._on_video_format_changed(vformat)) # self._mpv.register_event_callback(lambda event: self._on_event(event)) self._mpv._event_callbacks.append(self._on_event) logger.debug('Player initialize finished.')
class MpvPlayer(PlayerBase): # The audio is always muted, which is needed because not all the # youtube-dl videos are silent. The keep-open flag stops mpv from closing # after the video is over. DEFAULT_FLAGS = ['mute'] DEFAULT_ARGS = { 'vo': 'gpu,libmpv,x11', 'config': False, 'keep-open': 'always' } def __init__(self, flags: Optional[str] = None) -> None: super().__init__() # Converting the flags passed by parameter (str) to a tuple flags = flags.split() if flags not in (None, '') else [] flags.extend(self.DEFAULT_FLAGS) args = {} if logging.root.level <= logging.INFO: args['log_handler'] = print args['loglevel'] = 'info' args['wid'] = str(int(self.winId())) # sip.voidptr -> int -> str args.update(self.DEFAULT_ARGS) self._mpv = MPV(*flags, **args) @property def pause(self) -> bool: return self._mpv.pause @pause.setter def pause(self, do_pause: bool) -> None: logging.info("Playing/Pausing video") self._mpv.pause = do_pause @property def position(self) -> int: time = self._mpv.playback_time return round(time * 1000) if time is not None else 0 def seek(self, ms: int, relative: bool = False) -> None: """ Mpv will throw an error if the position is changed before the video starts, so this waits for 'seekable' to be set to True. """ self._mpv.wait_for_property('seekable') logging.info("Position set to %d milliseconds", ms) self._mpv.seek(round(ms / 1000, 2), reference='relative' if relative else 'absolute') def start_video(self, media: str, is_playing: bool = True) -> None: logging.info("Started new video") self._mpv.play(media) # Mpv starts automatically playing the video if not is_playing: self.pause = True
def init_player(self): def mpv_log(loglevel, component, message): self.print('Mpv log: [{}] {}: {}'.format(loglevel, component, message)) mpv_args = [] mpv_kw = { 'wid': int(self.ui.video.winId()), 'keep-open': 'yes', 'rebase-start-time': 'no', 'framedrop': 'no', 'osd-level': '2', 'osd-fractions': 'yes', } for opt in self.mpv_options: if '=' in opt: k, v = opt.split('=', 1) mpv_kw[k] = v else: mpv_args.append(opt) player = MPV(*mpv_args, log_handler=mpv_log, **mpv_kw) self.player = player player.pause = True def on_player_loaded(): if self.ffmpeg_bin: self.check_ffmpeg_seek_problem() self.load_state() self.ui.loading.hide() self.state_loaded = True self.player_loaded.connect(on_player_loaded) def on_playback_len(s): self.playback_len = s player.unobserve_property('duration', on_playback_len) def on_playback_pos(s): if self.playback_pos is None: self.player_loaded.emit() self.playback_pos = s self.statusbar_update.emit() self.ui.seekbar.update() player.observe_property('time-pos', on_playback_pos) player.observe_property('duration', on_playback_len) player.play(self.filename)
def mpv_extract_info(filename): """Return list of entries extracted from a path or URL using mpv. If an error occur during the extraction, return None. """ mp = MPV(ytdl=True, vid=False) mp.play(filename) while mp.duration is None: sleep(0.25) if mp.playback_abort: return None info = { 'filename': filename, 'title': mp.media_title.decode(), 'duration': mp.osd.duration, 'error': False, 'playing': False, 'selected': False } mp.quit() return [info]
import logging import sys from telegram.ext import Updater, CommandHandler, MessageHandler, Filters from mpv import MPV # logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger() logger.setLevel(logging.INFO) mpv = MPV(ytdl=True, vo='null') def start(update, context): update.message.reply_text("yo!") def play(update, context): mpv.play(update.message.text) def main(): if len(sys.argv) != 2: print(f"usage: {sys.argv[0]} <token>") exit(1) updater = Updater(sys.argv[1], use_context=True)
class Omp(object): """Omni Media Player meta object. Attributes: entries (list): list of all tracks json_file (str): path to save JSON playlist mode (str): the mode to pick and play tracks mp (MPV): an mpv instance play_backward (bool): flag show if to play the previous track play_list (list): list of tracks according to mode played (list): list of previously played tracks playing (int): index of playing track in played playlist (iterator): iterator of tracks according to mode search_res (iterator): title-searched results vid (str): flag show if video output is enabled I/O handlers (defined by front-end): print_msg(message, error=False): print a message property_handler(name, val): called when a mpv property updated read_input(prompt): prompt for user input refresh(): update interface content """ def __new__(cls, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf): self = super(Comp, cls).__new__(cls) self.play_backward, self.reading = False, False self.playing = -1 self.json_file, self.mode, self.vid = json_file, mode, mpv_vid self.entries, self.played = entries, [] self.playlist, self.search_res = iter(()), deque() self.mp = MPV(input_default_bindings=True, input_vo_keyboard=True, ytdl=True, ytdl_format=ytdlf) return self def __init__(self, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf): if mpv_vo is not None: self.mp['vo'] = mpv_vo @self.mp.property_observer('mute') @self.mp.property_observer('pause') @self.mp.property_observer('time-pos') def observer(name, value): self.property_handler(name, value) self.mp.register_key_binding('q', lambda state, key: None) def __enter__(self): return self def update_play_list(self, pick): """Update the list of entries to be played.""" if pick == 'current': self.play_list = [self.current()] elif pick == 'all': self.play_list = deque(self.entries) self.play_list.rotate(-self.idx()) else: self.play_list = [i for i in self.entries if i.get('selected')] def update_playlist(self): """Update the playlist to be used by play function.""" action, pick = self.mode.split('-') self.update_play_list(pick) if action == 'play': self.playlist = iter(self.play_list) elif action == 'repeat': self.playlist = cycle(self.play_list) else: self.playlist = iter(lambda: choice(self.play_list), None) if self.playing < -1: self.played = self.played[:self.playing + 1] def seek(self, amount, reference='relative', precision='default-precise'): """Wrap mp.seek with a try clause to avoid crash when nothing is being played. """ try: self.mp.seek(amount, reference, precision) except: pass def next(self, force=False, backward=False): self.play_backward = backward if self.mp.idle_active: self.play(force) else: self.seek(100, 'absolute-percent') if force: self.mp.pause = False def search(self, backward=False): """Prompt then search for a pattern.""" p = re.compile(self.gets('/'), re.IGNORECASE) entries = deque(self.entries) entries.rotate(-self.idx()) self.search_res = deque( filter(lambda entry: p.search(entry['title']) is not None, entries)) if backward: self.search_res.reverse() if self.search_res: self.move(self.idx(self.search_res[0]) - self.idx()) else: self.update_status(_("Pattern not found"), curses.color_pair(1)) def next_search(self, backward=False): """Repeat previous search.""" if self.search_res: self.search_res.rotate(1 if backward else -1) self.move(self.idx(self.search_res[0]) - self.idx()) else: self.update_status(_("Pattern not found"), curses.color_pair(1)) def dump_json(self): s = self.read_input( _("Save playlist to [{}]: ").format(self.json_file)) self.json_file = abspath(expanduser(expandvars(s or self.json_file))) try: makedirs(dirname(self.json_file), exist_ok=True) except: errmsg = _("'{}': Can't open file for writing").format( self.json_file) self.print_msg(errmsg, error=True) else: with open(self.json_file, 'w') as f: json.dump(self.entries, f, ensure_ascii=False, indent=2, sort_keys=True) self.print_msg(_("'{}' written").format(self.json_file)) def __exit__(self, exc_type, exc_value, traceback): self.mp.quit()
class VideoPlayer: def terminate(self): self.ctx.free() self.mpv.terminate() def __init__(self, filename): self.filename = filename self.open = True self.fbo = gl.glGenFramebuffers(1) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo) self.texture = gl.glGenTextures(1) gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.texture, 0) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, 100, 100, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, None) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) self.playbackPos = (0, ) self.volume = (0, ) self.loop = 'inf' self.mpv = MPV(log_handler=print, loglevel='debug') def get_process_address(_, name): print(name) address = glfw.get_proc_address(name.decode('utf8')) return ctypes.cast(address, ctypes.c_void_p).value proc_addr_wrapper = OpenGlCbGetProcAddrFn(get_process_address) self.ctx = MpvRenderContext( self.mpv, 'opengl', opengl_init_params={'get_proc_address': proc_addr_wrapper}) self.mpv.play(self.filename) self.mpv.volume = 0 def render(self): videowindow, self.open = imgui.begin("Video window {}".format( self.filename), self.open, flags=imgui.WINDOW_NO_SCROLLBAR) w, h = imgui.get_window_size() if imgui.APPEARING: imgui.set_window_size(400, 300) w, h = imgui.core.get_content_region_available() w = int(max(w, 0)) h = int(max(h - 85, 0)) if not self.open: imgui.end() self.terminate() return if self.ctx.update() and w > 0 and h > 0: gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.fbo) gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, w, h, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, None) gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) self.ctx.render(flip_y=False, opengl_fbo={ 'w': w, 'h': h, 'fbo': self.fbo }) try: imgui.text("Filename: {} fbo: {} tex: {}".format( self.mpv.filename, self.fbo, self.texture)) except: imgui.text("Filename: {} fbo: {} tex: {}".format( self.mpv.filename, self.fbo, self.texture)) try: imgui.text("{0:.2f}s/{1:.2f}s ({2:.2f}s remaining)".format( self.mpv.time_pos, self.mpv.duration, self.mpv.playtime_remaining)) except: imgui.text("Loading...") imgui.image(self.texture, w, h) imgui.push_item_width(-1) changed, values = imgui.slider_float("##Playback Percentage", *self.playbackPos, min_value=0.0, max_value=100.0, format="Playback Percentage %.0f", power=1.0) if changed and values: try: self.mpv.command('seek', values, 'absolute-percent') except: pass self.playbackPos = (values, ) elif self.mpv.percent_pos: self.playbackPos = (self.mpv.percent_pos, ) changed, values = imgui.slider_float("##Volume", *self.volume, min_value=0.0, max_value=100.0, format="Volume %.0f", power=1.0) if changed: self.mpv.volume = values self.volume = (values, ) elif self.mpv.volume: self.volume = (self.mpv.volume, ) imgui.end()
class AudioPlayer: def __init__(self, filename): self.filename = filename self.mpv = MPV(input_default_bindings=True, input_vo_keyboard=True) self.mpv.pause = True self.mpv.play(self.filename) self.mpv.wait_for_property("length") self.length = self.mpv.length * 1000 def position(self): return self.mpv.time_pos * 1000 def progress(self): return self.position() / self.length def pause(self): self.mpv.pause = True def play(self): self.mpv.pause = False def toggle_pause(self): self.mpv.pause = not self.mpv.pause def seek_progress(self, progress): self.mpv.seek(self.mpv.length * progress, "absolute", "exact") def seek_relative(self, ms): self.mpv.time_pos += ms / 1000 def observe_position(self, observer): def converting_observer(prop, value): observer(value * 1000) self.mpv.observe_property("time-pos", converting_observer) def __del__(self): # TODO may never be called, see https://github.com/RafeKettler/magicmethods/blob/master/magicmethods.pdf self.mpv.quit() self.mpv.terminate()