mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-06-09 15:13:32 -07:00
fix(startup): release autoplay gate before first subtitle line
- Send synthetic `__warm__` payload when no current subtitle exists so the gate can release without waiting for a subtitle event that can't fire while paused - Visible-overlay readiness accepts `__warm__` once the overlay is content-ready, rejects it otherwise - Autoplay gate self-retries via scheduled polling when signal target isn't ready, removing reliance on an external flush event - Skip duplicate desktop notification when overlay or startup sequencer already delivered it
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import type { SubtitleData } from '../../types';
|
||||
import { resolveAutoplayReadyMaxReleaseAttempts } from './startup-autoplay-release-policy';
|
||||
|
||||
const PENDING_AUTOPLAY_READY_RETRY_DELAY_MS = 200;
|
||||
const MAX_PENDING_AUTOPLAY_READY_RETRY_ATTEMPTS = 75;
|
||||
|
||||
type MpvClientLike = {
|
||||
connected?: boolean;
|
||||
requestProperty: (property: string) => Promise<unknown>;
|
||||
@@ -34,12 +37,22 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
||||
let autoPlayReadySignalMediaPath: string | null = null;
|
||||
let autoPlayReadySignalGeneration = 0;
|
||||
let pendingAutoplayReadySignal: AutoplayReadySignal | null = null;
|
||||
let pendingAutoplayReadyRetryToken = 0;
|
||||
let pendingAutoplayReadyRetryAttempts = 0;
|
||||
let scheduledPendingAutoplayReadyRetryToken: number | null = null;
|
||||
const now = deps.now ?? (() => Date.now());
|
||||
|
||||
const invalidatePendingAutoplayReadyRetry = (): void => {
|
||||
pendingAutoplayReadyRetryToken += 1;
|
||||
pendingAutoplayReadyRetryAttempts = 0;
|
||||
scheduledPendingAutoplayReadyRetryToken = null;
|
||||
};
|
||||
|
||||
const invalidatePendingAutoplayReadyFallbacks = (): void => {
|
||||
autoPlayReadySignalMediaPath = null;
|
||||
pendingAutoplayReadySignal = null;
|
||||
autoPlayReadySignalGeneration += 1;
|
||||
invalidatePendingAutoplayReadyRetry();
|
||||
};
|
||||
|
||||
const isSignalTargetReady = (signal: AutoplayReadySignal): boolean =>
|
||||
@@ -52,18 +65,43 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
||||
pendingAutoplayReadySignal = null;
|
||||
autoPlayReadySignalMediaPath = getSignalMediaPath();
|
||||
autoPlayReadySignalGeneration += 1;
|
||||
invalidatePendingAutoplayReadyRetry();
|
||||
};
|
||||
|
||||
const setPendingAutoplayReadySignal = (signal: AutoplayReadySignal): void => {
|
||||
const setPendingAutoplayReadySignal = (signal: AutoplayReadySignal): boolean => {
|
||||
if (
|
||||
pendingAutoplayReadySignal &&
|
||||
pendingAutoplayReadySignal.mediaPath === signal.mediaPath &&
|
||||
pendingAutoplayReadySignal.payload.text === signal.payload.text &&
|
||||
pendingAutoplayReadySignal.requestedAtMs <= signal.requestedAtMs
|
||||
) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
pendingAutoplayReadySignal = signal;
|
||||
pendingAutoplayReadyRetryAttempts = 0;
|
||||
return true;
|
||||
};
|
||||
|
||||
const schedulePendingAutoplayReadyRetry = (): void => {
|
||||
if (scheduledPendingAutoplayReadyRetryToken === pendingAutoplayReadyRetryToken) {
|
||||
return;
|
||||
}
|
||||
if (pendingAutoplayReadyRetryAttempts >= MAX_PENDING_AUTOPLAY_READY_RETRY_ATTEMPTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const retryToken = pendingAutoplayReadyRetryToken;
|
||||
pendingAutoplayReadyRetryAttempts += 1;
|
||||
scheduledPendingAutoplayReadyRetryToken = retryToken;
|
||||
deps.schedule(() => {
|
||||
if (scheduledPendingAutoplayReadyRetryToken === retryToken) {
|
||||
scheduledPendingAutoplayReadyRetryToken = null;
|
||||
}
|
||||
if (retryToken !== pendingAutoplayReadyRetryToken || !pendingAutoplayReadySignal) {
|
||||
return;
|
||||
}
|
||||
flushPendingAutoplayReadySignal();
|
||||
}, PENDING_AUTOPLAY_READY_RETRY_DELAY_MS);
|
||||
};
|
||||
|
||||
const releaseAutoplayReadySignal = (signal: AutoplayReadySignal): void => {
|
||||
@@ -139,6 +177,7 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
||||
};
|
||||
|
||||
pendingAutoplayReadySignal = null;
|
||||
invalidatePendingAutoplayReadyRetry();
|
||||
autoPlayReadySignalMediaPath = mediaPath;
|
||||
const playbackGeneration = ++autoPlayReadySignalGeneration;
|
||||
deps.signalPluginAutoplayReady();
|
||||
@@ -152,10 +191,13 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
||||
return;
|
||||
}
|
||||
if (!isSignalTargetReady(signal)) {
|
||||
setPendingAutoplayReadySignal(signal);
|
||||
deps.logDebug(
|
||||
`[autoplay-ready] deferred until signal target is ready for media ${signal.mediaPath}`,
|
||||
);
|
||||
const pendingSignalChanged = setPendingAutoplayReadySignal(signal);
|
||||
schedulePendingAutoplayReadyRetry();
|
||||
if (pendingSignalChanged) {
|
||||
deps.logDebug(
|
||||
`[autoplay-ready] deferred until signal target is ready for media ${signal.mediaPath}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user