refactor(tsukihime): swap Animetosho backend for TsukiHime API (#165)

This commit is contained in:
2026-07-13 22:03:35 -07:00
committed by GitHub
parent 49b926e08c
commit 2e2ee3f028
90 changed files with 2253 additions and 1695 deletions
+15 -1
View File
@@ -236,7 +236,7 @@ export function applyCoreDomainConfig(context: ResolveContext): void {
'openCharacterDictionaryManager',
'openRuntimeOptions',
'openJimaku',
'openAnimetosho',
'openTsukihime',
'openSessionHelp',
'openControllerSelect',
'openControllerDebug',
@@ -254,6 +254,20 @@ export function applyCoreDomainConfig(context: ResolveContext): void {
}
}
if (src.shortcuts.openTsukihime === undefined) {
const legacyOpenTsukihime = src.shortcuts.openAnimetosho;
if (typeof legacyOpenTsukihime === 'string' || legacyOpenTsukihime === null) {
resolved.shortcuts.openTsukihime = legacyOpenTsukihime;
} else if (legacyOpenTsukihime !== undefined) {
warn(
'shortcuts.openAnimetosho',
legacyOpenTsukihime,
resolved.shortcuts.openTsukihime,
'Expected string or null.',
);
}
}
const timeout = asNumber(src.shortcuts.multiCopyTimeoutMs);
if (timeout !== undefined && timeout > 0) {
resolved.shortcuts.multiCopyTimeoutMs = Math.floor(timeout);
+18 -9
View File
@@ -80,18 +80,27 @@ export function applySubtitleDomainConfig(context: ResolveContext): void {
}
}
if (isObject(src.animetosho)) {
const apiBaseUrl = asString(src.animetosho.apiBaseUrl);
if (apiBaseUrl !== undefined) resolved.animetosho.apiBaseUrl = apiBaseUrl;
const currentTsukihimeSource = isObject(src.tsukihime) ? src.tsukihime : null;
if (src.tsukihime !== undefined && !currentTsukihimeSource) {
warn('tsukihime', src.tsukihime, resolved.tsukihime, 'Expected object.');
}
const maxSearchResults = asNumber(src.animetosho.maxSearchResults);
const legacyTsukihimeSource =
src.tsukihime === undefined && isObject(src.animetosho) ? src.animetosho : null;
const tsukihimeSource = currentTsukihimeSource ?? legacyTsukihimeSource;
const tsukihimeSourcePath = currentTsukihimeSource ? 'tsukihime' : 'animetosho';
if (tsukihimeSource) {
const apiBaseUrl = asString(tsukihimeSource.apiBaseUrl);
if (apiBaseUrl !== undefined) resolved.tsukihime.apiBaseUrl = apiBaseUrl;
const maxSearchResults = asNumber(tsukihimeSource.maxSearchResults);
if (maxSearchResults !== undefined && Math.floor(maxSearchResults) > 0) {
resolved.animetosho.maxSearchResults = Math.floor(maxSearchResults);
} else if (src.animetosho.maxSearchResults !== undefined) {
resolved.tsukihime.maxSearchResults = Math.floor(maxSearchResults);
} else if (tsukihimeSource.maxSearchResults !== undefined) {
warn(
'animetosho.maxSearchResults',
src.animetosho.maxSearchResults,
resolved.animetosho.maxSearchResults,
`${tsukihimeSourcePath}.maxSearchResults`,
tsukihimeSource.maxSearchResults,
resolved.tsukihime.maxSearchResults,
'Expected positive number.',
);
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { asBoolean } from './shared';
export function applyTopLevelConfig(context: ResolveContext): void {
const { src, resolved, warn } = context;
const knownTopLevelKeys = new Set(Object.keys(resolved));
const knownTopLevelKeys = new Set([...Object.keys(resolved), 'animetosho']);
for (const key of Object.keys(src)) {
if (!knownTopLevelKeys.has(key)) {
warn(key, src[key], undefined, 'Unknown top-level config key; ignored.');
@@ -0,0 +1,63 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { resolveConfig } from '../resolve';
test('resolveConfig maps legacy Animetosho settings to TsukiHime', () => {
const { resolved, warnings } = resolveConfig({
animetosho: {
apiBaseUrl: 'https://legacy.example/v1',
maxSearchResults: 23,
},
shortcuts: {
openAnimetosho: 'Ctrl+Alt+T',
},
});
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://legacy.example/v1');
assert.equal(resolved.tsukihime.maxSearchResults, 23);
assert.equal(resolved.shortcuts.openTsukihime, 'Ctrl+Alt+T');
assert.deepEqual(warnings, []);
});
test('resolveConfig gives current TsukiHime settings precedence over legacy aliases', () => {
const { resolved, warnings } = resolveConfig({
animetosho: {
apiBaseUrl: 'https://legacy.example/v1',
maxSearchResults: 23,
},
tsukihime: {
apiBaseUrl: 'https://current.example/v1',
maxSearchResults: 7,
},
shortcuts: {
openAnimetosho: 'Ctrl+Alt+T',
openTsukihime: null,
},
});
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://current.example/v1');
assert.equal(resolved.tsukihime.maxSearchResults, 7);
assert.equal(resolved.shortcuts.openTsukihime, null);
assert.deepEqual(warnings, []);
});
test('resolveConfig does not fall back when the current TsukiHime setting is invalid', () => {
const { resolved, warnings } = resolveConfig({
animetosho: {
apiBaseUrl: 'https://legacy.example/v1',
maxSearchResults: 23,
},
tsukihime: 'invalid' as never,
});
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://api.tsukihime.org/v1');
assert.equal(resolved.tsukihime.maxSearchResults, 10);
assert.deepEqual(warnings, [
{
path: 'tsukihime',
value: 'invalid',
fallback: resolved.tsukihime,
message: 'Expected object.',
},
]);
});