diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 6eb2d48..71da40a 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -639,16 +639,15 @@ export function registerIpcHandlers( } }); -/** - * Handles saving an exported video file. - * Shows a save dialog, normalizes the file path for the current OS, - * ensures the directory exists, and writes the video data. - * @param _ - Unused event parameter. - * @param videoData - The exported video as an ArrayBuffer. - * @param fileName - Suggested filename for the save dialog. - * @returns Object with success status, optional file path, and error details. - */ - + /** + * Handles saving an exported video file. + * Shows a save dialog, normalizes the file path for the current OS, + * ensures the directory exists, and writes the video data. + * @param _ - Unused event parameter. + * @param videoData - The exported video as an ArrayBuffer. + * @param fileName - Suggested filename for the save dialog. + * @returns Object with success status, optional file path, and error details. + */ ipcMain.handle("save-exported-video", async (_, videoData: ArrayBuffer, fileName: string) => { try { diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index ffe3add..98bdcae 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -320,7 +320,6 @@ export default function VideoEditor() { aspectRatio, webcamLayoutPreset, webcamMaskShape, - webcamSizePreset, webcamPosition, exportQuality, exportFormat, diff --git a/src/components/video-editor/timeline/TimelineEditor.tsx b/src/components/video-editor/timeline/TimelineEditor.tsx index 5ef8c7b..b1254b5 100644 --- a/src/components/video-editor/timeline/TimelineEditor.tsx +++ b/src/components/video-editor/timeline/TimelineEditor.tsx @@ -1313,7 +1313,6 @@ export default function TimelineEditor({ selectedBlurId, selectedSpeedId, annotationRegions, - blurRegions, currentTime, onSelectAnnotation, keyShortcuts, diff --git a/src/lib/exporter/streamingDecoder.ts b/src/lib/exporter/streamingDecoder.ts index 6001cac..ca1406e 100644 --- a/src/lib/exporter/streamingDecoder.ts +++ b/src/lib/exporter/streamingDecoder.ts @@ -217,15 +217,15 @@ export class StreamingVideoDecoder { return this.metadata; } -/** - * Decodes all video frames from the loaded source and invokes a callback for each. - * Handles trimming and speed adjustments, and resamples to the target frame rate. - * On Windows, early decode termination is tolerated to work around driver quirks. - * @param targetFrameRate - Desired output frame rate. - * @param trimRegions - Array of time regions to keep (others discarded). - * @param speedRegions - Array of speed adjustments for specific time ranges. - * @param onFrame - Async callback receiving each decoded VideoFrame. - */ + /** + * Decodes all video frames from the loaded source and invokes a callback for each. + * Handles trimming and speed adjustments, and resamples to the target frame rate. + * On Windows, early decode termination is tolerated to work around driver quirks. + * @param targetFrameRate - Desired output frame rate. + * @param trimRegions - Array of time regions to keep (others discarded). + * @param speedRegions - Array of speed adjustments for specific time ranges. + * @param onFrame - Async callback receiving each decoded VideoFrame. + */ async decodeAll( targetFrameRate: number, trimRegions: TrimRegion[] | undefined, @@ -501,31 +501,35 @@ export class StreamingVideoDecoder { } this.decoder = null; - const isWindows = typeof navigator !== "undefined" && /Windows/.test(navigator.userAgent); + const isWindows = typeof navigator !== "undefined" && /Windows/.test(navigator.userAgent); -if (shouldFailDecodeEndedEarly({ - cancelled: this.cancelled, - lastDecodedFrameSec, - requiredEndSec, - streamDurationSec: this.metadata.streamDuration, -})) { - const decodedAtLabel = lastDecodedFrameSec === null ? "no decoded frame" : `${lastDecodedFrameSec.toFixed(3)}s`; - const decodeGapSec = lastDecodedFrameSec === null ? Infinity : requiredEndSec - lastDecodedFrameSec; + if ( + shouldFailDecodeEndedEarly({ + cancelled: this.cancelled, + lastDecodedFrameSec, + requiredEndSec, + streamDurationSec: this.metadata.streamDuration, + }) + ) { + const decodedAtLabel = + lastDecodedFrameSec === null ? "no decoded frame" : `${lastDecodedFrameSec.toFixed(3)}s`; + const decodeGapSec = + lastDecodedFrameSec === null ? Infinity : requiredEndSec - lastDecodedFrameSec; - // On Windows, tolerate a small decode gap: up to 10% of required duration, capped at 3 seconds. - const maxToleratedGap = Math.min(3.0, requiredEndSec * 0.1); + // On Windows, tolerate a small decode gap: up to 10% of required duration, capped at 3 seconds. + const maxToleratedGap = Math.min(3.0, requiredEndSec * 0.1); - if (isWindows && lastDecodedFrameSec !== null && decodeGapSec <= maxToleratedGap) { - console.warn( - `[StreamingVideoDecoder] Decode ended early on Windows with a gap of ${decodeGapSec.toFixed(2)}s ` + - `(max tolerated: ${maxToleratedGap.toFixed(2)}s) – proceeding anyway.`, - ); - } else { - throw new Error( - `Video decode ended early at ${decodedAtLabel} (needed ${requiredEndSec.toFixed(3)}s).`, - ); - } -} + if (isWindows && lastDecodedFrameSec !== null && decodeGapSec <= maxToleratedGap) { + console.warn( + `[StreamingVideoDecoder] Decode ended early on Windows with a gap of ${decodeGapSec.toFixed(2)}s ` + + `(max tolerated: ${maxToleratedGap.toFixed(2)}s) – proceeding anyway.`, + ); + } else { + throw new Error( + `Video decode ended early at ${decodedAtLabel} (needed ${requiredEndSec.toFixed(3)}s).`, + ); + } + } } private computeSegments(