diff --git a/qt/aqt/mpv.py b/qt/aqt/mpv.py index eab2e4514..e42077047 100644 --- a/qt/aqt/mpv.py +++ b/qt/aqt/mpv.py @@ -245,10 +245,13 @@ class MPVBase: else: r, w, e = select.select([self._sock], [], [], 1) if r: - b = self._sock.recv(1024) - if not b: - break - buf += b + try: + b = self._sock.recv(1024) + if not b: + break + buf += b + except ConnectionResetError: + return newline = buf.find(b"\n") while newline >= 0: @@ -373,7 +376,7 @@ class MPVBase: return self._get_response(timeout) except MPVCommandError as e: raise MPVCommandError("%r: %s" % (message["command"], e)) - except MPVTimeoutError as e: + except Exception as e: if _retry: print("mpv timed out, restarting") self._stop_process() @@ -381,6 +384,11 @@ class MPVBase: else: raise + def _register_callbacks(self): + """Will be called after mpv restart to reinitialize callbacks + defined in MPV subclass + """ + # # Public API # @@ -400,6 +408,7 @@ class MPVBase: self._start_socket() self._prepare_thread() self._start_thread() + self._register_callbacks() def close(self): """Shutdown the mpv process and our communication setup. @@ -438,6 +447,9 @@ class MPV(MPVBase): super().__init__(*args, **kwargs) + self._register_callbacks() + + def _register_callbacks(self): self._callbacks = {} self._property_serials = {} self._new_serial = iter(range(sys.maxsize)) @@ -448,6 +460,10 @@ class MPV(MPVBase): if not inspect.ismethod(method): continue + # Bypass MPVError: no such event 'init' + if method_name == "on_init": + continue + if method_name.startswith("on_property_"): name = method_name[12:] name = name.replace("_", "-") @@ -479,16 +495,10 @@ class MPV(MPVBase): """Start up the communication threads. """ super()._start_thread() - self._event_thread = threading.Thread(target=self._event_reader) - self._event_thread.daemon = True - self._event_thread.start() - - def _stop_thread(self): - """Stop the communication threads. - """ - super()._stop_thread() - if hasattr(self, "_event_thread"): - self._event_thread.join() + if not hasattr(self, "_event_thread"): + self._event_thread = threading.Thread(target=self._event_reader) + self._event_thread.daemon = True + self._event_thread.start() # # Event/callback API @@ -496,7 +506,7 @@ class MPV(MPVBase): def _event_reader(self): """Collect incoming event messages and call the event handler. """ - while not self._stop_event.is_set(): + while True: message = self._get_event(timeout=1) if message is None: continue diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 5c8634308..f1e096a09 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -19,7 +19,7 @@ from anki.lang import _ from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag from anki.utils import isLin, isMac, isWin from aqt import gui_hooks -from aqt.mpv import MPV, MPVBase +from aqt.mpv import MPV, MPVBase, MPVCommandError from aqt.qt import * from aqt.taskman import TaskManager from aqt.utils import restoreGeom, saveGeom, showWarning, startup_info @@ -334,6 +334,16 @@ class MpvManager(MPV, SoundOrVideoPlayer): self.default_argv += ["--config-dir=" + base_path] super().__init__(window_id=None, debug=False) + def on_init(self) -> None: + try: + self.command("keybind", "q", "stop") + self.command("keybind", "Q", "stop") + self.command("keybind", "CLOSE_WIN", "stop") + self.command("keybind", "ctrl+w", "stop") + self.command("keybind", "ctrl+c", "stop") + except MPVCommandError: + print("mpv too old") + def play(self, tag: AVTag, on_done: OnDoneCallback) -> None: assert isinstance(tag, SoundOrVideoTag) self._on_done = on_done @@ -350,8 +360,8 @@ class MpvManager(MPV, SoundOrVideoPlayer): def seek_relative(self, secs: int) -> None: self.command("seek", secs, "relative") - def on_property_idle_active(self, val) -> None: - if val and self._on_done: + def on_end_file(self) -> None: + if self._on_done: self._on_done() def shutdown(self) -> None: