From 87cd2510c47bd6ee00c85ae1972d25a1b48b73af Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Fri, 22 May 2026 21:40:01 +0700 Subject: [PATCH] refactor(editor): extract mp4 export routing --- src/components/video-editor/VideoEditor.tsx | 38 +++---- .../video-editor/mp4ExportRouting.test.ts | 99 +++++++++++++++++++ .../video-editor/mp4ExportRouting.ts | 57 +++++++++++ 3 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 src/components/video-editor/mp4ExportRouting.test.ts create mode 100644 src/components/video-editor/mp4ExportRouting.ts diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 3e53a29c..3d8c27f0 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -88,6 +88,7 @@ import { } from "@/utils/aspectRatioUtils"; import { ExtensionIcon } from "./ExtensionIcon"; import { calculateMp4ExportDimensions, calculateMp4SourceDimensions } from "./exportDimensions"; +import { resolveMp4ExportRouting } from "./mp4ExportRouting"; import { useNvidiaCudaExportOptIn } from "./useNvidiaCudaExportOptIn"; const PhCursorFill = (props: { className?: string; weight?: "fill" | "regular" }) => ( @@ -4135,25 +4136,24 @@ export default function VideoEditor() { const selectedMp4FrameRate = smokeExportConfig.enabled ? (smokeExportConfig.fps ?? settings.mp4FrameRate ?? mp4FrameRate) : (settings.mp4FrameRate ?? mp4FrameRate); - const pipelineModel = smokeExportConfig.enabled - ? (smokeExportConfig.pipelineModel ?? "modern") - : (settings.pipelineModel ?? exportPipelineModel); - const useExperimentalNativeExport = - pipelineModel === "modern" && - (smokeExportConfig.enabled ? smokeExportConfig.useNativeExport : true); - const useExperimentalNvidiaCudaExport = - useExperimentalNativeExport && - experimentalNvidiaCudaExport && - nvidiaCudaExportAvailable; - const backendPreference = - pipelineModel === "legacy" - ? "webcodecs" - : smokeExportConfig.enabled - ? (smokeExportConfig.backendPreference ?? - (smokeExportConfig.useNativeExport ? "breeze" : "webcodecs")) - : useExperimentalNativeExport - ? "auto" - : (settings.backendPreference ?? exportBackendPreference); + const { + pipelineModel, + useExperimentalNativeExport, + useExperimentalNvidiaCudaExport, + backendPreference, + } = resolveMp4ExportRouting({ + smokeExportConfig: { + enabled: smokeExportConfig.enabled, + pipelineModel: smokeExportConfig.pipelineModel, + useNativeExport: smokeExportConfig.useNativeExport, + backendPreference: smokeExportConfig.backendPreference, + }, + settings, + exportPipelineModel, + exportBackendPreference, + experimentalNvidiaCudaExport, + nvidiaCudaExportAvailable, + }); const supportedSourceDimensions = await ensureSupportedMp4SourceDimensions(selectedMp4FrameRate); const { width: exportWidth, height: exportHeight } = diff --git a/src/components/video-editor/mp4ExportRouting.test.ts b/src/components/video-editor/mp4ExportRouting.test.ts new file mode 100644 index 00000000..72f943c9 --- /dev/null +++ b/src/components/video-editor/mp4ExportRouting.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "vitest"; + +import { resolveMp4ExportRouting } from "./mp4ExportRouting"; + +const baseOptions = { + smokeExportConfig: { + enabled: false, + useNativeExport: false, + }, + settings: {}, + exportPipelineModel: "modern" as const, + exportBackendPreference: "breeze" as const, + experimentalNvidiaCudaExport: false, + nvidiaCudaExportAvailable: false, +}; + +describe("resolveMp4ExportRouting", () => { + it("uses the modern native auto route for normal MP4 exports by default", () => { + expect(resolveMp4ExportRouting(baseOptions)).toEqual({ + pipelineModel: "modern", + useExperimentalNativeExport: true, + useExperimentalNvidiaCudaExport: false, + backendPreference: "auto", + }); + }); + + it("forces WebCodecs and disables native export for the legacy pipeline", () => { + expect( + resolveMp4ExportRouting({ + ...baseOptions, + settings: { pipelineModel: "legacy", backendPreference: "breeze" }, + }), + ).toEqual({ + pipelineModel: "legacy", + useExperimentalNativeExport: false, + useExperimentalNvidiaCudaExport: false, + backendPreference: "webcodecs", + }); + }); + + it("keeps smoke exports on WebCodecs unless smoke native export is requested", () => { + expect( + resolveMp4ExportRouting({ + ...baseOptions, + smokeExportConfig: { + enabled: true, + useNativeExport: false, + }, + }), + ).toEqual({ + pipelineModel: "modern", + useExperimentalNativeExport: false, + useExperimentalNvidiaCudaExport: false, + backendPreference: "webcodecs", + }); + + expect( + resolveMp4ExportRouting({ + ...baseOptions, + smokeExportConfig: { + enabled: true, + useNativeExport: true, + }, + }), + ).toEqual({ + pipelineModel: "modern", + useExperimentalNativeExport: true, + useExperimentalNvidiaCudaExport: false, + backendPreference: "breeze", + }); + }); + + it("only enables NVIDIA CUDA when native export is active and the device is available", () => { + expect( + resolveMp4ExportRouting({ + ...baseOptions, + experimentalNvidiaCudaExport: true, + nvidiaCudaExportAvailable: true, + }).useExperimentalNvidiaCudaExport, + ).toBe(true); + + expect( + resolveMp4ExportRouting({ + ...baseOptions, + settings: { pipelineModel: "legacy" }, + experimentalNvidiaCudaExport: true, + nvidiaCudaExportAvailable: true, + }).useExperimentalNvidiaCudaExport, + ).toBe(false); + + expect( + resolveMp4ExportRouting({ + ...baseOptions, + experimentalNvidiaCudaExport: true, + nvidiaCudaExportAvailable: false, + }).useExperimentalNvidiaCudaExport, + ).toBe(false); + }); +}); diff --git a/src/components/video-editor/mp4ExportRouting.ts b/src/components/video-editor/mp4ExportRouting.ts new file mode 100644 index 00000000..95b157de --- /dev/null +++ b/src/components/video-editor/mp4ExportRouting.ts @@ -0,0 +1,57 @@ +import type { + ExportBackendPreference, + ExportPipelineModel, + ExportSettings, +} from "@/lib/exporter"; +import type { SmokeExportConfig } from "./smokeExportConfig"; + +export type Mp4ExportRouting = { + pipelineModel: ExportPipelineModel; + useExperimentalNativeExport: boolean; + useExperimentalNvidiaCudaExport: boolean; + backendPreference: ExportBackendPreference; +}; + +export function resolveMp4ExportRouting({ + smokeExportConfig, + settings, + exportPipelineModel, + exportBackendPreference, + experimentalNvidiaCudaExport, + nvidiaCudaExportAvailable, +}: { + smokeExportConfig: Pick< + SmokeExportConfig, + "enabled" | "pipelineModel" | "useNativeExport" | "backendPreference" + >; + settings: Pick; + exportPipelineModel: ExportPipelineModel; + exportBackendPreference: ExportBackendPreference; + experimentalNvidiaCudaExport: boolean; + nvidiaCudaExportAvailable: boolean; +}): Mp4ExportRouting { + const pipelineModel = smokeExportConfig.enabled + ? (smokeExportConfig.pipelineModel ?? "modern") + : (settings.pipelineModel ?? exportPipelineModel); + const useExperimentalNativeExport = + pipelineModel === "modern" && + (smokeExportConfig.enabled ? smokeExportConfig.useNativeExport : true); + const useExperimentalNvidiaCudaExport = + useExperimentalNativeExport && experimentalNvidiaCudaExport && nvidiaCudaExportAvailable; + const backendPreference = + pipelineModel === "legacy" + ? "webcodecs" + : smokeExportConfig.enabled + ? (smokeExportConfig.backendPreference ?? + (smokeExportConfig.useNativeExport ? "breeze" : "webcodecs")) + : useExperimentalNativeExport + ? "auto" + : (settings.backendPreference ?? exportBackendPreference); + + return { + pipelineModel, + useExperimentalNativeExport, + useExperimentalNvidiaCudaExport, + backendPreference, + }; +}