Improve Venmic Usability (#504)

This commit is contained in:
Noah 2024-04-20 16:09:40 +02:00 committed by GitHub
parent 8eaa5206b9
commit 2649598361
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 127 additions and 59 deletions

View file

@ -27,7 +27,7 @@
"arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8" "arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8"
}, },
"optionalDependencies": { "optionalDependencies": {
"@vencord/venmic": "^3.4.1" "@vencord/venmic": "^3.4.2"
}, },
"devDependencies": { "devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2", "@fal-works/esbuild-plugin-global-externals": "^2.1.2",

View file

@ -11,8 +11,8 @@ dependencies:
optionalDependencies: optionalDependencies:
'@vencord/venmic': '@vencord/venmic':
specifier: ^3.4.1 specifier: ^3.4.2
version: 3.4.1 version: 3.4.2
devDependencies: devDependencies:
'@fal-works/esbuild-plugin-global-externals': '@fal-works/esbuild-plugin-global-externals':
@ -1000,8 +1000,8 @@ packages:
type-fest: 3.13.1 type-fest: 3.13.1
dev: true dev: true
/@vencord/venmic@3.4.1: /@vencord/venmic@3.4.2:
resolution: {integrity: sha512-PkMXx53nxiYBLWxiMRaBjBm8aTTJTcueKsMZ0v35TtIQ93yfuSzfCilDFLs3kYz1uQHArTaI9IGQykgmSfe/2w==} resolution: {integrity: sha512-nwGjarof1wVvecGksGONfb+PduhY4gSuHTm39LktiQayHS69+yv4lepq4k1lxZzjOgFTy5VXfmJqxt+BpqoUUQ==}
engines: {node: '>=14.15'} engines: {node: '>=14.15'}
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true

View file

@ -4,17 +4,58 @@
* Copyright (c) 2023 Vendicated and Vencord contributors * Copyright (c) 2023 Vendicated and Vencord contributors
*/ */
import type { PatchBay } from "@vencord/venmic"; import type { PatchBay as PatchBayType } from "@vencord/venmic";
import { app, ipcMain } from "electron"; import { app, ipcMain } from "electron";
import { join } from "path"; import { join } from "path";
import { IpcEvents } from "shared/IpcEvents"; import { IpcEvents } from "shared/IpcEvents";
import { STATIC_DIR } from "shared/paths"; import { STATIC_DIR } from "shared/paths";
type LinkData = Parameters<PatchBay["link"]>[0]; type LinkData = Parameters<PatchBayType["link"]>[0];
let PatchBay: typeof PatchBayType | undefined;
let patchBayInstance: PatchBayType | undefined;
let imported = false;
let initialized = false; let initialized = false;
let patchBay: import("@vencord/venmic").PatchBay | undefined;
let isGlibcxxToOld = false; let hasPipewirePulse = false;
let isGlibCxxOutdated = false;
function importVenmic() {
if (imported) {
return;
}
imported = true;
try {
PatchBay = (require(join(STATIC_DIR, `dist/venmic-${process.arch}.node`)) as typeof import("@vencord/venmic"))
.PatchBay;
hasPipewirePulse = PatchBay.hasPipeWire();
} catch (e: any) {
console.error("Failed to import venmic", e);
isGlibCxxOutdated = (e?.stack || e?.message || "").toLowerCase().includes("glibc");
}
}
function obtainVenmic() {
if (!imported) {
importVenmic();
}
if (PatchBay && !initialized) {
initialized = true;
try {
patchBayInstance = new PatchBay();
} catch (e: any) {
console.error("Failed to instantiate venmic", e);
}
}
return patchBayInstance;
}
function getRendererAudioServicePid() { function getRendererAudioServicePid() {
return ( return (
@ -25,33 +66,17 @@ function getRendererAudioServicePid() {
); );
} }
function obtainVenmic() {
if (!initialized) {
initialized = true;
try {
const { PatchBay } = require(
join(STATIC_DIR, `dist/venmic-${process.arch}.node`)
) as typeof import("@vencord/venmic");
patchBay = new PatchBay();
} catch (e: any) {
console.error("Failed to initialise venmic. Make sure you're using pipewire", e);
isGlibcxxToOld = (e?.stack || e?.message || "").toLowerCase().includes("glibc");
}
}
return patchBay;
}
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
const audioPid = getRendererAudioServicePid(); const audioPid = getRendererAudioServicePid();
const list = obtainVenmic() const list = obtainVenmic()
?.list() ?.list()
.filter(s => s["application.process.id"] !== audioPid) .filter(s => s["application.process.id"] !== audioPid)
.map(s => s["application.name"]); .map(s => s["application.name"]);
return list const uniqueTargets = [...new Set(list)];
? { ok: true, targets: [...new Set(list)] } // Remove duplicates
: { ok: false, isGlibcxxToOld }; return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
}); });
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => { ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => {

View file

@ -62,7 +62,9 @@ export const VesktopNative = {
/** only available on Linux. */ /** only available on Linux. */
virtmic: { virtmic: {
list: () => list: () =>
invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST), invoke<
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean }
>(IpcEvents.VIRT_MIC_LIST),
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround), start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) =>
invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers),

