Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 1 addition & 130 deletions src/components/video-editor/VideoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,12 @@ import {
type ExportPipelineModel,
type ExportProgress,
type ExportQuality,
type ExportRenderBackend,
type ExportSettings,
FrameRenderer,
GIF_SIZE_PRESETS,
GifExporter,
type GifFrameRate,
type GifSizePreset,
isValidMp4FrameRate,
ModernVideoExporter,
probeSupportedMp4Dimensions,
type SupportedMp4Dimensions,
Expand Down Expand Up @@ -140,6 +138,7 @@ import {
validateProjectData,
} from "./projectPersistence";
import { SettingsPanel } from "./SettingsPanel";
import { getDevOpenRecordingConfig, getSmokeExportConfig } from "./smokeExportConfig";
import { useVideoEditorAudio } from "./audio/useVideoEditorAudio";
import {
APP_HEADER_ICON_BUTTON_CLASS,
Expand Down Expand Up @@ -233,27 +232,6 @@ type CancelableExporter = {
cancel(): void;
};

type SmokeExportConfig = {
enabled: boolean;
inputPath: string | null;
outputPath: string | null;
useNativeExport: boolean;
encodingMode?: ExportEncodingMode;
shadowIntensity?: number;
webcamInputPath?: string | null;
webcamShadow?: number;
webcamSize?: number;
pipelineModel?: ExportPipelineModel;
backendPreference?: ExportBackendPreference;
renderBackend?: ExportRenderBackend;
maxEncodeQueue?: number;
maxDecodeQueue?: number;
maxPendingFrames?: number;
projectPath?: string | null;
quality?: ExportQuality;
fps?: ExportMp4FrameRate;
};

const EXPORT_BLOB_STREAM_CHUNK_BYTES = 16 * 1024 * 1024;

async function streamExportBlobToTempFile(blob: Blob, extension: string): Promise<string | null> {
Expand Down Expand Up @@ -312,11 +290,6 @@ type SaveProjectOptions = {
captureThumbnail?: boolean;
};

type DevOpenRecordingConfig = {
inputPath: string | null;
webcamInputPath: string | null;
};

async function writeSmokeExportReport(
outputPath: string | null,
report: Record<string, unknown>,
Expand Down Expand Up @@ -366,108 +339,6 @@ function cloneStructured<T>(value: T): T {
return globalThis.structuredClone(value);
}

function parseSmokeExportNumber(value: string | null): number | undefined {
if (value === null) {
return undefined;
}

const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
}

function parseSmokeExportNonNegativeNumber(value: string | null): number | undefined {
if (value === null) {
return undefined;
}

const parsed = Number.parseFloat(value);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
}

function parseSmokeExportQuality(value: string | null): ExportQuality | undefined {
if (value === "medium" || value === "good" || value === "high" || value === "source") {
return value;
}
return undefined;
}

function parseSmokeExportFps(value: string | null): ExportMp4FrameRate | undefined {
if (value === null) return undefined;
const parsed = Number.parseInt(value, 10);
return isValidMp4FrameRate(parsed) ? parsed : undefined;
}

function parseSmokeRenderBackend(value: string | null): ExportRenderBackend | undefined {
return value === "webgl" || value === "webgpu" ? value : undefined;
}

function getSmokeExportConfig(search: string): SmokeExportConfig {
const params = new URLSearchParams(search);
const enabled = params.get("smokeExport") === "1";

return {
enabled,
inputPath: enabled ? params.get("smokeInput") : null,
outputPath: enabled ? params.get("smokeOutput") : null,
useNativeExport: enabled ? params.get("smokeUseNativeExport") === "1" : false,
encodingMode:
enabled && params.get("smokeEncodingMode") === "fast"
? "fast"
: enabled && params.get("smokeEncodingMode") === "balanced"
? "balanced"
: enabled && params.get("smokeEncodingMode") === "quality"
? "quality"
: undefined,
shadowIntensity: enabled
? parseSmokeExportNonNegativeNumber(params.get("smokeShadowIntensity"))
: undefined,
webcamInputPath: enabled ? params.get("smokeWebcamInput") : null,
webcamShadow: enabled
? parseSmokeExportNonNegativeNumber(params.get("smokeWebcamShadow"))
: undefined,
webcamSize: enabled
? parseSmokeExportNonNegativeNumber(params.get("smokeWebcamSize"))
: undefined,
pipelineModel:
enabled && params.get("smokePipelineModel") === "modern"
? "modern"
: enabled && params.get("smokePipelineModel") === "legacy"
? "legacy"
: undefined,
backendPreference:
enabled && params.get("smokeBackendPreference") === "auto"
? "auto"
: enabled && params.get("smokeBackendPreference") === "webcodecs"
? "webcodecs"
: enabled && params.get("smokeBackendPreference") === "breeze"
? "breeze"
: undefined,
renderBackend: enabled
? parseSmokeRenderBackend(params.get("smokeRenderBackend"))
: undefined,
maxEncodeQueue: enabled
? parseSmokeExportNumber(params.get("smokeMaxEncodeQueue"))
: undefined,
maxDecodeQueue: enabled
? parseSmokeExportNumber(params.get("smokeMaxDecodeQueue"))
: undefined,
maxPendingFrames: enabled
? parseSmokeExportNumber(params.get("smokeMaxPendingFrames"))
: undefined,
projectPath: enabled ? params.get("smokeProject") : null,
quality: enabled ? parseSmokeExportQuality(params.get("smokeQuality")) : undefined,
fps: enabled ? parseSmokeExportFps(params.get("smokeFps")) : undefined,
};
}

function getDevOpenRecordingConfig(search: string): DevOpenRecordingConfig {
const params = new URLSearchParams(search);
return {
inputPath: params.get("devOpenInput"),
webcamInputPath: params.get("devOpenWebcam"),
};
}

function isComparableObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
Expand Down
120 changes: 120 additions & 0 deletions src/components/video-editor/smokeExportConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { describe, expect, it } from "vitest";

import { getDevOpenRecordingConfig, getSmokeExportConfig } from "./smokeExportConfig";

describe("getSmokeExportConfig", () => {
it("keeps smoke export disabled when the flag is absent", () => {
expect(getSmokeExportConfig("?smokeInput=/tmp/input.mp4")).toEqual({
enabled: false,
inputPath: null,
outputPath: null,
useNativeExport: false,
encodingMode: undefined,
shadowIntensity: undefined,
webcamInputPath: null,
webcamShadow: undefined,
webcamSize: undefined,
pipelineModel: undefined,
backendPreference: undefined,
renderBackend: undefined,
maxEncodeQueue: undefined,
maxDecodeQueue: undefined,
maxPendingFrames: undefined,
projectPath: null,
quality: undefined,
fps: undefined,
});
});

it("parses the enabled smoke export query contract", () => {
const config = getSmokeExportConfig(
"?smokeExport=1" +
"&smokeInput=/tmp/input.mp4" +
"&smokeOutput=/tmp/output.mp4" +
"&smokeUseNativeExport=1" +
"&smokeEncodingMode=quality" +
"&smokeShadowIntensity=0" +
"&smokeWebcamInput=/tmp/webcam.mp4" +
"&smokeWebcamShadow=1.5" +
"&smokeWebcamSize=0.75" +
"&smokePipelineModel=legacy" +
"&smokeBackendPreference=breeze" +
"&smokeRenderBackend=webgpu" +
"&smokeMaxEncodeQueue=8" +
"&smokeMaxDecodeQueue=9" +
"&smokeMaxPendingFrames=10" +
"&smokeProject=/tmp/project.recordly" +
"&smokeQuality=source" +
"&smokeFps=60",
);

expect(config).toEqual({
enabled: true,
inputPath: "/tmp/input.mp4",
outputPath: "/tmp/output.mp4",
useNativeExport: true,
encodingMode: "quality",
shadowIntensity: 0,
webcamInputPath: "/tmp/webcam.mp4",
webcamShadow: 1.5,
webcamSize: 0.75,
pipelineModel: "legacy",
backendPreference: "breeze",
renderBackend: "webgpu",
maxEncodeQueue: 8,
maxDecodeQueue: 9,
maxPendingFrames: 10,
projectPath: "/tmp/project.recordly",
quality: "source",
fps: 60,
});
});

it("drops invalid optional smoke export values", () => {
const config = getSmokeExportConfig(
"?smokeExport=1" +
"&smokeEncodingMode=slow" +
"&smokeShadowIntensity=-1" +
"&smokeWebcamShadow=nan" +
"&smokeWebcamSize=-0.1" +
"&smokePipelineModel=classic" +
"&smokeBackendPreference=native" +
"&smokeRenderBackend=canvas" +
"&smokeMaxEncodeQueue=0" +
"&smokeMaxDecodeQueue=-4" +
"&smokeMaxPendingFrames=abc" +
"&smokeQuality=ultra" +
"&smokeFps=25",
);

expect(config).toMatchObject({
enabled: true,
useNativeExport: false,
encodingMode: undefined,
shadowIntensity: undefined,
webcamShadow: undefined,
webcamSize: undefined,
pipelineModel: undefined,
backendPreference: undefined,
renderBackend: undefined,
maxEncodeQueue: undefined,
maxDecodeQueue: undefined,
maxPendingFrames: undefined,
quality: undefined,
fps: undefined,
});
});
});

describe("getDevOpenRecordingConfig", () => {
it("reads dev-open paths independently from smoke export", () => {
expect(
getDevOpenRecordingConfig(
"?devOpenInput=/tmp/input.mp4&devOpenWebcam=/tmp/webcam.mp4",
),
).toEqual({
inputPath: "/tmp/input.mp4",
webcamInputPath: "/tmp/webcam.mp4",
});
});
});
Loading