From e4263d4597cd7d08be54180ef4c87dac1f3f1664 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Tue, 17 Mar 2026 19:37:12 +0800 Subject: [PATCH] fix: sync webcam preview playback speed --- electron/electron-env.d.ts | 6 +++ electron/ipc/handlers.ts | 43 ++++++++++++++++++- electron/main.ts | 4 +- electron/preload.ts | 3 ++ src/components/launch/LaunchWindow.tsx | 6 ++- src/components/video-editor/VideoPlayback.tsx | 9 +++- src/hooks/useScreenRecorder.ts | 36 +++++++++++++++- src/vite-env.d.ts | 6 +++ 8 files changed, 106 insertions(+), 7 deletions(-) diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index 17fd20f..0725348 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -29,6 +29,12 @@ interface Window { openSourceSelector: () => Promise; selectSource: (source: ProcessedDesktopSource) => Promise; getSelectedSource: () => Promise; + requestCameraAccess: () => Promise<{ + success: boolean; + granted: boolean; + status: string; + error?: string; + }>; getAssetBasePath: () => Promise; storeRecordedVideo: ( videoData: ArrayBuffer, diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index 2a6c406..82440c8 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -1,7 +1,16 @@ import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; -import { app, BrowserWindow, desktopCapturer, dialog, ipcMain, screen, shell } from "electron"; +import { + app, + BrowserWindow, + desktopCapturer, + dialog, + ipcMain, + screen, + shell, + systemPreferences, +} from "electron"; import { normalizeProjectMedia, normalizeRecordingSession, @@ -185,6 +194,38 @@ export function registerIpcHandlers( return selectedSource; }); + ipcMain.handle("request-camera-access", async () => { + if (process.platform !== "darwin") { + return { success: true, granted: true, status: "granted" }; + } + + try { + const status = systemPreferences.getMediaAccessStatus("camera"); + if (status === "granted") { + return { success: true, granted: true, status }; + } + + if (status === "not-determined") { + const granted = await systemPreferences.askForMediaAccess("camera"); + return { + success: true, + granted, + status: granted ? "granted" : systemPreferences.getMediaAccessStatus("camera"), + }; + } + + return { success: true, granted: false, status }; + } catch (error) { + console.error("Failed to request camera access:", error); + return { + success: false, + granted: false, + status: "unknown", + error: String(error), + }; + } + }); + ipcMain.handle("open-source-selector", () => { const sourceSelectorWin = getSourceSelectorWindow(); if (sourceSelectorWin) { diff --git a/electron/main.ts b/electron/main.ts index 23bcc49..86d4e37 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -333,12 +333,12 @@ app.on("activate", () => { app.whenReady().then(async () => { // Allow microphone/media permission checks session.defaultSession.setPermissionCheckHandler((_webContents, permission) => { - const allowed = ["media", "audioCapture", "microphone"]; + const allowed = ["media", "audioCapture", "microphone", "videoCapture", "camera"]; return allowed.includes(permission); }); session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback) => { - const allowed = ["media", "audioCapture", "microphone"]; + const allowed = ["media", "audioCapture", "microphone", "videoCapture", "camera"]; callback(allowed.includes(permission)); }); diff --git a/electron/preload.ts b/electron/preload.ts index b918377..ac9451d 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -27,6 +27,9 @@ contextBridge.exposeInMainWorld("electronAPI", { getSelectedSource: () => { return ipcRenderer.invoke("get-selected-source"); }, + requestCameraAccess: () => { + return ipcRenderer.invoke("request-camera-access"); + }, storeRecordedVideo: (videoData: ArrayBuffer, fileName: string) => { return ipcRenderer.invoke("store-recorded-video", videoData, fileName); diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index bca5d4a..4bf6df8 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -248,7 +248,11 @@ export function LaunchWindow() {