mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-30 06:12:06 -07:00
fix: restore integrated texthooker startup
This commit is contained in:
5
changes/259-texthooker-integrated-startup.md
Normal file
5
changes/259-texthooker-integrated-startup.md
Normal file
@@ -0,0 +1,5 @@
|
||||
type: fixed
|
||||
area: main
|
||||
|
||||
- Keep integrated `--start --texthooker` launches on the full app-ready startup path so the texthooker page and websocket servers start together during normal playback startup.
|
||||
- Stop the mpv/plugin auto-start flow from spawning a separate standalone texthooker helper during normal `subminer <video>` launches.
|
||||
@@ -191,6 +191,14 @@ function M.create(ctx)
|
||||
else
|
||||
table.insert(args, "--hide-visible-overlay")
|
||||
end
|
||||
|
||||
local texthooker_enabled = overrides.texthooker_enabled
|
||||
if texthooker_enabled == nil then
|
||||
texthooker_enabled = opts.texthooker_enabled
|
||||
end
|
||||
if texthooker_enabled then
|
||||
table.insert(args, "--texthooker")
|
||||
end
|
||||
end
|
||||
|
||||
return args
|
||||
@@ -242,50 +250,10 @@ function M.create(ctx)
|
||||
return overrides
|
||||
end
|
||||
|
||||
local function build_texthooker_args()
|
||||
local args = { state.binary_path, "--texthooker", "--port", tostring(opts.texthooker_port) }
|
||||
local log_level = normalize_log_level(opts.log_level)
|
||||
if log_level ~= "info" then
|
||||
table.insert(args, "--log-level")
|
||||
table.insert(args, log_level)
|
||||
end
|
||||
return args
|
||||
end
|
||||
|
||||
local function ensure_texthooker_running(callback)
|
||||
if not opts.texthooker_enabled then
|
||||
if callback then
|
||||
callback()
|
||||
return
|
||||
end
|
||||
|
||||
if state.texthooker_running then
|
||||
callback()
|
||||
return
|
||||
end
|
||||
|
||||
local args = build_texthooker_args()
|
||||
subminer_log("info", "texthooker", "Starting texthooker process: " .. table.concat(args, " "))
|
||||
state.texthooker_running = true
|
||||
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = args,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
}, function(success, result, error)
|
||||
if not success or (result and result.status ~= 0) then
|
||||
state.texthooker_running = false
|
||||
subminer_log(
|
||||
"warn",
|
||||
"texthooker",
|
||||
"Texthooker process exited unexpectedly: " .. (error or (result and result.stderr) or "unknown error")
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Start overlay immediately; overlay start path retries on readiness failures.
|
||||
callback()
|
||||
end
|
||||
|
||||
local function start_overlay(overrides)
|
||||
|
||||
@@ -664,8 +664,8 @@ do
|
||||
fire_event(recorded, "file-loaded")
|
||||
local start_call = find_start_call(recorded.async_calls)
|
||||
assert_true(start_call ~= nil, "auto-start should issue --start command")
|
||||
local texthooker_call = find_texthooker_call(recorded.async_calls)
|
||||
assert_true(texthooker_call ~= nil, "auto-start should issue texthooker helper command when enabled")
|
||||
assert_true(call_has_arg(start_call, "--texthooker"), "auto-start should include --texthooker on the main --start command when enabled")
|
||||
assert_true(find_control_call(recorded.async_calls, "--texthooker") == nil, "auto-start should not issue a separate texthooker helper command")
|
||||
assert_true(
|
||||
call_has_arg(start_call, "--show-visible-overlay"),
|
||||
"auto-start with visible overlay enabled should include --show-visible-overlay on --start"
|
||||
@@ -678,10 +678,6 @@ do
|
||||
find_control_call(recorded.async_calls, "--show-visible-overlay") ~= nil,
|
||||
"auto-start with visible overlay enabled should issue a separate --show-visible-overlay command"
|
||||
)
|
||||
assert_true(
|
||||
find_call_index(recorded.async_calls, start_call) < find_call_index(recorded.async_calls, texthooker_call),
|
||||
"auto-start should launch --start before separate --texthooker helper startup"
|
||||
)
|
||||
assert_true(
|
||||
not has_property_set(recorded.property_sets, "pause", true),
|
||||
"auto-start visible overlay should not force pause without explicit pause-until-ready option"
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
commandNeedsOverlayRuntime,
|
||||
hasExplicitCommand,
|
||||
isHeadlessInitialCommand,
|
||||
isStandaloneTexthookerCommand,
|
||||
parseArgs,
|
||||
shouldRunSettingsOnlyStartup,
|
||||
shouldStartApp,
|
||||
@@ -79,6 +80,14 @@ test('youtube playback does not use generic overlay-runtime bootstrap classifica
|
||||
assert.equal(commandNeedsOverlayStartupPrereqs(args), true);
|
||||
});
|
||||
|
||||
test('standalone texthooker classification excludes integrated start flow', () => {
|
||||
const standalone = parseArgs(['--texthooker']);
|
||||
const integrated = parseArgs(['--start', '--texthooker']);
|
||||
|
||||
assert.equal(isStandaloneTexthookerCommand(standalone), true);
|
||||
assert.equal(isStandaloneTexthookerCommand(integrated), false);
|
||||
});
|
||||
|
||||
test('parseArgs handles jellyfin item listing controls', () => {
|
||||
const args = parseArgs([
|
||||
'--jellyfin-items',
|
||||
|
||||
@@ -397,6 +397,54 @@ export function isHeadlessInitialCommand(args: CliArgs): boolean {
|
||||
return args.refreshKnownWords;
|
||||
}
|
||||
|
||||
export function isStandaloneTexthookerCommand(args: CliArgs): boolean {
|
||||
return (
|
||||
args.texthooker &&
|
||||
!args.background &&
|
||||
!args.start &&
|
||||
!Boolean(args.youtubePlay) &&
|
||||
!args.launchMpv &&
|
||||
!args.stop &&
|
||||
!args.toggle &&
|
||||
!args.toggleVisibleOverlay &&
|
||||
!args.settings &&
|
||||
!args.setup &&
|
||||
!args.show &&
|
||||
!args.hide &&
|
||||
!args.showVisibleOverlay &&
|
||||
!args.hideVisibleOverlay &&
|
||||
!args.copySubtitle &&
|
||||
!args.copySubtitleMultiple &&
|
||||
!args.mineSentence &&
|
||||
!args.mineSentenceMultiple &&
|
||||
!args.updateLastCardFromClipboard &&
|
||||
!args.refreshKnownWords &&
|
||||
!args.toggleSecondarySub &&
|
||||
!args.triggerFieldGrouping &&
|
||||
!args.triggerSubsync &&
|
||||
!args.markAudioCard &&
|
||||
!args.openRuntimeOptions &&
|
||||
!args.anilistStatus &&
|
||||
!args.anilistLogout &&
|
||||
!args.anilistSetup &&
|
||||
!args.anilistRetryQueue &&
|
||||
!args.dictionary &&
|
||||
!args.stats &&
|
||||
!args.jellyfin &&
|
||||
!args.jellyfinLogin &&
|
||||
!args.jellyfinLogout &&
|
||||
!args.jellyfinLibraries &&
|
||||
!args.jellyfinItems &&
|
||||
!args.jellyfinSubtitles &&
|
||||
!args.jellyfinPlay &&
|
||||
!args.jellyfinRemoteAnnounce &&
|
||||
!args.jellyfinPreviewAuth &&
|
||||
!args.help &&
|
||||
!args.autoStartOverlay &&
|
||||
!args.generateConfig
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldStartApp(args: CliArgs): boolean {
|
||||
if (args.stop && !args.start) return false;
|
||||
if (
|
||||
|
||||
@@ -176,7 +176,7 @@ test('runAppReadyRuntime skips heavy startup when shouldSkipHeavyStartup returns
|
||||
assert.ok(calls.indexOf('handleFirstRunSetup') < calls.indexOf('handleInitialArgs'));
|
||||
});
|
||||
|
||||
test('runAppReadyRuntime uses minimal startup for texthooker-only mode', async () => {
|
||||
test('runAppReadyRuntime keeps websocket startup in texthooker-only mode but skips overlay window', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
texthookerOnlyMode: true,
|
||||
reloadConfig: () => calls.push('reloadConfig'),
|
||||
@@ -185,7 +185,16 @@ test('runAppReadyRuntime uses minimal startup for texthooker-only mode', async (
|
||||
|
||||
await runAppReadyRuntime(deps);
|
||||
|
||||
assert.deepEqual(calls, ['ensureDefaultConfigBootstrap', 'reloadConfig', 'handleInitialArgs']);
|
||||
assert.ok(calls.includes('reloadConfig'));
|
||||
assert.ok(calls.includes('createMpvClient'));
|
||||
assert.ok(calls.includes('startAnnotationWebsocket:6678'));
|
||||
assert.ok(calls.includes('startTexthooker:5174:ws://127.0.0.1:6678'));
|
||||
assert.ok(calls.includes('createSubtitleTimingTracker'));
|
||||
assert.ok(calls.includes('handleFirstRunSetup'));
|
||||
assert.ok(calls.includes('handleInitialArgs'));
|
||||
assert.ok(calls.includes('log:Texthooker-only mode enabled; skipping overlay window.'));
|
||||
assert.equal(calls.includes('initializeOverlayRuntime'), false);
|
||||
assert.equal(calls.includes('setVisibleOverlayVisible:true'), false);
|
||||
});
|
||||
|
||||
test('runAppReadyRuntime skips Jellyfin remote startup when dependency is not wired', async () => {
|
||||
|
||||
@@ -62,6 +62,7 @@ function createDeps(overrides: Partial<CliCommandServiceDeps> = {}) {
|
||||
let mpvSocketPath = '/tmp/subminer.sock';
|
||||
let texthookerPort = 5174;
|
||||
const osd: string[] = [];
|
||||
let texthookerWebsocketUrl: string | undefined;
|
||||
|
||||
const deps: CliCommandServiceDeps = {
|
||||
getMpvSocketPath: () => mpvSocketPath,
|
||||
@@ -82,9 +83,10 @@ function createDeps(overrides: Partial<CliCommandServiceDeps> = {}) {
|
||||
calls.push(`setTexthookerPort:${port}`);
|
||||
},
|
||||
getTexthookerPort: () => texthookerPort,
|
||||
getTexthookerWebsocketUrl: () => texthookerWebsocketUrl,
|
||||
shouldOpenTexthookerBrowser: () => true,
|
||||
ensureTexthookerRunning: (port) => {
|
||||
calls.push(`ensureTexthookerRunning:${port}`);
|
||||
ensureTexthookerRunning: (port, websocketUrl) => {
|
||||
calls.push(`ensureTexthookerRunning:${port}:${websocketUrl ?? ''}`);
|
||||
},
|
||||
openTexthookerInBrowser: (url) => {
|
||||
calls.push(`openTexthookerInBrowser:${url}`);
|
||||
@@ -354,10 +356,20 @@ test('handleCliCommand runs texthooker flow with browser open', () => {
|
||||
|
||||
handleCliCommand(args, 'initial', deps);
|
||||
|
||||
assert.ok(calls.includes('ensureTexthookerRunning:5174'));
|
||||
assert.ok(calls.includes('ensureTexthookerRunning:5174:'));
|
||||
assert.ok(calls.includes('openTexthookerInBrowser:http://127.0.0.1:5174'));
|
||||
});
|
||||
|
||||
test('handleCliCommand forwards resolved websocket url to texthooker startup', () => {
|
||||
const { deps, calls } = createDeps({
|
||||
getTexthookerWebsocketUrl: () => 'ws://127.0.0.1:6678',
|
||||
});
|
||||
|
||||
handleCliCommand(makeArgs({ texthooker: true }), 'initial', deps);
|
||||
|
||||
assert.ok(calls.includes('ensureTexthookerRunning:5174:ws://127.0.0.1:6678'));
|
||||
});
|
||||
|
||||
test('handleCliCommand reports async mine errors to OSD', async () => {
|
||||
const { deps, calls, osd } = createDeps({
|
||||
mineSentenceCard: async () => {
|
||||
|
||||
@@ -10,8 +10,9 @@ export interface CliCommandServiceDeps {
|
||||
isTexthookerRunning: () => boolean;
|
||||
setTexthookerPort: (port: number) => void;
|
||||
getTexthookerPort: () => number;
|
||||
getTexthookerWebsocketUrl: () => string | undefined;
|
||||
shouldOpenTexthookerBrowser: () => boolean;
|
||||
ensureTexthookerRunning: (port: number) => void;
|
||||
ensureTexthookerRunning: (port: number, websocketUrl?: string) => void;
|
||||
openTexthookerInBrowser: (url: string) => void;
|
||||
stopApp: () => void;
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
@@ -84,7 +85,7 @@ interface MpvClientLike {
|
||||
|
||||
interface TexthookerServiceLike {
|
||||
isRunning: () => boolean;
|
||||
start: (port: number) => void;
|
||||
start: (port: number, websocketUrl?: string) => void;
|
||||
}
|
||||
|
||||
interface MpvCliRuntime {
|
||||
@@ -98,6 +99,7 @@ interface TexthookerCliRuntime {
|
||||
service: TexthookerServiceLike;
|
||||
getPort: () => number;
|
||||
setPort: (port: number) => void;
|
||||
getWebsocketUrl: () => string | undefined;
|
||||
shouldOpenBrowser: () => boolean;
|
||||
openInBrowser: (url: string) => void;
|
||||
}
|
||||
@@ -194,10 +196,11 @@ export function createCliCommandDepsRuntime(
|
||||
isTexthookerRunning: () => options.texthooker.service.isRunning(),
|
||||
setTexthookerPort: options.texthooker.setPort,
|
||||
getTexthookerPort: options.texthooker.getPort,
|
||||
getTexthookerWebsocketUrl: options.texthooker.getWebsocketUrl,
|
||||
shouldOpenTexthookerBrowser: options.texthooker.shouldOpenBrowser,
|
||||
ensureTexthookerRunning: (port) => {
|
||||
ensureTexthookerRunning: (port, websocketUrl) => {
|
||||
if (!options.texthooker.service.isRunning()) {
|
||||
options.texthooker.service.start(port);
|
||||
options.texthooker.service.start(port, websocketUrl);
|
||||
}
|
||||
},
|
||||
openTexthookerInBrowser: options.texthooker.openInBrowser,
|
||||
@@ -473,7 +476,7 @@ export function handleCliCommand(
|
||||
);
|
||||
} else if (args.texthooker) {
|
||||
const texthookerPort = deps.getTexthookerPort();
|
||||
deps.ensureTexthookerRunning(texthookerPort);
|
||||
deps.ensureTexthookerRunning(texthookerPort, deps.getTexthookerWebsocketUrl());
|
||||
if (deps.shouldOpenTexthookerBrowser()) {
|
||||
deps.openTexthookerInBrowser(`http://127.0.0.1:${texthookerPort}`);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,13 @@ interface AppReadyConfigLike {
|
||||
};
|
||||
}
|
||||
|
||||
type TexthookerWebsocketConfigLike = Pick<AppReadyConfigLike, 'annotationWebsocket' | 'websocket'>;
|
||||
|
||||
type TexthookerWebsocketDefaults = {
|
||||
defaultWebsocketPort: number;
|
||||
defaultAnnotationWebsocketPort: number;
|
||||
};
|
||||
|
||||
export interface AppReadyRuntimeDeps {
|
||||
ensureDefaultConfigBootstrap: () => void;
|
||||
loadSubtitlePosition: () => void;
|
||||
@@ -169,6 +176,29 @@ function getStartupCriticalConfigErrors(config: AppReadyConfigLike): string[] {
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function resolveTexthookerWebsocketUrl(
|
||||
config: TexthookerWebsocketConfigLike,
|
||||
defaults: TexthookerWebsocketDefaults,
|
||||
hasMpvWebsocketPlugin: boolean,
|
||||
): string | undefined {
|
||||
const wsConfig = config.websocket || {};
|
||||
const wsEnabled = wsConfig.enabled ?? 'auto';
|
||||
const wsPort = wsConfig.port || defaults.defaultWebsocketPort;
|
||||
const annotationWsConfig = config.annotationWebsocket || {};
|
||||
const annotationWsEnabled = annotationWsConfig.enabled !== false;
|
||||
const annotationWsPort = annotationWsConfig.port || defaults.defaultAnnotationWebsocketPort;
|
||||
|
||||
if (annotationWsEnabled) {
|
||||
return `ws://127.0.0.1:${annotationWsPort}`;
|
||||
}
|
||||
|
||||
if (wsEnabled === true || (wsEnabled === 'auto' && !hasMpvWebsocketPlugin)) {
|
||||
return `ws://127.0.0.1:${wsPort}`;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function shouldAutoInitializeOverlayRuntimeFromConfig(config: RuntimeConfigLike): boolean {
|
||||
return config.auto_start_overlay === true;
|
||||
}
|
||||
@@ -201,12 +231,6 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
return;
|
||||
}
|
||||
|
||||
if (deps.texthookerOnlyMode) {
|
||||
deps.reloadConfig();
|
||||
deps.handleInitialArgs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (deps.shouldUseMinimalStartup?.()) {
|
||||
deps.reloadConfig();
|
||||
deps.handleInitialArgs();
|
||||
@@ -262,7 +286,14 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
const annotationWsEnabled = annotationWsConfig.enabled !== false;
|
||||
const annotationWsPort = annotationWsConfig.port || deps.defaultAnnotationWebsocketPort;
|
||||
const texthookerPort = deps.defaultTexthookerPort;
|
||||
let texthookerWebsocketUrl: string | undefined;
|
||||
const texthookerWebsocketUrl = resolveTexthookerWebsocketUrl(
|
||||
config,
|
||||
{
|
||||
defaultWebsocketPort: deps.defaultWebsocketPort,
|
||||
defaultAnnotationWebsocketPort: deps.defaultAnnotationWebsocketPort,
|
||||
},
|
||||
deps.hasMpvWebsocketPlugin(),
|
||||
);
|
||||
|
||||
if (wsEnabled === true || (wsEnabled === 'auto' && !deps.hasMpvWebsocketPlugin())) {
|
||||
deps.startSubtitleWebsocket(wsPort);
|
||||
@@ -272,9 +303,6 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
|
||||
if (annotationWsEnabled) {
|
||||
deps.startAnnotationWebsocket(annotationWsPort);
|
||||
texthookerWebsocketUrl = `ws://127.0.0.1:${annotationWsPort}`;
|
||||
} else if (wsEnabled === true || (wsEnabled === 'auto' && !deps.hasMpvWebsocketPlugin())) {
|
||||
texthookerWebsocketUrl = `ws://127.0.0.1:${wsPort}`;
|
||||
}
|
||||
|
||||
if (config.texthooker?.launchAtStartup !== false) {
|
||||
|
||||
@@ -75,7 +75,7 @@ function getStartupModeFlags(initialArgs: CliArgs | null | undefined): {
|
||||
} {
|
||||
return {
|
||||
shouldUseMinimalStartup: Boolean(
|
||||
initialArgs?.texthooker ||
|
||||
(initialArgs && isStandaloneTexthookerCommand(initialArgs)) ||
|
||||
(initialArgs?.stats &&
|
||||
(initialArgs.statsCleanup || initialArgs.statsBackground || initialArgs.statsStop)),
|
||||
),
|
||||
@@ -128,6 +128,7 @@ import {
|
||||
commandNeedsOverlayStartupPrereqs,
|
||||
commandNeedsOverlayRuntime,
|
||||
isHeadlessInitialCommand,
|
||||
isStandaloneTexthookerCommand,
|
||||
parseArgs,
|
||||
shouldRunSettingsOnlyStartup,
|
||||
shouldStartApp,
|
||||
@@ -4334,6 +4335,9 @@ const { handleCliCommand, handleInitialArgs } = composeCliStartupHandlers({
|
||||
setLogLevel: (level) => setLogLevel(level, 'cli'),
|
||||
texthookerService,
|
||||
getResolvedConfig: () => getResolvedConfig(),
|
||||
defaultWebsocketPort: DEFAULT_CONFIG.websocket.port,
|
||||
defaultAnnotationWebsocketPort: DEFAULT_CONFIG.annotationWebsocket.port,
|
||||
hasMpvWebsocketPlugin: () => hasMpvWebsocketPlugin(),
|
||||
openExternal: (url: string) => shell.openExternal(url),
|
||||
logBrowserOpenError: (url: string, error: unknown) =>
|
||||
logger.error(`Failed to open browser for texthooker URL: ${url}`, error),
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface CliCommandRuntimeServiceContext {
|
||||
showOsd: CliCommandRuntimeServiceDepsParams['mpv']['showOsd'];
|
||||
getTexthookerPort: () => number;
|
||||
setTexthookerPort: (port: number) => void;
|
||||
getTexthookerWebsocketUrl: () => string | undefined;
|
||||
shouldOpenBrowser: () => boolean;
|
||||
openInBrowser: (url: string) => void;
|
||||
isOverlayInitialized: () => boolean;
|
||||
@@ -71,6 +72,7 @@ function createCliCommandDepsFromContext(
|
||||
service: context.texthookerService,
|
||||
getPort: context.getTexthookerPort,
|
||||
setPort: context.setTexthookerPort,
|
||||
getWebsocketUrl: context.getTexthookerWebsocketUrl,
|
||||
shouldOpenBrowser: context.shouldOpenBrowser,
|
||||
openInBrowser: context.openInBrowser,
|
||||
},
|
||||
|
||||
@@ -132,6 +132,7 @@ export interface CliCommandRuntimeServiceDepsParams {
|
||||
service: CliCommandDepsRuntimeOptions['texthooker']['service'];
|
||||
getPort: CliCommandDepsRuntimeOptions['texthooker']['getPort'];
|
||||
setPort: CliCommandDepsRuntimeOptions['texthooker']['setPort'];
|
||||
getWebsocketUrl: CliCommandDepsRuntimeOptions['texthooker']['getWebsocketUrl'];
|
||||
shouldOpenBrowser: CliCommandDepsRuntimeOptions['texthooker']['shouldOpenBrowser'];
|
||||
openInBrowser: CliCommandDepsRuntimeOptions['texthooker']['openInBrowser'];
|
||||
};
|
||||
@@ -293,6 +294,7 @@ export function createCliCommandRuntimeServiceDeps(
|
||||
service: params.texthooker.service,
|
||||
getPort: params.texthooker.getPort,
|
||||
setPort: params.texthooker.setPort,
|
||||
getWebsocketUrl: params.texthooker.getWebsocketUrl,
|
||||
shouldOpenBrowser: params.texthooker.shouldOpenBrowser,
|
||||
openInBrowser: params.texthooker.openInBrowser,
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ test('build cli command context deps maps handlers and values', () => {
|
||||
texthookerService: { start: () => null, status: () => ({ running: false }) } as never,
|
||||
getTexthookerPort: () => 5174,
|
||||
setTexthookerPort: (port) => calls.push(`port:${port}`),
|
||||
getTexthookerWebsocketUrl: () => 'ws://127.0.0.1:6678',
|
||||
shouldOpenBrowser: () => true,
|
||||
openExternal: async (url) => calls.push(`open:${url}`),
|
||||
logBrowserOpenError: (url) => calls.push(`open-error:${url}`),
|
||||
@@ -82,6 +83,7 @@ test('build cli command context deps maps handlers and values', () => {
|
||||
const deps = buildDeps();
|
||||
assert.equal(deps.getSocketPath(), '/tmp/mpv.sock');
|
||||
assert.equal(deps.getTexthookerPort(), 5174);
|
||||
assert.equal(deps.getTexthookerWebsocketUrl(), 'ws://127.0.0.1:6678');
|
||||
assert.equal(deps.shouldOpenBrowser(), true);
|
||||
assert.equal(deps.isOverlayInitialized(), true);
|
||||
assert.equal(deps.hasMainWindow(), true);
|
||||
|
||||
@@ -10,6 +10,7 @@ export function createBuildCliCommandContextDepsHandler(deps: {
|
||||
texthookerService: CliCommandContextFactoryDeps['texthookerService'];
|
||||
getTexthookerPort: () => number;
|
||||
setTexthookerPort: (port: number) => void;
|
||||
getTexthookerWebsocketUrl: () => string | undefined;
|
||||
shouldOpenBrowser: () => boolean;
|
||||
openExternal: (url: string) => Promise<unknown>;
|
||||
logBrowserOpenError: (url: string, error: unknown) => void;
|
||||
@@ -58,6 +59,7 @@ export function createBuildCliCommandContextDepsHandler(deps: {
|
||||
texthookerService: deps.texthookerService,
|
||||
getTexthookerPort: deps.getTexthookerPort,
|
||||
setTexthookerPort: deps.setTexthookerPort,
|
||||
getTexthookerWebsocketUrl: deps.getTexthookerWebsocketUrl,
|
||||
shouldOpenBrowser: deps.shouldOpenBrowser,
|
||||
openExternal: deps.openExternal,
|
||||
logBrowserOpenError: deps.logBrowserOpenError,
|
||||
|
||||
@@ -14,7 +14,13 @@ test('cli command context factory composes main deps and context handlers', () =
|
||||
const createContext = createCliCommandContextFactory({
|
||||
appState,
|
||||
texthookerService: { isRunning: () => false, start: () => null },
|
||||
getResolvedConfig: () => ({ texthooker: { openBrowser: true } }),
|
||||
getResolvedConfig: () => ({
|
||||
texthooker: { openBrowser: true },
|
||||
annotationWebsocket: { enabled: true, port: 6678 },
|
||||
}),
|
||||
defaultWebsocketPort: 6677,
|
||||
defaultAnnotationWebsocketPort: 6678,
|
||||
hasMpvWebsocketPlugin: () => false,
|
||||
openExternal: async () => {},
|
||||
logBrowserOpenError: () => {},
|
||||
showMpvOsd: (text) => calls.push(`osd:${text}`),
|
||||
|
||||
@@ -14,7 +14,13 @@ test('cli command context main deps builder maps state and callbacks', async ()
|
||||
const build = createBuildCliCommandContextMainDepsHandler({
|
||||
appState,
|
||||
texthookerService: { isRunning: () => false, start: () => null },
|
||||
getResolvedConfig: () => ({ texthooker: { openBrowser: true } }),
|
||||
getResolvedConfig: () => ({
|
||||
texthooker: { openBrowser: true },
|
||||
annotationWebsocket: { enabled: true, port: 6678 },
|
||||
}),
|
||||
defaultWebsocketPort: 6677,
|
||||
defaultAnnotationWebsocketPort: 6678,
|
||||
hasMpvWebsocketPlugin: () => false,
|
||||
openExternal: async (url) => {
|
||||
calls.push(`open:${url}`);
|
||||
},
|
||||
@@ -110,6 +116,7 @@ test('cli command context main deps builder maps state and callbacks', async ()
|
||||
assert.equal(deps.getTexthookerPort(), 5174);
|
||||
deps.setTexthookerPort(5175);
|
||||
assert.equal(appState.texthookerPort, 5175);
|
||||
assert.equal(deps.getTexthookerWebsocketUrl(), 'ws://127.0.0.1:6678');
|
||||
assert.equal(deps.shouldOpenBrowser(), true);
|
||||
deps.showOsd('hello');
|
||||
deps.initializeOverlay();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CliArgs } from '../../cli/args';
|
||||
import { resolveTexthookerWebsocketUrl } from '../../core/services/startup';
|
||||
import type { CliCommandContextFactoryDeps } from './cli-command-context';
|
||||
|
||||
type CliCommandContextMainState = {
|
||||
@@ -12,7 +13,14 @@ export function createBuildCliCommandContextMainDepsHandler(deps: {
|
||||
appState: CliCommandContextMainState;
|
||||
setLogLevel?: (level: NonNullable<CliArgs['logLevel']>) => void;
|
||||
texthookerService: CliCommandContextFactoryDeps['texthookerService'];
|
||||
getResolvedConfig: () => { texthooker?: { openBrowser?: boolean } };
|
||||
getResolvedConfig: () => {
|
||||
texthooker?: { openBrowser?: boolean };
|
||||
websocket?: { enabled?: boolean | 'auto'; port?: number };
|
||||
annotationWebsocket?: { enabled?: boolean; port?: number };
|
||||
};
|
||||
defaultWebsocketPort: number;
|
||||
defaultAnnotationWebsocketPort: number;
|
||||
hasMpvWebsocketPlugin: () => boolean;
|
||||
openExternal: (url: string) => Promise<unknown>;
|
||||
logBrowserOpenError: (url: string, error: unknown) => void;
|
||||
showMpvOsd: (text: string) => void;
|
||||
@@ -68,6 +76,15 @@ export function createBuildCliCommandContextMainDepsHandler(deps: {
|
||||
setTexthookerPort: (port: number) => {
|
||||
deps.appState.texthookerPort = port;
|
||||
},
|
||||
getTexthookerWebsocketUrl: () =>
|
||||
resolveTexthookerWebsocketUrl(
|
||||
deps.getResolvedConfig(),
|
||||
{
|
||||
defaultWebsocketPort: deps.defaultWebsocketPort,
|
||||
defaultAnnotationWebsocketPort: deps.defaultAnnotationWebsocketPort,
|
||||
},
|
||||
deps.hasMpvWebsocketPlugin(),
|
||||
),
|
||||
shouldOpenBrowser: () => deps.getResolvedConfig().texthooker?.openBrowser !== false,
|
||||
openExternal: (url: string) => deps.openExternal(url),
|
||||
logBrowserOpenError: (url: string, error: unknown) => deps.logBrowserOpenError(url, error),
|
||||
|
||||
@@ -18,6 +18,7 @@ function createDeps() {
|
||||
texthookerService: {} as never,
|
||||
getTexthookerPort: () => 6677,
|
||||
setTexthookerPort: () => {},
|
||||
getTexthookerWebsocketUrl: () => 'ws://127.0.0.1:6678',
|
||||
shouldOpenBrowser: () => true,
|
||||
openExternal: async () => {},
|
||||
logBrowserOpenError: (url: string) => browserErrors.push(url),
|
||||
|
||||
@@ -15,6 +15,7 @@ export type CliCommandContextFactoryDeps = {
|
||||
texthookerService: CliCommandRuntimeServiceContextHandlers['texthookerService'];
|
||||
getTexthookerPort: () => number;
|
||||
setTexthookerPort: (port: number) => void;
|
||||
getTexthookerWebsocketUrl: () => string | undefined;
|
||||
shouldOpenBrowser: () => boolean;
|
||||
openExternal: (url: string) => Promise<unknown>;
|
||||
logBrowserOpenError: (url: string, error: unknown) => void;
|
||||
@@ -67,6 +68,7 @@ export function createCliCommandContext(
|
||||
texthookerService: deps.texthookerService,
|
||||
getTexthookerPort: deps.getTexthookerPort,
|
||||
setTexthookerPort: deps.setTexthookerPort,
|
||||
getTexthookerWebsocketUrl: deps.getTexthookerWebsocketUrl,
|
||||
shouldOpenBrowser: deps.shouldOpenBrowser,
|
||||
openInBrowser: (url: string) => {
|
||||
void deps.openExternal(url).catch((error) => {
|
||||
|
||||
@@ -11,6 +11,9 @@ test('composeCliStartupHandlers returns callable CLI startup handlers', () => {
|
||||
setLogLevel: () => {},
|
||||
texthookerService: {} as never,
|
||||
getResolvedConfig: () => ({}) as never,
|
||||
defaultWebsocketPort: 6677,
|
||||
defaultAnnotationWebsocketPort: 6678,
|
||||
hasMpvWebsocketPlugin: () => false,
|
||||
openExternal: async () => {},
|
||||
logBrowserOpenError: () => {},
|
||||
showMpvOsd: () => {},
|
||||
|
||||
Reference in New Issue
Block a user