mirror of
https://github.com/SrIzan10/helium.git
synced 2026-06-06 00:56:58 +00:00
279 lines
7.4 KiB
TypeScript
279 lines
7.4 KiB
TypeScript
export interface PlatformInfo {
|
|
platform: string;
|
|
isLinux: boolean;
|
|
isMac: boolean;
|
|
isWindows: boolean;
|
|
isElectron: boolean;
|
|
supportsLoopbackAudio: boolean;
|
|
supportsVenmic: boolean;
|
|
}
|
|
|
|
export interface DesktopSource {
|
|
id: string;
|
|
name: string;
|
|
thumbnail: string;
|
|
appIcon: string | null;
|
|
display_id?: string;
|
|
}
|
|
|
|
export interface VenmicLinkOptions {
|
|
include?: Record<string, string>[];
|
|
exclude?: Record<string, string>[];
|
|
ignore_devices?: boolean;
|
|
only_speakers?: boolean;
|
|
only_default_speakers?: boolean;
|
|
}
|
|
|
|
interface HeliumElectronAPI {
|
|
isElectron: boolean;
|
|
getPlatform: () => Promise<PlatformInfo>;
|
|
getSources: () => Promise<DesktopSource[]>;
|
|
onSourcesAvailable: (callback: (sources: DesktopSource[]) => void) => void;
|
|
selectSource: (sourceId: string | null) => void;
|
|
removeSourcesListener: () => void;
|
|
venmicAvailable: () => Promise<boolean>;
|
|
venmicList: () => Promise<Record<string, string>[]>;
|
|
venmicLink: (options: VenmicLinkOptions) => Promise<boolean>;
|
|
venmicUnlink: () => Promise<boolean>;
|
|
checkScreenPermission: () => Promise<string>;
|
|
openScreenPermissionSettings: () => Promise<boolean>;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
heliumElectron?: HeliumElectronAPI;
|
|
}
|
|
}
|
|
|
|
export function useElectron() {
|
|
const isElectron = ref(false);
|
|
const platformInfo = ref<PlatformInfo | null>(null);
|
|
const audioSources = ref<Record<string, string>[]>([]);
|
|
const isVenmicLinked = ref(false);
|
|
|
|
const checkElectron = () => {
|
|
if (import.meta.client) {
|
|
isElectron.value = !!window.heliumElectron?.isElectron;
|
|
}
|
|
return isElectron.value;
|
|
};
|
|
|
|
const getPlatformInfo = async (): Promise<PlatformInfo | null> => {
|
|
if (!checkElectron()) {
|
|
return {
|
|
platform: 'browser',
|
|
isLinux: false,
|
|
isMac: false,
|
|
isWindows: false,
|
|
isElectron: false,
|
|
supportsLoopbackAudio: false,
|
|
supportsVenmic: false,
|
|
};
|
|
}
|
|
|
|
try {
|
|
platformInfo.value = await window.heliumElectron!.getPlatform();
|
|
return platformInfo.value;
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to get platform info:', error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const getDesktopSources = async (): Promise<DesktopSource[]> => {
|
|
if (!checkElectron()) return [];
|
|
|
|
try {
|
|
return await window.heliumElectron!.getSources();
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to get sources:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const onSourcesAvailable = (callback: (sources: DesktopSource[]) => void) => {
|
|
if (!checkElectron()) return;
|
|
window.heliumElectron!.onSourcesAvailable(callback);
|
|
};
|
|
|
|
const selectSource = (sourceId: string | null) => {
|
|
if (!checkElectron()) return;
|
|
window.heliumElectron!.selectSource(sourceId);
|
|
};
|
|
|
|
const removeSourcesListener = () => {
|
|
if (!checkElectron()) return;
|
|
window.heliumElectron!.removeSourcesListener();
|
|
};
|
|
|
|
const isVenmicAvailable = async (): Promise<boolean> => {
|
|
if (!checkElectron()) return false;
|
|
try {
|
|
return await window.heliumElectron!.venmicAvailable();
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const getVenmicSources = async (): Promise<Record<string, string>[]> => {
|
|
if (!checkElectron()) return [];
|
|
try {
|
|
const sources = await window.heliumElectron!.venmicList();
|
|
audioSources.value = sources;
|
|
return sources;
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to list venmic sources:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const linkVenmicAudio = async (options: VenmicLinkOptions = {}): Promise<boolean> => {
|
|
if (!checkElectron()) return false;
|
|
try {
|
|
const success = await window.heliumElectron!.venmicLink(options);
|
|
isVenmicLinked.value = success;
|
|
return success;
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to link venmic:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const linkAllAudio = async (): Promise<boolean> => {
|
|
return linkVenmicAudio({
|
|
exclude: [],
|
|
ignore_devices: true,
|
|
only_speakers: true,
|
|
only_default_speakers: false,
|
|
});
|
|
};
|
|
|
|
const linkAppAudio = async (appName: string): Promise<boolean> => {
|
|
return linkVenmicAudio({
|
|
include: [{ 'application.name': appName }],
|
|
});
|
|
};
|
|
|
|
const unlinkVenmicAudio = async (): Promise<boolean> => {
|
|
if (!checkElectron()) return false;
|
|
try {
|
|
const success = await window.heliumElectron!.venmicUnlink();
|
|
isVenmicLinked.value = !success;
|
|
return success;
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to unlink venmic:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const startScreenShareWithAudio = async (options: {
|
|
video?: boolean | MediaTrackConstraints;
|
|
audioSource?: 'all' | 'none' | string;
|
|
} = {}): Promise<MediaStream | null> => {
|
|
const { video = true, audioSource = 'all' } = options;
|
|
const platform = await getPlatformInfo();
|
|
|
|
try {
|
|
if (platform?.isLinux && platform.supportsVenmic && audioSource !== 'none') {
|
|
if (audioSource === 'all') {
|
|
await linkAllAudio();
|
|
} else {
|
|
await linkAppAudio(audioSource);
|
|
}
|
|
}
|
|
|
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
video,
|
|
audio: audioSource !== 'none',
|
|
});
|
|
|
|
return stream;
|
|
} catch (error) {
|
|
console.error('[useElectron] Failed to start screen share:', error);
|
|
if (platform?.isLinux && isVenmicLinked.value) {
|
|
await unlinkVenmicAudio();
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const stopScreenShare = async (stream: MediaStream | null) => {
|
|
if (stream) {
|
|
stream.getTracks().forEach(track => track.stop());
|
|
}
|
|
|
|
const platform = platformInfo.value;
|
|
if (platform?.isLinux && isVenmicLinked.value) {
|
|
await unlinkVenmicAudio();
|
|
}
|
|
};
|
|
|
|
const supportsAudioScreenShare = computed(() => {
|
|
if (!platformInfo.value) return false;
|
|
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(() => {
|
|
checkElectron();
|
|
if (isElectron.value) {
|
|
getPlatformInfo();
|
|
}
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
removeSourcesListener();
|
|
if (isVenmicLinked.value) {
|
|
unlinkVenmicAudio();
|
|
}
|
|
});
|
|
|
|
return {
|
|
isElectron: readonly(isElectron),
|
|
platformInfo: readonly(platformInfo),
|
|
audioSources: readonly(audioSources),
|
|
isVenmicLinked: readonly(isVenmicLinked),
|
|
supportsAudioScreenShare,
|
|
|
|
checkElectron,
|
|
getPlatformInfo,
|
|
getDesktopSources,
|
|
onSourcesAvailable,
|
|
selectSource,
|
|
removeSourcesListener,
|
|
|
|
isVenmicAvailable,
|
|
getVenmicSources,
|
|
linkVenmicAudio,
|
|
linkAllAudio,
|
|
linkAppAudio,
|
|
unlinkVenmicAudio,
|
|
getScreenPermissionStatus,
|
|
openScreenPermissionSettings,
|
|
|
|
startScreenShareWithAudio,
|
|
stopScreenShare,
|
|
};
|
|
}
|