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", | ||||
|  |  | |||
							
								
								
									
										8
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue