mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
Enhance AniList character dictionary sync and subtitle features (#15)
This commit is contained in:
@@ -5,6 +5,7 @@ export const CORE_DEFAULT_CONFIG: Pick<
|
||||
| 'subtitlePosition'
|
||||
| 'keybindings'
|
||||
| 'websocket'
|
||||
| 'annotationWebsocket'
|
||||
| 'logging'
|
||||
| 'texthooker'
|
||||
| 'shortcuts'
|
||||
@@ -19,10 +20,15 @@ export const CORE_DEFAULT_CONFIG: Pick<
|
||||
enabled: 'auto',
|
||||
port: 6677,
|
||||
},
|
||||
annotationWebsocket: {
|
||||
enabled: true,
|
||||
port: 6678,
|
||||
},
|
||||
logging: {
|
||||
level: 'info',
|
||||
},
|
||||
texthooker: {
|
||||
launchAtStartup: true,
|
||||
openBrowser: true,
|
||||
},
|
||||
shortcuts: {
|
||||
|
||||
@@ -86,6 +86,18 @@ export const INTEGRATIONS_DEFAULT_CONFIG: Pick<
|
||||
anilist: {
|
||||
enabled: false,
|
||||
accessToken: '',
|
||||
characterDictionary: {
|
||||
enabled: false,
|
||||
refreshTtlHours: 168,
|
||||
maxLoaded: 3,
|
||||
evictionPolicy: 'delete',
|
||||
profileScope: 'all',
|
||||
collapsibleSections: {
|
||||
description: false,
|
||||
characterInformation: false,
|
||||
voicedBy: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
jellyfin: {
|
||||
enabled: false,
|
||||
|
||||
@@ -8,6 +8,8 @@ export const SUBTITLE_DEFAULT_CONFIG: Pick<ResolvedConfig, 'subtitleStyle'> = {
|
||||
autoPauseVideoOnYomitanPopup: false,
|
||||
hoverTokenColor: '#f4dbd6',
|
||||
hoverTokenBackgroundColor: 'rgba(54, 58, 79, 0.84)',
|
||||
nameMatchEnabled: true,
|
||||
nameMatchColor: '#f5bde6',
|
||||
fontFamily: 'M PLUS 1 Medium, Source Han Sans JP, Noto Sans CJK JP',
|
||||
fontSize: 35,
|
||||
fontColor: '#cad3f5',
|
||||
@@ -37,7 +39,7 @@ export const SUBTITLE_DEFAULT_CONFIG: Pick<ResolvedConfig, 'subtitleStyle'> = {
|
||||
mode: 'single',
|
||||
matchMode: 'headword',
|
||||
singleColor: '#f5a97f',
|
||||
bandedColors: ['#ed8796', '#f5a97f', '#f9e2af', '#a6e3a1', '#8aadf4'],
|
||||
bandedColors: ['#ed8796', '#f5a97f', '#f9e2af', '#8bd5ca', '#8aadf4'],
|
||||
},
|
||||
secondary: {
|
||||
fontFamily: 'Inter, Noto Sans, Helvetica Neue, sans-serif',
|
||||
|
||||
@@ -18,10 +18,13 @@ test('config option registry includes critical paths and has unique entries', ()
|
||||
|
||||
for (const requiredPath of [
|
||||
'logging.level',
|
||||
'annotationWebsocket.enabled',
|
||||
'startupWarmups.lowPowerMode',
|
||||
'subtitleStyle.enableJlpt',
|
||||
'subtitleStyle.autoPauseVideoOnYomitanPopup',
|
||||
'ankiConnect.enabled',
|
||||
'anilist.characterDictionary.enabled',
|
||||
'anilist.characterDictionary.collapsibleSections.description',
|
||||
'immersionTracking.enabled',
|
||||
]) {
|
||||
assert.ok(paths.includes(requiredPath), `missing config path: ${requiredPath}`);
|
||||
@@ -34,6 +37,7 @@ test('config template sections include expected domains and unique keys', () =>
|
||||
const keys = CONFIG_TEMPLATE_SECTIONS.map((section) => section.key);
|
||||
const requiredKeys: (typeof keys)[number][] = [
|
||||
'websocket',
|
||||
'annotationWebsocket',
|
||||
'startupWarmups',
|
||||
'subtitleStyle',
|
||||
'ankiConnect',
|
||||
|
||||
@@ -12,6 +12,12 @@ export function buildCoreConfigOptionRegistry(
|
||||
defaultValue: defaultConfig.logging.level,
|
||||
description: 'Minimum log level for runtime logging.',
|
||||
},
|
||||
{
|
||||
path: 'texthooker.launchAtStartup',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.texthooker.launchAtStartup,
|
||||
description: 'Launch texthooker server automatically when SubMiner starts.',
|
||||
},
|
||||
{
|
||||
path: 'websocket.enabled',
|
||||
kind: 'enum',
|
||||
@@ -25,6 +31,18 @@ export function buildCoreConfigOptionRegistry(
|
||||
defaultValue: defaultConfig.websocket.port,
|
||||
description: 'Built-in subtitle websocket server port.',
|
||||
},
|
||||
{
|
||||
path: 'annotationWebsocket.enabled',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.annotationWebsocket.enabled,
|
||||
description: 'Annotated subtitle websocket server enabled state.',
|
||||
},
|
||||
{
|
||||
path: 'annotationWebsocket.port',
|
||||
kind: 'number',
|
||||
defaultValue: defaultConfig.annotationWebsocket.port,
|
||||
description: 'Annotated subtitle websocket server port.',
|
||||
},
|
||||
{
|
||||
path: 'subsync.defaultMode',
|
||||
kind: 'enum',
|
||||
|
||||
@@ -135,6 +135,64 @@ export function buildIntegrationConfigOptionRegistry(
|
||||
description:
|
||||
'Optional explicit AniList access token override; leave empty to use locally stored token from setup.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.enabled',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.enabled,
|
||||
description:
|
||||
'Enable automatic Yomitan character dictionary sync for currently watched AniList media.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.refreshTtlHours',
|
||||
kind: 'number',
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.refreshTtlHours,
|
||||
description:
|
||||
'Legacy setting; merged character dictionary retention is now usage-based and this value is ignored.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.maxLoaded',
|
||||
kind: 'number',
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.maxLoaded,
|
||||
description:
|
||||
'Maximum number of most-recently-used anime snapshots included in the merged Yomitan character dictionary.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.evictionPolicy',
|
||||
kind: 'enum',
|
||||
enumValues: ['disable', 'delete'],
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.evictionPolicy,
|
||||
description:
|
||||
'Legacy setting; merged character dictionary eviction is usage-based and this value is ignored.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.profileScope',
|
||||
kind: 'enum',
|
||||
enumValues: ['all', 'active'],
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.profileScope,
|
||||
description: 'Yomitan profile scope for dictionary enable/disable updates.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.collapsibleSections.description',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.collapsibleSections.description,
|
||||
description:
|
||||
'Open the Description section by default in character dictionary glossary entries.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.collapsibleSections.characterInformation',
|
||||
kind: 'boolean',
|
||||
defaultValue:
|
||||
defaultConfig.anilist.characterDictionary.collapsibleSections.characterInformation,
|
||||
description:
|
||||
'Open the Character Information section by default in character dictionary glossary entries.',
|
||||
},
|
||||
{
|
||||
path: 'anilist.characterDictionary.collapsibleSections.voicedBy',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.anilist.characterDictionary.collapsibleSections.voicedBy,
|
||||
description:
|
||||
'Open the Voiced by section by default in character dictionary glossary entries.',
|
||||
},
|
||||
{
|
||||
path: 'jellyfin.enabled',
|
||||
kind: 'boolean',
|
||||
|
||||
@@ -47,6 +47,20 @@ export function buildSubtitleConfigOptionRegistry(
|
||||
defaultValue: defaultConfig.subtitleStyle.hoverTokenBackgroundColor,
|
||||
description: 'CSS color used for hovered subtitle token background highlight in mpv.',
|
||||
},
|
||||
{
|
||||
path: 'subtitleStyle.nameMatchEnabled',
|
||||
kind: 'boolean',
|
||||
defaultValue: defaultConfig.subtitleStyle.nameMatchEnabled,
|
||||
description:
|
||||
'Enable subtitle token coloring for matches from the SubMiner character dictionary.',
|
||||
},
|
||||
{
|
||||
path: 'subtitleStyle.nameMatchColor',
|
||||
kind: 'string',
|
||||
defaultValue: defaultConfig.subtitleStyle.nameMatchColor,
|
||||
description:
|
||||
'Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.',
|
||||
},
|
||||
{
|
||||
path: 'subtitleStyle.frequencyDictionary.enabled',
|
||||
kind: 'boolean',
|
||||
|
||||
@@ -10,7 +10,7 @@ const CORE_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
|
||||
},
|
||||
{
|
||||
title: 'Texthooker Server',
|
||||
description: ['Control whether browser opens automatically for texthooker.'],
|
||||
description: ['Configure texthooker startup launch and browser opening behavior.'],
|
||||
key: 'texthooker',
|
||||
},
|
||||
{
|
||||
@@ -21,6 +21,14 @@ const CORE_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
|
||||
],
|
||||
key: 'websocket',
|
||||
},
|
||||
{
|
||||
title: 'Annotation WebSocket',
|
||||
description: [
|
||||
'Dedicated annotated subtitle websocket for bundled texthooker and token-aware clients.',
|
||||
'Independent from websocket.auto and defaults to port 6678.',
|
||||
],
|
||||
key: 'annotationWebsocket',
|
||||
},
|
||||
{
|
||||
title: 'Logging',
|
||||
description: ['Controls logging verbosity.', 'Set to debug for full runtime diagnostics.'],
|
||||
@@ -104,7 +112,11 @@ const INTEGRATION_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
|
||||
},
|
||||
{
|
||||
title: 'Anilist',
|
||||
description: ['Anilist API credentials and update behavior.'],
|
||||
description: [
|
||||
'Anilist API credentials and update behavior.',
|
||||
'Includes optional auto-sync for a merged MRU-based character dictionary in bundled Yomitan.',
|
||||
'Character dictionaries are keyed by AniList media ID (no season/franchise merge).',
|
||||
],
|
||||
key: 'anilist',
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user