mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
332 lines
12 KiB
TypeScript
332 lines
12 KiB
TypeScript
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
import { ResolveContext } from './context';
|
|
import { asBoolean, asNumber, asString, isObject } from './shared';
|
|
|
|
function normalizeExternalProfilePath(value: string): string {
|
|
const trimmed = value.trim();
|
|
if (trimmed === '~') {
|
|
return os.homedir();
|
|
}
|
|
if (trimmed.startsWith('~/') || trimmed.startsWith('~\\')) {
|
|
return path.join(os.homedir(), trimmed.slice(2));
|
|
}
|
|
return trimmed;
|
|
}
|
|
|
|
export function applyIntegrationConfig(context: ResolveContext): void {
|
|
const { src, resolved, warn } = context;
|
|
|
|
if (isObject(src.ai)) {
|
|
const booleanKeys = ['enabled'] as const;
|
|
for (const key of booleanKeys) {
|
|
const value = asBoolean(src.ai[key]);
|
|
if (value !== undefined) {
|
|
resolved.ai[key] = value;
|
|
} else if (src.ai[key] !== undefined) {
|
|
warn(`ai.${key}`, src.ai[key], resolved.ai[key], 'Expected boolean.');
|
|
}
|
|
}
|
|
|
|
const stringKeys = ['apiKey', 'apiKeyCommand', 'baseUrl', 'model', 'systemPrompt'] as const;
|
|
for (const key of stringKeys) {
|
|
const value = asString(src.ai[key]);
|
|
if (value !== undefined) {
|
|
resolved.ai[key] = value;
|
|
} else if (src.ai[key] !== undefined) {
|
|
warn(`ai.${key}`, src.ai[key], resolved.ai[key], 'Expected string.');
|
|
}
|
|
}
|
|
|
|
const requestTimeoutMs = asNumber(src.ai.requestTimeoutMs);
|
|
if (
|
|
requestTimeoutMs !== undefined &&
|
|
Number.isInteger(requestTimeoutMs) &&
|
|
requestTimeoutMs > 0
|
|
) {
|
|
resolved.ai.requestTimeoutMs = requestTimeoutMs;
|
|
} else if (src.ai.requestTimeoutMs !== undefined) {
|
|
warn(
|
|
'ai.requestTimeoutMs',
|
|
src.ai.requestTimeoutMs,
|
|
resolved.ai.requestTimeoutMs,
|
|
'Expected positive integer.',
|
|
);
|
|
}
|
|
} else if (src.ai !== undefined) {
|
|
warn('ai', src.ai, resolved.ai, 'Expected object.');
|
|
}
|
|
|
|
if (isObject(src.anilist)) {
|
|
const enabled = asBoolean(src.anilist.enabled);
|
|
if (enabled !== undefined) {
|
|
resolved.anilist.enabled = enabled;
|
|
} else if (src.anilist.enabled !== undefined) {
|
|
warn('anilist.enabled', src.anilist.enabled, resolved.anilist.enabled, 'Expected boolean.');
|
|
}
|
|
|
|
const accessToken = asString(src.anilist.accessToken);
|
|
if (accessToken !== undefined) {
|
|
resolved.anilist.accessToken = accessToken;
|
|
} else if (src.anilist.accessToken !== undefined) {
|
|
warn(
|
|
'anilist.accessToken',
|
|
src.anilist.accessToken,
|
|
resolved.anilist.accessToken,
|
|
'Expected string.',
|
|
);
|
|
}
|
|
|
|
if (isObject(src.anilist.characterDictionary)) {
|
|
const characterDictionary = src.anilist.characterDictionary;
|
|
|
|
const dictionaryEnabled = asBoolean(characterDictionary.enabled);
|
|
if (dictionaryEnabled !== undefined) {
|
|
resolved.anilist.characterDictionary.enabled = dictionaryEnabled;
|
|
} else if (characterDictionary.enabled !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.enabled',
|
|
characterDictionary.enabled,
|
|
resolved.anilist.characterDictionary.enabled,
|
|
'Expected boolean.',
|
|
);
|
|
}
|
|
|
|
const refreshTtlHours = asNumber(characterDictionary.refreshTtlHours);
|
|
if (refreshTtlHours !== undefined) {
|
|
const normalized = Math.min(24 * 365, Math.max(1, Math.floor(refreshTtlHours)));
|
|
if (normalized !== refreshTtlHours) {
|
|
warn(
|
|
'anilist.characterDictionary.refreshTtlHours',
|
|
characterDictionary.refreshTtlHours,
|
|
normalized,
|
|
'Out of range; clamped to 1..8760 hours.',
|
|
);
|
|
}
|
|
resolved.anilist.characterDictionary.refreshTtlHours = normalized;
|
|
} else if (characterDictionary.refreshTtlHours !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.refreshTtlHours',
|
|
characterDictionary.refreshTtlHours,
|
|
resolved.anilist.characterDictionary.refreshTtlHours,
|
|
'Expected number.',
|
|
);
|
|
}
|
|
|
|
const maxLoaded = asNumber(characterDictionary.maxLoaded);
|
|
if (maxLoaded !== undefined) {
|
|
const normalized = Math.min(20, Math.max(1, Math.floor(maxLoaded)));
|
|
if (normalized !== maxLoaded) {
|
|
warn(
|
|
'anilist.characterDictionary.maxLoaded',
|
|
characterDictionary.maxLoaded,
|
|
normalized,
|
|
'Out of range; clamped to 1..20.',
|
|
);
|
|
}
|
|
resolved.anilist.characterDictionary.maxLoaded = normalized;
|
|
} else if (characterDictionary.maxLoaded !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.maxLoaded',
|
|
characterDictionary.maxLoaded,
|
|
resolved.anilist.characterDictionary.maxLoaded,
|
|
'Expected number.',
|
|
);
|
|
}
|
|
|
|
const evictionPolicyRaw = asString(characterDictionary.evictionPolicy);
|
|
if (evictionPolicyRaw !== undefined) {
|
|
const evictionPolicy = evictionPolicyRaw.trim().toLowerCase();
|
|
if (evictionPolicy === 'disable' || evictionPolicy === 'delete') {
|
|
resolved.anilist.characterDictionary.evictionPolicy = evictionPolicy;
|
|
} else {
|
|
warn(
|
|
'anilist.characterDictionary.evictionPolicy',
|
|
characterDictionary.evictionPolicy,
|
|
resolved.anilist.characterDictionary.evictionPolicy,
|
|
"Expected one of: 'disable', 'delete'.",
|
|
);
|
|
}
|
|
} else if (characterDictionary.evictionPolicy !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.evictionPolicy',
|
|
characterDictionary.evictionPolicy,
|
|
resolved.anilist.characterDictionary.evictionPolicy,
|
|
'Expected string.',
|
|
);
|
|
}
|
|
|
|
const profileScopeRaw = asString(characterDictionary.profileScope);
|
|
if (profileScopeRaw !== undefined) {
|
|
const profileScope = profileScopeRaw.trim().toLowerCase();
|
|
if (profileScope === 'all' || profileScope === 'active') {
|
|
resolved.anilist.characterDictionary.profileScope = profileScope;
|
|
} else {
|
|
warn(
|
|
'anilist.characterDictionary.profileScope',
|
|
characterDictionary.profileScope,
|
|
resolved.anilist.characterDictionary.profileScope,
|
|
"Expected one of: 'all', 'active'.",
|
|
);
|
|
}
|
|
} else if (characterDictionary.profileScope !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.profileScope',
|
|
characterDictionary.profileScope,
|
|
resolved.anilist.characterDictionary.profileScope,
|
|
'Expected string.',
|
|
);
|
|
}
|
|
|
|
if (isObject(characterDictionary.collapsibleSections)) {
|
|
const collapsibleSections = characterDictionary.collapsibleSections;
|
|
const keys = ['description', 'characterInformation', 'voicedBy'] as const;
|
|
for (const key of keys) {
|
|
const value = asBoolean(collapsibleSections[key]);
|
|
if (value !== undefined) {
|
|
resolved.anilist.characterDictionary.collapsibleSections[key] = value;
|
|
} else if (collapsibleSections[key] !== undefined) {
|
|
warn(
|
|
`anilist.characterDictionary.collapsibleSections.${key}`,
|
|
collapsibleSections[key],
|
|
resolved.anilist.characterDictionary.collapsibleSections[key],
|
|
'Expected boolean.',
|
|
);
|
|
}
|
|
}
|
|
} else if (characterDictionary.collapsibleSections !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary.collapsibleSections',
|
|
characterDictionary.collapsibleSections,
|
|
resolved.anilist.characterDictionary.collapsibleSections,
|
|
'Expected object.',
|
|
);
|
|
}
|
|
} else if (src.anilist.characterDictionary !== undefined) {
|
|
warn(
|
|
'anilist.characterDictionary',
|
|
src.anilist.characterDictionary,
|
|
resolved.anilist.characterDictionary,
|
|
'Expected object.',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (isObject(src.yomitan)) {
|
|
const externalProfilePath = asString(src.yomitan.externalProfilePath);
|
|
if (externalProfilePath !== undefined) {
|
|
resolved.yomitan.externalProfilePath = normalizeExternalProfilePath(externalProfilePath);
|
|
} else if (src.yomitan.externalProfilePath !== undefined) {
|
|
warn(
|
|
'yomitan.externalProfilePath',
|
|
src.yomitan.externalProfilePath,
|
|
resolved.yomitan.externalProfilePath,
|
|
'Expected string.',
|
|
);
|
|
}
|
|
} else if (src.yomitan !== undefined) {
|
|
warn('yomitan', src.yomitan, resolved.yomitan, 'Expected object.');
|
|
}
|
|
|
|
if (isObject(src.jellyfin)) {
|
|
const enabled = asBoolean(src.jellyfin.enabled);
|
|
if (enabled !== undefined) {
|
|
resolved.jellyfin.enabled = enabled;
|
|
} else if (src.jellyfin.enabled !== undefined) {
|
|
warn(
|
|
'jellyfin.enabled',
|
|
src.jellyfin.enabled,
|
|
resolved.jellyfin.enabled,
|
|
'Expected boolean.',
|
|
);
|
|
}
|
|
|
|
const stringKeys = [
|
|
'serverUrl',
|
|
'username',
|
|
'deviceId',
|
|
'clientName',
|
|
'clientVersion',
|
|
'defaultLibraryId',
|
|
'iconCacheDir',
|
|
'transcodeVideoCodec',
|
|
] as const;
|
|
for (const key of stringKeys) {
|
|
const value = asString(src.jellyfin[key]);
|
|
if (value !== undefined) {
|
|
resolved.jellyfin[key] = value as (typeof resolved.jellyfin)[typeof key];
|
|
} else if (src.jellyfin[key] !== undefined) {
|
|
warn(`jellyfin.${key}`, src.jellyfin[key], resolved.jellyfin[key], 'Expected string.');
|
|
}
|
|
}
|
|
|
|
const booleanKeys = [
|
|
'remoteControlEnabled',
|
|
'remoteControlAutoConnect',
|
|
'autoAnnounce',
|
|
'directPlayPreferred',
|
|
'pullPictures',
|
|
] as const;
|
|
for (const key of booleanKeys) {
|
|
const value = asBoolean(src.jellyfin[key]);
|
|
if (value !== undefined) {
|
|
resolved.jellyfin[key] = value as (typeof resolved.jellyfin)[typeof key];
|
|
} else if (src.jellyfin[key] !== undefined) {
|
|
warn(`jellyfin.${key}`, src.jellyfin[key], resolved.jellyfin[key], 'Expected boolean.');
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(src.jellyfin.directPlayContainers)) {
|
|
resolved.jellyfin.directPlayContainers = src.jellyfin.directPlayContainers
|
|
.filter((item): item is string => typeof item === 'string')
|
|
.map((item) => item.trim().toLowerCase())
|
|
.filter((item) => item.length > 0);
|
|
} else if (src.jellyfin.directPlayContainers !== undefined) {
|
|
warn(
|
|
'jellyfin.directPlayContainers',
|
|
src.jellyfin.directPlayContainers,
|
|
resolved.jellyfin.directPlayContainers,
|
|
'Expected string array.',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (isObject(src.discordPresence)) {
|
|
const enabled = asBoolean(src.discordPresence.enabled);
|
|
if (enabled !== undefined) {
|
|
resolved.discordPresence.enabled = enabled;
|
|
} else if (src.discordPresence.enabled !== undefined) {
|
|
warn(
|
|
'discordPresence.enabled',
|
|
src.discordPresence.enabled,
|
|
resolved.discordPresence.enabled,
|
|
'Expected boolean.',
|
|
);
|
|
}
|
|
|
|
const updateIntervalMs = asNumber(src.discordPresence.updateIntervalMs);
|
|
if (updateIntervalMs !== undefined) {
|
|
resolved.discordPresence.updateIntervalMs = Math.max(1_000, Math.floor(updateIntervalMs));
|
|
} else if (src.discordPresence.updateIntervalMs !== undefined) {
|
|
warn(
|
|
'discordPresence.updateIntervalMs',
|
|
src.discordPresence.updateIntervalMs,
|
|
resolved.discordPresence.updateIntervalMs,
|
|
'Expected number.',
|
|
);
|
|
}
|
|
|
|
const debounceMs = asNumber(src.discordPresence.debounceMs);
|
|
if (debounceMs !== undefined) {
|
|
resolved.discordPresence.debounceMs = Math.max(0, Math.floor(debounceMs));
|
|
} else if (src.discordPresence.debounceMs !== undefined) {
|
|
warn(
|
|
'discordPresence.debounceMs',
|
|
src.discordPresence.debounceMs,
|
|
resolved.discordPresence.debounceMs,
|
|
'Expected number.',
|
|
);
|
|
}
|
|
}
|
|
}
|