refactor mpv reconnect scheduling into transport layer

This commit is contained in:
2026-02-14 15:13:07 -08:00
parent c432f35a91
commit bf1a866f2f
3 changed files with 89 additions and 13 deletions

View File

@@ -14,7 +14,9 @@ import {
splitMpvMessagesFromBuffer, splitMpvMessagesFromBuffer,
} from "./mpv-protocol"; } from "./mpv-protocol";
import { requestMpvInitialState, subscribeToMpvProperties } from "./mpv-properties"; import { requestMpvInitialState, subscribeToMpvProperties } from "./mpv-properties";
import { getMpvReconnectDelay } from "./mpv-transport"; import {
scheduleMpvReconnect,
} from "./mpv-transport";
export { export {
MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY, MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY,
@@ -192,20 +194,20 @@ export class MpvIpcClient implements MpvClient {
} }
private scheduleReconnect(): void { private scheduleReconnect(): void {
const reconnectTimer = this.deps.getReconnectTimer(); this.reconnectAttempt = scheduleMpvReconnect({
if (reconnectTimer) { attempt: this.reconnectAttempt,
clearTimeout(reconnectTimer); hasConnectedOnce: this.hasConnectedOnce,
} getReconnectTimer: () => this.deps.getReconnectTimer(),
const attempt = this.reconnectAttempt++; setReconnectTimer: (timer) => this.deps.setReconnectTimer(timer),
const delay = getMpvReconnectDelay(attempt, this.hasConnectedOnce); onReconnectAttempt: (attempt, delay) => {
this.deps.setReconnectTimer(
setTimeout(() => {
console.log( console.log(
`Attempting to reconnect to MPV (attempt ${attempt + 1}, delay ${delay}ms)...`, `Attempting to reconnect to MPV (attempt ${attempt}, delay ${delay}ms)...`,
); );
},
connect: () => {
this.connect(); this.connect();
}, delay), },
); });
} }
private processBuffer(): void { private processBuffer(): void {

View File

@@ -1,6 +1,9 @@
import test from "node:test"; import test from "node:test";
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { getMpvReconnectDelay } from "./mpv-transport"; import {
getMpvReconnectDelay,
scheduleMpvReconnect,
} from "./mpv-transport";
test("getMpvReconnectDelay follows existing reconnect ramp", () => { test("getMpvReconnectDelay follows existing reconnect ramp", () => {
assert.equal(getMpvReconnectDelay(0, true), 1000); assert.equal(getMpvReconnectDelay(0, true), 1000);
@@ -14,3 +17,48 @@ test("getMpvReconnectDelay follows existing reconnect ramp", () => {
assert.equal(getMpvReconnectDelay(4, false), 1000); assert.equal(getMpvReconnectDelay(4, false), 1000);
assert.equal(getMpvReconnectDelay(6, false), 2000); assert.equal(getMpvReconnectDelay(6, false), 2000);
}); });
test("scheduleMpvReconnect clears existing timer and increments attempt", () => {
const existing = {} as ReturnType<typeof setTimeout>;
const cleared: Array<ReturnType<typeof setTimeout> | null> = [];
const setTimers: Array<ReturnType<typeof setTimeout> | null> = [];
const calls: Array<{ attempt: number; delay: number }> = [];
let connected = 0;
const originalSetTimeout = globalThis.setTimeout;
const originalClearTimeout = globalThis.clearTimeout;
(globalThis as any).setTimeout = (handler: () => void, _delay: number) => {
handler();
return 1 as unknown as ReturnType<typeof setTimeout>;
};
(globalThis as any).clearTimeout = (timer: ReturnType<typeof setTimeout> | null) => {
cleared.push(timer);
};
const nextAttempt = scheduleMpvReconnect({
attempt: 3,
hasConnectedOnce: true,
getReconnectTimer: () => existing,
setReconnectTimer: (timer) => {
setTimers.push(timer);
},
onReconnectAttempt: (attempt, delay) => {
calls.push({ attempt, delay });
},
connect: () => {
connected += 1;
},
});
(globalThis as any).setTimeout = originalSetTimeout;
(globalThis as any).clearTimeout = originalClearTimeout;
assert.equal(nextAttempt, 4);
assert.equal(cleared.length, 1);
assert.equal(cleared[0], existing);
assert.equal(setTimers.length, 1);
assert.equal(calls.length, 1);
assert.equal(calls[0].attempt, 4);
assert.equal(calls[0].delay, getMpvReconnectDelay(3, true));
assert.equal(connected, 1);
});

View File

@@ -26,3 +26,29 @@ export function getMpvReconnectDelay(
} }
return 2000; return 2000;
} }
export interface MpvReconnectSchedulerDeps {
attempt: number;
hasConnectedOnce: boolean;
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
onReconnectAttempt: (attempt: number, delay: number) => void;
connect: () => void;
}
export function scheduleMpvReconnect(
deps: MpvReconnectSchedulerDeps,
): number {
const reconnectTimer = deps.getReconnectTimer();
if (reconnectTimer) {
clearTimeout(reconnectTimer);
}
const delay = getMpvReconnectDelay(deps.attempt, deps.hasConnectedOnce);
deps.setReconnectTimer(
setTimeout(() => {
deps.onReconnectAttempt(deps.attempt + 1, delay);
deps.connect();
}, delay),
);
return deps.attempt + 1;
}