mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
refactor: extract startup bootstrap runtime orchestration
This commit is contained in:
105
src/core/services/startup-bootstrap-runtime-service.test.ts
Normal file
105
src/core/services/startup-bootstrap-runtime-service.test.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import {
|
||||||
|
runStartupBootstrapRuntimeService,
|
||||||
|
} from "./startup-bootstrap-runtime-service";
|
||||||
|
import { CliArgs } from "../../cli/args";
|
||||||
|
|
||||||
|
function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||||
|
return {
|
||||||
|
start: false,
|
||||||
|
stop: false,
|
||||||
|
toggle: false,
|
||||||
|
toggleVisibleOverlay: false,
|
||||||
|
toggleInvisibleOverlay: false,
|
||||||
|
settings: false,
|
||||||
|
show: false,
|
||||||
|
hide: false,
|
||||||
|
showVisibleOverlay: false,
|
||||||
|
hideVisibleOverlay: false,
|
||||||
|
showInvisibleOverlay: false,
|
||||||
|
hideInvisibleOverlay: false,
|
||||||
|
copySubtitle: false,
|
||||||
|
copySubtitleMultiple: false,
|
||||||
|
mineSentence: false,
|
||||||
|
mineSentenceMultiple: false,
|
||||||
|
updateLastCardFromClipboard: false,
|
||||||
|
toggleSecondarySub: false,
|
||||||
|
triggerFieldGrouping: false,
|
||||||
|
triggerSubsync: false,
|
||||||
|
markAudioCard: false,
|
||||||
|
openRuntimeOptions: false,
|
||||||
|
texthooker: false,
|
||||||
|
help: false,
|
||||||
|
autoStartOverlay: false,
|
||||||
|
generateConfig: false,
|
||||||
|
backupOverwrite: false,
|
||||||
|
verbose: false,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test("runStartupBootstrapRuntimeService configures startup state and starts lifecycle", () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
const args = makeArgs({
|
||||||
|
verbose: true,
|
||||||
|
socketPath: "/tmp/custom.sock",
|
||||||
|
texthookerPort: 9001,
|
||||||
|
backend: "x11",
|
||||||
|
autoStartOverlay: true,
|
||||||
|
texthooker: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = runStartupBootstrapRuntimeService({
|
||||||
|
argv: ["node", "main.ts", "--verbose"],
|
||||||
|
parseArgs: () => args,
|
||||||
|
setLogLevelEnv: (level) => calls.push(`setLog:${level}`),
|
||||||
|
enableVerboseLogging: () => calls.push("enableVerbose"),
|
||||||
|
forceX11Backend: () => calls.push("forceX11"),
|
||||||
|
enforceUnsupportedWaylandMode: () => calls.push("enforceWayland"),
|
||||||
|
getDefaultSocketPath: () => "/tmp/default.sock",
|
||||||
|
defaultTexthookerPort: 5174,
|
||||||
|
runGenerateConfigFlow: () => false,
|
||||||
|
startAppLifecycle: () => calls.push("startLifecycle"),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.initialArgs, args);
|
||||||
|
assert.equal(result.mpvSocketPath, "/tmp/custom.sock");
|
||||||
|
assert.equal(result.texthookerPort, 9001);
|
||||||
|
assert.equal(result.backendOverride, "x11");
|
||||||
|
assert.equal(result.autoStartOverlay, true);
|
||||||
|
assert.equal(result.texthookerOnlyMode, true);
|
||||||
|
assert.deepEqual(calls, [
|
||||||
|
"enableVerbose",
|
||||||
|
"forceX11",
|
||||||
|
"enforceWayland",
|
||||||
|
"startLifecycle",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("runStartupBootstrapRuntimeService skips lifecycle when generate-config flow handled", () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
const args = makeArgs({ generateConfig: true, logLevel: "warn" });
|
||||||
|
|
||||||
|
const result = runStartupBootstrapRuntimeService({
|
||||||
|
argv: ["node", "main.ts", "--generate-config"],
|
||||||
|
parseArgs: () => args,
|
||||||
|
setLogLevelEnv: (level) => calls.push(`setLog:${level}`),
|
||||||
|
enableVerboseLogging: () => calls.push("enableVerbose"),
|
||||||
|
forceX11Backend: () => calls.push("forceX11"),
|
||||||
|
enforceUnsupportedWaylandMode: () => calls.push("enforceWayland"),
|
||||||
|
getDefaultSocketPath: () => "/tmp/default.sock",
|
||||||
|
defaultTexthookerPort: 5174,
|
||||||
|
runGenerateConfigFlow: () => true,
|
||||||
|
startAppLifecycle: () => calls.push("startLifecycle"),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.mpvSocketPath, "/tmp/default.sock");
|
||||||
|
assert.equal(result.texthookerPort, 5174);
|
||||||
|
assert.equal(result.backendOverride, null);
|
||||||
|
assert.deepEqual(calls, [
|
||||||
|
"setLog:warn",
|
||||||
|
"forceX11",
|
||||||
|
"enforceWayland",
|
||||||
|
]);
|
||||||
|
});
|
||||||
53
src/core/services/startup-bootstrap-runtime-service.ts
Normal file
53
src/core/services/startup-bootstrap-runtime-service.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { CliArgs } from "../../cli/args";
|
||||||
|
|
||||||
|
export interface StartupBootstrapRuntimeState {
|
||||||
|
initialArgs: CliArgs;
|
||||||
|
mpvSocketPath: string;
|
||||||
|
texthookerPort: number;
|
||||||
|
backendOverride: string | null;
|
||||||
|
autoStartOverlay: boolean;
|
||||||
|
texthookerOnlyMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StartupBootstrapRuntimeDeps {
|
||||||
|
argv: string[];
|
||||||
|
parseArgs: (argv: string[]) => CliArgs;
|
||||||
|
setLogLevelEnv: (level: string) => void;
|
||||||
|
enableVerboseLogging: () => void;
|
||||||
|
forceX11Backend: (args: CliArgs) => void;
|
||||||
|
enforceUnsupportedWaylandMode: (args: CliArgs) => void;
|
||||||
|
getDefaultSocketPath: () => string;
|
||||||
|
defaultTexthookerPort: number;
|
||||||
|
runGenerateConfigFlow: (args: CliArgs) => boolean;
|
||||||
|
startAppLifecycle: (args: CliArgs) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runStartupBootstrapRuntimeService(
|
||||||
|
deps: StartupBootstrapRuntimeDeps,
|
||||||
|
): StartupBootstrapRuntimeState {
|
||||||
|
const initialArgs = deps.parseArgs(deps.argv);
|
||||||
|
|
||||||
|
if (initialArgs.logLevel) {
|
||||||
|
deps.setLogLevelEnv(initialArgs.logLevel);
|
||||||
|
} else if (initialArgs.verbose) {
|
||||||
|
deps.enableVerboseLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
deps.forceX11Backend(initialArgs);
|
||||||
|
deps.enforceUnsupportedWaylandMode(initialArgs);
|
||||||
|
|
||||||
|
const state: StartupBootstrapRuntimeState = {
|
||||||
|
initialArgs,
|
||||||
|
mpvSocketPath: initialArgs.socketPath ?? deps.getDefaultSocketPath(),
|
||||||
|
texthookerPort: initialArgs.texthookerPort ?? deps.defaultTexthookerPort,
|
||||||
|
backendOverride: initialArgs.backend ?? null,
|
||||||
|
autoStartOverlay: initialArgs.autoStartOverlay,
|
||||||
|
texthookerOnlyMode: initialArgs.texthooker,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!deps.runGenerateConfigFlow(initialArgs)) {
|
||||||
|
deps.startAppLifecycle(initialArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
66
src/main.ts
66
src/main.ts
@@ -236,6 +236,7 @@ import {
|
|||||||
createSubtitleTimingTrackerRuntimeService,
|
createSubtitleTimingTrackerRuntimeService,
|
||||||
} from "./core/services/startup-resource-runtime-service";
|
} from "./core/services/startup-resource-runtime-service";
|
||||||
import { runGenerateConfigFlowRuntimeService } from "./core/services/config-generation-runtime-service";
|
import { runGenerateConfigFlowRuntimeService } from "./core/services/config-generation-runtime-service";
|
||||||
|
import { runStartupBootstrapRuntimeService } from "./core/services/startup-bootstrap-runtime-service";
|
||||||
import {
|
import {
|
||||||
runSubsyncManualFromIpcRuntimeService,
|
runSubsyncManualFromIpcRuntimeService,
|
||||||
triggerSubsyncFromConfigRuntimeService,
|
triggerSubsyncFromConfigRuntimeService,
|
||||||
@@ -479,27 +480,28 @@ function updateCurrentMediaPath(mediaPath: unknown): void {
|
|||||||
|
|
||||||
let subsyncInProgress = false;
|
let subsyncInProgress = false;
|
||||||
|
|
||||||
const initialArgs = parseArgs(process.argv);
|
const startupState = runStartupBootstrapRuntimeService({
|
||||||
if (initialArgs.logLevel) {
|
argv: process.argv,
|
||||||
process.env.SUBMINER_LOG_LEVEL = initialArgs.logLevel;
|
parseArgs: (argv) => parseArgs(argv),
|
||||||
} else if (initialArgs.verbose) {
|
setLogLevelEnv: (level) => {
|
||||||
|
process.env.SUBMINER_LOG_LEVEL = level;
|
||||||
|
},
|
||||||
|
enableVerboseLogging: () => {
|
||||||
process.env.SUBMINER_LOG_LEVEL = "debug";
|
process.env.SUBMINER_LOG_LEVEL = "debug";
|
||||||
}
|
},
|
||||||
|
forceX11Backend: (args) => {
|
||||||
forceX11Backend(initialArgs);
|
forceX11Backend(args);
|
||||||
enforceUnsupportedWaylandMode(initialArgs);
|
},
|
||||||
|
enforceUnsupportedWaylandMode: (args) => {
|
||||||
let mpvSocketPath = initialArgs.socketPath ?? getDefaultSocketPath();
|
enforceUnsupportedWaylandMode(args);
|
||||||
let texthookerPort = initialArgs.texthookerPort ?? DEFAULT_TEXTHOOKER_PORT;
|
},
|
||||||
const backendOverride = initialArgs.backend ?? null;
|
getDefaultSocketPath: () => getDefaultSocketPath(),
|
||||||
const autoStartOverlay = initialArgs.autoStartOverlay;
|
defaultTexthookerPort: DEFAULT_TEXTHOOKER_PORT,
|
||||||
const texthookerOnlyMode = initialArgs.texthooker;
|
runGenerateConfigFlow: (args) =>
|
||||||
|
runGenerateConfigFlowRuntimeService(args, {
|
||||||
if (
|
shouldStartApp: (nextArgs) => shouldStartApp(nextArgs),
|
||||||
!runGenerateConfigFlowRuntimeService(initialArgs, {
|
generateConfig: async (nextArgs) =>
|
||||||
shouldStartApp: (args) => shouldStartApp(args),
|
generateDefaultConfigFile(nextArgs, {
|
||||||
generateConfig: async (args) =>
|
|
||||||
generateDefaultConfigFile(args, {
|
|
||||||
configDir: CONFIG_DIR,
|
configDir: CONFIG_DIR,
|
||||||
defaultConfig: DEFAULT_CONFIG,
|
defaultConfig: DEFAULT_CONFIG,
|
||||||
generateTemplate: (config) => generateConfigTemplate(config as never),
|
generateTemplate: (config) => generateConfigTemplate(config as never),
|
||||||
@@ -513,14 +515,14 @@ if (
|
|||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
) {
|
startAppLifecycle: (args) => {
|
||||||
startAppLifecycleService(initialArgs, createAppLifecycleDepsRuntimeService({
|
startAppLifecycleService(args, createAppLifecycleDepsRuntimeService({
|
||||||
app,
|
app,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
shouldStartApp: (args) => shouldStartApp(args),
|
shouldStartApp: (nextArgs) => shouldStartApp(nextArgs),
|
||||||
parseArgs: (argv) => parseArgs(argv),
|
parseArgs: (argv) => parseArgs(argv),
|
||||||
handleCliCommand: (args, source) => handleCliCommand(args, source),
|
handleCliCommand: (nextArgs, source) => handleCliCommand(nextArgs, source),
|
||||||
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
||||||
logNoRunningInstance: () => appLogger.logNoRunningInstance(),
|
logNoRunningInstance: () => appLogger.logNoRunningInstance(),
|
||||||
onReady: async () => {
|
onReady: async () => {
|
||||||
@@ -557,8 +559,8 @@ if (
|
|||||||
},
|
},
|
||||||
getOverlayWindowsCount: () => getOverlayWindows().length,
|
getOverlayWindowsCount: () => getOverlayWindows().length,
|
||||||
tokenizeSubtitle: (text) => tokenizeSubtitle(text),
|
tokenizeSubtitle: (text) => tokenizeSubtitle(text),
|
||||||
broadcastToOverlayWindows: (channel, ...args) => {
|
broadcastToOverlayWindows: (channel, ...channelArgs) => {
|
||||||
broadcastToOverlayWindows(channel, ...args);
|
broadcastToOverlayWindows(channel, ...channelArgs);
|
||||||
},
|
},
|
||||||
updateCurrentMediaPath: (mediaPath) => {
|
updateCurrentMediaPath: (mediaPath) => {
|
||||||
updateCurrentMediaPath(mediaPath);
|
updateCurrentMediaPath(mediaPath);
|
||||||
@@ -690,7 +692,15 @@ if (
|
|||||||
updateInvisibleOverlayVisibility();
|
updateInvisibleOverlayVisibility();
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialArgs = startupState.initialArgs;
|
||||||
|
let mpvSocketPath = startupState.mpvSocketPath;
|
||||||
|
let texthookerPort = startupState.texthookerPort;
|
||||||
|
const backendOverride = startupState.backendOverride;
|
||||||
|
const autoStartOverlay = startupState.autoStartOverlay;
|
||||||
|
const texthookerOnlyMode = startupState.texthookerOnlyMode;
|
||||||
|
|
||||||
function handleCliCommand(
|
function handleCliCommand(
|
||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
|
|||||||
Reference in New Issue
Block a user