fix(stats): don't write sentence clip to expression-audio field

- `getStatsDirectMiningAudioFieldNames` now takes a `mode` param; sentence mode skips `ExpressionAudio`
- Update test expectation: `ExpressionAudio` should be `undefined` after sentence mining
- Clarify changelog wording for the audio-field fix
- Reformat `SearchTab.test.ts` assertions (no behavior change)
This commit is contained in:
2026-06-06 01:58:01 -07:00
parent 83897c81d1
commit 2fca5bbdba
4 changed files with 10 additions and 5 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ area: stats
- Fixed vocab-page example sentence mining buttons failing when the Anki deck setting is blank or Yomitan card formats are ordered with a non-term card first. - Fixed vocab-page example sentence mining buttons failing when the Anki deck setting is blank or Yomitan card formats are ordered with a non-term card first.
- Fixed vocab-page example word and audio mining so English subtitles are only written to Selection Text for sentence cards, leaving word cards to show the normal Yomitan dictionary glossary. - Fixed vocab-page example word and audio mining so English subtitles are only written to Selection Text for sentence cards, leaving word cards to show the normal Yomitan dictionary glossary.
- Fixed stats-page mining audio updates so generated sentence clips populate `SentenceAudio` when that field exists, while preserving configured expression-audio behavior for direct sentence cards. - Fixed stats-page sentence mining audio updates so generated sentence clips populate `SentenceAudio` without also writing the same clip to the configured expression-audio field.
- Fixed stats-page word mining so the hidden Yomitan helper uses the same configured word-audio sources as the normal Yomitan plus button. - Fixed stats-page word mining so the hidden Yomitan helper uses the same configured word-audio sources as the normal Yomitan plus button.
- Fixed stats-page sentence mining to use the current Anki deck/settings at request time, create direct sentence cards before slow media generation completes, and include stored English subtitle text as Selection Text. - Fixed stats-page sentence mining to use the current Anki deck/settings at request time, create direct sentence cards before slow media generation completes, and include stored English subtitle text as Selection Text.
- Fixed secondary subtitle auto-selection to prefer regular English tracks over Signs/Songs tracks when both are available. - Fixed secondary subtitle auto-selection to prefer regular English tracks over Signs/Songs tracks when both are available.
@@ -1852,7 +1852,7 @@ describe('stats server API routes', () => {
const updateRequest = requests.find((request) => request.action === 'updateNoteFields'); const updateRequest = requests.find((request) => request.action === 'updateNoteFields');
const audioValue = updateRequest?.params?.note?.fields?.SentenceAudio; const audioValue = updateRequest?.params?.note?.fields?.SentenceAudio;
assert.match(audioValue ?? '', /^\[sound:subminer_audio_\d+\.mp3\]$/); assert.match(audioValue ?? '', /^\[sound:subminer_audio_\d+\.mp3\]$/);
assert.equal(updateRequest?.params?.note?.fields?.ExpressionAudio, audioValue); assert.equal(updateRequest?.params?.note?.fields?.ExpressionAudio, undefined);
}); });
}); });
}); });
+6 -1
View File
@@ -172,6 +172,7 @@ function getStatsWordMiningAudioFieldName(
function getStatsDirectMiningAudioFieldNames( function getStatsDirectMiningAudioFieldNames(
ankiConfig: AnkiConnectConfig, ankiConfig: AnkiConnectConfig,
noteInfo: StatsServerNoteInfo | null, noteInfo: StatsServerNoteInfo | null,
mode: 'sentence' | 'audio',
): string[] { ): string[] {
const configuredAudioField = ankiConfig.fields?.audio ?? 'ExpressionAudio'; const configuredAudioField = ankiConfig.fields?.audio ?? 'ExpressionAudio';
if (!ankiConfig.isLapis?.enabled && !ankiConfig.isKiku?.enabled) { if (!ankiConfig.isLapis?.enabled && !ankiConfig.isKiku?.enabled) {
@@ -185,6 +186,10 @@ function getStatsDirectMiningAudioFieldNames(
? resolveStatsNoteFieldName(noteInfo, configuredAudioField) ? resolveStatsNoteFieldName(noteInfo, configuredAudioField)
: null; : null;
if (mode === 'sentence') {
return uniqueFieldNames(sentenceAudioField);
}
return uniqueFieldNames(sentenceAudioField, expressionAudioField); return uniqueFieldNames(sentenceAudioField, expressionAudioField);
} }
@@ -1395,7 +1400,7 @@ export function createStatsApp(
client.storeMediaFile(audioFilename, audioBuffer), client.storeMediaFile(audioFilename, audioBuffer),
); );
const audioValue = `[sound:${audioFilename}]`; const audioValue = `[sound:${audioFilename}]`;
for (const fieldName of getStatsDirectMiningAudioFieldNames(ankiConfig, noteInfo)) { for (const fieldName of getStatsDirectMiningAudioFieldNames(ankiConfig, noteInfo, mode)) {
mediaFields[fieldName] = audioValue; mediaFields[fieldName] = audioValue;
} }
} catch (err) { } catch (err) {
@@ -22,11 +22,11 @@ test('SearchTab forwards stored secondary subtitle text when mining from search
test('SearchTab enables headword sentence search by default and forwards the toggle', () => { test('SearchTab enables headword sentence search by default and forwards the toggle', () => {
const source = fs.readFileSync(SEARCH_TAB_PATH, 'utf8'); const source = fs.readFileSync(SEARCH_TAB_PATH, 'utf8');
assert.match(source, /const \[searchByHeadword,\s*setSearchByHeadword\] = useState\(true\);/);
assert.match( assert.match(
source, source,
/const \[searchByHeadword,\s*setSearchByHeadword\] = useState\(true\);/, /apiClient\s*\.\s*searchSentences\(trimmed,\s*SEARCH_LIMIT,\s*searchByHeadword\)/,
); );
assert.match(source, /apiClient\s*\.\s*searchSentences\(trimmed,\s*SEARCH_LIMIT,\s*searchByHeadword\)/);
assert.match(source, /checked=\{searchByHeadword\}/); assert.match(source, /checked=\{searchByHeadword\}/);
assert.match(source, /setSearchByHeadword\(event\.target\.checked\)/); assert.match(source, /setSearchByHeadword\(event\.target\.checked\)/);
}); });