mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 15:25:44 +00:00
fix(export): cancel timed out finalization work
This commit is contained in:
@@ -189,4 +189,24 @@ describe("finalizationTimeout", () => {
|
||||
expect(advanced.lastRenderProgress).toBe(100);
|
||||
expect(advanced.lastAudioProgress).toBe(0.5);
|
||||
});
|
||||
|
||||
it("ignores non-finite render progress without poisoning later updates", () => {
|
||||
const invalid = advanceFinalizationProgress({
|
||||
renderProgress: Number.NaN,
|
||||
audioProgress: 0.25,
|
||||
state: INITIAL_FINALIZATION_PROGRESS_STATE,
|
||||
});
|
||||
expect(invalid.progressed).toBe(true);
|
||||
expect(invalid.lastRenderProgress).toBe(-1);
|
||||
expect(invalid.lastAudioProgress).toBe(0.25);
|
||||
|
||||
const recovered = advanceFinalizationProgress({
|
||||
renderProgress: 99,
|
||||
audioProgress: 0.25,
|
||||
state: invalid,
|
||||
});
|
||||
expect(recovered.progressed).toBe(true);
|
||||
expect(recovered.lastRenderProgress).toBe(99);
|
||||
expect(recovered.lastAudioProgress).toBe(0.25);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,12 +75,18 @@ export function advanceFinalizationProgress({
|
||||
audioProgress?: number;
|
||||
state: FinalizationProgressState;
|
||||
}): FinalizationProgressState & { progressed: boolean } {
|
||||
const normalizedRenderProgress = Math.max(0, Math.min(renderProgress, 100));
|
||||
const normalizedRenderProgress =
|
||||
typeof renderProgress === "number" && Number.isFinite(renderProgress)
|
||||
? Math.max(0, Math.min(renderProgress, 100))
|
||||
: null;
|
||||
const normalizedAudioProgress =
|
||||
typeof audioProgress === "number" && Number.isFinite(audioProgress)
|
||||
? Math.max(0, Math.min(audioProgress, 1))
|
||||
: null;
|
||||
const nextRenderProgress = Math.max(state.lastRenderProgress, normalizedRenderProgress);
|
||||
const nextRenderProgress =
|
||||
normalizedRenderProgress === null
|
||||
? state.lastRenderProgress
|
||||
: Math.max(state.lastRenderProgress, normalizedRenderProgress);
|
||||
const nextAudioProgress =
|
||||
normalizedAudioProgress === null
|
||||
? state.lastAudioProgress
|
||||
@@ -122,12 +128,17 @@ export async function withFinalizationTimeout<T>({
|
||||
workload,
|
||||
})
|
||||
: null;
|
||||
const watchdog: FinalizationProgressWatchdog | null =
|
||||
progressAware && idleTimeoutMs !== null && idleTimeoutMs !== undefined
|
||||
? {
|
||||
refreshProgress: () => undefined,
|
||||
}
|
||||
: null;
|
||||
const hasIdleWatchdog =
|
||||
progressAware &&
|
||||
typeof idleTimeoutMs === "number" &&
|
||||
Number.isFinite(idleTimeoutMs) &&
|
||||
idleTimeoutMs >= 0;
|
||||
const resolvedIdleTimeoutMs = hasIdleWatchdog ? idleTimeoutMs : null;
|
||||
const watchdog: FinalizationProgressWatchdog | null = hasIdleWatchdog
|
||||
? {
|
||||
refreshProgress: () => undefined,
|
||||
}
|
||||
: null;
|
||||
|
||||
try {
|
||||
return await Promise.race([
|
||||
@@ -136,16 +147,16 @@ export async function withFinalizationTimeout<T>({
|
||||
const rejectWithMessage = (message: string) => {
|
||||
reject(new Error(message));
|
||||
};
|
||||
if (watchdog && idleTimeoutMs !== null) {
|
||||
if (watchdog && resolvedIdleTimeoutMs !== null) {
|
||||
const refreshProgress = () => {
|
||||
if (idleTimeoutId) {
|
||||
clearTimeout(idleTimeoutId);
|
||||
}
|
||||
idleTimeoutId = setTimeout(() => {
|
||||
rejectWithMessage(
|
||||
`Export timed out during ${stage} after ${Math.ceil(idleTimeoutMs / 1000)} seconds without observable progress`,
|
||||
`Export timed out during ${stage} after ${Math.ceil(resolvedIdleTimeoutMs / 1000)} seconds without observable progress`,
|
||||
);
|
||||
}, idleTimeoutMs);
|
||||
}, resolvedIdleTimeoutMs);
|
||||
};
|
||||
watchdog.refreshProgress = refreshProgress;
|
||||
onWatchdogChanged?.(watchdog);
|
||||
|
||||
@@ -985,7 +985,6 @@ export class ModernVideoExporter {
|
||||
}
|
||||
|
||||
const sessionId = this.nativeExportSessionId;
|
||||
this.nativeExportSessionId = null;
|
||||
console.log(`[VideoExporter] Finalizing ${NATIVE_EXPORT_ENGINE_NAME} export`, {
|
||||
sessionId,
|
||||
audioMode: audioPlan.audioMode,
|
||||
@@ -1009,6 +1008,7 @@ export class ModernVideoExporter {
|
||||
`${NATIVE_EXPORT_ENGINE_NAME} export finalization`,
|
||||
audioPlan.audioMode === "none" ? "default" : "audio",
|
||||
);
|
||||
this.nativeExportSessionId = null;
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
|
||||
@@ -710,8 +710,6 @@ export class VideoExporter {
|
||||
}
|
||||
|
||||
const sessionId = this.nativeExportSessionId;
|
||||
this.nativeExportSessionId = null;
|
||||
|
||||
const result = await this.awaitWithFinalizationTimeout(
|
||||
window.electronAPI.nativeVideoExportFinish(sessionId, {
|
||||
audioMode: audioPlan.audioMode,
|
||||
@@ -727,6 +725,7 @@ export class VideoExporter {
|
||||
"native export finalization",
|
||||
audioPlan.audioMode === "none" ? "default" : "audio",
|
||||
);
|
||||
this.nativeExportSessionId = null;
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return {
|
||||
@@ -1152,6 +1151,7 @@ export class VideoExporter {
|
||||
}
|
||||
|
||||
this.muxer = null;
|
||||
this.audioProcessor?.cancel();
|
||||
this.audioProcessor = null;
|
||||
this.activeFinalizationProgressWatchdog = null;
|
||||
this.lastFinalizationRenderProgress =
|
||||
|
||||
Reference in New Issue
Block a user