mirror of
https://github.com/SrIzan10/helium.git
synced 2026-06-06 00:56:58 +00:00
fix: macos screen capture prompt
This commit is contained in:
@@ -36,6 +36,7 @@ interface HeliumElectronAPI {
|
|||||||
venmicLink: (options: VenmicLinkOptions) => Promise<boolean>;
|
venmicLink: (options: VenmicLinkOptions) => Promise<boolean>;
|
||||||
venmicUnlink: () => Promise<boolean>;
|
venmicUnlink: () => Promise<boolean>;
|
||||||
checkScreenPermission: () => Promise<string>;
|
checkScreenPermission: () => Promise<string>;
|
||||||
|
openScreenPermissionSettings: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -212,6 +213,28 @@ export function useElectron() {
|
|||||||
return platformInfo.value.supportsLoopbackAudio || platformInfo.value.supportsVenmic;
|
return platformInfo.value.supportsLoopbackAudio || platformInfo.value.supportsVenmic;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getScreenPermissionStatus = async (): Promise<string> => {
|
||||||
|
if (!checkElectron()) return "granted";
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await window.heliumElectron!.checkScreenPermission();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[useElectron] Failed to check screen permission:", error);
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openScreenPermissionSettings = async (): Promise<boolean> => {
|
||||||
|
if (!checkElectron()) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await window.heliumElectron!.openScreenPermissionSettings();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[useElectron] Failed to open screen permission settings:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkElectron();
|
checkElectron();
|
||||||
if (isElectron.value) {
|
if (isElectron.value) {
|
||||||
@@ -246,9 +269,10 @@ export function useElectron() {
|
|||||||
linkAllAudio,
|
linkAllAudio,
|
||||||
linkAppAudio,
|
linkAppAudio,
|
||||||
unlinkVenmicAudio,
|
unlinkVenmicAudio,
|
||||||
|
getScreenPermissionStatus,
|
||||||
|
openScreenPermissionSettings,
|
||||||
|
|
||||||
startScreenShareWithAudio,
|
startScreenShareWithAudio,
|
||||||
stopScreenShare,
|
stopScreenShare,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useWebSocket } from "@vueuse/core";
|
import { useWebSocket } from "@vueuse/core";
|
||||||
|
import { toast } from "vue-sonner";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -66,6 +67,7 @@ import { useElectron } from "~/composables/useElectron";
|
|||||||
import PresetSelect from "~/components/app/PresetSelect.vue";
|
import PresetSelect from "~/components/app/PresetSelect.vue";
|
||||||
|
|
||||||
const streamerStore = useStreamerStore();
|
const streamerStore = useStreamerStore();
|
||||||
|
const { t } = useI18n();
|
||||||
const videofeedRef = ref<HTMLVideoElement | null>(null);
|
const videofeedRef = ref<HTMLVideoElement | null>(null);
|
||||||
const localStream = ref<MediaStream | null>(null);
|
const localStream = ref<MediaStream | null>(null);
|
||||||
const wsUrl = useWebSocketUrl();
|
const wsUrl = useWebSocketUrl();
|
||||||
@@ -83,6 +85,8 @@ const {
|
|||||||
linkAllAudio,
|
linkAllAudio,
|
||||||
linkAppAudio,
|
linkAppAudio,
|
||||||
unlinkVenmicAudio,
|
unlinkVenmicAudio,
|
||||||
|
getScreenPermissionStatus,
|
||||||
|
openScreenPermissionSettings,
|
||||||
} = useElectron();
|
} = useElectron();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -231,6 +235,7 @@ async function startScreenShare() {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to start screen share:", error);
|
console.error("Failed to start screen share:", error);
|
||||||
|
await handleScreenShareError(error);
|
||||||
cleanupStreaming();
|
cleanupStreaming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,9 +298,36 @@ async function changeScreenShareSource() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to change screen share source:", error);
|
console.error("Failed to change screen share source:", error);
|
||||||
|
await handleScreenShareError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleScreenShareError(error: unknown): Promise<void> {
|
||||||
|
const isPermissionDeniedError =
|
||||||
|
error instanceof DOMException && error.name === "NotAllowedError";
|
||||||
|
|
||||||
|
if (!isPermissionDeniedError || !isElectron.value || !platformInfo.value?.isMac) {
|
||||||
|
toast.error(t("failedToStartScreenShare"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionStatus = await getScreenPermissionStatus();
|
||||||
|
|
||||||
|
if (permissionStatus === "granted") {
|
||||||
|
toast.error(t("failedToStartScreenShare"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openedSettings = await openScreenPermissionSettings();
|
||||||
|
|
||||||
|
if (openedSettings) {
|
||||||
|
toast.error(t("screenRecordingPermissionRequired"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.error(t("screenRecordingPermissionRequiredNoShortcut"));
|
||||||
|
}
|
||||||
|
|
||||||
async function cleanupStreaming() {
|
async function cleanupStreaming() {
|
||||||
if (localStream.value) {
|
if (localStream.value) {
|
||||||
localStream.value.getTracks().forEach((track) => {
|
localStream.value.getTracks().forEach((track) => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
ipcMain,
|
ipcMain,
|
||||||
desktopCapturer,
|
desktopCapturer,
|
||||||
session,
|
session,
|
||||||
|
shell,
|
||||||
systemPreferences,
|
systemPreferences,
|
||||||
type IpcMainInvokeEvent,
|
type IpcMainInvokeEvent,
|
||||||
type DesktopCapturerSource,
|
type DesktopCapturerSource,
|
||||||
@@ -208,6 +209,20 @@ ipcMain.handle('helium:check-screen-permission', () => {
|
|||||||
return 'granted';
|
return 'granted';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('helium:open-screen-permission-settings', async () => {
|
||||||
|
if (!isMac) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await shell.openExternal('x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Helium] Failed to open macOS screen recording settings:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
if (!gotTheLock) {
|
if (!gotTheLock) {
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ const heliumElectronAPI = {
|
|||||||
venmicUnlink: (): Promise<boolean> => ipcRenderer.invoke('helium:venmic-unlink'),
|
venmicUnlink: (): Promise<boolean> => ipcRenderer.invoke('helium:venmic-unlink'),
|
||||||
|
|
||||||
checkScreenPermission: (): Promise<string> => ipcRenderer.invoke('helium:check-screen-permission'),
|
checkScreenPermission: (): Promise<string> => ipcRenderer.invoke('helium:check-screen-permission'),
|
||||||
|
openScreenPermissionSettings: (): Promise<boolean> =>
|
||||||
|
ipcRenderer.invoke('helium:open-screen-permission-settings'),
|
||||||
};
|
};
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('heliumElectron', heliumElectronAPI);
|
contextBridge.exposeInMainWorld('heliumElectron', heliumElectronAPI);
|
||||||
|
|||||||
@@ -59,5 +59,8 @@
|
|||||||
"audioSource": "Audio Source",
|
"audioSource": "Audio Source",
|
||||||
"allSystemAudio": "All System Audio",
|
"allSystemAudio": "All System Audio",
|
||||||
"refreshSources": "Refresh Sources",
|
"refreshSources": "Refresh Sources",
|
||||||
"audioSupported": "Audio Supported"
|
"audioSupported": "Audio Supported",
|
||||||
|
"failedToStartScreenShare": "Failed to start screen share.",
|
||||||
|
"screenRecordingPermissionRequired": "macOS blocked screen capture. Allow Helium in System Settings > Privacy & Security > Screen Recording, then restart Helium.",
|
||||||
|
"screenRecordingPermissionRequiredNoShortcut": "macOS blocked screen capture. Open System Settings > Privacy & Security > Screen Recording, allow Helium, then restart Helium."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,5 +59,8 @@
|
|||||||
"audioSource": "Fuente de audio",
|
"audioSource": "Fuente de audio",
|
||||||
"allSystemAudio": "Todo el audio del sistema",
|
"allSystemAudio": "Todo el audio del sistema",
|
||||||
"refreshSources": "Actualizar fuentes",
|
"refreshSources": "Actualizar fuentes",
|
||||||
"audioSupported": "Audio soportado"
|
"audioSupported": "Audio soportado",
|
||||||
|
"failedToStartScreenShare": "No se pudo iniciar el uso compartido de pantalla.",
|
||||||
|
"screenRecordingPermissionRequired": "macOS bloqueó la captura de pantalla. Permite Helium en Configuración del Sistema > Privacidad y seguridad > Grabación de pantalla y luego reinicia Helium.",
|
||||||
|
"screenRecordingPermissionRequiredNoShortcut": "macOS bloqueó la captura de pantalla. Abre Configuración del Sistema > Privacidad y seguridad > Grabación de pantalla, permite Helium y luego reinicia Helium."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user