mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
add a hook for when playback begins
This commit is contained in:
parent
6af7933084
commit
cfa0f65311
5 changed files with 62 additions and 18 deletions
|
@ -75,28 +75,56 @@ class _AddCardsWillShowHistoryMenuHook:
|
|||
add_cards_will_show_history_menu = _AddCardsWillShowHistoryMenuHook()
|
||||
|
||||
|
||||
class _AvPlayerDidPlayHook:
|
||||
_hooks: List[Callable[[], None]] = []
|
||||
class _AvPlayerDidBeginPlayingHook:
|
||||
_hooks: List[Callable[["aqt.sound.Player", "anki.sound.AVTag"], None]] = []
|
||||
|
||||
def append(self, cb: Callable[[], None]) -> None:
|
||||
"""()"""
|
||||
def append(
|
||||
self, cb: Callable[["aqt.sound.Player", "anki.sound.AVTag"], None]
|
||||
) -> None:
|
||||
"""(player: aqt.sound.Player, tag: anki.sound.AVTag)"""
|
||||
self._hooks.append(cb)
|
||||
|
||||
def remove(self, cb: Callable[[], None]) -> None:
|
||||
def remove(
|
||||
self, cb: Callable[["aqt.sound.Player", "anki.sound.AVTag"], None]
|
||||
) -> None:
|
||||
if cb in self._hooks:
|
||||
self._hooks.remove(cb)
|
||||
|
||||
def __call__(self) -> None:
|
||||
def __call__(self, player: aqt.sound.Player, tag: anki.sound.AVTag) -> None:
|
||||
for hook in self._hooks:
|
||||
try:
|
||||
hook()
|
||||
hook(player, tag)
|
||||
except:
|
||||
# if the hook fails, remove it
|
||||
self._hooks.remove(hook)
|
||||
raise
|
||||
|
||||
|
||||
av_player_did_play = _AvPlayerDidPlayHook()
|
||||
av_player_did_begin_playing = _AvPlayerDidBeginPlayingHook()
|
||||
|
||||
|
||||
class _AvPlayerDidEndPlayingHook:
|
||||
_hooks: List[Callable[["aqt.sound.Player"], None]] = []
|
||||
|
||||
def append(self, cb: Callable[["aqt.sound.Player"], None]) -> None:
|
||||
"""(player: aqt.sound.Player)"""
|
||||
self._hooks.append(cb)
|
||||
|
||||
def remove(self, cb: Callable[["aqt.sound.Player"], None]) -> None:
|
||||
if cb in self._hooks:
|
||||
self._hooks.remove(cb)
|
||||
|
||||
def __call__(self, player: aqt.sound.Player) -> None:
|
||||
for hook in self._hooks:
|
||||
try:
|
||||
hook(player)
|
||||
except:
|
||||
# if the hook fails, remove it
|
||||
self._hooks.remove(hook)
|
||||
raise
|
||||
|
||||
|
||||
av_player_did_end_playing = _AvPlayerDidEndPlayingHook()
|
||||
|
||||
|
||||
class _AvPlayerWillPlayHook:
|
||||
|
|
|
@ -1180,7 +1180,7 @@ Difference to correct time: %s."""
|
|||
hooks.card_odue_was_invalid.append(self.onOdueInvalid)
|
||||
|
||||
gui_hooks.av_player_will_play.append(self.on_av_player_will_play)
|
||||
gui_hooks.av_player_did_play.append(self.on_av_player_did_play)
|
||||
gui_hooks.av_player_did_end_playing.append(self.on_av_player_did_end_playing)
|
||||
|
||||
self._activeWindowOnPlay: Optional[QWidget] = None
|
||||
|
||||
|
@ -1207,7 +1207,7 @@ and if the problem comes up again, please ask on the support site."""
|
|||
|
||||
self._activeWindowOnPlay = self.app.activeWindow() or self._activeWindowOnPlay
|
||||
|
||||
def on_av_player_did_play(self) -> None:
|
||||
def on_av_player_did_end_playing(self, player: Any) -> None:
|
||||
"Restore window focus after a video was played."
|
||||
w = self._activeWindowOnPlay
|
||||
if not self.app.activeWindow() and w and not sip.isdeleted(w) and w.isVisible():
|
||||
|
|
|
@ -36,6 +36,12 @@ OnDoneCallback = Callable[[], None]
|
|||
class Player(ABC):
|
||||
@abstractmethod
|
||||
def play(self, tag: AVTag, on_done: OnDoneCallback) -> None:
|
||||
"""Play a file.
|
||||
|
||||
When reimplementing, make sure to call
|
||||
gui_hooks.av_player_did_begin_playing(self, tag)
|
||||
on the main thread after playback begins.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
@ -142,8 +148,8 @@ class AVPlayer:
|
|||
return self._enqueued.pop(0)
|
||||
|
||||
def _on_play_finished(self) -> None:
|
||||
gui_hooks.av_player_did_end_playing(self.current_player)
|
||||
self.current_player = None
|
||||
gui_hooks.av_player_did_play()
|
||||
self._play_next_if_idle()
|
||||
|
||||
def _play_next_if_idle(self) -> None:
|
||||
|
@ -238,9 +244,13 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
|
|||
def _play(self, tag: AVTag) -> None:
|
||||
assert isinstance(tag, SoundOrVideoTag)
|
||||
self._process = subprocess.Popen(self.args + [tag.filename], env=self.env)
|
||||
self._wait_for_termination()
|
||||
self._wait_for_termination(tag)
|
||||
|
||||
def _wait_for_termination(self, tag: AVTag):
|
||||
self._taskman.run_on_main(
|
||||
lambda: gui_hooks.av_player_did_begin_playing(self, tag)
|
||||
)
|
||||
|
||||
def _wait_for_termination(self):
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
|
@ -344,6 +354,7 @@ class MpvManager(MPV, SoundOrVideoPlayer):
|
|||
self._on_done = on_done
|
||||
path = os.path.join(os.getcwd(), tag.filename)
|
||||
self.command("loadfile", path, "append-play")
|
||||
gui_hooks.av_player_did_begin_playing(self, tag)
|
||||
|
||||
def stop(self) -> None:
|
||||
self.command("stop")
|
||||
|
@ -388,7 +399,7 @@ class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer):
|
|||
self._process = subprocess.Popen(
|
||||
self.args + [tag.filename], env=self.env, stdin=subprocess.PIPE
|
||||
)
|
||||
self._wait_for_termination()
|
||||
self._wait_for_termination(tag)
|
||||
|
||||
def command(self, *args) -> None:
|
||||
"""Send a command over the slave interface.
|
||||
|
|
|
@ -34,6 +34,7 @@ from typing import Any, List, Optional, cast
|
|||
|
||||
from anki.sound import AVTag, TTSTag
|
||||
from anki.utils import checksum, isWin, tmpdir
|
||||
from aqt import gui_hooks
|
||||
from aqt.sound import OnDoneCallback, PlayerInterrupted, SimpleProcessPlayer
|
||||
|
||||
|
||||
|
@ -128,8 +129,7 @@ class MacTTSPlayer(TTSProcessPlayer):
|
|||
# write the input text to stdin
|
||||
self._process.stdin.write(tag.field_text.encode("utf8"))
|
||||
self._process.stdin.close()
|
||||
|
||||
self._wait_for_termination()
|
||||
self._wait_for_termination(tag)
|
||||
|
||||
def get_available_voices(self) -> List[TTSVoice]:
|
||||
cmd = subprocess.run(
|
||||
|
@ -170,7 +170,7 @@ class MacTTSFilePlayer(MacTTSPlayer):
|
|||
# write the input text to stdin
|
||||
self._process.stdin.write(tag.field_text.encode("utf8"))
|
||||
self._process.stdin.close()
|
||||
self._wait_for_termination()
|
||||
self._wait_for_termination(tag)
|
||||
|
||||
def _on_done(self, ret: Future, cb: OnDoneCallback) -> None:
|
||||
try:
|
||||
|
@ -433,6 +433,7 @@ if isWin:
|
|||
native_voice = voice.handle
|
||||
self.speaker.Voice = native_voice
|
||||
self.speaker.Speak(tag.field_text, 1)
|
||||
gui_hooks.av_player_did_begin_playing(self, tag)
|
||||
|
||||
# wait 100ms
|
||||
while not self.speaker.WaitUntilDone(100):
|
||||
|
|
|
@ -199,7 +199,11 @@ hooks = [
|
|||
# Sound/video
|
||||
###################
|
||||
Hook(name="av_player_will_play", args=["tag: anki.sound.AVTag"]),
|
||||
Hook(name="av_player_did_play"),
|
||||
Hook(
|
||||
name="av_player_did_begin_playing",
|
||||
args=["player: aqt.sound.Player", "tag: anki.sound.AVTag"],
|
||||
),
|
||||
Hook(name="av_player_did_end_playing", args=["player: aqt.sound.Player"]),
|
||||
# Other
|
||||
###################
|
||||
Hook(
|
||||
|
|
Loading…
Reference in a new issue