From 4e5b7a4f5a4cf00c7e2aae33ac20775210f53048 Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Thu, 7 May 2026 09:37:43 +0200 Subject: [PATCH] test: log source copy fast path blockers --- src/lib/exporter/videoExporter.test.ts | 17 ++++++- src/lib/exporter/videoExporter.ts | 68 ++++++++++++++++++-------- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/lib/exporter/videoExporter.test.ts b/src/lib/exporter/videoExporter.test.ts index 3f14ec1..d168b62 100644 --- a/src/lib/exporter/videoExporter.test.ts +++ b/src/lib/exporter/videoExporter.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "vitest"; -import { isSourceCopyFastPathEligible, type VideoExporterConfig } from "./videoExporter"; +import { + getSourceCopyFastPathBlockers, + isSourceCopyFastPathEligible, + type VideoExporterConfig, +} from "./videoExporter"; function createConfig(overrides: Partial = {}): VideoExporterConfig { return { @@ -123,3 +127,14 @@ describe("isSourceCopyFastPathEligible", () => { ).toBe(false); }); }); + +describe("getSourceCopyFastPathBlockers", () => { + it("reports the source-size mismatch that blocks copy-only export", () => { + expect( + getSourceCopyFastPathBlockers(createConfig({ height: 1080 }), { + width: 1920, + height: 1032, + }), + ).toContain("output-size 1920x1080 differs from source 1920x1032"); + }); +}); diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index 973e6be..10525a1 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -92,25 +92,39 @@ export function isSourceCopyFastPathEligible( config: VideoExporterConfig, videoInfo: { width: number; height: number }, ) { - return ( - config.width === videoInfo.width && - config.height === videoInfo.height && - !config.webcamVideoUrl && - !hasActiveTimeRegions(config.trimRegions) && - !hasActiveSpeedRegions(config.speedRegions) && - !hasActiveTimeRegions(config.zoomRegions) && - !hasActiveTimeRegions(config.annotationRegions) && - !hasNativeCursorOverlay(config) && - !hasCursorHighlightOverlay(config) && - isDefaultCrop(config.cropRegion) && - (config.padding ?? 0) <= SOURCE_COPY_EPSILON && - (config.videoPadding ?? 0) <= SOURCE_COPY_EPSILON && - (config.borderRadius ?? 0) <= SOURCE_COPY_EPSILON && - !config.showShadow && - config.shadowIntensity <= SOURCE_COPY_EPSILON && - !config.showBlur && - (config.motionBlurAmount ?? 0) <= SOURCE_COPY_EPSILON - ); + return getSourceCopyFastPathBlockers(config, videoInfo).length === 0; +} + +export function getSourceCopyFastPathBlockers( + config: VideoExporterConfig, + videoInfo: { width: number; height: number }, +) { + const blockers: string[] = []; + + if (config.width !== videoInfo.width || config.height !== videoInfo.height) { + blockers.push( + `output-size ${config.width}x${config.height} differs from source ${videoInfo.width}x${videoInfo.height}`, + ); + } + if (config.webcamVideoUrl) blockers.push("webcam overlay is enabled"); + if (hasActiveTimeRegions(config.trimRegions)) blockers.push("trim regions are present"); + if (hasActiveSpeedRegions(config.speedRegions)) blockers.push("speed regions are present"); + if (hasActiveTimeRegions(config.zoomRegions)) blockers.push("zoom regions are present"); + if (hasActiveTimeRegions(config.annotationRegions)) + blockers.push("annotation regions are present"); + if (hasNativeCursorOverlay(config)) blockers.push("editable cursor overlay is enabled"); + if (hasCursorHighlightOverlay(config)) blockers.push("cursor highlight overlay is enabled"); + if (!isDefaultCrop(config.cropRegion)) blockers.push("crop is not default"); + if ((config.padding ?? 0) > SOURCE_COPY_EPSILON) blockers.push("padding is not zero"); + if ((config.videoPadding ?? 0) > SOURCE_COPY_EPSILON) blockers.push("video padding is not zero"); + if ((config.borderRadius ?? 0) > SOURCE_COPY_EPSILON) blockers.push("roundness is not zero"); + if (config.showShadow || config.shadowIntensity > SOURCE_COPY_EPSILON) { + blockers.push("shadow is enabled"); + } + if (config.showBlur) blockers.push("background blur is enabled"); + if ((config.motionBlurAmount ?? 0) > SOURCE_COPY_EPSILON) blockers.push("motion blur is enabled"); + + return blockers; } function isMp4Source(videoUrl: string, blob: Blob) { @@ -645,12 +659,22 @@ export class VideoExporter { } private async trySourceCopyFastPath(videoInfo: { width: number; height: number }) { - if (!isSourceCopyFastPathEligible(this.config, videoInfo)) { + const blockers = getSourceCopyFastPathBlockers(this.config, videoInfo); + if (blockers.length > 0) { + console.info("[VideoExporter] source-copy fast path disabled", { + blockers, + output: { width: this.config.width, height: this.config.height }, + source: videoInfo, + }); return null; } const sourceBlob = await this.loadSourceBlob(); if (!sourceBlob || !isMp4Source(this.config.videoUrl, sourceBlob)) { + console.info("[VideoExporter] source-copy fast path disabled", { + blockers: ["source is not a readable MP4"], + source: videoInfo, + }); return null; } @@ -665,6 +689,10 @@ export class VideoExporter { estimatedTimeRemaining: 0, phase: "finalizing", }); + console.info("[VideoExporter] using source-copy fast path", { + source: videoInfo, + bytes: sourceBlob.size, + }); return { success: true,