mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 03:16:46 -07:00
fix: restore overlay ownership during plugin auto-start
This commit is contained in:
@@ -372,12 +372,9 @@ function M.create(ctx)
|
||||
end)
|
||||
end
|
||||
|
||||
launch_overlay_with_retry(1)
|
||||
if texthooker_enabled then
|
||||
ensure_texthooker_running(function()
|
||||
launch_overlay_with_retry(1)
|
||||
end)
|
||||
else
|
||||
launch_overlay_with_retry(1)
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -481,31 +478,33 @@ function M.create(ctx)
|
||||
state.texthooker_running = false
|
||||
disarm_auto_play_ready_gate()
|
||||
|
||||
ensure_texthooker_running(function()
|
||||
local start_args = build_command_args("start")
|
||||
subminer_log("info", "process", "Starting overlay: " .. table.concat(start_args, " "))
|
||||
local start_args = build_command_args("start")
|
||||
subminer_log("info", "process", "Starting overlay: " .. table.concat(start_args, " "))
|
||||
|
||||
state.overlay_running = true
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = start_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.overlay_running = false
|
||||
subminer_log(
|
||||
"error",
|
||||
"process",
|
||||
"Overlay start failed: " .. (error or (result and result.stderr) or "unknown error")
|
||||
)
|
||||
show_osd("Restart failed")
|
||||
else
|
||||
show_osd("Restarted successfully")
|
||||
end
|
||||
end)
|
||||
state.overlay_running = true
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = start_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.overlay_running = false
|
||||
subminer_log(
|
||||
"error",
|
||||
"process",
|
||||
"Overlay start failed: " .. (error or (result and result.stderr) or "unknown error")
|
||||
)
|
||||
show_osd("Restart failed")
|
||||
else
|
||||
show_osd("Restarted successfully")
|
||||
end
|
||||
end)
|
||||
|
||||
if opts.texthooker_enabled then
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -344,6 +344,27 @@ local function count_start_calls(async_calls)
|
||||
return count
|
||||
end
|
||||
|
||||
local function find_texthooker_call(async_calls)
|
||||
for _, call in ipairs(async_calls) do
|
||||
local args = call.args or {}
|
||||
for i = 1, #args do
|
||||
if args[i] == "--texthooker" then
|
||||
return call
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function find_call_index(async_calls, target_call)
|
||||
for index, call in ipairs(async_calls) do
|
||||
if call == target_call then
|
||||
return index
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function find_control_call(async_calls, flag)
|
||||
for _, call in ipairs(async_calls) do
|
||||
local args = call.args or {}
|
||||
@@ -643,6 +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, "--show-visible-overlay"),
|
||||
"auto-start with visible overlay enabled should include --show-visible-overlay on --start"
|
||||
@@ -655,6 +678,10 @@ 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"
|
||||
|
||||
@@ -176,6 +176,22 @@ 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 () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
texthookerOnlyMode: true,
|
||||
reloadConfig: () => calls.push('reloadConfig'),
|
||||
handleInitialArgs: () => calls.push('handleInitialArgs'),
|
||||
});
|
||||
|
||||
await runAppReadyRuntime(deps);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'ensureDefaultConfigBootstrap',
|
||||
'reloadConfig',
|
||||
'handleInitialArgs',
|
||||
]);
|
||||
});
|
||||
|
||||
test('runAppReadyRuntime skips Jellyfin remote startup when dependency is not wired', async () => {
|
||||
const { deps, calls } = makeDeps({
|
||||
startJellyfinRemoteSession: undefined,
|
||||
|
||||
@@ -200,6 +200,12 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
return;
|
||||
}
|
||||
|
||||
if (deps.texthookerOnlyMode) {
|
||||
deps.reloadConfig();
|
||||
deps.handleInitialArgs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (deps.shouldUseMinimalStartup?.()) {
|
||||
deps.reloadConfig();
|
||||
deps.handleInitialArgs();
|
||||
|
||||
42
src/main.ts
42
src/main.ts
@@ -3037,10 +3037,11 @@ const { appReadyRuntimeRunner } = composeAppReadyRuntime({
|
||||
Boolean(appState.initialArgs && isHeadlessInitialCommand(appState.initialArgs)),
|
||||
shouldUseMinimalStartup: () =>
|
||||
Boolean(
|
||||
appState.initialArgs?.stats &&
|
||||
(appState.initialArgs?.statsCleanup ||
|
||||
appState.initialArgs?.statsBackground ||
|
||||
appState.initialArgs?.statsStop),
|
||||
appState.initialArgs?.texthooker ||
|
||||
(appState.initialArgs?.stats &&
|
||||
(appState.initialArgs?.statsCleanup ||
|
||||
appState.initialArgs?.statsBackground ||
|
||||
appState.initialArgs?.statsStop)),
|
||||
),
|
||||
shouldSkipHeavyStartup: () =>
|
||||
Boolean(
|
||||
@@ -3130,6 +3131,39 @@ void initializeDiscordPresenceService();
|
||||
const handleCliCommand = createCliCommandRuntimeHandler({
|
||||
handleTexthookerOnlyModeTransitionMainDeps: {
|
||||
isTexthookerOnlyMode: () => appState.texthookerOnlyMode,
|
||||
ensureOverlayStartupPrereqs: () => {
|
||||
if (appState.subtitlePosition === null) {
|
||||
loadSubtitlePosition();
|
||||
}
|
||||
if (appState.keybindings.length === 0) {
|
||||
appState.keybindings = resolveKeybindings(getResolvedConfig(), DEFAULT_KEYBINDINGS);
|
||||
}
|
||||
if (!appState.mpvClient) {
|
||||
appState.mpvClient = createMpvClientRuntimeService();
|
||||
}
|
||||
if (!appState.runtimeOptionsManager) {
|
||||
appState.runtimeOptionsManager = new RuntimeOptionsManager(
|
||||
() => configService.getConfig().ankiConnect,
|
||||
{
|
||||
applyAnkiPatch: (patch) => {
|
||||
if (appState.ankiIntegration) {
|
||||
appState.ankiIntegration.applyRuntimeConfigPatch(patch);
|
||||
}
|
||||
},
|
||||
getSubtitleStyleConfig: () => configService.getConfig().subtitleStyle,
|
||||
onOptionsChanged: () => {
|
||||
subtitleProcessingController.invalidateTokenizationCache();
|
||||
subtitlePrefetchService?.onSeek(lastObservedTimePos);
|
||||
broadcastRuntimeOptionsChanged();
|
||||
refreshOverlayShortcuts();
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
if (!appState.subtitleTimingTracker) {
|
||||
appState.subtitleTimingTracker = new SubtitleTimingTracker();
|
||||
}
|
||||
},
|
||||
setTexthookerOnlyMode: (enabled) => {
|
||||
appState.texthookerOnlyMode = enabled;
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ test('cli prechecks main deps builder maps transition handlers', () => {
|
||||
isTexthookerOnlyMode: () => true,
|
||||
setTexthookerOnlyMode: (enabled) => calls.push(`set:${enabled}`),
|
||||
commandNeedsOverlayRuntime: () => true,
|
||||
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
|
||||
startBackgroundWarmups: () => calls.push('warmups'),
|
||||
logInfo: (message) => calls.push(`info:${message}`),
|
||||
})();
|
||||
@@ -15,7 +16,8 @@ test('cli prechecks main deps builder maps transition handlers', () => {
|
||||
assert.equal(deps.isTexthookerOnlyMode(), true);
|
||||
assert.equal(deps.commandNeedsOverlayRuntime({} as never), true);
|
||||
deps.setTexthookerOnlyMode(false);
|
||||
deps.ensureOverlayStartupPrereqs();
|
||||
deps.startBackgroundWarmups();
|
||||
deps.logInfo('x');
|
||||
assert.deepEqual(calls, ['set:false', 'warmups', 'info:x']);
|
||||
assert.deepEqual(calls, ['set:false', 'prereqs', 'warmups', 'info:x']);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(dep
|
||||
isTexthookerOnlyMode: () => boolean;
|
||||
setTexthookerOnlyMode: (enabled: boolean) => void;
|
||||
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
|
||||
ensureOverlayStartupPrereqs: () => void;
|
||||
startBackgroundWarmups: () => void;
|
||||
logInfo: (message: string) => void;
|
||||
}) {
|
||||
@@ -11,6 +12,7 @@ export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(dep
|
||||
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
|
||||
setTexthookerOnlyMode: (enabled: boolean) => deps.setTexthookerOnlyMode(enabled),
|
||||
commandNeedsOverlayRuntime: (args: CliArgs) => deps.commandNeedsOverlayRuntime(args),
|
||||
ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(),
|
||||
startBackgroundWarmups: () => deps.startBackgroundWarmups(),
|
||||
logInfo: (message: string) => deps.logInfo(message),
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ test('texthooker precheck no-ops when mode is disabled', () => {
|
||||
isTexthookerOnlyMode: () => false,
|
||||
setTexthookerOnlyMode: () => {},
|
||||
commandNeedsOverlayRuntime: () => true,
|
||||
ensureOverlayStartupPrereqs: () => {},
|
||||
startBackgroundWarmups: () => {
|
||||
warmups += 1;
|
||||
},
|
||||
@@ -22,12 +23,16 @@ test('texthooker precheck disables mode and warms up on start command', () => {
|
||||
let mode = true;
|
||||
let warmups = 0;
|
||||
let logs = 0;
|
||||
let prereqs = 0;
|
||||
const handlePrecheck = createHandleTexthookerOnlyModeTransitionHandler({
|
||||
isTexthookerOnlyMode: () => mode,
|
||||
setTexthookerOnlyMode: (enabled) => {
|
||||
mode = enabled;
|
||||
},
|
||||
commandNeedsOverlayRuntime: () => false,
|
||||
ensureOverlayStartupPrereqs: () => {
|
||||
prereqs += 1;
|
||||
},
|
||||
startBackgroundWarmups: () => {
|
||||
warmups += 1;
|
||||
},
|
||||
@@ -38,6 +43,7 @@ test('texthooker precheck disables mode and warms up on start command', () => {
|
||||
|
||||
handlePrecheck({ start: true, texthooker: false } as never);
|
||||
assert.equal(mode, false);
|
||||
assert.equal(prereqs, 1);
|
||||
assert.equal(warmups, 1);
|
||||
assert.equal(logs, 1);
|
||||
});
|
||||
@@ -50,6 +56,7 @@ test('texthooker precheck no-ops for texthooker command', () => {
|
||||
mode = enabled;
|
||||
},
|
||||
commandNeedsOverlayRuntime: () => true,
|
||||
ensureOverlayStartupPrereqs: () => {},
|
||||
startBackgroundWarmups: () => {},
|
||||
logInfo: () => {},
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ export function createHandleTexthookerOnlyModeTransitionHandler(deps: {
|
||||
isTexthookerOnlyMode: () => boolean;
|
||||
setTexthookerOnlyMode: (enabled: boolean) => void;
|
||||
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
|
||||
ensureOverlayStartupPrereqs: () => void;
|
||||
startBackgroundWarmups: () => void;
|
||||
logInfo: (message: string) => void;
|
||||
}) {
|
||||
@@ -13,6 +14,7 @@ export function createHandleTexthookerOnlyModeTransitionHandler(deps: {
|
||||
!args.texthooker &&
|
||||
(args.start || deps.commandNeedsOverlayRuntime(args))
|
||||
) {
|
||||
deps.ensureOverlayStartupPrereqs();
|
||||
deps.setTexthookerOnlyMode(false);
|
||||
deps.logInfo('Disabling texthooker-only mode after overlay/start command.');
|
||||
deps.startBackgroundWarmups();
|
||||
|
||||
@@ -9,6 +9,7 @@ test('cli command runtime handler applies precheck and forwards command with con
|
||||
isTexthookerOnlyMode: () => true,
|
||||
setTexthookerOnlyMode: () => calls.push('set-mode'),
|
||||
commandNeedsOverlayRuntime: () => true,
|
||||
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
|
||||
startBackgroundWarmups: () => calls.push('warmups'),
|
||||
logInfo: (message) => calls.push(`log:${message}`),
|
||||
},
|
||||
@@ -24,6 +25,7 @@ test('cli command runtime handler applies precheck and forwards command with con
|
||||
handler({ start: true } as never);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'prereqs',
|
||||
'set-mode',
|
||||
'log:Disabling texthooker-only mode after overlay/start command.',
|
||||
'warmups',
|
||||
|
||||
Reference in New Issue
Block a user