refactor(editor): extract nvidia export opt-in hook

This commit is contained in:
wiiiii123
2026-05-22 21:04:05 +07:00
parent 6e466a3aff
commit db0757c8e2
3 changed files with 175 additions and 49 deletions
+11 -49
View File
@@ -52,7 +52,6 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
import { Toaster } from "@/components/ui/sonner";
import { useI18n } from "@/contexts/I18nContext";
import { useShortcuts } from "@/contexts/ShortcutsContext";
import { loadAppSetting, saveAppSetting } from "@/lib/appSettings";
import {
calculateOutputDimensions,
DEFAULT_MP4_CODEC,
@@ -89,6 +88,7 @@ import {
} from "@/utils/aspectRatioUtils";
import { ExtensionIcon } from "./ExtensionIcon";
import { calculateMp4ExportDimensions, calculateMp4SourceDimensions } from "./exportDimensions";
import { useNvidiaCudaExportOptIn } from "./useNvidiaCudaExportOptIn";
const PhCursorFill = (props: { className?: string; weight?: "fill" | "regular" }) => (
<Cursor weight="fill" className={props.className} />
@@ -109,8 +109,6 @@ const PhSettings = (props: { className?: string; weight?: "fill" | "regular" })
<Gear weight={props.weight ?? "regular"} className={props.className} />
);
const NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY = "recordly.export.experimentalNvidiaCuda";
import type { SourceAudioTrackSettings } from "@/components/video-editor/audio/audioTypes";
import { extensionHost } from "@/lib/extensions";
import { useVideoEditorAudio } from "./audio/useVideoEditorAudio";
@@ -554,10 +552,16 @@ export default function VideoEditor() {
const [exportPipelineModel, setExportPipelineModel] = useState<ExportPipelineModel>(
initialEditorPreferences.exportPipelineModel,
);
const [nvidiaCudaExportAvailable, setNvidiaCudaExportAvailable] = useState(false);
const [experimentalNvidiaCudaExport, setExperimentalNvidiaCudaExportState] = useState(
() => loadAppSetting<boolean>(NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY) === true,
);
const enableModernExportPipeline = useCallback(() => {
setExportPipelineModel("modern");
}, []);
const {
nvidiaCudaExportAvailable,
experimentalNvidiaCudaExport,
setExperimentalNvidiaCudaExport,
} = useNvidiaCudaExportOptIn({
onEnabled: enableModernExportPipeline,
});
const [mp4FrameRate, setMp4FrameRate] = useState<ExportMp4FrameRate>(
initialEditorPreferences.mp4FrameRate ?? DEFAULT_MP4_EXPORT_FRAME_RATE,
);
@@ -628,48 +632,6 @@ export default function VideoEditor() {
});
}, []);
useEffect(() => {
let cancelled = false;
void (async () => {
try {
const result = await window.electronAPI?.getNativeExportCapabilities?.();
if (cancelled) {
return;
}
const available = result?.capabilities?.nvidiaCuda.available === true;
setNvidiaCudaExportAvailable(available);
if (!available) {
setExperimentalNvidiaCudaExportState(false);
}
} catch (error) {
if (cancelled) {
return;
}
console.warn("[export] Failed to load native export capabilities", error);
setNvidiaCudaExportAvailable(false);
setExperimentalNvidiaCudaExportState(false);
}
})();
return () => {
cancelled = true;
};
}, []);
const setExperimentalNvidiaCudaExport = useCallback(
(enabled: boolean) => {
const nextEnabled = Boolean(enabled && nvidiaCudaExportAvailable);
setExperimentalNvidiaCudaExportState(nextEnabled);
saveAppSetting(NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY, nextEnabled);
if (nextEnabled) {
setExportPipelineModel("modern");
}
},
[nvidiaCudaExportAvailable],
);
useEffect(() => {
autoSuggestedVideoPathRef.current = null;
pendingFreshRecordingAutoSuggestTelemetryCountRef.current = 0;
@@ -0,0 +1,68 @@
import { afterEach, describe, expect, it } from "vitest";
import {
isNvidiaCudaExportAvailable,
loadInitialNvidiaCudaExportOptIn,
NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY,
resolveNvidiaCudaExportOptIn,
saveNvidiaCudaExportOptIn,
} from "./useNvidiaCudaExportOptIn";
function stubElectronSettings(initialValues: Record<string, unknown> = {}) {
const store = new Map(Object.entries(initialValues));
Object.defineProperty(globalThis, "electronAPI", {
configurable: true,
value: {
getAppSetting: (key: string) => (store.has(key) ? store.get(key) : null),
setAppSetting: (key: string, value: unknown) => {
store.set(key, value);
return true;
},
} as Pick<Window["electronAPI"], "getAppSetting" | "setAppSetting">,
});
return store;
}
describe("nvidiaCudaExportOptIn", () => {
afterEach(() => {
Reflect.deleteProperty(globalThis, "electronAPI");
});
it("only treats explicit native CUDA availability as available", () => {
expect(
isNvidiaCudaExportAvailable({
capabilities: { nvidiaCuda: { available: true } },
}),
).toBe(true);
expect(
isNvidiaCudaExportAvailable({
capabilities: { nvidiaCuda: { available: false } },
}),
).toBe(false);
expect(isNvidiaCudaExportAvailable({ capabilities: {} })).toBe(false);
expect(isNvidiaCudaExportAvailable(null)).toBe(false);
});
it("requires both user request and runtime availability before enabling", () => {
expect(resolveNvidiaCudaExportOptIn(true, true)).toBe(true);
expect(resolveNvidiaCudaExportOptIn(true, false)).toBe(false);
expect(resolveNvidiaCudaExportOptIn(false, true)).toBe(false);
expect(resolveNvidiaCudaExportOptIn(false, false)).toBe(false);
});
it("loads and saves the local opt-in flag through app settings", () => {
const store = stubElectronSettings({
[NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY]: true,
});
expect(loadInitialNvidiaCudaExportOptIn()).toBe(true);
expect(saveNvidiaCudaExportOptIn(false)).toBe(true);
expect(store.get(NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY)).toBe(false);
});
it("defaults to disabled when the app setting is unavailable", () => {
expect(loadInitialNvidiaCudaExportOptIn()).toBe(false);
});
});
@@ -0,0 +1,96 @@
import { useCallback, useEffect, useState } from "react";
import { loadAppSetting, saveAppSetting } from "@/lib/appSettings";
export const NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY =
"recordly.export.experimentalNvidiaCuda";
type NativeExportCapabilitiesResult = {
capabilities?: {
nvidiaCuda?: {
available?: boolean;
};
};
} | null;
export function isNvidiaCudaExportAvailable(
result: NativeExportCapabilitiesResult | undefined,
) {
return result?.capabilities?.nvidiaCuda?.available === true;
}
export function resolveNvidiaCudaExportOptIn(
requested: boolean,
nvidiaCudaExportAvailable: boolean,
) {
return Boolean(requested && nvidiaCudaExportAvailable);
}
export function loadInitialNvidiaCudaExportOptIn() {
return loadAppSetting<boolean>(NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY) === true;
}
export function saveNvidiaCudaExportOptIn(enabled: boolean) {
return saveAppSetting(NVIDIA_CUDA_EXPORT_OPT_IN_SETTING_KEY, enabled);
}
export function useNvidiaCudaExportOptIn({
onEnabled,
}: {
onEnabled?: () => void;
} = {}) {
const [nvidiaCudaExportAvailable, setNvidiaCudaExportAvailable] = useState(false);
const [experimentalNvidiaCudaExport, setExperimentalNvidiaCudaExportState] = useState(
loadInitialNvidiaCudaExportOptIn,
);
useEffect(() => {
let cancelled = false;
void (async () => {
try {
const result = await window.electronAPI?.getNativeExportCapabilities?.();
if (cancelled) {
return;
}
const available = isNvidiaCudaExportAvailable(result);
setNvidiaCudaExportAvailable(available);
if (!available) {
setExperimentalNvidiaCudaExportState(false);
}
} catch (error) {
if (cancelled) {
return;
}
console.warn("[export] Failed to load native export capabilities", error);
setNvidiaCudaExportAvailable(false);
setExperimentalNvidiaCudaExportState(false);
}
})();
return () => {
cancelled = true;
};
}, []);
const setExperimentalNvidiaCudaExport = useCallback(
(enabled: boolean) => {
const nextEnabled = resolveNvidiaCudaExportOptIn(
enabled,
nvidiaCudaExportAvailable,
);
setExperimentalNvidiaCudaExportState(nextEnabled);
saveNvidiaCudaExportOptIn(nextEnabled);
if (nextEnabled) {
onEnabled?.();
}
},
[nvidiaCudaExportAvailable, onEnabled],
);
return {
nvidiaCudaExportAvailable,
experimentalNvidiaCudaExport,
setExperimentalNvidiaCudaExport,
};
}