fix: restore webcam sessions and stop export deadlocks

This commit is contained in:
Siddharth
2026-03-17 18:50:05 -07:00
parent 0a0dd088c3
commit b33ec5e2d7
3 changed files with 75 additions and 7 deletions
+57 -5
View File
@@ -68,6 +68,53 @@ function setCurrentRecordingSessionState(session: RecordingSession | null) {
currentRecordingSession = session;
}
function getSessionManifestPathForVideo(videoPath: string) {
const parsed = path.parse(videoPath);
const baseName = parsed.name.endsWith("-webcam")
? parsed.name.slice(0, -"-webcam".length)
: parsed.name;
return path.join(parsed.dir, `${baseName}${RECORDING_SESSION_SUFFIX}`);
}
async function loadRecordedSessionForVideoPath(
videoPath: string,
): Promise<RecordingSession | null> {
const normalizedVideoPath = normalizeVideoSourcePath(videoPath);
if (!normalizedVideoPath) {
return null;
}
try {
const manifestPath = getSessionManifestPathForVideo(normalizedVideoPath);
const content = await fs.readFile(manifestPath, "utf-8");
const session = normalizeRecordingSession(JSON.parse(content));
if (!session) {
return null;
}
const normalizedSession: RecordingSession = {
...session,
screenVideoPath: normalizeVideoSourcePath(session.screenVideoPath) ?? session.screenVideoPath,
...(session.webcamVideoPath
? {
webcamVideoPath:
normalizeVideoSourcePath(session.webcamVideoPath) ?? session.webcamVideoPath,
}
: {}),
};
const targetPath = normalizePath(normalizedVideoPath);
const screenMatches = normalizePath(normalizedSession.screenVideoPath) === targetPath;
const webcamMatches = normalizedSession.webcamVideoPath
? normalizePath(normalizedSession.webcamVideoPath) === targetPath
: false;
return screenMatches || webcamMatches ? normalizedSession : null;
} catch {
return null;
}
}
async function storeRecordedSessionFiles(payload: StoreRecordedSessionInput) {
const createdAt =
typeof payload.createdAt === "number" && Number.isFinite(payload.createdAt)
@@ -675,11 +722,16 @@ export function registerIpcHandlers(
: { success: false };
});
ipcMain.handle("set-current-video-path", (_, path: string) => {
setCurrentRecordingSessionState({
screenVideoPath: normalizeVideoSourcePath(path) ?? path,
createdAt: Date.now(),
});
ipcMain.handle("set-current-video-path", async (_, path: string) => {
const restoredSession = await loadRecordedSessionForVideoPath(path);
if (restoredSession) {
setCurrentRecordingSessionState(restoredSession);
} else {
setCurrentRecordingSessionState({
screenVideoPath: normalizeVideoSourcePath(path) ?? path,
createdAt: Date.now(),
});
}
currentProjectPath = null;
return { success: true };
});
+9 -1
View File
@@ -167,6 +167,7 @@ export class GifExporter {
let frameIndex = 0;
webcamFrameQueue = this.config.webcamVideoUrl ? new AsyncVideoFrameQueue() : null;
let stopWebcamDecode = false;
let webcamDecodeError: Error | null = null;
const webcamDecodePromise =
this.webcamDecoder && webcamFrameQueue
@@ -178,9 +179,13 @@ export class GifExporter {
this.config.trimRegions,
this.config.speedRegions,
async (webcamFrame) => {
while (queue.length >= 12 && !this.cancelled) {
while (queue.length >= 12 && !this.cancelled && !stopWebcamDecode) {
await new Promise((resolve) => setTimeout(resolve, 2));
}
if (this.cancelled || stopWebcamDecode) {
webcamFrame.close();
return;
}
queue.enqueue(webcamFrame);
},
)
@@ -248,6 +253,9 @@ export class GifExporter {
return { success: false, error: "Export cancelled" };
}
stopWebcamDecode = true;
webcamFrameQueue?.destroy();
this.webcamDecoder?.cancel();
await webcamDecodePromise;
// Update progress to show we're now in the finalizing phase
+9 -1
View File
@@ -118,6 +118,7 @@ export class VideoExporter {
const frameDuration = 1_000_000 / this.config.frameRate; // in microseconds
let frameIndex = 0;
webcamFrameQueue = this.config.webcamVideoUrl ? new AsyncVideoFrameQueue() : null;
let stopWebcamDecode = false;
let webcamDecodeError: Error | null = null;
const webcamDecodePromise =
this.webcamDecoder && webcamFrameQueue
@@ -129,9 +130,13 @@ export class VideoExporter {
this.config.trimRegions,
this.config.speedRegions,
async (webcamFrame) => {
while (queue.length >= 12 && !this.cancelled) {
while (queue.length >= 12 && !this.cancelled && !stopWebcamDecode) {
await new Promise((resolve) => setTimeout(resolve, 2));
}
if (this.cancelled || stopWebcamDecode) {
webcamFrame.close();
return;
}
queue.enqueue(webcamFrame);
},
)
@@ -229,6 +234,9 @@ export class VideoExporter {
return { success: false, error: "Export cancelled" };
}
stopWebcamDecode = true;
webcamFrameQueue?.destroy();
this.webcamDecoder?.cancel();
await webcamDecodePromise;
// Finalize encoding