refactor(launcher): consolidate mpv socket readiness primitive

This commit is contained in:
2026-02-21 13:35:55 -08:00
parent a693cc1866
commit 2b77ab2406
7 changed files with 123 additions and 45 deletions

View File

@@ -19,7 +19,6 @@ import {
stopOverlay,
launchTexthookerOnly,
findAppBinary,
waitForSocket,
loadSubtitleIntoMpv,
runAppCommandWithInherit,
launchMpvIdleDetached,
@@ -361,7 +360,7 @@ async function main(): Promise<void> {
});
}
const ready = await waitForSocket(mpvSocketPath);
const ready = await waitForUnixSocketReady(mpvSocketPath, 10000);
const shouldStartOverlay = args.startOverlay || args.autoStartOverlay;
if (shouldStartOverlay) {
if (ready) {

61
launcher/mpv.test.ts Normal file
View File

@@ -0,0 +1,61 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import net from 'node:net';
import { EventEmitter } from 'node:events';
import { waitForUnixSocketReady } from './mpv';
import * as mpvModule from './mpv';
function createTempSocketPath(): { dir: string; socketPath: string } {
const baseDir = path.join(process.cwd(), '.tmp', 'launcher-mpv-tests');
fs.mkdirSync(baseDir, { recursive: true });
const dir = fs.mkdtempSync(path.join(baseDir, 'case-'));
return { dir, socketPath: path.join(dir, 'mpv.sock') };
}
test('mpv module exposes only canonical socket readiness helper', () => {
assert.equal('waitForSocket' in mpvModule, false);
});
test('waitForUnixSocketReady returns false when socket never appears', async () => {
const { dir, socketPath } = createTempSocketPath();
try {
const ready = await waitForUnixSocketReady(socketPath, 120);
assert.equal(ready, false);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('waitForUnixSocketReady returns false when path exists but is not socket', async () => {
const { dir, socketPath } = createTempSocketPath();
try {
fs.writeFileSync(socketPath, 'not-a-socket');
const ready = await waitForUnixSocketReady(socketPath, 200);
assert.equal(ready, false);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('waitForUnixSocketReady returns true when socket becomes connectable before timeout', async () => {
const { dir, socketPath } = createTempSocketPath();
fs.writeFileSync(socketPath, '');
const originalCreateConnection = net.createConnection;
try {
net.createConnection = (() => {
const socket = new EventEmitter() as net.Socket;
socket.destroy = (() => socket) as net.Socket['destroy'];
socket.setTimeout = (() => socket) as net.Socket['setTimeout'];
setTimeout(() => socket.emit('connect'), 25);
return socket;
}) as typeof net.createConnection;
const ready = await waitForUnixSocketReady(socketPath, 400);
assert.equal(ready, true);
} finally {
net.createConnection = originalCreateConnection;
fs.rmSync(dir, { recursive: true, force: true });
}
});

View File

@@ -416,23 +416,6 @@ export async function loadSubtitleIntoMpv(
}
}
export function waitForSocket(socketPath: string, timeoutMs = 10000): Promise<boolean> {
const start = Date.now();
return new Promise((resolve) => {
const timer = setInterval(() => {
if (fs.existsSync(socketPath)) {
clearInterval(timer);
resolve(true);
return;
}
if (Date.now() - start >= timeoutMs) {
clearInterval(timer);
resolve(false);
}
}, 100);
});
}
export function startMpv(
target: string,
targetKind: 'file' | 'url',
@@ -672,19 +655,6 @@ async function sleepMs(ms: number): Promise<void> {
await new Promise<void>((resolve) => setTimeout(resolve, ms));
}
async function waitForPathExists(filePath: string, timeoutMs: number): Promise<boolean> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
try {
if (fs.existsSync(filePath)) return true;
} catch {
// ignore transient fs errors
}
await sleepMs(150);
}
return false;
}
async function canConnectUnixSocket(socketPath: string): Promise<boolean> {
return await new Promise<boolean>((resolve) => {
const socket = net.createConnection(socketPath);
@@ -713,10 +683,13 @@ export async function waitForUnixSocketReady(
): Promise<boolean> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const exists = await waitForPathExists(socketPath, 300);
if (exists) {
const ready = await canConnectUnixSocket(socketPath);
if (ready) return true;
try {
if (fs.existsSync(socketPath)) {
const ready = await canConnectUnixSocket(socketPath);
if (ready) return true;
}
} catch {
// ignore transient fs errors
}
await sleepMs(150);
}