AudioShare: Even more granular selection, Allow device sharing (#750)

This commit is contained in:
Noah 2024-07-12 16:14:18 +02:00 committed by GitHub
parent 61bbd7f6aa
commit 9acc6652ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 18 deletions

View file

@ -72,7 +72,7 @@ ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
const { granularSelect } = Settings.store.audio ?? {}; const { granularSelect } = Settings.store.audio ?? {};
const targets = obtainVenmic() const targets = obtainVenmic()
?.list(granularSelect ? ["application.process.id"] : undefined) ?.list(granularSelect ? ["node.name"] : undefined)
.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 };

View file

@ -254,7 +254,13 @@ function AudioSettingsModal({
</Switch> </Switch>
<Switch <Switch
hideBorder hideBorder
onChange={v => (Settings.audio = { ...Settings.audio, ignoreDevices: v })} onChange={v =>
(Settings.audio = {
...Settings.audio,
ignoreDevices: v,
deviceSelect: v ? false : Settings.audio?.deviceSelect
})
}
value={Settings.audio?.ignoreDevices ?? true} value={Settings.audio?.ignoreDevices ?? true}
note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>} note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>}
> >
@ -271,6 +277,23 @@ function AudioSettingsModal({
> >
Granular Selection Granular Selection
</Switch> </Switch>
<Switch
hideBorder
onChange={value => {
Settings.audio = { ...Settings.audio, deviceSelect: value };
setAudioSources("None");
}}
value={Settings.audio?.deviceSelect ?? false}
disabled={Settings.audio?.ignoreDevices}
note={
<>
Allow to select devices such as microphones. Requires <b>Ignore Devices</b> to be turned
off.
</>
}
>
Device Selection
</Switch>
</Modals.ModalContent> </Modals.ModalContent>
<Modals.ModalFooter className="vcd-screen-picker-footer"> <Modals.ModalFooter className="vcd-screen-picker-footer">
<Button color={Button.Colors.TRANSPARENT} onClick={close}> <Button color={Button.Colors.TRANSPARENT} onClick={close}>
@ -423,6 +446,7 @@ function StreamSettings({
openSettings={openSettings} openSettings={openSettings}
includeSources={settings.includeSources} includeSources={settings.includeSources}
excludeSources={settings.excludeSources} excludeSources={settings.excludeSources}
deviceSelect={Settings.audio?.deviceSelect}
granularSelect={Settings.audio?.granularSelect} granularSelect={Settings.audio?.granularSelect}
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))} setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))} setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
@ -441,13 +465,23 @@ function hasMatchingProps(value: Node, other: Node) {
return Object.keys(value).every(key => value[key] === other[key]); return Object.keys(value).every(key => value[key] === other[key]);
} }
function mapToAudioItem(node: AudioSource, granularSelect?: 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 rtn: AudioItem[] = [];
const mediaClass = node["media.class"];
if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) {
return rtn;
}
if (!deviceSelect && node["device.id"]) {
return rtn;
}
const name = node["application.name"]; const name = node["application.name"];
if (name) { if (name) {
@ -458,9 +492,15 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
return rtn; return rtn;
} }
const binary = node["application.process.binary"]; const rawName = node["node.name"];
if (!name) { if (!name) {
rtn.push({ name: rawName, value: { "node.name": rawName } });
}
const binary = node["application.process.binary"];
if (!name && binary) {
rtn.push({ name: binary, value: { "application.process.binary": binary } }); rtn.push({ name: binary, value: { "application.process.binary": binary } });
} }
@ -469,10 +509,12 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
const first = rtn[0]; const first = rtn[0];
const firstValues = first.value as Node; const firstValues = first.value as Node;
if (pid) {
rtn.push({ rtn.push({
name: `${first.name} (${pid})`, name: `${first.name} (${pid})`,
value: { ...firstValues, "application.process.id": pid } value: { ...firstValues, "application.process.id": pid }
}); });
}
const mediaName = node["media.name"]; const mediaName = node["media.name"];
@ -483,16 +525,12 @@ function mapToAudioItem(node: AudioSource, granularSelect?: boolean): AudioItem[
}); });
} }
const mediaClass = node["media.class"]; if (mediaClass) {
if (!mediaClass) {
return rtn;
}
rtn.push({ rtn.push({
name: `${first.name} [${mediaClass}]`, name: `${first.name} [${mediaClass}]`,
value: { ...firstValues, "media.class": mediaClass } value: { ...firstValues, "media.class": mediaClass }
}); });
}
return rtn; return rtn;
} }
@ -535,6 +573,7 @@ function updateItems(setSources: (s: AudioSources) => void, sources?: AudioSourc
function AudioSourcePickerLinux({ function AudioSourcePickerLinux({
includeSources, includeSources,
excludeSources, excludeSources,
deviceSelect,
granularSelect, granularSelect,
openSettings, openSettings,
setIncludeSources, setIncludeSources,
@ -542,6 +581,7 @@ function AudioSourcePickerLinux({
}: { }: {
includeSources?: AudioSources; includeSources?: AudioSources;
excludeSources?: AudioSources; excludeSources?: AudioSources;
deviceSelect?: boolean;
granularSelect?: boolean; granularSelect?: boolean;
openSettings: () => void; openSettings: () => void;
setIncludeSources: (s: AudioSources) => void; setIncludeSources: (s: AudioSources) => void;
@ -592,7 +632,7 @@ function AudioSourcePickerLinux({
const allSources = sources.ok const allSources = sources.ok
? [...specialSources, ...sources.targets] ? [...specialSources, ...sources.targets]
.map(target => mapToAudioItem(target, granularSelect)) .map(target => mapToAudioItem(target, granularSelect, deviceSelect))
.flat() .flat()
.filter(uniqueName) .filter(uniqueName)
: []; : [];

View file

@ -30,6 +30,8 @@ export interface Settings {
audio?: { audio?: {
workaround?: boolean; workaround?: boolean;
deviceSelect?: boolean;
granularSelect?: boolean; granularSelect?: boolean;
ignoreVirtual?: boolean; ignoreVirtual?: boolean;