diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 4cb4875..6aae4b4 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -490,7 +490,24 @@ export function registerIpcHandlers( return { success: false, message: "No recorded video found" }; } - const latestVideo = videoFiles.sort().reverse()[0]; + // Sort by most recently modified to reliably get the latest recording. + // Lexicographic sort is unreliable (e.g. recording-9.webm > recording-10.webm). + let latestVideo: string | null = null; + let latestMtimeMs = 0; + for (const file of videoFiles) { + try { + const stat = await fs.stat(path.join(RECORDINGS_DIR, file)); + if (stat.mtimeMs > latestMtimeMs) { + latestMtimeMs = stat.mtimeMs; + latestVideo = file; + } + } catch { + // Skip inaccessible files. + } + } + if (!latestVideo) { + return { success: false, message: "No recorded video found" }; + } const videoPath = path.join(RECORDINGS_DIR, latestVideo); return { success: true, path: videoPath }; @@ -616,6 +633,18 @@ export function registerIpcHandlers( ipcMain.handle("open-external-url", async (_, url: string) => { try { + const ALLOWED_SCHEMES = ["http:", "https:", "mailto:"]; + let parsed: URL; + try { + parsed = new URL(url); + } catch { + return { success: false, error: "Invalid URL" }; + } + + if (!ALLOWED_SCHEMES.includes(parsed.protocol)) { + return { success: false, error: `Unsupported URL scheme: ${parsed.protocol}` }; + } + await shell.openExternal(url); return { success: true }; } catch (error) { diff --git a/src/lib/exporter/audioEncoder.ts b/src/lib/exporter/audioEncoder.ts index 490eed2..fba3568 100644 --- a/src/lib/exporter/audioEncoder.ts +++ b/src/lib/exporter/audioEncoder.ts @@ -459,7 +459,10 @@ export class AudioProcessor { } private cloneWithTimestamp(src: AudioData, newTimestamp: number): AudioData { - const isPlanar = src.format?.includes("planar") ?? false; + if (!src.format) { + throw new Error("AudioData format is required for cloning"); + } + const isPlanar = src.format.includes("planar"); const numPlanes = isPlanar ? src.numberOfChannels : 1; let totalSize = 0; @@ -476,7 +479,7 @@ export class AudioProcessor { } return new AudioData({ - format: src.format!, + format: src.format, sampleRate: src.sampleRate, numberOfFrames: src.numberOfFrames, numberOfChannels: src.numberOfChannels, diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index d0affd1..2c6ea8d 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -422,7 +422,7 @@ export class VideoExporter { })(); this.muxingPromises.push(muxingPromise); - this.encodeQueue--; + this.encodeQueue = Math.max(0, this.encodeQueue - 1); }, error: (error) => { console.error("[VideoExporter] Encoder error:", error);