2022-07-09 16:07:14 -04:00
|
|
|
#include "discordpage.h"
|
2022-08-02 06:41:32 -04:00
|
|
|
#include "log.h"
|
2022-10-10 15:50:26 -04:00
|
|
|
#include "mainwindow.h"
|
2022-07-09 16:07:14 -04:00
|
|
|
#include "virtmic.h"
|
|
|
|
|
2022-10-10 15:50:26 -04:00
|
|
|
#ifdef KXMLGUI
|
|
|
|
#include <KAboutData>
|
|
|
|
#include <KHelpMenu>
|
|
|
|
#include <KShortcutsDialog>
|
|
|
|
#include <KXmlGuiWindow>
|
|
|
|
#include <QAction>
|
|
|
|
|
|
|
|
#ifdef KGLOBALACCEL
|
|
|
|
#include <KGlobalAccel>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2022-07-09 16:07:14 -04:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QFile>
|
2022-10-10 15:50:26 -04:00
|
|
|
#include <QMessageBox>
|
2022-10-11 13:48:57 -04:00
|
|
|
#include <QNetworkReply>
|
2022-07-09 16:07:14 -04:00
|
|
|
#include <QTimer>
|
|
|
|
#include <QWebChannel>
|
|
|
|
#include <QWebEngineScript>
|
|
|
|
#include <QWebEngineScriptCollection>
|
|
|
|
#include <QWebEngineSettings>
|
|
|
|
|
|
|
|
DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|
|
|
setBackgroundColor(QColor("#202225"));
|
|
|
|
m_virtmicProcess.setProcessChannelMode(QProcess::ForwardedChannels);
|
|
|
|
|
|
|
|
connect(this, &QWebEnginePage::featurePermissionRequested, this,
|
|
|
|
&DiscordPage::featurePermissionRequested);
|
|
|
|
|
2022-07-28 11:34:54 -04:00
|
|
|
connect(this, &QWebEnginePage::loadStarted, [=]() {
|
|
|
|
runJavaScript(QString("window.discordScreenaudioVersion = '%1';")
|
|
|
|
.arg(QApplication::applicationVersion()));
|
|
|
|
});
|
|
|
|
|
2022-07-09 16:07:14 -04:00
|
|
|
settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
|
|
|
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
|
|
|
settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent,
|
|
|
|
true);
|
|
|
|
settings()->setAttribute(
|
|
|
|
QWebEngineSettings::AllowWindowActivationFromJavaScript, true);
|
|
|
|
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
|
|
|
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
|
|
|
false);
|
2022-07-27 08:53:16 -04:00
|
|
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
|
2022-10-10 15:54:26 -04:00
|
|
|
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
|
2022-07-09 16:07:14 -04:00
|
|
|
|
|
|
|
setUrl(QUrl("https://discord.com/app"));
|
|
|
|
|
2022-10-10 15:50:26 -04:00
|
|
|
injectScriptFile("userscript.js", ":/assets/userscript.js");
|
|
|
|
|
2023-01-13 17:33:47 -05:00
|
|
|
injectScriptText("vars.js",
|
|
|
|
QString("window.discordScreenaudioVersion = '%1'; "
|
|
|
|
"window.discordScreenaudioTrayEnabled = %2;")
|
|
|
|
.arg(QApplication::applicationVersion())
|
|
|
|
.arg(MainWindow::instance()
|
|
|
|
->settings()
|
|
|
|
->value("trayIcon", false)
|
|
|
|
.toBool()));
|
2022-10-10 15:50:26 -04:00
|
|
|
|
|
|
|
#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
|
2022-07-09 16:07:14 -04:00
|
|
|
|
|
|
|
connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
|
|
|
|
&DiscordPage::startStream);
|
|
|
|
}
|
|
|
|
|
2022-10-10 15:50:26 -04:00
|
|
|
void DiscordPage::injectScriptText(QString name, QString content) {
|
|
|
|
qDebug(mainLog) << "Injecting " << name;
|
|
|
|
|
|
|
|
QWebEngineScript script;
|
2022-07-09 16:07:14 -04:00
|
|
|
|
2022-10-10 15:50:26 -04:00
|
|
|
script.setSourceCode(content);
|
|
|
|
script.setName(name);
|
|
|
|
script.setWorldId(QWebEngineScript::MainWorld);
|
|
|
|
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
|
|
|
script.setRunsOnSubFrames(false);
|
|
|
|
|
|
|
|
scripts().insert(script);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscordPage::injectScriptFile(QString name, QString source) {
|
2022-10-11 13:48:57 -04:00
|
|
|
QFile file(source);
|
2022-07-09 16:07:14 -04:00
|
|
|
|
2022-10-11 13:48:57 -04:00
|
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
2022-07-09 16:07:14 -04:00
|
|
|
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
|
2022-10-11 13:48:57 -04:00
|
|
|
file.errorString().toLatin1().constData());
|
2022-07-09 16:07:14 -04:00
|
|
|
} else {
|
2022-10-11 13:48:57 -04:00
|
|
|
injectScriptText(name, file.readAll());
|
2022-07-09 16:07:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
|
|
|
QWebEnginePage::Feature feature) {
|
|
|
|
// Allow every permission asked
|
|
|
|
setFeaturePermission(securityOrigin, feature,
|
|
|
|
QWebEnginePage::PermissionGrantedByUser);
|
2022-07-26 10:02:36 -04:00
|
|
|
|
|
|
|
if (feature == QWebEnginePage::Feature::MediaAudioCapture) {
|
|
|
|
if (m_virtmicProcess.state() == QProcess::NotRunning) {
|
2022-08-02 06:41:32 -04:00
|
|
|
qDebug(virtmicLog) << "Starting Virtmic with no target to make sure "
|
|
|
|
"Discord can find all the audio devices";
|
2022-07-26 10:02:36 -04:00
|
|
|
m_virtmicProcess.start(QApplication::arguments()[0],
|
|
|
|
{"--virtmic", "None"});
|
|
|
|
}
|
|
|
|
}
|
2022-07-09 16:07:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
|
|
|
QWebEnginePage::NavigationType type,
|
|
|
|
bool isMainFrame) {
|
|
|
|
if (type == QWebEnginePage::NavigationTypeLinkClicked) {
|
|
|
|
QDesktopServices::openUrl(url);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2022-07-27 08:53:16 -04:00
|
|
|
bool ExternalPage::acceptNavigationRequest(const QUrl &url,
|
|
|
|
QWebEnginePage::NavigationType type,
|
|
|
|
bool isMainFrame) {
|
|
|
|
QDesktopServices::openUrl(url);
|
|
|
|
deleteLater();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) {
|
|
|
|
return new ExternalPage;
|
|
|
|
}
|
|
|
|
|
2022-07-09 16:07:14 -04:00
|
|
|
void DiscordPage::stopVirtmic() {
|
|
|
|
if (m_virtmicProcess.state() == QProcess::Running) {
|
2022-08-02 06:41:32 -04:00
|
|
|
qDebug(virtmicLog) << "Stopping Virtmic";
|
2022-07-09 16:07:14 -04:00
|
|
|
m_virtmicProcess.kill();
|
2022-07-26 10:33:34 -04:00
|
|
|
m_virtmicProcess.waitForFinished();
|
2022-07-09 16:07:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscordPage::startVirtmic(QString target) {
|
2022-11-22 10:35:47 -05:00
|
|
|
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
|
|
|
|
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
2022-07-09 16:07:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiscordPage::javaScriptConsoleMessage(
|
|
|
|
QWebEnginePage::JavaScriptConsoleMessageLevel level, const QString &message,
|
|
|
|
int lineNumber, const QString &sourceID) {
|
|
|
|
if (message == "!discord-screenaudio-start-stream") {
|
|
|
|
if (m_streamDialog.isHidden())
|
|
|
|
m_streamDialog.setHidden(false);
|
|
|
|
else
|
|
|
|
m_streamDialog.activateWindow();
|
2022-07-14 05:57:44 -04:00
|
|
|
m_streamDialog.updateTargets();
|
2022-07-09 16:07:14 -04:00
|
|
|
} else if (message == "!discord-screenaudio-stream-stopped") {
|
|
|
|
stopVirtmic();
|
2022-10-10 15:50:26 -04:00
|
|
|
} 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
|
2023-01-13 17:33:47 -05:00
|
|
|
} else if (message == "!discord-screenaudio-tray-true") {
|
|
|
|
MainWindow::instance()->setTrayIcon(true);
|
|
|
|
} else if (message == "!discord-screenaudio-tray-false") {
|
|
|
|
MainWindow::instance()->setTrayIcon(false);
|
2022-08-02 06:41:32 -04:00
|
|
|
} else if (message.startsWith("dsa: ")) {
|
|
|
|
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
|
2022-07-09 16:07:14 -04:00
|
|
|
} else {
|
2022-08-02 06:41:32 -04:00
|
|
|
qDebug(discordLog) << message;
|
2022-07-09 16:07:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-22 10:35:47 -05:00
|
|
|
void DiscordPage::startStream(bool video, bool audio, uint width, uint height,
|
|
|
|
uint frameRate, QString target) {
|
2022-07-09 16:07:14 -04:00
|
|
|
stopVirtmic();
|
2022-11-22 10:35:47 -05:00
|
|
|
startVirtmic(audio ? target : "[None]");
|
2022-07-09 16:07:14 -04:00
|
|
|
// Wait a bit for the virtmic to start
|
2022-11-22 10:35:47 -05:00
|
|
|
QTimer::singleShot(200, [=]() {
|
|
|
|
runJavaScript(
|
|
|
|
QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);")
|
|
|
|
.arg(video)
|
|
|
|
.arg(video ? width : 32)
|
|
|
|
.arg(video ? height : 16)
|
|
|
|
.arg(video ? frameRate : 1));
|
2022-07-09 16:07:14 -04:00
|
|
|
});
|
|
|
|
}
|
2022-10-10 15:50:26 -04:00
|
|
|
|
|
|
|
void DiscordPage::toggleMute() {
|
|
|
|
qDebug(shortcutLog) << "Toggling mute";
|
|
|
|
runJavaScript("window.discordScreenaudioToggleMute();");
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscordPage::toggleDeafen() {
|
|
|
|
qDebug(shortcutLog) << "Toggling deafen";
|
|
|
|
runJavaScript("window.discordScreenaudioToggleDeafen();");
|
|
|
|
}
|