Added: Playaudio endpoint

This commit is contained in:
Luc Mcgrady 2025-10-29 15:29:38 +00:00
parent 385d487220
commit 386133743e
No known key found for this signature in database
GPG key ID: 4F3D7A0B17CC3D9C
5 changed files with 42 additions and 7 deletions

View file

@ -30,6 +30,8 @@ service FrontendService {
// Save colour picker's custom colour palette // Save colour picker's custom colour palette
rpc SaveCustomColours(generic.Empty) returns (generic.Empty); rpc SaveCustomColours(generic.Empty) returns (generic.Empty);
rpc PlayAudio(PlayAudioRequest) returns (generic.Empty);
} }
service BackendFrontendService {} service BackendFrontendService {}
@ -43,3 +45,9 @@ message SetSchedulingStatesRequest {
string key = 1; string key = 1;
scheduler.SchedulingStates states = 2; scheduler.SchedulingStates states = 2;
} }
message PlayAudioRequest {
bool answer_side = 1;
uint32 index = 2;
uint64 cid = 3;
}

View file

@ -28,9 +28,10 @@ import aqt
import aqt.main import aqt.main
import aqt.operations import aqt.operations
from anki import hooks from anki import hooks
from anki.cards import Card from anki.cards import Card, CardId
from anki.collection import OpChanges, OpChangesOnly, Progress, SearchNode from anki.collection import OpChanges, OpChangesOnly, Progress, SearchNode
from anki.decks import UpdateDeckConfigs from anki.decks import UpdateDeckConfigs
from anki.frontend_pb2 import PlayAudioRequest
from anki.scheduler.v3 import SchedulingStatesWithContext, SetSchedulingStatesRequest from anki.scheduler.v3 import SchedulingStatesWithContext, SetSchedulingStatesRequest
from anki.scheduler_pb2 import NextCardDataResponse from anki.scheduler_pb2 import NextCardDataResponse
from anki.template import ( from anki.template import (
@ -45,6 +46,7 @@ from aqt.operations import on_op_finished
from aqt.operations.deck import update_deck_configs as update_deck_configs_op from aqt.operations.deck import update_deck_configs as update_deck_configs_op
from aqt.progress import ProgressUpdate from aqt.progress import ProgressUpdate
from aqt.qt import * from aqt.qt import *
from aqt.sound import play_clicked_audio_with_index
from aqt.theme import ThemeManager from aqt.theme import ThemeManager
from aqt.utils import aqt_data_path, show_warning, tr from aqt.utils import aqt_data_path, show_warning, tr
@ -683,6 +685,12 @@ def next_card_data() -> bytes:
return data.SerializeToString() return data.SerializeToString()
def play_audio():
req = PlayAudioRequest.FromString(request.data)
card = aqt.mw.col.get_card(CardId(req.cid))
play_clicked_audio_with_index(req.index, req.answer_side, card)
post_handler_list = [ post_handler_list = [
congrats_info, congrats_info,
get_deck_configs_for_update, get_deck_configs_for_update,
@ -700,6 +708,7 @@ post_handler_list = [
deck_options_ready, deck_options_ready,
save_custom_colours, save_custom_colours,
next_card_data, next_card_data,
play_audio,
] ]

View file

@ -925,11 +925,15 @@ def play_clicked_audio(pycmd: str, card: Card) -> None:
"""eg. if pycmd is 'play:q:0', play the first audio on the question side.""" """eg. if pycmd is 'play:q:0', play the first audio on the question side."""
play, context, str_idx = pycmd.split(":") play, context, str_idx = pycmd.split(":")
idx = int(str_idx) idx = int(str_idx)
if context == "q": play_clicked_audio_with_index(idx, context == "q", card)
tags = card.question_av_tags()
else:
def play_clicked_audio_with_index(index: int, answer_side: bool, card: Card):
if answer_side:
tags = card.answer_av_tags() tags = card.answer_av_tags()
av_player.play_tags([tags[idx]]) else:
tags = card.question_av_tags()
av_player.play_tags([tags[index]])
# Init defaults # Init defaults

View file

@ -54,6 +54,10 @@ base.href = "/";
document.head.appendChild(base); document.head.appendChild(base);
function pycmd(cmd: string) { function pycmd(cmd: string) {
window.parent.postMessage({ type: "pycmd", value: cmd }, "*"); const match = cmd.match(/play:(q|a):(\d+)/);
if (match) {
const [_, context, index] = match;
window.parent.postMessage({ type: "audio", answerSide: context == "a", index: parseInt(index) }, "*");
}
} }
globalThis.pycmd = pycmd; globalThis.pycmd = pycmd;

View file

@ -1,7 +1,7 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { CardAnswer, type NextCardDataResponse_NextCardData } from "@generated/anki/scheduler_pb"; import { CardAnswer, type NextCardDataResponse_NextCardData } from "@generated/anki/scheduler_pb";
import { nextCardData } from "@generated/backend"; import { nextCardData, playAudio } from "@generated/backend";
import { derived, get, writable } from "svelte/store"; import { derived, get, writable } from "svelte/store";
import type { InnerReviewerRequest } from "../reviewer-inner/reviewerRequest"; import type { InnerReviewerRequest } from "../reviewer-inner/reviewerRequest";
@ -37,6 +37,16 @@ export class ReviewerState {
onReady() { onReady() {
this.showQuestion(null); this.showQuestion(null);
addEventListener("message", this.onMessage.bind(this));
}
onMessage(e: MessageEvent<any>) {
switch (e.data.type) {
case "audio": {
playAudio({ answerSide: e.data.answerSide, index: e.data.index, cid: this.currentCard!.card!.id });
break;
}
}
} }
public registerIFrame(iframe: HTMLIFrameElement) { public registerIFrame(iframe: HTMLIFrameElement) {