mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 06:46:09 +00:00
refactor(editor): extract nvidia export opt-in hook
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user