fix(shortcuts): gate feature-dependent bindings

Disable Anki-dependent shortcuts when AnkiConnect is off and require jellyfin.enabled for remote startup warmups to avoid initializing disabled integrations.
This commit is contained in:
2026-02-21 17:50:09 -08:00
parent c749430c77
commit 4ad8109508
9 changed files with 251 additions and 40 deletions

View File

@@ -7,6 +7,7 @@ import {
function createConfig(overrides?: Partial<Record<string, unknown>>) {
return {
enabled: true,
remoteControlEnabled: true,
remoteControlAutoConnect: true,
serverUrl: 'http://localhost',
@@ -21,6 +22,34 @@ function createConfig(overrides?: Partial<Record<string, unknown>>) {
} as never;
}
test('start handler no-ops when jellyfin integration is disabled', async () => {
let created = false;
const startRemote = createStartJellyfinRemoteSessionHandler({
getJellyfinConfig: () => createConfig({ enabled: false }),
getCurrentSession: () => null,
setCurrentSession: () => {},
createRemoteSessionService: () => {
created = true;
return {
start: () => {},
stop: () => {},
advertiseNow: async () => true,
};
},
defaultDeviceId: 'default-device',
defaultClientName: 'SubMiner',
defaultClientVersion: '1.0',
handlePlay: async () => {},
handlePlaystate: async () => {},
handleGeneralCommand: async () => {},
logInfo: () => {},
logWarn: () => {},
});
await startRemote();
assert.equal(created, false);
});
test('start handler no-ops when remote control is disabled', async () => {
let created = false;
const startRemote = createStartJellyfinRemoteSessionHandler({
@@ -50,8 +79,11 @@ test('start handler no-ops when remote control is disabled', async () => {
});
test('start handler creates, starts, and stores session', async () => {
let storedSession: { start: () => void; stop: () => void; advertiseNow: () => Promise<boolean> } | null =
null;
let storedSession: {
start: () => void;
stop: () => void;
advertiseNow: () => Promise<boolean>;
} | null = null;
let started = false;
const infos: string[] = [];
const startRemote = createStartJellyfinRemoteSessionHandler({

View File

@@ -1,4 +1,5 @@
type JellyfinRemoteConfig = {
enabled: boolean;
remoteControlEnabled: boolean;
remoteControlAutoConnect: boolean;
serverUrl: string;
@@ -54,6 +55,7 @@ export function createStartJellyfinRemoteSessionHandler(deps: {
}) {
return async (): Promise<void> => {
const jellyfinConfig = deps.getJellyfinConfig();
if (jellyfinConfig.enabled === false) return;
if (jellyfinConfig.remoteControlEnabled === false) return;
if (jellyfinConfig.remoteControlAutoConnect === false) return;
if (!jellyfinConfig.serverUrl || !jellyfinConfig.accessToken || !jellyfinConfig.userId) return;
@@ -87,7 +89,9 @@ export function createStartJellyfinRemoteSessionHandler(deps: {
if (registered) {
deps.logInfo('Jellyfin cast target is visible to server sessions.');
} else {
deps.logWarn('Jellyfin remote connected but device not visible in server sessions yet.');
deps.logWarn(
'Jellyfin remote connected but device not visible in server sessions yet.',
);
}
});
}

View File

@@ -5,6 +5,14 @@ import {
createStartBackgroundWarmupsHandler,
} from './startup-warmups';
function shouldAutoConnectJellyfinRemote(config: {
enabled: boolean;
remoteControlEnabled: boolean;
remoteControlAutoConnect: boolean;
}): boolean {
return config.enabled && config.remoteControlEnabled && config.remoteControlAutoConnect;
}
test('launchBackgroundWarmupTask logs completion timing', async () => {
const debugLogs: string[] = [];
const launchTask = createLaunchBackgroundWarmupTaskHandler({
@@ -41,7 +49,7 @@ test('startBackgroundWarmups no-ops when already started', () => {
assert.equal(launches, 0);
});
test('startBackgroundWarmups schedules base warmups and optional jellyfin warmup', () => {
test('startBackgroundWarmups does not schedule jellyfin warmup when jellyfin.enabled is false', () => {
const labels: string[] = [];
let started = false;
const startWarmups = createStartBackgroundWarmupsHandler({
@@ -56,7 +64,41 @@ test('startBackgroundWarmups schedules base warmups and optional jellyfin warmup
createMecabTokenizerAndCheck: async () => {},
ensureYomitanExtensionLoaded: async () => {},
prewarmSubtitleDictionaries: async () => {},
shouldAutoConnectJellyfinRemote: () => true,
shouldAutoConnectJellyfinRemote: () =>
shouldAutoConnectJellyfinRemote({
enabled: false,
remoteControlEnabled: true,
remoteControlAutoConnect: true,
}),
startJellyfinRemoteSession: async () => {},
});
startWarmups();
assert.equal(started, true);
assert.deepEqual(labels, ['mecab', 'yomitan-extension', 'subtitle-dictionaries']);
});
test('startBackgroundWarmups schedules jellyfin warmup when all jellyfin flags are enabled', () => {
const labels: string[] = [];
let started = false;
const startWarmups = createStartBackgroundWarmupsHandler({
getStarted: () => started,
setStarted: (value) => {
started = value;
},
isTexthookerOnlyMode: () => false,
launchTask: (label) => {
labels.push(label);
},
createMecabTokenizerAndCheck: async () => {},
ensureYomitanExtensionLoaded: async () => {},
prewarmSubtitleDictionaries: async () => {},
shouldAutoConnectJellyfinRemote: () =>
shouldAutoConnectJellyfinRemote({
enabled: true,
remoteControlEnabled: true,
remoteControlAutoConnect: true,
}),
startJellyfinRemoteSession: async () => {},
});