Compare commits

...

9 Commits

Author SHA1 Message Date
Malte Jürgens
9faabe1f3e Merge pull request #66 from kherchel/fix/keybinds
Fix mute and deafen keybinds disconnecting from the call
2022-10-25 18:18:08 +00:00
Kacper Herchel
68473d04d8 Fix mute and deafen keybinds disconnecting from the call instead of performing their appropriate action 2022-10-25 17:01:10 +02:00
Malte Jürgens
7c8f72b8d8 fix 2022-10-11 22:17:53 +02:00
Malte Jürgens
27cdd9f9a5 code fixes 2022-10-11 19:48:57 +02:00
Malte Jürgens
d184823ee9 Enable smooth scrolling 2022-10-10 21:54:26 +02:00
Malte Jürgens
dd2beed4eb Implement Keybinds (#57) 2022-10-10 19:50:26 +00:00
Malte Jürgens
3740553aba Merge pull request #54 from D3SOX/improve-desktop-entry
Improve desktop entry
2022-09-29 16:47:49 +00:00
Nico
555f5c62f5 feat(desktop-entry): add categories and comment 2022-09-29 18:45:08 +02:00
Malte Jürgens
af3c62c263 update screenshot 2022-09-25 11:32:23 +02:00
12 changed files with 225 additions and 36 deletions

View File

@@ -23,6 +23,16 @@ if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS )
endif()
find_package(KF5XmlGui)
if(KF5XmlGui_FOUND)
add_definitions( -DKXMLGUI )
endif()
find_package(KF5GlobalAccel)
if(KF5GlobalAccel_FOUND)
add_definitions( -DKGLOBALACCEL )
endif()
set(discord-screenaudio_SRC
src/main.cpp
src/mainwindow.cpp
@@ -62,6 +72,12 @@ if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications)
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
endif()
if(KF5XmlGui_FOUND)
target_link_libraries(discord-screenaudio KF5::XmlGui)
endif()
if(KF5GlobalAccel_FOUND)
target_link_libraries(discord-screenaudio KF5::GlobalAccel)
endif()
install(TARGETS discord-screenaudio DESTINATION bin)
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps)

View File