View file

@ -331,9 +331,13 @@ function AudioSourcePickerLinux({
setOnlyDefaultSpeakers(b: boolean): void; setOnlyDefaultSpeakers(b: boolean): void;
}) { }) {
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), { const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
fallbackValue: { ok: true, targets: [] } fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
}); });
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null; const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
return ( return (
<> <>
@ -345,32 +349,38 @@ function AudioSourcePickerLinux({
<Forms.FormTitle>Audio Source</Forms.FormTitle> <Forms.FormTitle>Audio Source</Forms.FormTitle>
)} )}
{!sources.ok && {!sources.ok && sources.isGlibCxxOutdated && (
(sources.isGlibcxxToOld ? (
<Forms.FormText> <Forms.FormText>
Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you Failed to retrieve Audio Sources because your C++ library is too old to run
would like to stream with Audio, see{" "} <a href="https://github.com/Vencord/venmic" target="_blank">
<a venmic
href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a"
target="_blank"
>
this guide
</a> </a>
. See{" "}
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
this guide
</a>{" "}
for possible solutions.
</Forms.FormText> </Forms.FormText>
) : ( )}
<Forms.FormText>
Failed to retrieve Audio Sources. If you would like to stream with Audio, make sure you're
using Pipewire, not Pulseaudio
</Forms.FormText>
))}
{allSources && ( {hasPipewirePulse || ignorePulseWarning ? (
allSources && (
<Select <Select
options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))} options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))}
isSelected={s => s === audioSource} isSelected={s => s === audioSource}
select={setAudioSource} select={setAudioSource}
serialize={String} serialize={String}
/> />
)
) : (
<Text variant="text-sm/normal">
Could not find pipewire-pulse. This usually means that you do not run pipewire as your main
audio-server. <br />
You can still continue, however, please beware that you can only share audio of apps that are
running under pipewire.
<br />
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing</a>
</Text>
)} )}
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} /> <Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />

View file

@ -0,0 +1,24 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { addPatch } from "./shared";
addPatch({
patches: [
{
find: "lastOutputSystemDevice.justChanged",
replacement: {
// eslint-disable-next-line no-useless-escape
match: /(\i)\.default\.getState\(\).neverShowModal/,
replace: "$& || $self.shouldIgnore($1)"
}
}
],
shouldIgnore(state: any) {
return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share";
}
});

View file

@ -7,6 +7,7 @@
// TODO: Possibly auto generate glob if we have more patches in the future // TODO: Possibly auto generate glob if we have more patches in the future
import "./enableNotificationsByDefault"; import "./enableNotificationsByDefault";
import "./platformClass"; import "./platformClass";
import "./hideSwitchDevice";
import "./screenShareFixes"; import "./screenShareFixes";
import "./spellCheck"; import "./spellCheck";
import "./windowsTitleBar"; import "./windowsTitleBar";

View file

@ -11,11 +11,12 @@ import { isLinux } from "renderer/utils";
const logger = new Logger("VesktopStreamFixes"); const logger = new Logger("VesktopStreamFixes");
if (isLinux) { if (isLinux) {
const original = navigator.mediaDevices.getDisplayMedia; const originalMedia = navigator.mediaDevices.getDisplayMedia;
const originalDevices = navigator.mediaDevices.enumerateDevices;
async function getVirtmic() { async function getVirtmic() {
try { try {
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await originalDevices();
const audioDevice = devices.find(({ label }) => label === "vencord-screen-share"); const audioDevice = devices.find(({ label }) => label === "vencord-screen-share");
return audioDevice?.deviceId; return audioDevice?.deviceId;
} catch (error) { } catch (error) {
@ -23,8 +24,13 @@ if (isLinux) {
} }
} }
navigator.mediaDevices.enumerateDevices = async function () {
const result = await originalDevices.call(this);
return result.filter(x => x.label !== "vencord-screen-share");
};
navigator.mediaDevices.getDisplayMedia = async function (opts) { navigator.mediaDevices.getDisplayMedia = async function (opts) {
const stream = await original.call(this, opts); const stream = await originalMedia.call(this, opts);
const id = await getVirtmic(); const id = await getVirtmic();
const frameRate = Number(currentSettings?.fps); const frameRate = Number(currentSettings?.fps);