mirror of
https://github.com/smartfrigde/armcord.git
synced 2024-08-14 23:56:58 +00:00
feat: linux pulseaudio screenshare
This commit is contained in:
parent
3fc1757324
commit
e1e472bde3
12 changed files with 17 additions and 104 deletions
|
@ -36,7 +36,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/parser": "^5.59.2",
|
||||
"copyfiles": "^2.4.1",
|
||||
"electron": "29.1.4",
|
||||
"electron": "30.0.1",
|
||||
"electron-builder": "^24.9.1",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-dmitmel": "github:dmitmel/eslint-config-dmitmel",
|
||||
|
|
|
@ -41,8 +41,8 @@ devDependencies:
|
|||
specifier: ^2.4.1
|
||||
version: 2.4.1
|
||||
electron:
|
||||
specifier: 29.1.4
|
||||
version: 29.1.4
|
||||
specifier: 30.0.1
|
||||
version: 30.0.1
|
||||
electron-builder:
|
||||
specifier: ^24.9.1
|
||||
version: 24.9.1
|
||||
|
@ -1034,8 +1034,8 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron@29.1.4:
|
||||
resolution: {integrity: sha512-IWXys0SqgmIfrqXusUGQC0gGG7CCqA5vfmNsUMj8dFkAnK3lisKyjSESStWlrsste/OX/AAC5wsVlf23reUNnw==}
|
||||
/electron@30.0.1:
|
||||
resolution: {integrity: sha512-iwxkI/n2wBd29NH7TH0ZY8aWGzCoKpzJz+D10u7aGSJi1TV6d4MSM3rWyKvT/UkAHkTKOEgYfUyCa2vWQm8L0g==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
getLang,
|
||||
getLangName,
|
||||
getVersion,
|
||||
getWindowState,
|
||||
modInstallState,
|
||||
packageVersion,
|
||||
setConfigBulk,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {BrowserWindow, app, globalShortcut, ipcMain, shell} from "electron";
|
||||
import {BrowserWindow, globalShortcut, ipcMain, shell} from "electron";
|
||||
import path from "path";
|
||||
import {getConfig, registerGlobalKeybinds, setConfig} from "../utils";
|
||||
let keybindWindow: BrowserWindow;
|
||||
|
|
10
src/main.ts
10
src/main.ts
|
@ -11,15 +11,12 @@ import {
|
|||
getConfigSync,
|
||||
injectElectronFlags,
|
||||
installModLoader,
|
||||
modInstallState,
|
||||
setConfig,
|
||||
setLang,
|
||||
setWindowState,
|
||||
sleep
|
||||
setLang
|
||||
} from "./utils";
|
||||
import "./extensions/mods";
|
||||
import "./tray";
|
||||
import {createCustomWindow, createNativeWindow, createTransparentWindow, mainWindow} from "./window";
|
||||
import {createCustomWindow, createNativeWindow, createTransparentWindow} from "./window";
|
||||
import path from "path";
|
||||
import {createTManagerWindow} from "./themeManager/main";
|
||||
import {createSplashWindow} from "./splash/main";
|
||||
|
@ -67,7 +64,8 @@ if (!app.requestSingleInstanceLock() && getConfigSync("multiInstance") == (false
|
|||
crashReporter.start({uploadToServer: false});
|
||||
// enable webrtc capturer for wayland
|
||||
if (process.platform === "linux" && process.env.XDG_SESSION_TYPE?.toLowerCase() === "wayland") {
|
||||
app.commandLine.appendSwitch("enable-features=WebRTCPipeWireCapturer");
|
||||
app.commandLine.appendSwitch("enable-features", "WebRTCPipeWireCapturer,PulseaudioLoopbackForScreenShare");
|
||||
app.commandLine.appendSwitch("disable-features", "WebRtcAllowInputVolumeAdjustment");
|
||||
console.log("Wayland detected, using PipeWire for video capture.");
|
||||
}
|
||||
// work around chrome 66 disabling autoplay by default
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import {addScript, addStyle} from "../utils";
|
||||
import {addStyle} from "../utils";
|
||||
import {WebviewTag} from "electron";
|
||||
|
||||
var webview = `<webview src="${path.join("file://", __dirname, "../", "/settings/settings.html")}" preload="${path.join(
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
import {spawn} from "child_process";
|
||||
import fs from "fs";
|
||||
import {app} from "electron";
|
||||
import path from "path";
|
||||
|
||||
// quick and dirty way for audio screenshare on Linux
|
||||
// please PR a better non-shell solution that isn't a dependency mess
|
||||
//src https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux
|
||||
interface SinkInput {
|
||||
id: string;
|
||||
properties: Record<string, string>;
|
||||
}
|
||||
|
||||
const parseSinkInputs = (input: string): SinkInput[] => {
|
||||
const regex = /Sink Input #(\d+)\n([\s\S]+?)(?=\nSink Input #|\n{2,}|$)/g;
|
||||
const result: SinkInput[] = [];
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(input))) {
|
||||
const sinkInput: SinkInput = {
|
||||
id: match[1],
|
||||
properties: {}
|
||||
};
|
||||
|
||||
const propertiesRegex = /(\w+)\s*=\s*"([^"]*)"/g;
|
||||
let propertiesMatch;
|
||||
|
||||
while ((propertiesMatch = propertiesRegex.exec(match[2]))) {
|
||||
sinkInput.properties[propertiesMatch[1]] = propertiesMatch[2];
|
||||
}
|
||||
|
||||
result.push(sinkInput);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export function createVirtualDevice(sinkInput: number) {
|
||||
var script = `
|
||||
SINK_NAME=armcord
|
||||
export LC_ALL=C
|
||||
DEFAULT_OUTPUT=$(pactl info|sed -n -e 's/^.*Default Sink: //p')
|
||||
pactl load-module module-null-sink sink_name=$SINK_NAME
|
||||
pactl move-sink-input ${sinkInput} $SINK_NAME
|
||||
pactl load-module module-loopback source=$SINK_NAME.monitor sink=$DEFAULT_OUTPUT
|
||||
if pactl info|grep -w "PipeWire">/dev/null; then
|
||||
nohup pw-loopback --capture-props='node.target='$SINK_NAME --playback-props='media.class=Audio/Source node.name=virtmic node.description="virtmic"' >/dev/null &
|
||||
else
|
||||
pactl load-module module-remap-source master=$SINK_NAME.monitor source_name=virtmic source_properties=device.description=virtmic
|
||||
fi`;
|
||||
let scriptPath = path.join(app.getPath("temp"), "/", "script.sh");
|
||||
spawn("chmod +x " + scriptPath);
|
||||
fs.writeFileSync(scriptPath, script, "utf-8");
|
||||
const exec = spawn(scriptPath);
|
||||
}
|
||||
|
||||
export function isAudioSupported(): boolean {
|
||||
const pactl = spawn("pactl");
|
||||
|
||||
pactl.on("close", (code) => {
|
||||
if (code == 0) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
export function getSinks() {
|
||||
const pactl = spawn("pactl list sink-inputs");
|
||||
|
||||
pactl.stderr.on("data", (data) => {
|
||||
console.log(data);
|
||||
return parseSinkInputs(data);
|
||||
});
|
||||
pactl.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
throw Error("Couldn't get list of available apps for audio stream");
|
||||
}
|
||||
});
|
||||
}
|
|
@ -41,8 +41,8 @@ function registerCustomHandler(): void {
|
|||
//console.log(id);
|
||||
capturerWindow.close();
|
||||
let result = {id, name, width: 9999, height: 9999};
|
||||
if (process.platform === "win32") {
|
||||
callback({video: result, audio: "loopback"});
|
||||
if (process.platform === "linux") {
|
||||
callback({video: result, audio: "loopbackWithMute"});
|
||||
} else {
|
||||
callback({video: result});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {BrowserWindow, app, ipcMain, shell} from "electron";
|
||||
import {BrowserWindow, app, shell} from "electron";
|
||||
import {getDisplayVersion} from "../utils";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as fs from "fs";
|
||||
import {Menu, MessageBoxOptions, Tray, app, dialog, nativeImage} from "electron";
|
||||
import {createInviteWindow, mainWindow} from "./window";
|
||||
import {getConfig, getConfigLocation, getDisplayVersion, setConfig, setWindowState} from "./utils";
|
||||
import {getConfig, getConfigLocation, getDisplayVersion, setConfig} from "./utils";
|
||||
import * as path from "path";
|
||||
import {createSettingsWindow} from "./settings/main";
|
||||
export let tray: any = null;
|
||||
|
|
|
@ -9,13 +9,9 @@ import {
|
|||
firstRun,
|
||||
getConfig,
|
||||
getWindowState,
|
||||
modInstallState,
|
||||
registerGlobalKeybinds,
|
||||
setConfig,
|
||||
setLang,
|
||||
setWindowState,
|
||||
sleep,
|
||||
transparency
|
||||
setWindowState
|
||||
} from "./utils";
|
||||
import {registerIpc} from "./ipc";
|
||||
import {setMenu} from "./menu";
|
||||
|
@ -24,7 +20,6 @@ import contextMenu from "electron-context-menu";
|
|||
import os from "os";
|
||||
import {tray} from "./tray";
|
||||
import {iconPath} from "./main";
|
||||
import {createSetupWindow} from "./setup/main";
|
||||
export let mainWindow: BrowserWindow;
|
||||
export let inviteWindow: BrowserWindow;
|
||||
let forceQuit = false;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting.
|
||||
"resolveJsonModule": true, // Allows you to import JSON files just like how you can require() them. Do note that if you're accessing any JSON files outside of src, it'll mess up dist.
|
||||
"lib": ["ES2020", "DOM"], // Specifies what common libraries you have access to. If you're working in Node.js, you'll want to leave out the DOM library. But do make sure to include "@types/node" because otherwise, variables like "console" won't be defined.
|
||||
|
||||
"noUnusedLocals": true, // Warns you if you have unused variables.
|
||||
// Output //
|
||||
"module": "CommonJS", // Compiles ES6 imports to require() syntax.
|
||||
"removeComments": false,
|
||||
|
|
Loading…
Reference in a new issue