Refactor PlayAudio

This commit is contained in:
Luc Mcgrady 2025-10-31 08:28:39 +00:00
parent 112f951a13
commit 8da0491ae5
No known key found for this signature in database
GPG key ID: 4F3D7A0B17CC3D9C
5 changed files with 27 additions and 25 deletions

View file

@ -10,6 +10,7 @@ package anki.frontend;
import "anki/scheduler.proto"; import "anki/scheduler.proto";
import "anki/generic.proto"; import "anki/generic.proto";
import "anki/search.proto"; import "anki/search.proto";
import "anki/card_rendering.proto";
service FrontendService { service FrontendService {
// Returns values from the reviewer // Returns values from the reviewer
@ -31,8 +32,7 @@ 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);
// Plays an audio tag at an index in a specific card // Plays the listed audio tags
// If the index is blank, plays all audio on that side
rpc PlayAudio(PlayAudioRequest) returns (generic.Empty); rpc PlayAudio(PlayAudioRequest) returns (generic.Empty);
} }
@ -49,7 +49,5 @@ message SetSchedulingStatesRequest {
} }
message PlayAudioRequest { message PlayAudioRequest {
bool answer_side = 1; repeated card_rendering.AVTag tags = 1;
optional uint32 index = 2;
uint64 cid = 3;
} }

View file

@ -306,13 +306,16 @@ message NextCardDataResponse {
string css = 5; string css = 5;
string body_class = 6; string body_class = 6;
repeated card_rendering.AVTag question_av_tags = 7;
repeated card_rendering.AVTag answer_av_tags = 8;
// TODO: We can probably make this a little faster by using oneof and // TODO: We can probably make this a little faster by using oneof and
// preventing the partial_front and back being sent to svelte where it isn't // preventing the partial_front and back being sent to svelte where it isn't
// used. Alternatively we can use a completely different message for both // used. Alternatively we can use a completely different message for both
// Rust -> Python and the Python -> Svelte though this would be more // Rust -> Python and the Python -> Svelte though this would be more
// complicated to implement. // complicated to implement.
repeated card_rendering.RenderedTemplateNode partial_front = 7; repeated card_rendering.RenderedTemplateNode partial_front = 9;
repeated card_rendering.RenderedTemplateNode partial_back = 8; repeated card_rendering.RenderedTemplateNode partial_back = 10;
} }
optional NextCardData next_card = 1; optional NextCardData next_card = 1;

View file

@ -28,7 +28,7 @@ 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, CardId from anki.cards import Card
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.frontend_pb2 import PlayAudioRequest
@ -38,6 +38,7 @@ from anki.template import (
PartiallyRenderedCard, PartiallyRenderedCard,
TemplateRenderContext, TemplateRenderContext,
apply_custom_filters, apply_custom_filters,
av_tags_to_native,
) )
from anki.utils import dev_mode from anki.utils import dev_mode
from aqt.changenotetype import ChangeNotetypeDialog from aqt.changenotetype import ChangeNotetypeDialog
@ -670,8 +671,15 @@ def next_card_data() -> bytes:
qside, qside,
) )
qside = ctx.col()._backend.extract_av_tags(text=qside, question_side=True).text q_avtags = ctx.col()._backend.extract_av_tags(text=qside, question_side=True)
aside = ctx.col()._backend.extract_av_tags(text=aside, question_side=False).text a_avtags = ctx.col()._backend.extract_av_tags(text=aside, question_side=False)
# Assumes the av tags are empty in the original response
data.next_card.question_av_tags.extend(q_avtags.av_tags)
data.next_card.answer_av_tags.extend(a_avtags.av_tags)
qside = q_avtags.text
aside = a_avtags.text
qside = aqt.mw.prepare_card_text_for_display(qside) qside = aqt.mw.prepare_card_text_for_display(qside)
aside = aqt.mw.prepare_card_text_for_display(aside) aside = aqt.mw.prepare_card_text_for_display(aside)
@ -687,13 +695,7 @@ def next_card_data() -> bytes:
def play_audio(): def play_audio():
req = PlayAudioRequest.FromString(request.data) req = PlayAudioRequest.FromString(request.data)
card = aqt.mw.col.get_card(CardId(req.cid)) play_tags(av_tags_to_native(req.tags))
# TODO: Pass tags with next_card_data rather than rendering the card here.
tags = card.answer_av_tags() if req.answer_side else card.question_av_tags()
if req.HasField("index"):
play_tags([tags[req.index]])
else:
play_tags(tags)
post_handler_list = [ post_handler_list = [

View file

@ -413,11 +413,13 @@ impl crate::services::SchedulerService for Collection {
next_card: Some(NextCardData { next_card: Some(NextCardData {
queue: Some(queue.into()), queue: Some(queue.into()),
css: render.css, css: render.css.clone(),
// Filled by python // Filled by python
front: "".to_string(), front: "".to_string(),
back: "".to_string(), back: "".to_string(),
body_class: "".to_string(), body_class: "".to_string(),
question_av_tags: vec![],
answer_av_tags: vec![],
partial_front: rendered_nodes_to_proto(render.qnodes), partial_front: rendered_nodes_to_proto(render.qnodes),
partial_back: rendered_nodes_to_proto(render.anodes), partial_back: rendered_nodes_to_proto(render.anodes),

View file

@ -42,14 +42,11 @@ export class ReviewerState {
addEventListener("message", this.onMessage.bind(this)); addEventListener("message", this.onMessage.bind(this));
} }
playAudio(answerSide: boolean, index?: number) {
playAudio({ answerSide, index, cid: this.currentCard!.card!.id });
}
onMessage(e: MessageEvent<ReviewerRequest>) { onMessage(e: MessageEvent<ReviewerRequest>) {
switch (e.data.type) { switch (e.data.type) {
case "audio": { case "audio": {
this.playAudio(e.data.answerSide, e.data.index); const tags = get(this.answerShown) ? this._cardData!.answerAvTags : this._cardData!.questionAvTags;
playAudio({ tags: [tags[e.data.index]] });
break; break;
} }
} }
@ -113,7 +110,7 @@ export class ReviewerState {
const question = resp.nextCard?.front || ""; const question = resp.nextCard?.front || "";
this.updateHtml(question, resp?.nextCard?.css, resp?.nextCard?.bodyClass); this.updateHtml(question, resp?.nextCard?.css, resp?.nextCard?.bodyClass);
this.playAudio(false) playAudio({ tags: this._cardData!.questionAvTags });
this.beginAnsweringMs = Date.now(); this.beginAnsweringMs = Date.now();
} }
@ -124,7 +121,7 @@ export class ReviewerState {
public showAnswer() { public showAnswer() {
this.answerShown.set(true); this.answerShown.set(true);
this.playAudio(true) playAudio({ tags: this._cardData!.answerAvTags });
this.updateHtml(this._cardData?.back || ""); this.updateHtml(this._cardData?.back || "");
} }