mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 06:22:44 -08:00
refactor: extract additional main runtime dependency builders
This commit is contained in:
87
src/main/runtime/anilist-setup-protocol-main-deps.test.ts
Normal file
87
src/main/runtime/anilist-setup-protocol-main-deps.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler,
|
||||
createBuildHandleAnilistSetupProtocolUrlMainDepsHandler,
|
||||
createBuildNotifyAnilistSetupMainDepsHandler,
|
||||
createBuildRegisterSubminerProtocolClientMainDepsHandler,
|
||||
} from './anilist-setup-protocol-main-deps';
|
||||
|
||||
test('notify anilist setup main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildNotifyAnilistSetupMainDepsHandler({
|
||||
hasMpvClient: () => true,
|
||||
showMpvOsd: (message) => calls.push(`osd:${message}`),
|
||||
showDesktopNotification: (title) => calls.push(`notify:${title}`),
|
||||
logInfo: (message) => calls.push(`log:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.hasMpvClient(), true);
|
||||
deps.showMpvOsd('ok');
|
||||
deps.showDesktopNotification('SubMiner', { body: 'x' });
|
||||
deps.logInfo('done');
|
||||
assert.deepEqual(calls, ['osd:ok', 'notify:SubMiner', 'log:done']);
|
||||
});
|
||||
|
||||
test('consume anilist setup token main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler({
|
||||
consumeAnilistSetupCallbackUrl: () => true,
|
||||
saveToken: () => calls.push('save'),
|
||||
setCachedToken: () => calls.push('cache'),
|
||||
setResolvedState: () => calls.push('resolved'),
|
||||
setSetupPageOpened: () => calls.push('opened'),
|
||||
onSuccess: () => calls.push('success'),
|
||||
closeWindow: () => calls.push('close'),
|
||||
})();
|
||||
|
||||
assert.equal(
|
||||
deps.consumeAnilistSetupCallbackUrl({
|
||||
rawUrl: 'subminer://anilist-setup',
|
||||
saveToken: () => {},
|
||||
setCachedToken: () => {},
|
||||
setResolvedState: () => {},
|
||||
setSetupPageOpened: () => {},
|
||||
onSuccess: () => {},
|
||||
closeWindow: () => {},
|
||||
}),
|
||||
true,
|
||||
);
|
||||
deps.saveToken('token');
|
||||
deps.setCachedToken('token');
|
||||
deps.setResolvedState(Date.now());
|
||||
deps.setSetupPageOpened(true);
|
||||
deps.onSuccess();
|
||||
deps.closeWindow();
|
||||
assert.deepEqual(calls, ['save', 'cache', 'resolved', 'opened', 'success', 'close']);
|
||||
});
|
||||
|
||||
test('handle anilist setup protocol url main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildHandleAnilistSetupProtocolUrlMainDepsHandler({
|
||||
consumeAnilistSetupTokenFromUrl: () => true,
|
||||
logWarn: (message) => calls.push(`warn:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.consumeAnilistSetupTokenFromUrl('subminer://anilist-setup'), true);
|
||||
deps.logWarn('missing', null);
|
||||
assert.deepEqual(calls, ['warn:missing']);
|
||||
});
|
||||
|
||||
test('register subminer protocol client main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildRegisterSubminerProtocolClientMainDepsHandler({
|
||||
isDefaultApp: () => true,
|
||||
getArgv: () => ['electron', 'entry.js'],
|
||||
execPath: '/tmp/electron',
|
||||
resolvePath: (value) => `/abs/${value}`,
|
||||
setAsDefaultProtocolClient: () => true,
|
||||
logWarn: (message) => calls.push(`warn:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.isDefaultApp(), true);
|
||||
assert.deepEqual(deps.getArgv(), ['electron', 'entry.js']);
|
||||
assert.equal(deps.execPath, '/tmp/electron');
|
||||
assert.equal(deps.resolvePath('entry.js'), '/abs/entry.js');
|
||||
assert.equal(deps.setAsDefaultProtocolClient('subminer'), true);
|
||||
});
|
||||
64
src/main/runtime/anilist-setup-protocol-main-deps.ts
Normal file
64
src/main/runtime/anilist-setup-protocol-main-deps.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
createConsumeAnilistSetupTokenFromUrlHandler,
|
||||
createHandleAnilistSetupProtocolUrlHandler,
|
||||
createNotifyAnilistSetupHandler,
|
||||
createRegisterSubminerProtocolClientHandler,
|
||||
} from './anilist-setup-protocol';
|
||||
|
||||
type NotifyAnilistSetupMainDeps = Parameters<typeof createNotifyAnilistSetupHandler>[0];
|
||||
type ConsumeAnilistSetupTokenMainDeps = Parameters<
|
||||
typeof createConsumeAnilistSetupTokenFromUrlHandler
|
||||
>[0];
|
||||
type HandleAnilistSetupProtocolUrlMainDeps = Parameters<
|
||||
typeof createHandleAnilistSetupProtocolUrlHandler
|
||||
>[0];
|
||||
type RegisterSubminerProtocolClientMainDeps = Parameters<
|
||||
typeof createRegisterSubminerProtocolClientHandler
|
||||
>[0];
|
||||
|
||||
export function createBuildNotifyAnilistSetupMainDepsHandler(deps: NotifyAnilistSetupMainDeps) {
|
||||
return (): NotifyAnilistSetupMainDeps => ({
|
||||
hasMpvClient: () => deps.hasMpvClient(),
|
||||
showMpvOsd: (message: string) => deps.showMpvOsd(message),
|
||||
showDesktopNotification: (title: string, options: { body: string }) =>
|
||||
deps.showDesktopNotification(title, options),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler(
|
||||
deps: ConsumeAnilistSetupTokenMainDeps,
|
||||
) {
|
||||
return (): ConsumeAnilistSetupTokenMainDeps => ({
|
||||
consumeAnilistSetupCallbackUrl: (input) => deps.consumeAnilistSetupCallbackUrl(input),
|
||||
saveToken: (token: string) => deps.saveToken(token),
|
||||
setCachedToken: (token: string) => deps.setCachedToken(token),
|
||||
setResolvedState: (resolvedAt: number) => deps.setResolvedState(resolvedAt),
|
||||
setSetupPageOpened: (opened: boolean) => deps.setSetupPageOpened(opened),
|
||||
onSuccess: () => deps.onSuccess(),
|
||||
closeWindow: () => deps.closeWindow(),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildHandleAnilistSetupProtocolUrlMainDepsHandler(
|
||||
deps: HandleAnilistSetupProtocolUrlMainDeps,
|
||||
) {
|
||||
return (): HandleAnilistSetupProtocolUrlMainDeps => ({
|
||||
consumeAnilistSetupTokenFromUrl: (rawUrl: string) => deps.consumeAnilistSetupTokenFromUrl(rawUrl),
|
||||
logWarn: (message: string, details: unknown) => deps.logWarn(message, details),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildRegisterSubminerProtocolClientMainDepsHandler(
|
||||
deps: RegisterSubminerProtocolClientMainDeps,
|
||||
) {
|
||||
return (): RegisterSubminerProtocolClientMainDeps => ({
|
||||
isDefaultApp: () => deps.isDefaultApp(),
|
||||
getArgv: () => deps.getArgv(),
|
||||
execPath: deps.execPath,
|
||||
resolvePath: (value: string) => deps.resolvePath(value),
|
||||
setAsDefaultProtocolClient: (scheme: string, path?: string, args?: string[]) =>
|
||||
deps.setAsDefaultProtocolClient(scheme, path, args),
|
||||
logWarn: (message: string, details?: unknown) => deps.logWarn(message, details),
|
||||
});
|
||||
}
|
||||
84
src/main/runtime/jellyfin-cli-main-deps.test.ts
Normal file
84
src/main/runtime/jellyfin-cli-main-deps.test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildHandleJellyfinAuthCommandsMainDepsHandler,
|
||||
createBuildHandleJellyfinListCommandsMainDepsHandler,
|
||||
createBuildHandleJellyfinPlayCommandMainDepsHandler,
|
||||
createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler,
|
||||
} from './jellyfin-cli-main-deps';
|
||||
|
||||
test('jellyfin auth commands main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildHandleJellyfinAuthCommandsMainDepsHandler({
|
||||
patchRawConfig: () => calls.push('patch'),
|
||||
authenticateWithPassword: async () => ({}) as never,
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
})();
|
||||
|
||||
deps.patchRawConfig({ jellyfin: {} });
|
||||
await deps.authenticateWithPassword('', '', '', {
|
||||
deviceId: '',
|
||||
clientName: '',
|
||||
clientVersion: '',
|
||||
});
|
||||
deps.logInfo('ok');
|
||||
assert.deepEqual(calls, ['patch', 'info:ok']);
|
||||
});
|
||||
|
||||
test('jellyfin list commands main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildHandleJellyfinListCommandsMainDepsHandler({
|
||||
listJellyfinLibraries: async () => {
|
||||
calls.push('libraries');
|
||||
return [];
|
||||
},
|
||||
listJellyfinItems: async () => {
|
||||
calls.push('items');
|
||||
return [];
|
||||
},
|
||||
listJellyfinSubtitleTracks: async () => {
|
||||
calls.push('subtitles');
|
||||
return [];
|
||||
},
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
})();
|
||||
|
||||
await deps.listJellyfinLibraries({} as never, {} as never);
|
||||
await deps.listJellyfinItems({} as never, {} as never, { libraryId: '', limit: 1 });
|
||||
await deps.listJellyfinSubtitleTracks({} as never, {} as never, 'id');
|
||||
deps.logInfo('done');
|
||||
assert.deepEqual(calls, ['libraries', 'items', 'subtitles', 'info:done']);
|
||||
});
|
||||
|
||||
test('jellyfin play command main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildHandleJellyfinPlayCommandMainDepsHandler({
|
||||
playJellyfinItemInMpv: async () => {
|
||||
calls.push('play');
|
||||
},
|
||||
logWarn: (message) => calls.push(`warn:${message}`),
|
||||
})();
|
||||
|
||||
await deps.playJellyfinItemInMpv({} as never);
|
||||
deps.logWarn('missing');
|
||||
assert.deepEqual(calls, ['play', 'warn:missing']);
|
||||
});
|
||||
|
||||
test('jellyfin remote announce main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const session = { advertiseNow: async () => true };
|
||||
const deps = createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler({
|
||||
startJellyfinRemoteSession: async () => {
|
||||
calls.push('start');
|
||||
},
|
||||
getRemoteSession: () => session,
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
logWarn: (message) => calls.push(`warn:${message}`),
|
||||
})();
|
||||
|
||||
await deps.startJellyfinRemoteSession();
|
||||
assert.equal(deps.getRemoteSession(), session);
|
||||
deps.logInfo('visible');
|
||||
deps.logWarn('not-visible');
|
||||
assert.deepEqual(calls, ['start', 'info:visible', 'warn:not-visible']);
|
||||
});
|
||||
63
src/main/runtime/jellyfin-cli-main-deps.ts
Normal file
63
src/main/runtime/jellyfin-cli-main-deps.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type {
|
||||
createHandleJellyfinAuthCommands,
|
||||
} from './jellyfin-cli-auth';
|
||||
import type {
|
||||
createHandleJellyfinListCommands,
|
||||
} from './jellyfin-cli-list';
|
||||
import type {
|
||||
createHandleJellyfinPlayCommand,
|
||||
} from './jellyfin-cli-play';
|
||||
import type {
|
||||
createHandleJellyfinRemoteAnnounceCommand,
|
||||
} from './jellyfin-cli-remote-announce';
|
||||
|
||||
type HandleJellyfinAuthCommandsMainDeps = Parameters<typeof createHandleJellyfinAuthCommands>[0];
|
||||
type HandleJellyfinListCommandsMainDeps = Parameters<typeof createHandleJellyfinListCommands>[0];
|
||||
type HandleJellyfinPlayCommandMainDeps = Parameters<typeof createHandleJellyfinPlayCommand>[0];
|
||||
type HandleJellyfinRemoteAnnounceCommandMainDeps = Parameters<
|
||||
typeof createHandleJellyfinRemoteAnnounceCommand
|
||||
>[0];
|
||||
|
||||
export function createBuildHandleJellyfinAuthCommandsMainDepsHandler(
|
||||
deps: HandleJellyfinAuthCommandsMainDeps,
|
||||
) {
|
||||
return (): HandleJellyfinAuthCommandsMainDeps => ({
|
||||
patchRawConfig: (patch) => deps.patchRawConfig(patch),
|
||||
authenticateWithPassword: (serverUrl, username, password, clientInfo) =>
|
||||
deps.authenticateWithPassword(serverUrl, username, password, clientInfo),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildHandleJellyfinListCommandsMainDepsHandler(
|
||||
deps: HandleJellyfinListCommandsMainDeps,
|
||||
) {
|
||||
return (): HandleJellyfinListCommandsMainDeps => ({
|
||||
listJellyfinLibraries: (session, clientInfo) => deps.listJellyfinLibraries(session, clientInfo),
|
||||
listJellyfinItems: (session, clientInfo, params) =>
|
||||
deps.listJellyfinItems(session, clientInfo, params),
|
||||
listJellyfinSubtitleTracks: (session, clientInfo, itemId) =>
|
||||
deps.listJellyfinSubtitleTracks(session, clientInfo, itemId),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildHandleJellyfinPlayCommandMainDepsHandler(
|
||||
deps: HandleJellyfinPlayCommandMainDeps,
|
||||
) {
|
||||
return (): HandleJellyfinPlayCommandMainDeps => ({
|
||||
playJellyfinItemInMpv: (params) => deps.playJellyfinItemInMpv(params),
|
||||
logWarn: (message: string) => deps.logWarn(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler(
|
||||
deps: HandleJellyfinRemoteAnnounceCommandMainDeps,
|
||||
) {
|
||||
return (): HandleJellyfinRemoteAnnounceCommandMainDeps => ({
|
||||
startJellyfinRemoteSession: () => deps.startJellyfinRemoteSession(),
|
||||
getRemoteSession: () => deps.getRemoteSession(),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
logWarn: (message: string) => deps.logWarn(message),
|
||||
});
|
||||
}
|
||||
26
src/main/runtime/jellyfin-client-info-main-deps.test.ts
Normal file
26
src/main/runtime/jellyfin-client-info-main-deps.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildGetJellyfinClientInfoMainDepsHandler,
|
||||
createBuildGetResolvedJellyfinConfigMainDepsHandler,
|
||||
} from './jellyfin-client-info-main-deps';
|
||||
|
||||
test('get resolved jellyfin config main deps builder maps callbacks', () => {
|
||||
const resolved = { jellyfin: { url: 'https://example.com' } };
|
||||
const deps = createBuildGetResolvedJellyfinConfigMainDepsHandler({
|
||||
getResolvedConfig: () => resolved as never,
|
||||
})();
|
||||
assert.equal(deps.getResolvedConfig(), resolved);
|
||||
});
|
||||
|
||||
test('get jellyfin client info main deps builder maps callbacks', () => {
|
||||
const configured = { clientName: 'Configured' };
|
||||
const defaults = { clientName: 'Default' };
|
||||
const deps = createBuildGetJellyfinClientInfoMainDepsHandler({
|
||||
getResolvedJellyfinConfig: () => configured as never,
|
||||
getDefaultJellyfinConfig: () => defaults as never,
|
||||
})();
|
||||
|
||||
assert.equal(deps.getResolvedJellyfinConfig(), configured);
|
||||
assert.equal(deps.getDefaultJellyfinConfig(), defaults);
|
||||
});
|
||||
24
src/main/runtime/jellyfin-client-info-main-deps.ts
Normal file
24
src/main/runtime/jellyfin-client-info-main-deps.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
createGetJellyfinClientInfoHandler,
|
||||
createGetResolvedJellyfinConfigHandler,
|
||||
} from './jellyfin-client-info';
|
||||
|
||||
type GetResolvedJellyfinConfigMainDeps = Parameters<typeof createGetResolvedJellyfinConfigHandler>[0];
|
||||
type GetJellyfinClientInfoMainDeps = Parameters<typeof createGetJellyfinClientInfoHandler>[0];
|
||||
|
||||
export function createBuildGetResolvedJellyfinConfigMainDepsHandler(
|
||||
deps: GetResolvedJellyfinConfigMainDeps,
|
||||
) {
|
||||
return (): GetResolvedJellyfinConfigMainDeps => ({
|
||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildGetJellyfinClientInfoMainDepsHandler(
|
||||
deps: GetJellyfinClientInfoMainDeps,
|
||||
) {
|
||||
return (): GetJellyfinClientInfoMainDeps => ({
|
||||
getResolvedJellyfinConfig: () => deps.getResolvedJellyfinConfig(),
|
||||
getDefaultJellyfinConfig: () => deps.getDefaultJellyfinConfig(),
|
||||
});
|
||||
}
|
||||
72
src/main/runtime/jellyfin-command-dispatch-main-deps.test.ts
Normal file
72
src/main/runtime/jellyfin-command-dispatch-main-deps.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { CliArgs } from '../../cli/args';
|
||||
import { createBuildRunJellyfinCommandMainDepsHandler } from './jellyfin-command-dispatch-main-deps';
|
||||
|
||||
test('run jellyfin command main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const args = { raw: [] } as unknown as CliArgs;
|
||||
const config = {
|
||||
serverUrl: 'http://localhost:8096',
|
||||
accessToken: 'token',
|
||||
userId: 'uid',
|
||||
username: 'alice',
|
||||
};
|
||||
const clientInfo = { clientName: 'SubMiner' };
|
||||
|
||||
const deps = createBuildRunJellyfinCommandMainDepsHandler({
|
||||
getJellyfinConfig: () => config,
|
||||
defaultServerUrl: 'http://127.0.0.1:8096',
|
||||
getJellyfinClientInfo: () => clientInfo,
|
||||
handleAuthCommands: async () => {
|
||||
calls.push('auth');
|
||||
return false;
|
||||
},
|
||||
handleRemoteAnnounceCommand: async () => {
|
||||
calls.push('remote');
|
||||
return false;
|
||||
},
|
||||
handleListCommands: async () => {
|
||||
calls.push('list');
|
||||
return false;
|
||||
},
|
||||
handlePlayCommand: async () => {
|
||||
calls.push('play');
|
||||
return true;
|
||||
},
|
||||
})();
|
||||
|
||||
assert.equal(deps.getJellyfinConfig(), config);
|
||||
assert.equal(deps.defaultServerUrl, 'http://127.0.0.1:8096');
|
||||
assert.equal(deps.getJellyfinClientInfo(config), clientInfo);
|
||||
await deps.handleAuthCommands({
|
||||
args,
|
||||
jellyfinConfig: config,
|
||||
serverUrl: config.serverUrl,
|
||||
clientInfo,
|
||||
});
|
||||
await deps.handleRemoteAnnounceCommand(args);
|
||||
await deps.handleListCommands({
|
||||
args,
|
||||
session: {
|
||||
serverUrl: config.serverUrl,
|
||||
accessToken: config.accessToken,
|
||||
userId: config.userId,
|
||||
username: config.username,
|
||||
},
|
||||
clientInfo,
|
||||
jellyfinConfig: config,
|
||||
});
|
||||
await deps.handlePlayCommand({
|
||||
args,
|
||||
session: {
|
||||
serverUrl: config.serverUrl,
|
||||
accessToken: config.accessToken,
|
||||
userId: config.userId,
|
||||
username: config.username,
|
||||
},
|
||||
clientInfo,
|
||||
jellyfinConfig: config,
|
||||
});
|
||||
assert.deepEqual(calls, ['auth', 'remote', 'list', 'play']);
|
||||
});
|
||||
55
src/main/runtime/jellyfin-command-dispatch-main-deps.ts
Normal file
55
src/main/runtime/jellyfin-command-dispatch-main-deps.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { CliArgs } from '../../cli/args';
|
||||
|
||||
type JellyfinConfigBase = {
|
||||
serverUrl?: string;
|
||||
accessToken?: string;
|
||||
userId?: string;
|
||||
username?: string;
|
||||
};
|
||||
|
||||
type JellyfinSession = {
|
||||
serverUrl: string;
|
||||
accessToken: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
export type RunJellyfinCommandMainDeps<TClientInfo, TConfig extends JellyfinConfigBase> = {
|
||||
getJellyfinConfig: () => TConfig;
|
||||
defaultServerUrl: string;
|
||||
getJellyfinClientInfo: (config: TConfig) => TClientInfo;
|
||||
handleAuthCommands: (params: {
|
||||
args: CliArgs;
|
||||
jellyfinConfig: TConfig;
|
||||
serverUrl: string;
|
||||
clientInfo: TClientInfo;
|
||||
}) => Promise<boolean>;
|
||||
handleRemoteAnnounceCommand: (args: CliArgs) => Promise<boolean>;
|
||||
handleListCommands: (params: {
|
||||
args: CliArgs;
|
||||
session: JellyfinSession;
|
||||
clientInfo: TClientInfo;
|
||||
jellyfinConfig: TConfig;
|
||||
}) => Promise<boolean>;
|
||||
handlePlayCommand: (params: {
|
||||
args: CliArgs;
|
||||
session: JellyfinSession;
|
||||
clientInfo: TClientInfo;
|
||||
jellyfinConfig: TConfig;
|
||||
}) => Promise<boolean>;
|
||||
};
|
||||
|
||||
export function createBuildRunJellyfinCommandMainDepsHandler<
|
||||
TClientInfo,
|
||||
TConfig extends JellyfinConfigBase,
|
||||
>(deps: RunJellyfinCommandMainDeps<TClientInfo, TConfig>) {
|
||||
return (): RunJellyfinCommandMainDeps<TClientInfo, TConfig> => ({
|
||||
getJellyfinConfig: () => deps.getJellyfinConfig(),
|
||||
defaultServerUrl: deps.defaultServerUrl,
|
||||
getJellyfinClientInfo: (config: TConfig) => deps.getJellyfinClientInfo(config),
|
||||
handleAuthCommands: (params) => deps.handleAuthCommands(params),
|
||||
handleRemoteAnnounceCommand: (args: CliArgs) => deps.handleRemoteAnnounceCommand(args),
|
||||
handleListCommands: (params) => deps.handleListCommands(params),
|
||||
handlePlayCommand: (params) => deps.handlePlayCommand(params),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler,
|
||||
createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler,
|
||||
createBuildWaitForMpvConnectedMainDepsHandler,
|
||||
} from './jellyfin-remote-connection-main-deps';
|
||||
|
||||
test('wait for mpv connected main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const client = { connected: false, connect: () => calls.push('connect') };
|
||||
const deps = createBuildWaitForMpvConnectedMainDepsHandler({
|
||||
getMpvClient: () => client,
|
||||
now: () => 123,
|
||||
sleep: async () => {
|
||||
calls.push('sleep');
|
||||
},
|
||||
})();
|
||||
|
||||
assert.equal(deps.getMpvClient(), client);
|
||||
assert.equal(deps.now(), 123);
|
||||
await deps.sleep(10);
|
||||
assert.deepEqual(calls, ['sleep']);
|
||||
});
|
||||
|
||||
test('launch mpv for jellyfin main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const proc = {
|
||||
on: () => {},
|
||||
unref: () => {
|
||||
calls.push('unref');
|
||||
},
|
||||
};
|
||||
const deps = createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler({
|
||||
getSocketPath: () => '/tmp/mpv.sock',
|
||||
platform: 'darwin',
|
||||
execPath: '/tmp/subminer',
|
||||
defaultMpvLogPath: '/tmp/mpv.log',
|
||||
defaultMpvArgs: ['--no-config'],
|
||||
removeSocketPath: (socketPath) => calls.push(`rm:${socketPath}`),
|
||||
spawnMpv: (args) => {
|
||||
calls.push(`spawn:${args.join(' ')}`);
|
||||
return proc;
|
||||
},
|
||||
logWarn: (message) => calls.push(`warn:${message}`),
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.getSocketPath(), '/tmp/mpv.sock');
|
||||
assert.equal(deps.platform, 'darwin');
|
||||
assert.equal(deps.execPath, '/tmp/subminer');
|
||||
assert.equal(deps.defaultMpvLogPath, '/tmp/mpv.log');
|
||||
assert.deepEqual(deps.defaultMpvArgs, ['--no-config']);
|
||||
deps.removeSocketPath('/tmp/mpv.sock');
|
||||
deps.spawnMpv(['--idle=yes']);
|
||||
deps.logInfo('launched');
|
||||
deps.logWarn('bad', null);
|
||||
assert.deepEqual(calls, ['rm:/tmp/mpv.sock', 'spawn:--idle=yes', 'info:launched', 'warn:bad']);
|
||||
});
|
||||
|
||||
test('ensure mpv connected for jellyfin main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const client = { connected: true, connect: () => {} };
|
||||
const waitPromise = Promise.resolve(true);
|
||||
const inFlight = Promise.resolve(false);
|
||||
const deps = createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler({
|
||||
getMpvClient: () => client,
|
||||
setMpvClient: () => calls.push('set-client'),
|
||||
createMpvClient: () => client,
|
||||
waitForMpvConnected: () => waitPromise,
|
||||
launchMpvIdleForJellyfinPlayback: () => calls.push('launch'),
|
||||
getAutoLaunchInFlight: () => inFlight,
|
||||
setAutoLaunchInFlight: () => calls.push('set-in-flight'),
|
||||
connectTimeoutMs: 7000,
|
||||
autoLaunchTimeoutMs: 15000,
|
||||
})();
|
||||
|
||||
assert.equal(deps.getMpvClient(), client);
|
||||
deps.setMpvClient(client);
|
||||
assert.equal(deps.createMpvClient(), client);
|
||||
assert.equal(await deps.waitForMpvConnected(1), true);
|
||||
deps.launchMpvIdleForJellyfinPlayback();
|
||||
assert.equal(deps.getAutoLaunchInFlight(), inFlight);
|
||||
deps.setAutoLaunchInFlight(null);
|
||||
assert.equal(deps.connectTimeoutMs, 7000);
|
||||
assert.equal(deps.autoLaunchTimeoutMs, 15000);
|
||||
assert.deepEqual(calls, ['set-client', 'launch', 'set-in-flight']);
|
||||
});
|
||||
45
src/main/runtime/jellyfin-remote-connection-main-deps.ts
Normal file
45
src/main/runtime/jellyfin-remote-connection-main-deps.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type {
|
||||
EnsureMpvConnectedDeps,
|
||||
LaunchMpvForJellyfinDeps,
|
||||
WaitForMpvConnectedDeps,
|
||||
} from './jellyfin-remote-connection';
|
||||
|
||||
export function createBuildWaitForMpvConnectedMainDepsHandler(deps: WaitForMpvConnectedDeps) {
|
||||
return (): WaitForMpvConnectedDeps => ({
|
||||
getMpvClient: () => deps.getMpvClient(),
|
||||
now: () => deps.now(),
|
||||
sleep: (delayMs: number) => deps.sleep(delayMs),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler(
|
||||
deps: LaunchMpvForJellyfinDeps,
|
||||
) {
|
||||
return (): LaunchMpvForJellyfinDeps => ({
|
||||
getSocketPath: () => deps.getSocketPath(),
|
||||
platform: deps.platform,
|
||||
execPath: deps.execPath,
|
||||
defaultMpvLogPath: deps.defaultMpvLogPath,
|
||||
defaultMpvArgs: deps.defaultMpvArgs,
|
||||
removeSocketPath: (socketPath: string) => deps.removeSocketPath(socketPath),
|
||||
spawnMpv: (args: string[]) => deps.spawnMpv(args),
|
||||
logWarn: (message: string, error: unknown) => deps.logWarn(message, error),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler(
|
||||
deps: EnsureMpvConnectedDeps,
|
||||
) {
|
||||
return (): EnsureMpvConnectedDeps => ({
|
||||
getMpvClient: () => deps.getMpvClient(),
|
||||
setMpvClient: (client) => deps.setMpvClient(client),
|
||||
createMpvClient: () => deps.createMpvClient(),
|
||||
waitForMpvConnected: (timeoutMs: number) => deps.waitForMpvConnected(timeoutMs),
|
||||
launchMpvIdleForJellyfinPlayback: () => deps.launchMpvIdleForJellyfinPlayback(),
|
||||
getAutoLaunchInFlight: () => deps.getAutoLaunchInFlight(),
|
||||
setAutoLaunchInFlight: (promise) => deps.setAutoLaunchInFlight(promise),
|
||||
connectTimeoutMs: deps.connectTimeoutMs,
|
||||
autoLaunchTimeoutMs: deps.autoLaunchTimeoutMs,
|
||||
});
|
||||
}
|
||||
25
src/main/runtime/mpv-jellyfin-defaults-main-deps.test.ts
Normal file
25
src/main/runtime/mpv-jellyfin-defaults-main-deps.test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildApplyJellyfinMpvDefaultsMainDepsHandler,
|
||||
createBuildGetDefaultSocketPathMainDepsHandler,
|
||||
} from './mpv-jellyfin-defaults-main-deps';
|
||||
|
||||
test('apply jellyfin mpv defaults main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildApplyJellyfinMpvDefaultsMainDepsHandler({
|
||||
sendMpvCommandRuntime: (_client, command) => calls.push(command.join(':')),
|
||||
jellyfinLangPref: 'ja,jp',
|
||||
})();
|
||||
|
||||
deps.sendMpvCommandRuntime({}, ['set_property', 'aid', 'auto']);
|
||||
assert.equal(deps.jellyfinLangPref, 'ja,jp');
|
||||
assert.deepEqual(calls, ['set_property:aid:auto']);
|
||||
});
|
||||
|
||||
test('get default socket path main deps builder maps platform', () => {
|
||||
const deps = createBuildGetDefaultSocketPathMainDepsHandler({
|
||||
platform: 'darwin',
|
||||
})();
|
||||
assert.equal(deps.platform, 'darwin');
|
||||
});
|
||||
24
src/main/runtime/mpv-jellyfin-defaults-main-deps.ts
Normal file
24
src/main/runtime/mpv-jellyfin-defaults-main-deps.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
createApplyJellyfinMpvDefaultsHandler,
|
||||
createGetDefaultSocketPathHandler,
|
||||
} from './mpv-jellyfin-defaults';
|
||||
|
||||
type ApplyJellyfinMpvDefaultsMainDeps = Parameters<typeof createApplyJellyfinMpvDefaultsHandler>[0];
|
||||
type GetDefaultSocketPathMainDeps = Parameters<typeof createGetDefaultSocketPathHandler>[0];
|
||||
|
||||
export function createBuildApplyJellyfinMpvDefaultsMainDepsHandler(
|
||||
deps: ApplyJellyfinMpvDefaultsMainDeps,
|
||||
) {
|
||||
return (): ApplyJellyfinMpvDefaultsMainDeps => ({
|
||||
sendMpvCommandRuntime: (client, command) => deps.sendMpvCommandRuntime(client, command),
|
||||
jellyfinLangPref: deps.jellyfinLangPref,
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildGetDefaultSocketPathMainDepsHandler(
|
||||
deps: GetDefaultSocketPathMainDeps,
|
||||
) {
|
||||
return (): GetDefaultSocketPathMainDeps => ({
|
||||
platform: deps.platform,
|
||||
});
|
||||
}
|
||||
30
src/main/runtime/overlay-bootstrap-main-deps.test.ts
Normal file
30
src/main/runtime/overlay-bootstrap-main-deps.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildOverlayContentMeasurementStoreMainDepsHandler,
|
||||
createBuildOverlayModalRuntimeMainDepsHandler,
|
||||
} from './overlay-bootstrap-main-deps';
|
||||
|
||||
test('overlay content measurement store main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildOverlayContentMeasurementStoreMainDepsHandler({
|
||||
now: () => 42,
|
||||
warn: (message) => calls.push(`warn:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.now(), 42);
|
||||
deps.warn('bad payload');
|
||||
assert.deepEqual(calls, ['warn:bad payload']);
|
||||
});
|
||||
|
||||
test('overlay modal runtime main deps builder maps window resolvers', () => {
|
||||
const mainWindow = { id: 'main' };
|
||||
const invisibleWindow = { id: 'invisible' };
|
||||
const deps = createBuildOverlayModalRuntimeMainDepsHandler({
|
||||
getMainWindow: () => mainWindow as never,
|
||||
getInvisibleWindow: () => invisibleWindow as never,
|
||||
})();
|
||||
|
||||
assert.equal(deps.getMainWindow(), mainWindow);
|
||||
assert.equal(deps.getInvisibleWindow(), invisibleWindow);
|
||||
});
|
||||
22
src/main/runtime/overlay-bootstrap-main-deps.ts
Normal file
22
src/main/runtime/overlay-bootstrap-main-deps.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { OverlayWindowResolver } from '../overlay-runtime';
|
||||
|
||||
type OverlayContentMeasurementStoreMainDeps = {
|
||||
now: () => number;
|
||||
warn: (message: string) => void;
|
||||
};
|
||||
|
||||
export function createBuildOverlayContentMeasurementStoreMainDepsHandler(
|
||||
deps: OverlayContentMeasurementStoreMainDeps,
|
||||
) {
|
||||
return (): OverlayContentMeasurementStoreMainDeps => ({
|
||||
now: () => deps.now(),
|
||||
warn: (message: string) => deps.warn(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildOverlayModalRuntimeMainDepsHandler(deps: OverlayWindowResolver) {
|
||||
return (): OverlayWindowResolver => ({
|
||||
getMainWindow: () => deps.getMainWindow(),
|
||||
getInvisibleWindow: () => deps.getInvisibleWindow(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildBroadcastRuntimeOptionsChangedMainDepsHandler,
|
||||
createBuildGetRuntimeOptionsStateMainDepsHandler,
|
||||
createBuildOpenRuntimeOptionsPaletteMainDepsHandler,
|
||||
createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler,
|
||||
createBuildSendToActiveOverlayWindowMainDepsHandler,
|
||||
createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler,
|
||||
} from './overlay-runtime-main-actions-main-deps';
|
||||
|
||||
test('get runtime options state main deps builder maps callbacks', () => {
|
||||
const manager = { listOptions: () => [] };
|
||||
const deps = createBuildGetRuntimeOptionsStateMainDepsHandler({
|
||||
getRuntimeOptionsManager: () => manager,
|
||||
})();
|
||||
assert.equal(deps.getRuntimeOptionsManager(), manager);
|
||||
});
|
||||
|
||||
test('restore secondary sub visibility main deps builder maps callbacks', () => {
|
||||
const deps = createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler({
|
||||
getMpvClient: () => ({ connected: true, restorePreviousSecondarySubVisibility: () => {} }),
|
||||
})();
|
||||
assert.equal(deps.getMpvClient()?.connected, true);
|
||||
});
|
||||
|
||||
test('broadcast runtime options changed main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildBroadcastRuntimeOptionsChangedMainDepsHandler({
|
||||
broadcastRuntimeOptionsChangedRuntime: () => calls.push('broadcast-runtime'),
|
||||
getRuntimeOptionsState: () => [],
|
||||
broadcastToOverlayWindows: (channel) => calls.push(channel),
|
||||
})();
|
||||
|
||||
deps.broadcastRuntimeOptionsChangedRuntime(() => [], () => {});
|
||||
deps.broadcastToOverlayWindows('runtime-options:changed');
|
||||
assert.deepEqual(deps.getRuntimeOptionsState(), []);
|
||||
assert.deepEqual(calls, ['broadcast-runtime', 'runtime-options:changed']);
|
||||
});
|
||||
|
||||
test('send to active overlay window main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildSendToActiveOverlayWindowMainDepsHandler({
|
||||
sendToActiveOverlayWindowRuntime: () => {
|
||||
calls.push('send');
|
||||
return true;
|
||||
},
|
||||
})();
|
||||
|
||||
assert.equal(deps.sendToActiveOverlayWindowRuntime('x'), true);
|
||||
assert.deepEqual(calls, ['send']);
|
||||
});
|
||||
|
||||
test('set overlay debug visualization main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler({
|
||||
setOverlayDebugVisualizationEnabledRuntime: () => calls.push('set-runtime'),
|
||||
getCurrentEnabled: () => false,
|
||||
setCurrentEnabled: () => calls.push('set-current'),
|
||||
broadcastToOverlayWindows: () => calls.push('broadcast'),
|
||||
})();
|
||||
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(false, true, () => {}, () => {});
|
||||
assert.equal(deps.getCurrentEnabled(), false);
|
||||
deps.setCurrentEnabled(true);
|
||||
deps.broadcastToOverlayWindows('overlay:debug');
|
||||
assert.deepEqual(calls, ['set-runtime', 'set-current', 'broadcast']);
|
||||
});
|
||||
|
||||
test('open runtime options palette main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildOpenRuntimeOptionsPaletteMainDepsHandler({
|
||||
openRuntimeOptionsPaletteRuntime: () => calls.push('open'),
|
||||
})();
|
||||
|
||||
deps.openRuntimeOptionsPaletteRuntime();
|
||||
assert.deepEqual(calls, ['open']);
|
||||
});
|
||||
89
src/main/runtime/overlay-runtime-main-actions-main-deps.ts
Normal file
89
src/main/runtime/overlay-runtime-main-actions-main-deps.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
createBroadcastRuntimeOptionsChangedHandler,
|
||||
createGetRuntimeOptionsStateHandler,
|
||||
createOpenRuntimeOptionsPaletteHandler,
|
||||
createRestorePreviousSecondarySubVisibilityHandler,
|
||||
createSendToActiveOverlayWindowHandler,
|
||||
createSetOverlayDebugVisualizationEnabledHandler,
|
||||
} from './overlay-runtime-main-actions';
|
||||
|
||||
type GetRuntimeOptionsStateMainDeps = Parameters<typeof createGetRuntimeOptionsStateHandler>[0];
|
||||
type RestorePreviousSecondarySubVisibilityMainDeps = Parameters<
|
||||
typeof createRestorePreviousSecondarySubVisibilityHandler
|
||||
>[0];
|
||||
type BroadcastRuntimeOptionsChangedMainDeps = Parameters<
|
||||
typeof createBroadcastRuntimeOptionsChangedHandler
|
||||
>[0];
|
||||
type SendToActiveOverlayWindowMainDeps = Parameters<typeof createSendToActiveOverlayWindowHandler>[0];
|
||||
type SetOverlayDebugVisualizationEnabledMainDeps = Parameters<
|
||||
typeof createSetOverlayDebugVisualizationEnabledHandler
|
||||
>[0];
|
||||
type OpenRuntimeOptionsPaletteMainDeps = Parameters<typeof createOpenRuntimeOptionsPaletteHandler>[0];
|
||||
|
||||
export function createBuildGetRuntimeOptionsStateMainDepsHandler(
|
||||
deps: GetRuntimeOptionsStateMainDeps,
|
||||
) {
|
||||
return (): GetRuntimeOptionsStateMainDeps => ({
|
||||
getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler(
|
||||
deps: RestorePreviousSecondarySubVisibilityMainDeps,
|
||||
) {
|
||||
return (): RestorePreviousSecondarySubVisibilityMainDeps => ({
|
||||
getMpvClient: () => deps.getMpvClient(),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildBroadcastRuntimeOptionsChangedMainDepsHandler(
|
||||
deps: BroadcastRuntimeOptionsChangedMainDeps,
|
||||
) {
|
||||
return (): BroadcastRuntimeOptionsChangedMainDeps => ({
|
||||
broadcastRuntimeOptionsChangedRuntime: (getRuntimeOptionsState, broadcastToOverlayWindows) =>
|
||||
deps.broadcastRuntimeOptionsChangedRuntime(getRuntimeOptionsState, broadcastToOverlayWindows),
|
||||
getRuntimeOptionsState: () => deps.getRuntimeOptionsState(),
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) =>
|
||||
deps.broadcastToOverlayWindows(channel, ...args),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildSendToActiveOverlayWindowMainDepsHandler(
|
||||
deps: SendToActiveOverlayWindowMainDeps,
|
||||
) {
|
||||
return (): SendToActiveOverlayWindowMainDeps => ({
|
||||
sendToActiveOverlayWindowRuntime: (channel, payload, runtimeOptions) =>
|
||||
deps.sendToActiveOverlayWindowRuntime(channel, payload, runtimeOptions),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler(
|
||||
deps: SetOverlayDebugVisualizationEnabledMainDeps,
|
||||
) {
|
||||
return (): SetOverlayDebugVisualizationEnabledMainDeps => ({
|
||||
setOverlayDebugVisualizationEnabledRuntime: (
|
||||
currentEnabled,
|
||||
nextEnabled,
|
||||
setCurrentEnabled,
|
||||
broadcastToOverlayWindows,
|
||||
) =>
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(
|
||||
currentEnabled,
|
||||
nextEnabled,
|
||||
setCurrentEnabled,
|
||||
broadcastToOverlayWindows,
|
||||
),
|
||||
getCurrentEnabled: () => deps.getCurrentEnabled(),
|
||||
setCurrentEnabled: (enabled: boolean) => deps.setCurrentEnabled(enabled),
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) =>
|
||||
deps.broadcastToOverlayWindows(channel, ...args),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildOpenRuntimeOptionsPaletteMainDepsHandler(
|
||||
deps: OpenRuntimeOptionsPaletteMainDeps,
|
||||
) {
|
||||
return (): OpenRuntimeOptionsPaletteMainDeps => ({
|
||||
openRuntimeOptionsPaletteRuntime: () => deps.openRuntimeOptionsPaletteRuntime(),
|
||||
});
|
||||
}
|
||||
99
src/main/runtime/runtime-bootstrap-main-deps.test.ts
Normal file
99
src/main/runtime/runtime-bootstrap-main-deps.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildAnilistStateRuntimeMainDepsHandler,
|
||||
createBuildConfigDerivedRuntimeMainDepsHandler,
|
||||
createBuildImmersionMediaRuntimeMainDepsHandler,
|
||||
createBuildMainSubsyncRuntimeMainDepsHandler,
|
||||
} from './runtime-bootstrap-main-deps';
|
||||
|
||||
test('immersion media runtime main deps builder maps callbacks', async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildImmersionMediaRuntimeMainDepsHandler({
|
||||
getResolvedConfig: () => ({ immersionTracking: { dbPath: '/tmp/db.sqlite' } }),
|
||||
defaultImmersionDbPath: '/tmp/default.sqlite',
|
||||
getTracker: () => ({ handleMediaChange: () => calls.push('track') }),
|
||||
getMpvClient: () => ({ connected: true }),
|
||||
getCurrentMediaPath: () => '/tmp/media.mkv',
|
||||
getCurrentMediaTitle: () => 'Title',
|
||||
sleep: async () => {
|
||||
calls.push('sleep');
|
||||
},
|
||||
seedWaitMs: 25,
|
||||
seedAttempts: 3,
|
||||
logDebug: (message) => calls.push(`debug:${message}`),
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
})();
|
||||
|
||||
assert.equal(deps.defaultImmersionDbPath, '/tmp/default.sqlite');
|
||||
assert.deepEqual(deps.getResolvedConfig(), { immersionTracking: { dbPath: '/tmp/db.sqlite' } });
|
||||
assert.deepEqual(deps.getMpvClient(), { connected: true });
|
||||
assert.equal(deps.getCurrentMediaPath(), '/tmp/media.mkv');
|
||||
assert.equal(deps.getCurrentMediaTitle(), 'Title');
|
||||
assert.equal(deps.seedWaitMs, 25);
|
||||
assert.equal(deps.seedAttempts, 3);
|
||||
await deps.sleep?.(1);
|
||||
deps.logDebug('a');
|
||||
deps.logInfo('b');
|
||||
assert.deepEqual(calls, ['sleep', 'debug:a', 'info:b']);
|
||||
});
|
||||
|
||||
test('anilist state runtime main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildAnilistStateRuntimeMainDepsHandler({
|
||||
getClientSecretState: () => ({ status: 'resolved' } as never),
|
||||
setClientSecretState: () => calls.push('set-client'),
|
||||
getRetryQueueState: () => ({ pending: 1 } as never),
|
||||
setRetryQueueState: () => calls.push('set-queue'),
|
||||
getUpdateQueueSnapshot: () => ({ pending: 2 } as never),
|
||||
clearStoredToken: () => calls.push('clear-stored'),
|
||||
clearCachedAccessToken: () => calls.push('clear-cached'),
|
||||
})();
|
||||
|
||||
assert.deepEqual(deps.getClientSecretState(), { status: 'resolved' });
|
||||
assert.deepEqual(deps.getRetryQueueState(), { pending: 1 });
|
||||
assert.deepEqual(deps.getUpdateQueueSnapshot(), { pending: 2 });
|
||||
deps.setClientSecretState({} as never);
|
||||
deps.setRetryQueueState({} as never);
|
||||
deps.clearStoredToken();
|
||||
deps.clearCachedAccessToken();
|
||||
assert.deepEqual(calls, ['set-client', 'set-queue', 'clear-stored', 'clear-cached']);
|
||||
});
|
||||
|
||||
test('config derived runtime main deps builder maps callbacks', () => {
|
||||
const deps = createBuildConfigDerivedRuntimeMainDepsHandler({
|
||||
getResolvedConfig: () => ({ jimaku: {} } as never),
|
||||
getRuntimeOptionsManager: () => null,
|
||||
platform: 'darwin',
|
||||
defaultJimakuLanguagePreference: 'ja',
|
||||
defaultJimakuMaxEntryResults: 20,
|
||||
defaultJimakuApiBaseUrl: 'https://api.example.com',
|
||||
})();
|
||||
|
||||
assert.deepEqual(deps.getResolvedConfig(), { jimaku: {} });
|
||||
assert.equal(deps.getRuntimeOptionsManager(), null);
|
||||
assert.equal(deps.platform, 'darwin');
|
||||
assert.equal(deps.defaultJimakuLanguagePreference, 'ja');
|
||||
assert.equal(deps.defaultJimakuMaxEntryResults, 20);
|
||||
assert.equal(deps.defaultJimakuApiBaseUrl, 'https://api.example.com');
|
||||
});
|
||||
|
||||
test('main subsync runtime main deps builder maps callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildMainSubsyncRuntimeMainDepsHandler({
|
||||
getMpvClient: () => ({ connected: true }) as never,
|
||||
getResolvedConfig: () => ({ subsync: {} } as never),
|
||||
getSubsyncInProgress: () => true,
|
||||
setSubsyncInProgress: () => calls.push('set-progress'),
|
||||
showMpvOsd: (text) => calls.push(`osd:${text}`),
|
||||
openManualPicker: () => calls.push('open-picker'),
|
||||
})();
|
||||
|
||||
assert.deepEqual(deps.getMpvClient(), { connected: true });
|
||||
assert.deepEqual(deps.getResolvedConfig(), { subsync: {} });
|
||||
assert.equal(deps.getSubsyncInProgress(), true);
|
||||
deps.setSubsyncInProgress(false);
|
||||
deps.showMpvOsd('ready');
|
||||
deps.openManualPicker({} as never);
|
||||
assert.deepEqual(calls, ['set-progress', 'osd:ready', 'open-picker']);
|
||||
});
|
||||
54
src/main/runtime/runtime-bootstrap-main-deps.ts
Normal file
54
src/main/runtime/runtime-bootstrap-main-deps.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { AnilistStateRuntimeDeps } from './anilist-state';
|
||||
import type { ConfigDerivedRuntimeDeps } from './config-derived';
|
||||
import type { ImmersionMediaRuntimeDeps } from './immersion-media';
|
||||
import type { MainSubsyncRuntimeDeps } from './subsync-runtime';
|
||||
|
||||
export function createBuildImmersionMediaRuntimeMainDepsHandler(deps: ImmersionMediaRuntimeDeps) {
|
||||
return (): ImmersionMediaRuntimeDeps => ({
|
||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||
defaultImmersionDbPath: deps.defaultImmersionDbPath,
|
||||
getTracker: () => deps.getTracker(),
|
||||
getMpvClient: () => deps.getMpvClient(),
|
||||
getCurrentMediaPath: () => deps.getCurrentMediaPath(),
|
||||
getCurrentMediaTitle: () => deps.getCurrentMediaTitle(),
|
||||
sleep: deps.sleep,
|
||||
seedWaitMs: deps.seedWaitMs,
|
||||
seedAttempts: deps.seedAttempts,
|
||||
logDebug: (message: string) => deps.logDebug(message),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildAnilistStateRuntimeMainDepsHandler(deps: AnilistStateRuntimeDeps) {
|
||||
return (): AnilistStateRuntimeDeps => ({
|
||||
getClientSecretState: () => deps.getClientSecretState(),
|
||||
setClientSecretState: (next) => deps.setClientSecretState(next),
|
||||
getRetryQueueState: () => deps.getRetryQueueState(),
|
||||
setRetryQueueState: (next) => deps.setRetryQueueState(next),
|
||||
getUpdateQueueSnapshot: () => deps.getUpdateQueueSnapshot(),
|
||||
clearStoredToken: () => deps.clearStoredToken(),
|
||||
clearCachedAccessToken: () => deps.clearCachedAccessToken(),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildConfigDerivedRuntimeMainDepsHandler(deps: ConfigDerivedRuntimeDeps) {
|
||||
return (): ConfigDerivedRuntimeDeps => ({
|
||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||
getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(),
|
||||
platform: deps.platform,
|
||||
defaultJimakuLanguagePreference: deps.defaultJimakuLanguagePreference,
|
||||
defaultJimakuMaxEntryResults: deps.defaultJimakuMaxEntryResults,
|
||||
defaultJimakuApiBaseUrl: deps.defaultJimakuApiBaseUrl,
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildMainSubsyncRuntimeMainDepsHandler(deps: MainSubsyncRuntimeDeps) {
|
||||
return (): MainSubsyncRuntimeDeps => ({
|
||||
getMpvClient: () => deps.getMpvClient(),
|
||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||
getSubsyncInProgress: () => deps.getSubsyncInProgress(),
|
||||
setSubsyncInProgress: (inProgress: boolean) => deps.setSubsyncInProgress(inProgress),
|
||||
showMpvOsd: (text: string) => deps.showMpvOsd(text),
|
||||
openManualPicker: (payload) => deps.openManualPicker(payload),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user