mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-28 16:49:50 -07:00
feat(anki): mirror mpv software volume into generated sentence audio (#155)
This commit is contained in:
@@ -93,6 +93,7 @@ test('loads defaults when config is missing', () => {
|
||||
systemPrompt: '',
|
||||
});
|
||||
assert.equal(config.ankiConnect.media.normalizeAudio, true);
|
||||
assert.equal(config.ankiConnect.media.mirrorMpvVolume, true);
|
||||
assert.equal(config.startupWarmups.lowPowerMode, false);
|
||||
assert.equal(config.startupWarmups.mecab, true);
|
||||
assert.equal(config.startupWarmups.yomitanExtension, true);
|
||||
@@ -168,6 +169,31 @@ test('loads defaults when config is missing', () => {
|
||||
assert.equal(config.mpv.aniskipButtonKey, 'TAB');
|
||||
});
|
||||
|
||||
test('rejects invalid mpv volume mirroring values', () => {
|
||||
const dir = makeTempDir();
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'config.jsonc'),
|
||||
`{
|
||||
"ankiConnect": {
|
||||
"media": {
|
||||
"mirrorMpvVolume": "false"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const service = new ConfigService(dir);
|
||||
|
||||
assert.equal(
|
||||
service.getConfig().ankiConnect.media.mirrorMpvVolume,
|
||||
DEFAULT_CONFIG.ankiConnect.media.mirrorMpvVolume,
|
||||
);
|
||||
assert.ok(
|
||||
service.getWarnings().some((warning) => warning.path === 'ankiConnect.media.mirrorMpvVolume'),
|
||||
);
|
||||
});
|
||||
|
||||
test('parses updates config and warns on invalid values', () => {
|
||||
const validDir = makeTempDir();
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -52,6 +52,7 @@ export const INTEGRATIONS_DEFAULT_CONFIG: Pick<
|
||||
animatedCrf: 35,
|
||||
syncAnimatedImageToWordAudio: true,
|
||||
normalizeAudio: true,
|
||||
mirrorMpvVolume: true,
|
||||
audioPadding: 0,
|
||||
fallbackDuration: 3.0,
|
||||
maxMediaDuration: 30,
|
||||
|
||||
@@ -111,6 +111,7 @@ test('config option registry includes critical paths and has unique entries', ()
|
||||
'ankiConnect.enabled',
|
||||
'subtitleStyle.nameMatchEnabled',
|
||||
'ankiConnect.media.normalizeAudio',
|
||||
'ankiConnect.media.mirrorMpvVolume',
|
||||
'anilist.characterDictionary.collapsibleSections.description',
|
||||
'mpv.executablePath',
|
||||
'mpv.launchMode',
|
||||
|
||||
@@ -185,7 +185,15 @@ export function buildIntegrationConfigOptionRegistry(
|
||||
path: 'ankiConnect.media.normalizeAudio',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.ankiConnect.media.normalizeAudio,
|
||||
description: 'Normalize generated sentence audio loudness during media extraction.',
|
||||
description:
|
||||
'Normalize generated sentence audio loudness during media extraction. Changes apply live.',
|
||||
},
|
||||
{
|
||||
path: 'ankiConnect.media.mirrorMpvVolume',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.ankiConnect.media.mirrorMpvVolume,
|
||||
description:
|
||||
"Apply mpv's current software volume curve to generated sentence audio. Changes apply live.",
|
||||
},
|
||||
{
|
||||
path: 'ankiConnect.media.generateImage',
|
||||
|
||||
@@ -135,7 +135,7 @@ const INTEGRATION_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
|
||||
title: 'AnkiConnect Integration',
|
||||
description: ['Automatic Anki updates and media generation options.'],
|
||||
notes: [
|
||||
'Hot-reload: ankiConnect.ai.enabled, knownWords, nPlusOne, fields.word/audio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, and isKiku.fieldGrouping update live while SubMiner is running.',
|
||||
'Hot-reload: ankiConnect.ai.enabled, media.normalizeAudio/mirrorMpvVolume, knownWords, nPlusOne, fields.word/audio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, and isKiku.fieldGrouping update live while SubMiner is running.',
|
||||
'Shared AI provider transport settings are read from top-level ai and typically require restart.',
|
||||
'Most other AnkiConnect settings still require restart.',
|
||||
],
|
||||
|
||||
@@ -106,6 +106,22 @@ export function applyAnkiConnectResolution(context: ResolveContext): void {
|
||||
},
|
||||
};
|
||||
|
||||
if (hasOwn(media, 'mirrorMpvVolume')) {
|
||||
const parsed = asBoolean(media.mirrorMpvVolume);
|
||||
if (parsed === undefined) {
|
||||
context.resolved.ankiConnect.media.mirrorMpvVolume =
|
||||
DEFAULT_CONFIG.ankiConnect.media.mirrorMpvVolume;
|
||||
context.warn(
|
||||
'ankiConnect.media.mirrorMpvVolume',
|
||||
media.mirrorMpvVolume,
|
||||
context.resolved.ankiConnect.media.mirrorMpvVolume,
|
||||
'Expected boolean.',
|
||||
);
|
||||
} else {
|
||||
context.resolved.ankiConnect.media.mirrorMpvVolume = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasOwn(behavior, 'notificationType')) {
|
||||
const parsed = asNotificationType(behavior.notificationType);
|
||||
if (parsed === undefined) {
|
||||
|
||||
@@ -165,6 +165,15 @@ test('settings registry exposes specialized controls for config-assisted inputs'
|
||||
assert.equal(field('discordPresence.presenceStyle').control, 'select');
|
||||
});
|
||||
|
||||
test('settings registry exposes mpv volume mirroring as a mining toggle', () => {
|
||||
const volumeField = field('ankiConnect.media.mirrorMpvVolume');
|
||||
|
||||
assert.equal(volumeField.category, 'mining-anki');
|
||||
assert.equal(volumeField.section, 'Media Capture');
|
||||
assert.equal(volumeField.control, 'boolean');
|
||||
assert.equal(volumeField.defaultValue, true);
|
||||
});
|
||||
|
||||
test('settings registry exposes YouTube media cache mode as a labeled select', () => {
|
||||
const mediaCacheMode = field('youtube.mediaCache.mode');
|
||||
const mediaCacheMaxHeight = field('youtube.mediaCache.maxHeight');
|
||||
@@ -313,6 +322,8 @@ test('settings registry marks safe live config paths as hot-reloadable', () => {
|
||||
'subsync.replace',
|
||||
'ankiConnect.behavior.autoUpdateNewCards',
|
||||
'ankiConnect.deck',
|
||||
'ankiConnect.media.normalizeAudio',
|
||||
'ankiConnect.media.mirrorMpvVolume',
|
||||
'ankiConnect.knownWords.highlightEnabled',
|
||||
'ankiConnect.knownWords.refreshMinutes',
|
||||
'ankiConnect.knownWords.addMinedWordsImmediately',
|
||||
|
||||
@@ -236,6 +236,7 @@ const LABEL_OVERRIDES: Record<string, string> = {
|
||||
'mpv.pauseUntilOverlayReady': 'Pause Until Overlay Ready',
|
||||
'mpv.aniskipEnabled': 'Enable AniSkip',
|
||||
'mpv.aniskipButtonKey': 'AniSkip Button Key',
|
||||
'ankiConnect.media.mirrorMpvVolume': 'Mirror mpv Volume',
|
||||
'discordPresence.updateIntervalMs': 'Update Interval (ms)',
|
||||
};
|
||||
|
||||
@@ -671,6 +672,8 @@ function restartBehaviorForPath(path: string): ConfigSettingsRestartBehavior {
|
||||
path === 'secondarySub.defaultMode' ||
|
||||
path === 'ankiConnect.deck' ||
|
||||
path === 'ankiConnect.ai.enabled' ||
|
||||
path === 'ankiConnect.media.normalizeAudio' ||
|
||||
path === 'ankiConnect.media.mirrorMpvVolume' ||
|
||||
path === 'ankiConnect.behavior.autoUpdateNewCards' ||
|
||||
path === 'ankiConnect.knownWords.highlightEnabled' ||
|
||||
path === 'ankiConnect.knownWords.refreshMinutes' ||
|
||||
|
||||
Reference in New Issue
Block a user