diff --git a/electron-builder.json5 b/electron-builder.json5 index 003d925..53899bc 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -25,7 +25,13 @@ ] } ], - "artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}" + "artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}", + "hardenedRuntime": true, + "entitlements": "entitlements.mac.plist", + "extendInfo": { + "NSMicrophoneUsageDescription": "Microphone", + "NSCameraUsageDescription": "Camera" + } }, "win": { "icon": "public/icon.ico", diff --git a/electron/macPermission.ts b/electron/macPermission.ts new file mode 100644 index 0000000..1e7146c --- /dev/null +++ b/electron/macPermission.ts @@ -0,0 +1,63 @@ +import {systemPreferences} from 'electron' + +enum EMediaType { + microphone = 'microphone', // 麦克风 + camera = 'camera', // 相机 +} + +/** + * 访问状态 + * 'not-determined':[未确定]表示用户尚未做出决定,或者系统尚未提示用户进行授权。 + * 'granted':[已授权]表示用户已经明确授予了应用的权限。 + * 'denied':[拒绝]表示用户拒绝了应用授权的请求。 + * 'restricted':[受限]在某些情况下,可能是由于系统策略或其他安全限制导致应用无法获得改权限。 + * 'unknown':[未知]在无法确定权限状态的情况下返回,可能是因为某种错误或其他不可预知的情况。 + */ +type IAccessStatus = 'not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown' + +/** + * 请求媒体权限 + * @param mediaType + */ +const requestMediaAccess = async (mediaType: EMediaType): Promise => { + + try { + // 获取当前媒体设备(在这里指麦克风或摄像头)的访问权限状态 + const privilege: IAccessStatus = systemPreferences.getMediaAccessStatus(mediaType) + + if (privilege !== 'granted') { + // 未授权,则重新唤起系统弹框,等待用户点击授权 + await systemPreferences.askForMediaAccess(mediaType) + // 请求权限后,再次获取媒体访问状态并返回 + return systemPreferences.getMediaAccessStatus(mediaType) + } + // 已授权,则直接返回媒体访问状态 + return privilege + } catch (e) { + console.error('Failed to request media access:', e) + return 'unknown' + } + +} + +const requestAllMediaAccess = async ()=> { + + while (true) { + let microphoneResult = await requestMediaAccess(EMediaType.microphone); + + if (microphoneResult == 'granted') { + break; + } + } + + while (true) { + let cameraResult = await requestMediaAccess(EMediaType.camera); + + if (cameraResult == 'granted') { + break; + } + } + +} + +export default requestAllMediaAccess; \ No newline at end of file diff --git a/electron/main.js b/electron/main.js index 56e9ab5..46f35dc 100644 --- a/electron/main.js +++ b/electron/main.js @@ -6,6 +6,7 @@ import * as urlUtil from "node:url" import {autoUpdater, UpdateInfo} from "electron-updater" import * as url from "node:url" import packageJson from "../package.json"; +import requestAllMediaAccess from "./macPermission"; // The built directory structure // @@ -69,6 +70,11 @@ function createWindow() { nativeTheme.themeSource = theme + //request camera and microphone permission(only for macOS) + if (process.platform === 'darwin') { + requestAllMediaAccess(); + } + // win = new BrowserWindow({ // icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'), // webPreferences: { diff --git a/entitlements.mac.plist b/entitlements.mac.plist new file mode 100644 index 0000000..f107ce1 --- /dev/null +++ b/entitlements.mac.plist @@ -0,0 +1,16 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + + \ No newline at end of file