@@ -11,7 +11,7 @@ Unlike a lot of other solutions, the audio here is directly fed into the
screenshare and not passed to the user microphone
([see explanation](#how-it-works)).
![Screenshot_20220718_194357](https://user-images.githubusercontent.com/48161361/179571245-11ea05f3-fb5e-4aef-9132-2736e122ef04.png)
![Screenshot_20220925_112945](https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png)
The purpose of this project is **not** to provide an alternative to the original
Discord client. Rather, it should be used in addition to the original client in
@@ -51,12 +51,14 @@ You have multiple options:
- Basic building tools
- CMake
- Qt5, QtWebEngine and Kf5Notifications
- Qt5 and QtWebEngine
- **PipeWire** (it currently doesn't work with PulseAudio)
- Git
- _Kf5Notifications (optional, for better notifications)_
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
On Debian:
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev pkg-config libpipewire-0.3-dev git`
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git`
### Building

View File

@@ -2,5 +2,7 @@
Type=Application
Name=discord-screenaudio
Exec=discord-screenaudio
Comment=A custom discord client that supports streaming with audio on Linux.
Icon=de.shorsh.discord-screenaudio
Categories=Network;InstantMessaging;
Terminal=false

View File

@@ -29,7 +29,7 @@
<screenshots>
<screenshot type="default">
<image>
https://user-images.githubusercontent.com/48161361/179571245-11ea05f3-fb5e-4aef-9132-2736e122ef04.png
https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png
</image>
</screenshot>
</screenshots>

View File

@@ -135,12 +135,21 @@ setInterval(() => {
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0
) {
for (const el of document.getElementsByClassName("info-3pQQBb")) {
const aboutEl = document.createElement("div");
let aboutEl;
if (window.discordScreenaudioKXMLGUI) {
aboutEl = document.createElement("a");
aboutEl.addEventListener("click", () => {
console.log("!discord-screenaudio-about");
});
} else {
aboutEl = document.createElement("div");
}
aboutEl.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`;
aboutEl.style.fontSize = "12px";
aboutEl.style.color = "var(--text-muted)";
aboutEl.style.textTransform = "none";
aboutEl.classList.add("dirscordScreenaudioAboutText");
aboutEl.style.cursor = "pointer";
el.appendChild(aboutEl);
}
}
@@ -149,6 +158,47 @@ setInterval(() => {
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 fontSize16-3zr6Io browserNotice-1u-Y5o"
).length
) {
const el = document
.getElementById("keybinds-tab")
.getElementsByClassName("children-1xdcWE")[0];
const div = document.createElement("div");
div.style.marginBottom = "50px";
const button = document.createElement("button");
button.classList =
"button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F";
button.innerText = "Edit Global Keybinds";
button.addEventListener("click", () => {
console.log("!discord-screenaudio-keybinds");
});
div.appendChild(button);
el.innerHTML = "";
el.appendChild(div);
}
const buttonContainer = document.getElementsByClassName("container-YkUktl")[0];
if (!buttonContainer) {
console.log('dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub');
}
const muteBtn = buttonContainer ? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[0] : null;
window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click();
const deafenBtn = buttonContainer ? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[1] : null;
window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click();
if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName(
"qualityIndicator-39wQDy"

View File

@@ -1,10 +1,26 @@
#include "discordpage.h"
#include "log.h"
#include "mainwindow.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KAboutData>
#include <KHelpMenu>
#include <KShortcutsDialog>
#include <KXmlGuiWindow>
#include <QAction>
#ifdef KGLOBALACCEL
#include <KGlobalAccel>
#endif
#endif
#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QMessageBox>
#include <QNetworkReply>
#include <QTimer>
#include <QWebChannel>
#include <QWebEngineScript>
@@ -33,46 +49,78 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
false);
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
setUrl(QUrl("https://discord.com/app"));
injectScript(":/assets/userscript.js");
injectVersion(QApplication::applicationVersion());
injectScriptFile("userscript.js", ":/assets/userscript.js");
injectScriptText("version.js",
QString("window.discordScreenaudioVersion = '%1';")
.arg(QApplication::applicationVersion()));
#ifdef KXMLGUI
injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;");
KAboutData aboutData(
"discord-screenaudio", "discord-screenaudio",
QApplication::applicationVersion(),
"Custom Discord client with the ability to stream audio on Linux",
KAboutLicense::GPL_V3, "Copyright 2022 (C) Malte Jürgens");
aboutData.setBugAddress("https://github.com/maltejur/discord-screenaudio");
aboutData.addAuthor("Malte Jürgens", "Author", "maltejur@dismail.de",
"https://github.com/maltejur");
aboutData.addCredit("edisionnano",
"For creating and documenting the approach for streaming "
"audio in Discord used in this project.",
QString(),
"https://github.com/edisionnano/"
"Screenshare-with-audio-on-Discord-with-Linux");
aboutData.addCredit(
"Curve", "For creating the Rohrkabel library used in this project.",
QString(), "https://github.com/Curve");
aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.3",
"https://github.com/Soundux/rohrkabel");
m_helpMenu = new KHelpMenu(parent, aboutData);
#ifdef KGLOBALACCEL
injectScriptText("kglobalaccel.js",
"window.discordScreenaudioKGLOBALACCEL = true;");
auto toggleMuteAction = new QAction(this);
toggleMuteAction->setText("Toggle Mute");
toggleMuteAction->setIcon(QIcon::fromTheme("microphone-sensitivity-muted"));
connect(toggleMuteAction, &QAction::triggered, this,
&DiscordPage::toggleMute);
auto toggleDeafenAction = new QAction(this);
toggleDeafenAction->setText("Toggle Deafen");
toggleDeafenAction->setIcon(QIcon::fromTheme("audio-volume-muted"));
connect(toggleDeafenAction, &QAction::triggered, this,
&DiscordPage::toggleDeafen);
m_actionCollection = new KActionCollection(this);
m_actionCollection->addAction("toggleMute", toggleMuteAction);
KGlobalAccel::setGlobalShortcut(toggleMuteAction, QList<QKeySequence>{});
m_actionCollection->addAction("toggleDeafen", toggleDeafenAction);
KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList<QKeySequence>{});
m_shortcutsDialog = new KShortcutsDialog(KShortcutsEditor::GlobalAction);
m_shortcutsDialog->addCollection(m_actionCollection);
#endif
#endif
connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
&DiscordPage::startStream);
}
void DiscordPage::injectScript(QString source) {
qDebug(mainLog) << "Injecting " << source;
void DiscordPage::injectScriptText(QString name, QString content) {
qDebug(mainLog) << "Injecting " << name;
QFile userscript(source);
if (!userscript.open(QIODevice::ReadOnly)) {
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
userscript.errorString().toLatin1().constData());
} else {
QByteArray userscriptJs = userscript.readAll();
QWebEngineScript script;
script.setSourceCode(userscriptJs);
script.setName("userscript.js");
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
scripts().insert(script);
}
}
void DiscordPage::injectVersion(QString version) {
QWebEngineScript script;
auto code = QString("window.discordScreenaudioVersion = '%1';").arg(version);
script.setSourceCode(code);
script.setName("version.js");
script.setSourceCode(content);
script.setName(name);
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
@@ -80,6 +128,17 @@ void DiscordPage::injectVersion(QString version) {
scripts().insert(script);
}
void DiscordPage::injectScriptFile(QString name, QString source) {
QFile file(source);
if (!file.open(QIODevice::ReadOnly)) {
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
file.errorString().toLatin1().constData());
} else {
injectScriptText(name, file.readAll());
}
}
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::Feature feature) {
// Allow every permission asked
@@ -144,6 +203,26 @@ void DiscordPage::javaScriptConsoleMessage(
m_streamDialog.updateTargets();
} else if (message == "!discord-screenaudio-stream-stopped") {
stopVirtmic();
} else if (message == "!discord-screenaudio-about") {
#ifdef KXMLGUI
m_helpMenu->aboutApplication();
#endif
} else if (message == "!discord-screenaudio-keybinds") {
#ifdef KXMLGUI
#ifdef KGLOBALACCEL
m_shortcutsDialog->show();
#else
QMessageBox::information(MainWindow::instance(), "discord-screenaudio",
"Keybinds are not supported on this platform "
"(KGlobalAccel is not available).",
QMessageBox::Ok);
#endif
#else
QMessageBox::information(MainWindow::instance(), "discord-screenaudio",
"Keybinds are not supported on this platform "
"(KXmlGui and KGlobalAccel are not available).",
QMessageBox::Ok);
#endif
} else if (message.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else {
@@ -163,3 +242,13 @@ void DiscordPage::startStream(QString target, uint width, uint height,
.arg(frameRate));
});
}
void DiscordPage::toggleMute() {
qDebug(shortcutLog) << "Toggling mute";
runJavaScript("window.discordScreenaudioToggleMute();");
}
void DiscordPage::toggleDeafen() {
qDebug(shortcutLog) << "Toggling deafen";
runJavaScript("window.discordScreenaudioToggleDeafen();");
}

View File

@@ -3,6 +3,12 @@
#include "streamdialog.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KActionCollection>
#include <KHelpMenu>
#include <KShortcutsDialog>
#endif
#include <QProcess>
#include <QWebEngineFullScreenRequest>
#include <QWebEnginePage>
@@ -16,6 +22,13 @@ public:
private:
StreamDialog m_streamDialog;
QProcess m_virtmicProcess;
#ifdef KXMLGUI
KHelpMenu *m_helpMenu;
#ifdef KGLOBALACCEL
KActionCollection *m_actionCollection;
KShortcutsDialog *m_shortcutsDialog;
#endif
#endif
bool acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType type,
bool isMainFrame) override;
@@ -24,10 +37,12 @@ private:
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
const QString &message, int lineNumber,
const QString &sourceID) override;
void injectScript(QString source);
void injectVersion(QString version);
void injectScriptText(QString name, QString content);
void injectScriptFile(QString name, QString source);
void stopVirtmic();
void startVirtmic(QString target);
void toggleMute();
void toggleDeafen();
private Q_SLOTS:
void featurePermissionRequested(const QUrl &securityOrigin,

View File

@@ -4,3 +4,4 @@ Q_LOGGING_CATEGORY(mainLog, "main");
Q_LOGGING_CATEGORY(discordLog, "discord");
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");
Q_LOGGING_CATEGORY(shortcutLog, "shortcut");

View File

@@ -6,3 +6,4 @@ Q_DECLARE_LOGGING_CATEGORY(mainLog);
Q_DECLARE_LOGGING_CATEGORY(discordLog);
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
Q_DECLARE_LOGGING_CATEGORY(shortcutLog);

View File

@@ -1,6 +1,10 @@
#include "mainwindow.h"
#include "virtmic.h"
#ifdef KXMLGUI
#include <KAboutData>
#endif
#include <QApplication>
#include <QCommandLineParser>
#include <QLoggingCategory>
@@ -26,6 +30,7 @@ int main(int argc, char *argv[]) {
QCommandLineOption degubOption("remote-debugging",
"Open Chromium Remote Debugging on port 9222");
parser.addOption(degubOption);
parser.process(app);
if (parser.isSet(virtmicOption)) {

View File

@@ -22,7 +22,11 @@
#include <QWebEngineSettings>
#include <QWidget>
MainWindow *MainWindow::m_instance = nullptr;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
assert(MainWindow::m_instance == nullptr);
MainWindow::m_instance = this;
setupWebView();
resize(1000, 700);
showMaximized();
@@ -68,3 +72,5 @@ void MainWindow::fullScreenRequested(
}
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
MainWindow *MainWindow::instance() { return m_instance; }

View File

@@ -15,6 +15,7 @@ class MainWindow : public QMainWindow {
public:
explicit MainWindow(QWidget *parent = nullptr);
static MainWindow *instance();
private:
void setupWebView();
@@ -23,6 +24,7 @@ private:
DiscordPage *m_discordPage;
void closeEvent(QCloseEvent *event) override;
bool m_wasMaximized;
static MainWindow *m_instance;
private Q_SLOTS:
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);