[codex] Make Windows mpv shortcut self-contained (#40)

This commit is contained in:
2026-04-03 21:35:18 -07:00
committed by GitHub
parent d6c72806bb
commit 7514985feb
131 changed files with 3367 additions and 716 deletions

View File

@@ -2138,7 +2138,7 @@ test('template generator includes known keys', () => {
);
assert.match(
output,
/"primarySubLanguages": \[\s*"ja",\s*"jpn"\s*\],? \/\/ Comma-separated primary subtitle language priority for YouTube auto-loading\./,
/"primarySubLanguages": \[\s*"ja",\s*"jpn"\s*\],? \/\/ Comma-separated primary subtitle language priority for managed subtitle auto-selection\./,
);
assert.doesNotMatch(output, /"mode": "automatic"/);
assert.doesNotMatch(output, /"fixWithAi": false/);

View File

@@ -35,7 +35,7 @@ const {
startupWarmups,
auto_start_overlay,
} = CORE_DEFAULT_CONFIG;
const { ankiConnect, jimaku, anilist, yomitan, jellyfin, discordPresence, ai, youtubeSubgen } =
const { ankiConnect, jimaku, anilist, mpv, yomitan, jellyfin, discordPresence, ai, youtubeSubgen } =
INTEGRATIONS_DEFAULT_CONFIG;
const { subtitleStyle, subtitleSidebar } = SUBTITLE_DEFAULT_CONFIG;
const { immersionTracking } = IMMERSION_DEFAULT_CONFIG;
@@ -60,6 +60,7 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
auto_start_overlay,
jimaku,
anilist,
mpv,
yomitan,
jellyfin,
discordPresence,

View File

@@ -5,6 +5,7 @@ export const INTEGRATIONS_DEFAULT_CONFIG: Pick<
| 'ankiConnect'
| 'jimaku'
| 'anilist'
| 'mpv'
| 'yomitan'
| 'jellyfin'
| 'discordPresence'
@@ -90,6 +91,9 @@ export const INTEGRATIONS_DEFAULT_CONFIG: Pick<
languagePreference: 'ja',
maxEntryResults: 10,
},
mpv: {
executablePath: '',
},
anilist: {
enabled: false,
accessToken: '',

View File

@@ -28,6 +28,7 @@ test('config option registry includes critical paths and has unique entries', ()
'ankiConnect.enabled',
'anilist.characterDictionary.enabled',
'anilist.characterDictionary.collapsibleSections.description',
'mpv.executablePath',
'yomitan.externalProfilePath',
'immersionTracking.enabled',
]) {
@@ -48,6 +49,7 @@ test('config template sections include expected domains and unique keys', () =>
'subtitleStyle',
'ankiConnect',
'yomitan',
'mpv',
'immersionTracking',
];

View File

@@ -87,7 +87,8 @@ export function buildCoreConfigOptionRegistry(
path: 'youtube.primarySubLanguages',
kind: 'string',
defaultValue: defaultConfig.youtube.primarySubLanguages.join(','),
description: 'Comma-separated primary subtitle language priority for YouTube auto-loading.',
description:
'Comma-separated primary subtitle language priority for managed subtitle auto-selection.',
},
{
path: 'controller.enabled',

View File

@@ -238,6 +238,13 @@ export function buildIntegrationConfigOptionRegistry(
description:
'Optional external Yomitan Electron profile path to use in read-only mode for shared dictionaries/settings. Example: ~/.config/gsm_overlay',
},
{
path: 'mpv.executablePath',
kind: 'string',
defaultValue: defaultConfig.mpv.executablePath,
description:
'Optional absolute path to mpv.exe for Windows launch flows. Leave empty to auto-discover from SUBMINER_MPV_PATH or PATH.',
},
{
path: 'jellyfin.enabled',
kind: 'boolean',

View File

@@ -74,7 +74,7 @@ const CORE_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
title: 'Secondary Subtitles',
description: [
'Dual subtitle track options.',
'Used by the YouTube subtitle loading flow as secondary language preferences.',
'Used by managed subtitle loading as secondary language preferences for local and YouTube playback.',
],
notes: ['Hot-reload: defaultMode updates live while SubMiner is running.'],
key: 'secondarySub',
@@ -131,7 +131,9 @@ const INTEGRATION_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
},
{
title: 'YouTube Playback Settings',
description: ['Defaults for SubMiner YouTube subtitle loading and languages.'],
description: [
'Defaults for managed subtitle language preferences and YouTube subtitle loading.',
],
key: 'youtube',
},
{
@@ -153,6 +155,14 @@ const INTEGRATION_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
],
key: 'yomitan',
},
{
title: 'MPV Launcher',
description: [
'Optional mpv.exe override for Windows playback entry points.',
'Leave mpv.executablePath blank to auto-discover mpv.exe from SUBMINER_MPV_PATH or PATH.',
],
key: 'mpv',
},
{
title: 'Jellyfin',
description: [

View File

@@ -0,0 +1,31 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolveConfig } from '../resolve';
test('resolveConfig trims configured mpv executable path', () => {
const { resolved, warnings } = resolveConfig({
mpv: {
executablePath: ' C:\\Program Files\\mpv\\mpv.exe ',
},
});
assert.equal(resolved.mpv.executablePath, 'C:\\Program Files\\mpv\\mpv.exe');
assert.deepEqual(warnings, []);
});
test('resolveConfig warns for invalid mpv executable path type', () => {
const { resolved, warnings } = resolveConfig({
mpv: {
executablePath: 42 as never,
},
});
assert.equal(resolved.mpv.executablePath, '');
assert.equal(warnings.length, 1);
assert.deepEqual(warnings[0], {
path: 'mpv.executablePath',
value: 42,
fallback: '',
message: 'Expected string.',
});
});

View File

@@ -228,6 +228,22 @@ export function applyIntegrationConfig(context: ResolveContext): void {
warn('yomitan', src.yomitan, resolved.yomitan, 'Expected object.');
}
if (isObject(src.mpv)) {
const executablePath = asString(src.mpv.executablePath);
if (executablePath !== undefined) {
resolved.mpv.executablePath = executablePath.trim();
} else if (src.mpv.executablePath !== undefined) {
warn(
'mpv.executablePath',
src.mpv.executablePath,
resolved.mpv.executablePath,
'Expected string.',
);
}
} else if (src.mpv !== undefined) {
warn('mpv', src.mpv, resolved.mpv, 'Expected object.');
}
if (isObject(src.jellyfin)) {
const enabled = asBoolean(src.jellyfin.enabled);
if (enabled !== undefined) {