Update venmic to 7.2.1 (#1319)

This commit is contained in:
Noah
2026-09-04 03:24:03 +02:00
committed by GitHub
parent d92eecad03
commit aa2753e1d5
8 changed files with 98 additions and 104 deletions
+1 -1
View File
@@ -31,7 +31,7 @@
"electron-updater": "6.6.2" "electron-updater": "6.6.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"@vencord/venmic": "^6.1.0" "@vencord/venmic": "^7.2.1"
}, },
"devDependencies": { "devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2", "@fal-works/esbuild-plugin-global-externals": "^2.1.2",
+5 -5
View File
@@ -96,8 +96,8 @@ importers:
version: 3.7.0 version: 3.7.0
optionalDependencies: optionalDependencies:
'@vencord/venmic': '@vencord/venmic':
specifier: ^6.1.0 specifier: ^7.2.1
version: 6.1.0(supports-color@7.2.0) version: 7.2.1(supports-color@7.2.0)
packages/libvesktop: packages/libvesktop:
devDependencies: devDependencies:
@@ -562,8 +562,8 @@ packages:
'@types/react': 18.3.1 '@types/react': 18.3.1
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@vencord/venmic@6.1.0': '@vencord/venmic@7.2.1':
resolution: {integrity: sha512-YiCtzml/W8tYbGhu3jm5jfbbEnl2slKKARNK0jO+8qV979k9eFnfIRTxvhMN/SWq1h8ZNJdXVwvXpffQwq0RuA==} resolution: {integrity: sha512-VwRDQDpLMgN328VeCdnZ/UwJpoBUbAi3rdXK3QqhCHC/u5p8mhze3/R1Y1Beoxe1MICITelE2dzATQ7pJU/xuw==}
engines: {node: '>=14.15'} engines: {node: '>=14.15'}
os: [linux] os: [linux]
@@ -3324,7 +3324,7 @@ snapshots:
ts-pattern: 5.9.0 ts-pattern: 5.9.0
type-fest: 5.8.0 type-fest: 5.8.0
'@vencord/venmic@6.1.0(supports-color@7.2.0)': '@vencord/venmic@7.2.1(supports-color@7.2.0)':
dependencies: dependencies:
cmake-js: 7.4.0(supports-color@7.2.0) cmake-js: 7.4.0(supports-color@7.2.0)
node-addon-api: 8.9.0 node-addon-api: 8.9.0
+1
View File
@@ -14,3 +14,4 @@ packages:
- packages/* - packages/*
minimumReleaseAgeExclude: minimumReleaseAgeExclude:
- electron - electron
- '@vencord/*'
+38 -48
View File
@@ -66,70 +66,60 @@ function getRendererAudioServicePid() {
); );
} }
function buildLinkData({ include, exclude }: { include?: Node[]; exclude?: Node[] }) {
const pid = getRendererAudioServicePid();
const { mute, ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround, onlySpeakers, onlyDefaultSpeakers } =
Settings.store.audio ?? {};
const data: LinkData = {
mute: mute ?? true,
include: include ?? [],
exclude: exclude ?? [],
only_speakers: onlySpeakers,
ignore_devices: ignoreDevices,
only_default_speakers: onlyDefaultSpeakers
};
data.exclude.push({ "application.process.id": pid });
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return data;
}
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
const audioPid = getRendererAudioServicePid(); const audioPid = getRendererAudioServicePid();
const { granularSelect } = Settings.store.audio ?? {}; const { granularSelect } = Settings.store.audio ?? {};
const targets = obtainVenmic() const targets = obtainVenmic()
?.list(granularSelect ? ["node.name"] : undefined) ?.list(granularSelect ? [] : ["application.name", "node.name"])
.filter(s => s["application.process.id"] !== audioPid); .filter(s => s["application.process.id"] !== audioPid);
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
}); });
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, include: Node[]) => { ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, include: Node[]) => {
const pid = getRendererAudioServicePid(); return obtainVenmic()?.link(buildLinkData({ include }));
const { ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround } = Settings.store.audio ?? {};
const data: LinkData = {
include,
exclude: [{ "application.process.id": pid }],
ignore_devices: ignoreDevices
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return obtainVenmic()?.link(data);
}); });
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, exclude: Node[]) => { ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, exclude: Node[]) => {
const pid = getRendererAudioServicePid(); return obtainVenmic()?.link(buildLinkData({ exclude }));
});
const { workaround, ignoreDevices, ignoreInputMedia, ignoreVirtual, onlySpeakers, onlyDefaultSpeakers } = ipcMain.handle(IpcEvents.VIRT_MIC_UNMUTE, () => {
Settings.store.audio ?? {}; return obtainVenmic()?.unmute();
const data: LinkData = {
include: [],
exclude: [{ "application.process.id": pid }, ...exclude],
only_speakers: onlySpeakers,
ignore_devices: ignoreDevices,
only_default_speakers: onlyDefaultSpeakers
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return obtainVenmic()?.link(data);
}); });
ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink()); ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink());
+1
View File
@@ -92,6 +92,7 @@ export const VesktopNative = {
>(IpcEvents.VIRT_MIC_LIST), >(IpcEvents.VIRT_MIC_LIST),
start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include), start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include),
startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude), startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude),
unmute: () => invoke<void>(IpcEvents.VIRT_MIC_UNMUTE),
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP) stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
}, },
clipboard: { clipboard: {
+49 -49
View File
@@ -121,15 +121,25 @@ addPatch({
if (isLinux) { if (isLinux) {
onceReady.then(() => { onceReady.then(() => {
FluxDispatcher.subscribe("STREAM_CLOSE", ({ streamKey }: { streamKey: string }) => { const ownsStream = (streamKey: string) => {
const owner = streamKey.split(":").at(-1); return streamKey.split(":").at(-1) === UserStore.getCurrentUser().id;
};
if (owner !== UserStore.getCurrentUser().id) { FluxDispatcher.subscribe("STREAM_CLOSE", ({ streamKey }: { streamKey: string }) => {
if (!ownsStream(streamKey)) {
return; return;
} }
VesktopNative.virtmic.stop(); VesktopNative.virtmic.stop();
}); });
FluxDispatcher.subscribe("STREAM_UPDATE", ({ streamKey }: { streamKey: string }) => {
if (!ownsStream(streamKey)) {
return;
}
VesktopNative.virtmic.unmute();
});
}); });
} }
@@ -216,6 +226,13 @@ function AudioSettingsModal({
actions={[{ text: "Back", variant: "secondary", onClick: close }]} actions={[{ text: "Back", variant: "secondary", onClick: close }]}
> >
<div className={cl("venmic-settings")}> <div className={cl("venmic-settings")}>
<FormSwitch
title="Initial Mute"
description="Fix an initial audio spike caused by chromium."
hideBorder
onChange={v => (Settings.audio = { ...Settings.audio, mute: v })}
value={Settings.audio?.mute ?? true}
/>
<FormSwitch <FormSwitch
title="Microphone Workaround" title="Microphone Workaround"
description="Work around an issue that causes the microphone to be shared instead of the correct audio. Only enable if you're experiencing this issue." description="Work around an issue that causes the microphone to be shared instead of the correct audio. Only enable if you're experiencing this issue."
@@ -447,78 +464,61 @@ function isSpecialSource(value?: AudioSource | AudioSources): value is SpecialSo
return typeof value === "string"; return typeof value === "string";
} }
function hasMatchingProps(value: Node, other: Node) {
return Object.keys(value).every(key => value[key] === other[key]);
}
function mapToAudioItem(node: AudioSource, granularSelect?: boolean, deviceSelect?: boolean): AudioItem[] { function mapToAudioItem(node: AudioSource, granularSelect?: boolean, deviceSelect?: boolean): AudioItem[] {
if (isSpecialSource(node)) { if (isSpecialSource(node)) {
return [{ name: node, value: node }]; return [{ name: node, value: node }];
} }
const rtn: AudioItem[] = [];
const mediaClass = node["media.class"]; const mediaClass = node["media.class"];
if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) { if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) {
return rtn; return [];
} }
if (!deviceSelect && node["device.id"]) { if (!deviceSelect && node["device.id"]) {
return rtn; return [];
} }
const name = node["application.name"]; const preferred = ["application.name", "node.description", "node.name", "application.process.binary"] as const;
const prop = preferred.find(prop => node[prop]);
if (name) { if (!prop) {
rtn.push({ name: name, value: { "application.name": name } }); return [];
} }
const name = node[prop]!;
const items: AudioItem[] = [{ name: name, value: { [prop]: name } }];
if (!granularSelect) { if (!granularSelect) {
return rtn; return items;
} }
const rawName = node["node.name"]; let granularName = name;
const granularProps: Node = { [prop]: name };
if (!name) { const append = (name: string, brackets: string) => {
rtn.push({ name: rawName, value: { "node.name": rawName } }); const value = node[name];
}
const binary = node["application.process.binary"]; if (!value) {
return;
}
if (!name && binary) { granularName += ` ${brackets[0]}${value}${brackets[1]}`;
rtn.push({ name: binary, value: { "application.process.binary": binary } }); granularProps[name] = value;
} };
const pid = node["application.process.id"]; append("application.process.id", "<>");
append("application.process.binary", "()");
append("media.name", "[]");
const first = rtn[0]; items.push({ name: granularName, value: granularProps });
const firstValues = first.value as Node;
if (pid) { return items;
rtn.push({ }
name: `${first.name} (${pid})`,
value: { ...firstValues, "application.process.id": pid }
});
}
const mediaName = node["media.name"]; function hasMatchingProps(value: Node, other: Node) {
const keys = Object.keys(value);
if (mediaName) { return keys.length === Object.keys(other).length && keys.every(key => value[key] === other[key]);
rtn.push({
name: `${first.name} [${mediaName}]`,
value: { ...firstValues, "media.name": mediaName }
});
}
if (mediaClass) {
rtn.push({
name: `${first.name} [${mediaClass}]`,
value: { ...firstValues, "media.class": mediaClass }
});
}
return rtn;
} }
function isItemSelected(sources?: AudioSources) { function isItemSelected(sources?: AudioSources) {
+1
View File
@@ -49,6 +49,7 @@ export const enum IpcEvents {
VIRT_MIC_LIST = "VCD_VIRT_MIC_LIST", VIRT_MIC_LIST = "VCD_VIRT_MIC_LIST",
VIRT_MIC_START = "VCD_VIRT_MIC_START", VIRT_MIC_START = "VCD_VIRT_MIC_START",
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL", VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
VIRT_MIC_UNMUTE = "VCD_VIRT_MIC_UNMUTE",
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP", VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE", CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE",
+1
View File
@@ -50,6 +50,7 @@ export interface Settings {
ignoreDevices?: boolean; ignoreDevices?: boolean;
ignoreInputMedia?: boolean; ignoreInputMedia?: boolean;
mute?: boolean;
onlySpeakers?: boolean; onlySpeakers?: boolean;
onlyDefaultSpeakers?: boolean; onlyDefaultSpeakers?: boolean;
}; };