mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-11-10 04:46:07 -08:00
refactor(main): drop pass-through main-deps builders (#306)
* refactor(main): drop mining/anki main-deps builders * refactor(anilist): drop main-deps builders * refactor(jellyfin): drop main-deps builders * refactor(overlay): drop overlay and shortcut main-deps builders * refactor(main): drop ipc, tray, and osd main-deps builders * refactor(startup): drop startup main-deps builders * test(scripts): ignore stray local requests in AUR download test Also apply prettier to launcher/mpv.test.ts so format:check:src passes. * fix(shortcuts): forward overlay numeric selection hooks through the composer composeShortcutRuntimes only passed the digit callbacks, so the visible-overlay selection added in #78 never ran from the multi-copy/mine-sentence shortcuts. * fix(stats): skip mpv auto-connect when stats starts the immersion tracker The immersion-startup main-deps builder never forwarded shouldAutoConnectMpv, so background stats and stats CLI startup always connected to mpv. Drop the builder and pass the deps straight to the handler. * build(deps): bump follow-redirects override to 1.16.2 Resolves GHSA-mrwp-f3q8-3g9p (high), which fails the quality-gate bun audit --audit-level high step. * test(subtitles): cover the null-tokenization settle path in prefetch test The mock ignored the value the test resolved, so the null case never ran. * test(e2e): surface mpv crashes and install current mpv on macOS CI The harness IPC client never handled socket close, so a crashed mpv left commands pending until the 120s test timeout with an empty mpv.log. Reject pending commands on close, write mpv's own log via --log-file, and refresh Homebrew on macOS CI so the installed mpv build no longer depends on the runner image. * test(e2e): render mpv through cocoa-cb OpenGL on macOS mpv 0.41.0_12 with libplacebo 7.387.0 segfaults in gpu-next's MoltenVK path on GitHub's paravirtual macOS GPU on the first frame, so the E2E session never started. * test(e2e): name mpv-stdio.log in the mpv close diagnostic
This commit is contained in:
1 parent
0129e03c0f
commit
2a0e931325
84 files changed
+719
-2754
No files matched your search
+39
-4
@@ -15,25 +15,38 @@ export type MpvClient = {
|
||||
|
||||
type MpvReply = { request_id?: number; error?: string; data?: unknown };
|
||||
|
||||
function videoOutputArgs(display: E2eDisplay): string[] {
|
||||
// Xvfb has no GPU; the software X11 output always works there.
|
||||
if (display.kind === 'xvfb') return ['--vo=x11'];
|
||||
// gpu-next's Vulkan path (libplacebo over MoltenVK) segfaults on the first frame
|
||||
// on GitHub's paravirtual macOS GPU with libplacebo 7.387. `--vo=libmpv` is mpv's
|
||||
// cocoa-cb OpenGL output, which avoids it and still opens the window the overlay tracks.
|
||||
if (process.platform === 'darwin') return ['--vo=libmpv'];
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function startMpv(options: {
|
||||
socketPath: string;
|
||||
mediaPath: string;
|
||||
subtitlePath: string;
|
||||
/** mpv's own verbose log (`--log-file`); `--no-terminal` silences its console output. */
|
||||
logPath: string;
|
||||
/** Process stdout/stderr, which still catches crash output from mpv's libraries. */
|
||||
stdioLogPath: string;
|
||||
display: E2eDisplay;
|
||||
}): Promise<ChildProcess> {
|
||||
const log = fs.openSync(options.logPath, 'w');
|
||||
const log = fs.openSync(options.stdioLogPath, 'w');
|
||||
const child = spawn(
|
||||
'mpv',
|
||||
[
|
||||
'--no-config',
|
||||
'--no-terminal',
|
||||
`--log-file=${options.logPath}`,
|
||||
'--pause',
|
||||
'--keep-open=yes',
|
||||
'--ao=null',
|
||||
`--geometry=${E2E_SCREEN.width}x${E2E_SCREEN.height}+0+0`,
|
||||
// Xvfb has no GPU; the software X11 output always works there.
|
||||
...(options.display.kind === 'xvfb' ? ['--vo=x11'] : []),
|
||||
...videoOutputArgs(options.display),
|
||||
`--input-ipc-server=${options.socketPath}`,
|
||||
`--sub-file=${options.subtitlePath}`,
|
||||
options.mediaPath,
|
||||
@@ -58,13 +71,31 @@ function tryConnect(socketPath: string): Promise<net.Socket | null> {
|
||||
});
|
||||
}
|
||||
|
||||
export async function connectMpv(socketPath: string): Promise<MpvClient> {
|
||||
/**
|
||||
* Connects to mpv's JSON IPC socket. Once the socket closes (mpv exited or
|
||||
* crashed), pending and later commands reject instead of hanging until the test
|
||||
* timeout; `describeClose` adds context such as mpv's exit status.
|
||||
*/
|
||||
export async function connectMpv(
|
||||
socketPath: string,
|
||||
describeClose: () => string = () => 'mpv IPC connection closed',
|
||||
): Promise<MpvClient> {
|
||||
const socket = await waitUntil(() => tryConnect(socketPath), {
|
||||
description: `mpv IPC socket ${socketPath}`,
|
||||
});
|
||||
const pending = new Map<number, (reply: MpvReply) => void>();
|
||||
let nextRequestId = 1;
|
||||
let buffer = '';
|
||||
let closedError: Error | null = null;
|
||||
|
||||
const failPending = () => {
|
||||
closedError ??= new Error(describeClose());
|
||||
for (const settle of pending.values()) settle({ error: closedError.message });
|
||||
pending.clear();
|
||||
};
|
||||
socket.on('close', failPending);
|
||||
// 'close' always follows 'error'; listening here keeps the error from being thrown.
|
||||
socket.on('error', () => {});
|
||||
|
||||
socket.setEncoding('utf8');
|
||||
socket.on('data', (chunk: string) => {
|
||||
@@ -83,6 +114,10 @@ export async function connectMpv(socketPath: string): Promise<MpvClient> {
|
||||
return {
|
||||
command: (...args) =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (closedError) {
|
||||
reject(new Error(`mpv ${JSON.stringify(args)} failed: ${closedError.message}`));
|
||||
return;
|
||||
}
|
||||
const requestId = nextRequestId++;
|
||||
pending.set(requestId, (reply) => {
|
||||
if (reply.error === 'success') resolve(reply.data);
|
||||
|
||||
+12
-2
@@ -265,15 +265,25 @@ export async function startE2eSession(options: E2eSessionOptions = {}): Promise<
|
||||
settings.close();
|
||||
await stopProcess(seeder.child);
|
||||
|
||||
const mpvLogPath = path.join(root, 'mpv.log');
|
||||
const mpvStdioLogPath = path.join(root, 'mpv-stdio.log');
|
||||
const mpvProcess = await startMpv({
|
||||
socketPath: mpvSocketPath,
|
||||
mediaPath,
|
||||
subtitlePath,
|
||||
logPath: path.join(root, 'mpv.log'),
|
||||
logPath: mpvLogPath,
|
||||
stdioLogPath: mpvStdioLogPath,
|
||||
display,
|
||||
});
|
||||
teardown.push(() => stopProcess(mpvProcess));
|
||||
const mpv = await connectMpv(mpvSocketPath);
|
||||
const mpv = await connectMpv(mpvSocketPath, () => {
|
||||
const { exitCode, signalCode } = mpvProcess;
|
||||
const status =
|
||||
exitCode === null && signalCode === null
|
||||
? 'mpv IPC connection closed'
|
||||
: `mpv exited (code ${String(exitCode)}, signal ${String(signalCode)})`;
|
||||
return `${status}; see ${mpvLogPath} and ${mpvStdioLogPath}`;
|
||||
});
|
||||
teardown.push(mpv.close);
|
||||
|
||||
const app = await launchApp(['--start']);
|
||||
|
||||
Reference in new issue
Block a user