feat: allow manual webcam footage import

This commit is contained in:
webadderall
2026-03-18 14:26:42 +11:00
parent 8eed3bbee1
commit 5f3e1e09e8
5 changed files with 107 additions and 0 deletions
@@ -141,6 +141,8 @@ interface SettingsPanelProps {
onBorderRadiusChange?: (radius: number) => void;
webcam?: WebcamOverlaySettings;
onWebcamChange?: (webcam: WebcamOverlaySettings) => void;
onUploadWebcam?: () => void;
onClearWebcam?: () => void;
padding?: number;
onPaddingChange?: (padding: number) => void;
cropRegion?: CropRegion;
@@ -222,6 +224,8 @@ export function SettingsPanel({
onBorderRadiusChange,
webcam,
onWebcamChange,
onUploadWebcam,
onClearWebcam,
padding = 50,
onPaddingChange,
cropRegion,
@@ -356,6 +360,8 @@ export function SettingsPanel({
}
};
const webcamFileName = webcam?.sourcePath?.split(/[\\/]/).pop() ?? null;
const zoomEnabled = Boolean(selectedZoomDepth);
const trimEnabled = Boolean(selectedTrimId);
@@ -816,6 +822,42 @@ export function SettingsPanel({
className="data-[state=checked]:bg-[#2563EB] scale-90"
/>
</div>
<div className="col-span-2 rounded-lg border border-white/5 bg-white/5 p-2">
<div className="flex items-center justify-between gap-2">
<div>
<div className="text-[10px] font-medium text-slate-300">
{tSettings("effects.webcamFootage")}
</div>
<div className="mt-0.5 text-[10px] text-slate-500">
{webcamFileName ?? tSettings("effects.webcamFootageDescription")}
</div>
</div>
<div className="flex items-center gap-1.5">
<Button
type="button"
variant="outline"
onClick={onUploadWebcam}
className="h-7 gap-1.5 border-white/10 bg-white/5 px-2 text-[10px] text-slate-200 hover:bg-white/10 hover:text-white"
>
<Upload className="h-3 w-3" />
{webcam?.sourcePath
? tSettings("effects.replaceWebcamFootage")
: tSettings("effects.uploadWebcamFootage")}
</Button>
{webcam?.sourcePath ? (
<Button
type="button"
variant="outline"
onClick={onClearWebcam}
className="h-7 gap-1.5 border-white/10 bg-white/5 px-2 text-[10px] text-slate-200 hover:bg-white/10 hover:text-white"
>
<Trash2 className="h-3 w-3" />
{tSettings("effects.removeWebcamFootage")}
</Button>
) : null}
</div>
</div>
</div>
<div className="p-2 rounded-lg bg-white/5 border border-white/5">
<SliderControl
label={tSettings("effects.roundness")}
@@ -455,6 +455,48 @@ export default function VideoEditor() {
gifSizePreset,
]);
const syncRecordingSessionWebcam = useCallback(
async (webcamPath: string | null) => {
const sourcePath = videoSourcePath ?? (videoPath ? fromFileUrl(videoPath) : null);
if (!sourcePath || !window.electronAPI.setCurrentRecordingSession) {
return;
}
await window.electronAPI.setCurrentRecordingSession({
videoPath: sourcePath,
webcamPath,
});
},
[videoPath, videoSourcePath],
);
const handleUploadWebcam = useCallback(async () => {
const result = await window.electronAPI.openVideoFilePicker();
if (!result.success || !result.path) {
return;
}
setWebcam((prev) => ({
...prev,
enabled: true,
sourcePath: result.path ?? null,
}));
await syncRecordingSessionWebcam(result.path);
toast.success(t("settings.effects.webcamFootageAdded"));
}, [syncRecordingSessionWebcam, t]);
const handleClearWebcam = useCallback(async () => {
setWebcam((prev) => ({
...prev,
enabled: false,
sourcePath: null,
}));
await syncRecordingSessionWebcam(null);
toast.success(t("settings.effects.webcamFootageRemoved"));
}, [syncRecordingSessionWebcam, t]);
useEffect(() => {
const snapshot = cloneSnapshot(buildHistorySnapshot());
@@ -2208,6 +2250,8 @@ export default function VideoEditor() {
onBorderRadiusChange={setBorderRadius}
webcam={webcam}
onWebcamChange={setWebcam}
onUploadWebcam={handleUploadWebcam}
onClearWebcam={handleClearWebcam}
padding={padding}
onPaddingChange={setPadding}
cropRegion={cropRegion}
+7
View File
@@ -26,6 +26,13 @@
"cursorClickBounce": "Cursor Click Bounce",
"cursorSway": "Cursor Sway",
"webcam": "Webcam Overlay",
"webcamFootage": "Webcam Footage",
"webcamFootageDescription": "No webcam footage linked to this video",
"uploadWebcamFootage": "Upload footage",
"replaceWebcamFootage": "Replace footage",
"removeWebcamFootage": "Remove footage",
"webcamFootageAdded": "Webcam footage linked",
"webcamFootageRemoved": "Webcam footage removed",
"webcamSize": "Webcam Size",
"webcamReactToZoom": "Webcam Reacts To Zoom",
"webcamRoundness": "Webcam Roundness",
+7
View File
@@ -26,6 +26,13 @@
"cursorClickBounce": "Rebote de clic del cursor",
"cursorSway": "Balanceo del cursor",
"webcam": "Superposición de cámara",
"webcamFootage": "Metraje de cámara",
"webcamFootageDescription": "No hay metraje de cámara vinculado a este video",
"uploadWebcamFootage": "Subir metraje",
"replaceWebcamFootage": "Reemplazar metraje",
"removeWebcamFootage": "Quitar metraje",
"webcamFootageAdded": "Metraje de cámara vinculado",
"webcamFootageRemoved": "Metraje de cámara eliminado",
"webcamSize": "Tamaño de cámara",
"webcamReactToZoom": "La cámara reacciona al zoom",
"webcamRoundness": "Redondez de cámara",
+7
View File
@@ -26,6 +26,13 @@
"cursorClickBounce": "光标点击弹跳",
"cursorSway": "光标摆动",
"webcam": "摄像头叠加",
"webcamFootage": "摄像头素材",
"webcamFootageDescription": "此视频尚未关联摄像头素材",
"uploadWebcamFootage": "上传素材",
"replaceWebcamFootage": "替换素材",
"removeWebcamFootage": "移除素材",
"webcamFootageAdded": "已关联摄像头素材",
"webcamFootageRemoved": "已移除摄像头素材",
"webcamSize": "摄像头大小",
"webcamReactToZoom": "摄像头随缩放变化",
"webcamRoundness": "摄像头圆角",