navigator.mediaDevices.chromiumGetDisplayMedia = navigator.mediaDevices.getDisplayMedia; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } const getAudioDevice = async (nameOfAudioDevice) => { await navigator.mediaDevices.getUserMedia({ audio: true, }); let audioDevice; while (audioDevice === undefined) { let devices = await navigator.mediaDevices.enumerateDevices(); audioDevice = devices.find(({ label }) => label === nameOfAudioDevice); if (!audioDevice) userscript.log( `Did not find '${nameOfAudioDevice}', trying again in 100ms` ); await sleep(100); } userscript.log(`Found '${nameOfAudioDevice}'`); return audioDevice; }; function setGetDisplayMedia(video = true, overrideArgs = undefined) { const getDisplayMedia = async (...args) => { var id; try { let myDiscordAudioSink = await getAudioDevice( "discord-screenaudio-virtmic" ); id = myDiscordAudioSink.deviceId; } catch (error) { id = "default"; } let captureSystemAudioStream = await navigator.mediaDevices.getUserMedia({ audio: { // We add our audio constraints here, to get a list of supported constraints use navigator.mediaDevices.getSupportedConstraints(); // We must capture a microphone, we use default since its the only deviceId that is the same for every Chromium user deviceId: { exact: id, }, // We want auto gain control, noise cancellation and noise suppression disabled so that our stream won't sound bad autoGainControl: false, echoCancellation: false, noiseSuppression: false, // By default Chromium sets channel count for audio devices to 1, we want it to be stereo in case we find a way for Discord to accept stereo screenshare too channelCount: 2, // You can set more audio constraints here, bellow are some examples //latency: 0, //sampleRate: 48000, //sampleSize: 16, //volume: 1.0 }, }); let [track] = captureSystemAudioStream.getAudioTracks(); const gdm = await navigator.mediaDevices.chromiumGetDisplayMedia( ...(overrideArgs ? [overrideArgs] : args || [{ video: true, audio: true }]) ); gdm.addTrack(track); if (!video) for (const track of gdm.getVideoTracks()) track.enabled = false; return gdm; }; navigator.mediaDevices.getDisplayMedia = getDisplayMedia; } setGetDisplayMedia(); let userscript; let muteBtn; let deafenBtn; let streamStartBtn; let streamStartBtnInitialDisplay; let streamStartBtnClone; let resolutionString; const clonedElements = []; const hiddenElements = []; let wasStreamActive = false; function createButton(text, onClick) { const button = document.createElement("button"); button.style.marginBottom = "20px"; button.classList = "button-ejjZWC lookFilled-1H2Jvj colorBrand-2M3O3N sizeSmall-3R2P2p grow-2T4nbg"; button.innerText = text; button.addEventListener("click", onClick); return button; } function createSwitch(text, enabled, onClick) { const container = document.createElement("div"); container.style.marginBottom = "20px"; container.className = "labelRow-NnoUIp"; const label = document.createElement("label"); label.innerText = text; label.className = "title-2yADjX"; container.appendChild(label); const svg = document.createElement("div"); container.appendChild(svg); function setSvgDisabled() { svg.innerHTML = `
`; } function setSvgEnabled() { svg.innerHTML = `
`; } function updateSvg() { if (enabled) setSvgEnabled(); else setSvgDisabled(); } container.addEventListener("click", () => { enabled = !enabled; updateSvg(); onClick(enabled); }); updateSvg(); return container; } // Fix for broken discord notifications after restart // (https://github.com/maltejur/discord-screenaudio/issues/17) Notification.requestPermission(); setTimeout(() => { new QWebChannel(qt.webChannelTransport, (channel) => { userscript = channel.objects.userscript; main(); }); }); function main() { userscript.muteToggled.connect(() => { muteBtn && muteBtn.click(); }); userscript.deafenToggled.connect(() => { deafenBtn && deafenBtn.click(); }); userscript.streamStarted.connect((video, width, height, frameRate) => { resolutionString = video ? `${height}p ${frameRate}FPS` : "Audio Only"; setGetDisplayMedia(video, { audio: true, video: { width, height, frameRate }, }); streamStartBtn.click(); streamStartBtn.style.display = streamStartBtnInitialDisplay; streamStartBtnClone.remove(); }); setInterval(async () => { const streamActive = document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU") .length > 0; if (!streamActive && wasStreamActive) userscript.stopVirtmic(); wasStreamActive = streamActive; if (streamActive) { clonedElements.forEach((el) => { el.remove(); }); clonedElements.length = 0; hiddenElements.forEach((el) => { el.style.display = "block"; }); hiddenElements.length = 0; } else { for (const el of [ document.getElementsByClassName("actionButtons-2vEOUh")?.[0] ?.children[1], document.querySelector( ".wrapper-3t3Yqv > div > div > div > div > .controlButton-2PMNom" ), ]) { if (!el) continue; if (el.classList.contains("discord-screenaudio-cloned")) continue; streamStartBtn = el; streamStartBtn.classList.add("discord-screenaudio-cloned"); streamStartBtnClone = streamStartBtn.cloneNode(true); streamStartBtnClone.title = "Share Your Screen with Audio"; streamStartBtnClone.addEventListener("click", () => { userscript.showStreamDialog(); }); streamStartBtnInitialDisplay = streamStartBtn.style.display; streamStartBtn.style.display = "none"; streamStartBtn.parentNode.insertBefore(streamStartBtnClone, el); clonedElements.push(streamStartBtnClone); hiddenElements.push(streamStartBtn); } } // Add about text in settings if ( document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0 ) { for (const el of document.getElementsByClassName("info-3pQQBb")) { let aboutEl; if (userscript.kxmlgui) { aboutEl = document.createElement("a"); aboutEl.addEventListener("click", () => { userscript.showHelpMenu(); }); } else { aboutEl = document.createElement("div"); } aboutEl.innerText = `discord-screenaudio ${userscript.version}`; aboutEl.style.fontSize = "12px"; aboutEl.style.color = "var(--text-muted)"; aboutEl.style.textTransform = "none"; aboutEl.style.display = "inline-block"; aboutEl.style.width = "100%"; aboutEl.classList.add("dirscordScreenaudioAboutText"); aboutEl.style.cursor = "pointer"; el.appendChild(aboutEl); } } // Remove stream settings if stream is active document.getElementById("manage-streams-change-windows")?.remove(); document.querySelector(`[aria-label="Stream Settings"]`)?.remove(); // Add event listener for keybind tab if ( document .getElementById("keybinds-tab") ?.getElementsByClassName( "container-3jbRo5 info-1hMolH browserNotice-1u-Y5o" ).length ) { const el = document .getElementById("keybinds-tab") .getElementsByClassName("children-1xdcWE")[0]; const div = document.createElement("div"); div.style.marginBottom = "50px"; div.appendChild( createButton("Edit Global Keybinds", () => { userscript.showShortcutsDialog(); }) ); el.innerHTML = ""; el.appendChild(div); } const buttonContainer = document.getElementsByClassName("container-YkUktl")[0]; if (!buttonContainer) { userscript.log( "Cannot locate Mute/Deafen/Settings button container, please report this on GitHub" ); } muteBtn = buttonContainer ? buttonContainer.getElementsByClassName( "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" )[0] : null; deafenBtn = buttonContainer ? buttonContainer.getElementsByClassName( "button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F" )[1] : null; if (resolutionString) { for (const el of document.getElementsByClassName( "qualityIndicator-39wQDy" )) { el.innerHTML = resolutionString; } } const accountTab = document.getElementById("my-account-tab"); if (accountTab) { const discordScreenaudioSettings = document.getElementById( "discord-screenaudio-settings" ); if (!discordScreenaudioSettings) { const firstDivider = accountTab.getElementsByClassName( "divider-3nqZNm marginTop40-Q4o1tS" )[0]; if (firstDivider) { const section = document.createElement("div"); section.className = "marginTop40-Q4o1tS"; section.id = "discord-screenaudio-settings"; const title = document.createElement("h2"); title.className = "h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH"; title.innerText = "discord-screenaudio"; section.appendChild(title); section.appendChild( createButton("Edit Global Keybinds", () => { userscript.showShortcutsDialog(); }) ); section.appendChild( createSwitch( "Move discord-screenaudio to the system tray instead of closing", await userscript.getBoolPref("trayIcon", false), (enabled) => { userscript.setTrayIcon(enabled); } ) ); section.appendChild( createSwitch( "Start discord-screenaudio hidden to tray", await userscript.getPref("startHidden", false), (hidden) => { userscript.setPref("startHidden", hidden); } ) ); const divider = document.createElement("div"); divider.className = "divider-3nqZNm marginTop40-Q4o1tS"; firstDivider.after(section); section.after(divider); } } } }, 500); }