diff --git a/changes/youtube-primary-subtitle-config.md b/changes/youtube-primary-subtitle-config.md new file mode 100644 index 0000000..dca64da --- /dev/null +++ b/changes/youtube-primary-subtitle-config.md @@ -0,0 +1,5 @@ +type: changed +area: launcher + +Moved YouTube primary subtitle language defaults to `youtube.primarySubLanguages`. +Removed the old `youtubeSubgen.primarySubLanguages` config path from the generated config and docs. diff --git a/config.example.jsonc b/config.example.jsonc index b94e0c2..2f5c223 100644 --- a/config.example.jsonc +++ b/config.example.jsonc @@ -417,20 +417,11 @@ // YouTube Playback Settings // Defaults for SubMiner YouTube subtitle loading and languages. // ========================================== - "youtubeSubgen": { - "whisperBin": "", // Legacy compatibility path kept for external subtitle fallback tools; not used by default. - "whisperModel": "", // Legacy compatibility model path kept for external subtitle fallback tooling; not used by default. - "whisperVadModel": "", // Legacy compatibility VAD path kept for external subtitle fallback tooling; not used by default. - "whisperThreads": 4, // Legacy thread tuning for subtitle fallback tooling; not used by default. - "fixWithAi": false, // Legacy subtitle fallback post-processing switch kept for compatibility; use is currently disabled by default. Values: true | false - "ai": { - "model": "", // Optional model override for legacy subtitle fallback post-processing; not used by default. - "systemPrompt": "" // Optional system prompt override for legacy subtitle fallback post-processing; not used by default. - }, // Ai setting. + "youtube": { "primarySubLanguages": [ "ja", "jpn" - ] // Comma-separated primary subtitle language priority used by the launcher. + ] // Comma-separated primary subtitle language priority for YouTube auto-loading. }, // Defaults for SubMiner YouTube subtitle loading and languages. // ========================================== diff --git a/launcher/config-domain-parsers.test.ts b/launcher/config-domain-parsers.test.ts index 4f3f090..cc3ddfa 100644 --- a/launcher/config-domain-parsers.test.ts +++ b/launcher/config-domain-parsers.test.ts @@ -29,6 +29,8 @@ test('parseLauncherYoutubeSubgenConfig keeps only valid typed values', () => { model: 'openrouter/subgen-model', systemPrompt: 'Fix subtitles only.', }, + }, + youtube: { primarySubLanguages: ['ja', 42, 'en'], }, secondarySub: { diff --git a/launcher/config/youtube-subgen-config.ts b/launcher/config/youtube-subgen-config.ts index eb1ecbd..3ed2e58 100644 --- a/launcher/config/youtube-subgen-config.ts +++ b/launcher/config/youtube-subgen-config.ts @@ -14,6 +14,9 @@ export function parseLauncherYoutubeSubgenConfig( youtubeSubgenRaw && typeof youtubeSubgenRaw === 'object' ? (youtubeSubgenRaw as Record) : null; + const youtubeRaw = root.youtube; + const youtube = + youtubeRaw && typeof youtubeRaw === 'object' ? (youtubeRaw as Record) : null; const secondarySubRaw = root.secondarySub; const secondarySub = secondarySubRaw && typeof secondarySubRaw === 'object' @@ -74,7 +77,7 @@ export function parseLauncherYoutubeSubgenConfig( } : undefined, ), - primarySubLanguages: asStringArray(youtubeSubgen?.primarySubLanguages), + primarySubLanguages: asStringArray(youtube?.primarySubLanguages), secondarySubLanguages: asStringArray(secondarySub?.secondarySubLanguages), jimakuApiKey: typeof jimaku?.apiKey === 'string' ? jimaku.apiKey : undefined, jimakuApiKeyCommand: diff --git a/src/config/config.test.ts b/src/config/config.test.ts index f9f8212..ada4876 100644 --- a/src/config/config.test.ts +++ b/src/config/config.test.ts @@ -919,7 +919,7 @@ test('accepts trailing commas in jsonc', () => { "enabled": "auto", "port": 7788, }, - "youtubeSubgen": { + "youtube": { "primarySubLanguages": ["ja", "en",], }, }`, @@ -929,7 +929,7 @@ test('accepts trailing commas in jsonc', () => { const service = new ConfigService(dir); const config = service.getConfig(); assert.equal(config.websocket.port, 7788); - assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ['ja', 'en']); + assert.deepEqual(config.youtube.primarySubLanguages, ['ja', 'en']); }); test('reloadConfigStrict rejects invalid jsonc and preserves previous config', () => { @@ -1149,8 +1149,10 @@ test('parses global shortcuts and startup settings', () => { "toggleVisibleOverlayGlobal": "Alt+Shift+U", "openJimaku": "Ctrl+Alt+J" }, + "youtube": { + "primarySubLanguages": ["ja", "jpn", "jp"] + }, "youtubeSubgen": { - "primarySubLanguages": ["ja", "jpn", "jp"], "whisperVadModel": "/models/vad.bin", "whisperThreads": 12, "fixWithAi": true @@ -1165,7 +1167,7 @@ test('parses global shortcuts and startup settings', () => { assert.equal(config.ai.apiKeyCommand, 'pass show subminer/ai'); assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, 'Alt+Shift+U'); assert.equal(config.shortcuts.openJimaku, 'Ctrl+Alt+J'); - assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ['ja', 'jpn', 'jp']); + assert.deepEqual(config.youtube.primarySubLanguages, ['ja', 'jpn', 'jp']); assert.equal(config.youtubeSubgen.whisperVadModel, '/models/vad.bin'); assert.equal(config.youtubeSubgen.whisperThreads, 12); assert.equal(config.youtubeSubgen.fixWithAi, true); @@ -2008,7 +2010,8 @@ test('template generator includes known keys', () => { assert.match(output, /"websocket":/); assert.match(output, /"discordPresence":/); assert.match(output, /"startupWarmups":/); - assert.match(output, /"youtubeSubgen":/); + assert.match(output, /"youtube":/); + assert.doesNotMatch(output, /"youtubeSubgen":/); assert.match(output, /"characterDictionary":\s*\{/); assert.match(output, /"preserveLineBreaks": false/); assert.match(output, /"knownWords"\s*:\s*\{/); @@ -2074,17 +2077,11 @@ test('template generator includes known keys', () => { ); assert.match( output, - /"fixWithAi": false,? \/\/ Legacy subtitle fallback post-processing switch kept for compatibility; use is currently disabled by default\. Values: true \| false/, - ); - assert.match( - output, - /"systemPrompt": "",? \/\/ Optional system prompt override for legacy subtitle fallback post-processing; not used by default\./, + /"primarySubLanguages": \[\s*"ja",\s*"jpn"\s*\],? \/\/ Comma-separated primary subtitle language priority for YouTube auto-loading\./, ); assert.doesNotMatch(output, /"mode": "automatic"/); - assert.match( - output, - /"whisperThreads": 4,? \/\/ Legacy thread tuning for subtitle fallback tooling; not used by default\./, - ); + assert.doesNotMatch(output, /"fixWithAi": false/); + assert.doesNotMatch(output, /"whisperThreads": 4/); assert.match( output, /"launchAtStartup": true,? \/\/ Launch texthooker server automatically when SubMiner starts\. Values: true \| false/, diff --git a/src/config/definitions.ts b/src/config/definitions.ts index 59c4f99..0692e66 100644 --- a/src/config/definitions.ts +++ b/src/config/definitions.ts @@ -30,6 +30,7 @@ const { controller, shortcuts, secondarySub, + youtube, subsync, startupWarmups, auto_start_overlay, @@ -51,6 +52,7 @@ export const DEFAULT_CONFIG: ResolvedConfig = { ankiConnect, shortcuts, secondarySub, + youtube, subsync, startupWarmups, subtitleStyle, diff --git a/src/config/definitions/defaults-core.ts b/src/config/definitions/defaults-core.ts index af11bc8..4d42915 100644 --- a/src/config/definitions/defaults-core.ts +++ b/src/config/definitions/defaults-core.ts @@ -11,6 +11,7 @@ export const CORE_DEFAULT_CONFIG: Pick< | 'controller' | 'shortcuts' | 'secondarySub' + | 'youtube' | 'subsync' | 'startupWarmups' | 'auto_start_overlay' @@ -93,6 +94,9 @@ export const CORE_DEFAULT_CONFIG: Pick< autoLoadSecondarySub: false, defaultMode: 'hover', }, + youtube: { + primarySubLanguages: ['ja', 'jpn'], + }, subsync: { defaultMode: 'auto', alass_path: '', diff --git a/src/config/definitions/defaults-integrations.ts b/src/config/definitions/defaults-integrations.ts index d6074d7..761a2e0 100644 --- a/src/config/definitions/defaults-integrations.ts +++ b/src/config/definitions/defaults-integrations.ts @@ -152,6 +152,5 @@ export const INTEGRATIONS_DEFAULT_CONFIG: Pick< model: '', systemPrompt: '', }, - primarySubLanguages: ['ja', 'jpn'], }, }; diff --git a/src/config/definitions/domain-registry.test.ts b/src/config/definitions/domain-registry.test.ts index 8051e73..4bc079f 100644 --- a/src/config/definitions/domain-registry.test.ts +++ b/src/config/definitions/domain-registry.test.ts @@ -22,6 +22,7 @@ test('config option registry includes critical paths and has unique entries', () 'controller.enabled', 'controller.scrollPixelsPerSecond', 'startupWarmups.lowPowerMode', + 'youtube.primarySubLanguages', 'subtitleStyle.enableJlpt', 'subtitleStyle.autoPauseVideoOnYomitanPopup', 'ankiConnect.enabled', @@ -43,6 +44,7 @@ test('config template sections include expected domains and unique keys', () => 'annotationWebsocket', 'controller', 'startupWarmups', + 'youtube', 'subtitleStyle', 'ankiConnect', 'yomitan', diff --git a/src/config/definitions/options-core.ts b/src/config/definitions/options-core.ts index 8f50a7f..1ab1fd4 100644 --- a/src/config/definitions/options-core.ts +++ b/src/config/definitions/options-core.ts @@ -83,6 +83,12 @@ export function buildCoreConfigOptionRegistry( defaultValue: defaultConfig.logging.level, description: 'Minimum log level for runtime logging.', }, + { + path: 'youtube.primarySubLanguages', + kind: 'string', + defaultValue: defaultConfig.youtube.primarySubLanguages.join(','), + description: 'Comma-separated primary subtitle language priority for YouTube auto-loading.', + }, { path: 'controller.enabled', kind: 'boolean', diff --git a/src/config/definitions/options-integrations.ts b/src/config/definitions/options-integrations.ts index 3656091..ff5feff 100644 --- a/src/config/definitions/options-integrations.ts +++ b/src/config/definitions/options-integrations.ts @@ -411,11 +411,5 @@ export function buildIntegrationConfigOptionRegistry( description: 'Optional system prompt override for legacy subtitle fallback post-processing; not used by default.', }, - { - path: 'youtubeSubgen.primarySubLanguages', - kind: 'string', - defaultValue: defaultConfig.youtubeSubgen.primarySubLanguages.join(','), - description: 'Comma-separated primary subtitle language priority used by the launcher.', - }, ]; } diff --git a/src/config/definitions/template-sections.ts b/src/config/definitions/template-sections.ts index 8be0158..b2918db 100644 --- a/src/config/definitions/template-sections.ts +++ b/src/config/definitions/template-sections.ts @@ -132,7 +132,7 @@ const INTEGRATION_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [ { title: 'YouTube Playback Settings', description: ['Defaults for SubMiner YouTube subtitle loading and languages.'], - key: 'youtubeSubgen', + key: 'youtube', }, { title: 'Anilist', diff --git a/src/config/resolve/core-domains.ts b/src/config/resolve/core-domains.ts index feac20c..9920e7e 100644 --- a/src/config/resolve/core-domains.ts +++ b/src/config/resolve/core-domains.ts @@ -524,6 +524,21 @@ export function applyCoreDomainConfig(context: ResolveContext): void { } } + if (isObject(src.youtube)) { + if (Array.isArray(src.youtube.primarySubLanguages)) { + resolved.youtube.primarySubLanguages = src.youtube.primarySubLanguages.filter( + (item): item is string => typeof item === 'string', + ); + } else if (src.youtube.primarySubLanguages !== undefined) { + warn( + 'youtube.primarySubLanguages', + src.youtube.primarySubLanguages, + resolved.youtube.primarySubLanguages, + 'Expected string array.', + ); + } + } + if (isObject(src.subsync)) { const mode = src.subsync.defaultMode; if (mode === 'auto' || mode === 'manual') { diff --git a/src/config/resolve/subtitle-domains.ts b/src/config/resolve/subtitle-domains.ts index 2d88bb6..81d77d1 100644 --- a/src/config/resolve/subtitle-domains.ts +++ b/src/config/resolve/subtitle-domains.ts @@ -135,16 +135,12 @@ export function applySubtitleDomainConfig(context: ResolveContext): void { warn('youtubeSubgen.ai', src.youtubeSubgen.ai, resolved.youtubeSubgen.ai, 'Expected object.'); } - if (Array.isArray(src.youtubeSubgen.primarySubLanguages)) { - resolved.youtubeSubgen.primarySubLanguages = src.youtubeSubgen.primarySubLanguages.filter( - (item): item is string => typeof item === 'string', - ); - } else if (src.youtubeSubgen.primarySubLanguages !== undefined) { + if (src.youtubeSubgen.primarySubLanguages !== undefined) { warn( 'youtubeSubgen.primarySubLanguages', src.youtubeSubgen.primarySubLanguages, - resolved.youtubeSubgen.primarySubLanguages, - 'Expected string array.', + undefined, + 'Removed. Use youtube.primarySubLanguages instead.', ); } } diff --git a/src/main.ts b/src/main.ts index dc8b865..51b9ae6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1337,7 +1337,7 @@ const startupOsdSequencer = createStartupOsdSequencer({ showOsd: (message) => showMpvOsd(message), }); const youtubePrimarySubtitleNotificationRuntime = createYoutubePrimarySubtitleNotificationRuntime({ - getPrimarySubtitleLanguages: () => getResolvedConfig().youtubeSubgen.primarySubLanguages, + getPrimarySubtitleLanguages: () => getResolvedConfig().youtube.primarySubLanguages, notifyFailure: (message) => reportYoutubeSubtitleFailure(message), schedule: (fn, delayMs) => setTimeout(fn, delayMs), clearSchedule: clearYoutubePrimarySubtitleNotificationTimer, diff --git a/src/types.ts b/src/types.ts index c80da93..61e30ca 100644 --- a/src/types.ts +++ b/src/types.ts @@ -683,6 +683,10 @@ export interface AiConfig { requestTimeoutMs?: number; } +export interface YoutubeConfig { + primarySubLanguages?: string[]; +} + export interface YoutubeSubgenConfig { whisperBin?: string; whisperModel?: string; @@ -690,7 +694,6 @@ export interface YoutubeSubgenConfig { whisperThreads?: number; fixWithAi?: boolean; ai?: AiFeatureConfig; - primarySubLanguages?: string[]; } export interface StatsConfig { @@ -750,6 +753,7 @@ export interface Config { jellyfin?: JellyfinConfig; discordPresence?: DiscordPresenceConfig; ai?: AiConfig; + youtube?: YoutubeConfig; youtubeSubgen?: YoutubeSubgenConfig; immersionTracking?: ImmersionTrackingConfig; stats?: StatsConfig; @@ -929,6 +933,9 @@ export interface ResolvedConfig { systemPrompt: string; requestTimeoutMs: number; }; + youtube: YoutubeConfig & { + primarySubLanguages: string[]; + }; youtubeSubgen: YoutubeSubgenConfig & { whisperBin: string; whisperModel: string; @@ -936,7 +943,6 @@ export interface ResolvedConfig { whisperThreads: number; fixWithAi: boolean; ai: AiFeatureConfig; - primarySubLanguages: string[]; }; immersionTracking: { enabled: boolean;