Compare commits

...

17 Commits

Author SHA1 Message Date
Malte Jürgens
f2de080e1b update labels 2022-11-22 16:39:58 +01:00
Malte Jürgens
f4a60f281d reworked stream dialog 2022-11-22 16:35:47 +01:00
Malte Jürgens
d693535d53 Revert "add clipboard permission"
This reverts commit b0a8815bb8.
2022-11-09 22:13:34 +01:00
Malte Jürgens
e41af697f7 fix error 2022-11-05 00:08:25 +01:00
Malte Jürgens
b0a8815bb8 add clipboard permission 2022-11-04 23:20:46 +01:00
Malte Jürgens
150fd4364e add --notify-send option 2022-11-04 23:16:49 +01:00
Malte Jürgens
a0a2924796 remove Vencord folder 2022-11-04 20:45:55 +00:00
Malte Jürgens
3c48621427 add developer field to metainfo 2022-10-28 21:57:32 +02:00
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
14 changed files with 382 additions and 123 deletions

View File

@@ -23,6 +23,16 @@ if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS ) add_definitions( -DKNOTIFICATIONS )
endif() 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 set(discord-screenaudio_SRC
src/main.cpp src/main.cpp
src/mainwindow.cpp src/mainwindow.cpp
@@ -62,6 +72,12 @@ if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications) target_link_libraries(discord-screenaudio KF5::Notifications)
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5) install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
endif() 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(TARGETS discord-screenaudio DESTINATION bin)
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps) 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 screenshare and not passed to the user microphone
([see explanation](#how-it-works)). ([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 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 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 - Basic building tools
- CMake - CMake
- Qt5, QtWebEngine and Kf5Notifications - Qt5 and QtWebEngine
- **PipeWire** (it currently doesn't work with PulseAudio) - **PipeWire** (it currently doesn't work with PulseAudio)
- Git - Git
- _Kf5Notifications (optional, for better notifications)_
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
On Debian: 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 ### Building

View File

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

View File

@@ -4,6 +4,7 @@
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license> <project_license>GPL-3.0+</project_license>
<name>discord-screenaudio</name> <name>discord-screenaudio</name>
<developer_name>Malte Jürgens</developer_name>
<releases> <releases>
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" /> <release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
</releases> </releases>
@@ -29,7 +30,7 @@
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image> <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> </image>
</screenshot> </screenshot>
</screenshots> </screenshots>

View File

@@ -25,7 +25,7 @@ const getAudioDevice = async (nameOfAudioDevice) => {
return audioDevice; return audioDevice;
}; };
function setGetDisplayMedia(overrideArgs = undefined) { function setGetDisplayMedia(video = true, overrideArgs = undefined) {
const getDisplayMedia = async (...args) => { const getDisplayMedia = async (...args) => {
var id; var id;
try { try {
@@ -63,6 +63,7 @@ function setGetDisplayMedia(overrideArgs = undefined) {
: args || [{ video: true, audio: true }]) : args || [{ video: true, audio: true }])
); );
gdm.addTrack(track); gdm.addTrack(track);
if (!video) for (const track of gdm.getVideoTracks()) track.enabled = false;
return gdm; return gdm;
}; };
navigator.mediaDevices.getDisplayMedia = getDisplayMedia; navigator.mediaDevices.getDisplayMedia = getDisplayMedia;
@@ -111,9 +112,16 @@ setInterval(() => {
const initialDisplay = el.style.display; const initialDisplay = el.style.display;
window.discordScreenaudioStartStream = (width, height, frameRate) => { window.discordScreenaudioStartStream = (
window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`; video,
setGetDisplayMedia({ width,
height,
frameRate
) => {
window.discordScreenaudioResolutionString = video
? `${height}p ${frameRate}FPS`
: "Audio Only";
setGetDisplayMedia(video, {
audio: true, audio: true,
video: { width, height, frameRate }, video: { width, height, frameRate },
}); });
@@ -135,12 +143,21 @@ setInterval(() => {
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0 document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0
) { ) {
for (const el of document.getElementsByClassName("info-3pQQBb")) { 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.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`;
aboutEl.style.fontSize = "12px"; aboutEl.style.fontSize = "12px";
aboutEl.style.color = "var(--text-muted)"; aboutEl.style.color = "var(--text-muted)";
aboutEl.style.textTransform = "none"; aboutEl.style.textTransform = "none";
aboutEl.classList.add("dirscordScreenaudioAboutText"); aboutEl.classList.add("dirscordScreenaudioAboutText");
aboutEl.style.cursor = "pointer";
el.appendChild(aboutEl); el.appendChild(aboutEl);
} }
} }
@@ -149,6 +166,54 @@ setInterval(() => {
document.getElementById("manage-streams-change-windows")?.remove(); document.getElementById("manage-streams-change-windows")?.remove();
document.querySelector(`[aria-label="Stream Settings"]`)?.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) { if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName( for (const el of document.getElementsByClassName(
"qualityIndicator-39wQDy" "qualityIndicator-39wQDy"

View File

@@ -1,10 +1,26 @@
#include "discordpage.h" #include "discordpage.h"
#include "log.h" #include "log.h"
#include "mainwindow.h"
#include "virtmic.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 <QApplication>
#include <QDesktopServices> #include <QDesktopServices>
#include <QFile> #include <QFile>
#include <QMessageBox>
#include <QNetworkReply>
#include <QTimer> #include <QTimer>
#include <QWebChannel> #include <QWebChannel>
#include <QWebEngineScript> #include <QWebEngineScript>
@@ -33,51 +49,94 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
false); false);
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
setUrl(QUrl("https://discord.com/app")); setUrl(QUrl("https://discord.com/app"));
injectScript(":/assets/userscript.js"); injectScriptFile("userscript.js", ":/assets/userscript.js");
injectVersion(QApplication::applicationVersion());
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, connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
&DiscordPage::startStream); &DiscordPage::startStream);
} }
void DiscordPage::injectScript(QString source) { void DiscordPage::injectScriptText(QString name, QString content) {
qDebug(mainLog) << "Injecting " << source; 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; QWebEngineScript script;
script.setSourceCode(userscriptJs); script.setSourceCode(content);
script.setName("userscript.js"); script.setName(name);
script.setWorldId(QWebEngineScript::MainWorld); script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation); script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false); script.setRunsOnSubFrames(false);
scripts().insert(script); scripts().insert(script);
}
} }
void DiscordPage::injectVersion(QString version) { void DiscordPage::injectScriptFile(QString name, QString source) {
QWebEngineScript script; QFile file(source);
auto code = QString("window.discordScreenaudioVersion = '%1';").arg(version); if (!file.open(QIODevice::ReadOnly)) {
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
script.setSourceCode(code); file.errorString().toLatin1().constData());
script.setName("version.js"); } else {
script.setWorldId(QWebEngineScript::MainWorld); injectScriptText(name, file.readAll());
script.setInjectionPoint(QWebEngineScript::DocumentCreation); }
script.setRunsOnSubFrames(false);
scripts().insert(script);
} }
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin, void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
@@ -127,10 +186,8 @@ void DiscordPage::stopVirtmic() {
} }
void DiscordPage::startVirtmic(QString target) { void DiscordPage::startVirtmic(QString target) {
if (target != "None") {
qDebug(virtmicLog) << "Starting Virtmic with target" << target; qDebug(virtmicLog) << "Starting Virtmic with target" << target;
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target}); m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
}
} }
void DiscordPage::javaScriptConsoleMessage( void DiscordPage::javaScriptConsoleMessage(
@@ -144,6 +201,26 @@ void DiscordPage::javaScriptConsoleMessage(
m_streamDialog.updateTargets(); m_streamDialog.updateTargets();
} else if (message == "!discord-screenaudio-stream-stopped") { } else if (message == "!discord-screenaudio-stream-stopped") {
stopVirtmic(); 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: ")) { } else if (message.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData(); qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else { } else {
@@ -151,15 +228,27 @@ void DiscordPage::javaScriptConsoleMessage(
} }
} }
void DiscordPage::startStream(QString target, uint width, uint height, void DiscordPage::startStream(bool video, bool audio, uint width, uint height,
uint frameRate) { uint frameRate, QString target) {
stopVirtmic(); stopVirtmic();
startVirtmic(target); startVirtmic(audio ? target : "[None]");
// Wait a bit for the virtmic to start // Wait a bit for the virtmic to start
QTimer::singleShot(target == "None" ? 0 : 200, [=]() { QTimer::singleShot(200, [=]() {
runJavaScript(QString("window.discordScreenaudioStartStream(%1, %2, %3);") runJavaScript(
.arg(width) QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);")
.arg(height) .arg(video)
.arg(frameRate)); .arg(video ? width : 32)
.arg(video ? height : 16)
.arg(video ? frameRate : 1));
}); });
} }
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 "streamdialog.h"
#include "virtmic.h" #include "virtmic.h"
#ifdef KXMLGUI
#include <KActionCollection>
#include <KHelpMenu>
#include <KShortcutsDialog>
#endif
#include <QProcess> #include <QProcess>
#include <QWebEngineFullScreenRequest> #include <QWebEngineFullScreenRequest>
#include <QWebEnginePage> #include <QWebEnginePage>
@@ -16,6 +22,13 @@ public:
private: private:
StreamDialog m_streamDialog; StreamDialog m_streamDialog;
QProcess m_virtmicProcess; QProcess m_virtmicProcess;
#ifdef KXMLGUI
KHelpMenu *m_helpMenu;
#ifdef KGLOBALACCEL
KActionCollection *m_actionCollection;
KShortcutsDialog *m_shortcutsDialog;
#endif
#endif
bool acceptNavigationRequest(const QUrl &url, bool acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType type, QWebEnginePage::NavigationType type,
bool isMainFrame) override; bool isMainFrame) override;
@@ -24,15 +37,18 @@ private:
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
const QString &message, int lineNumber, const QString &message, int lineNumber,
const QString &sourceID) override; const QString &sourceID) override;
void injectScript(QString source); void injectScriptText(QString name, QString content);
void injectVersion(QString version); void injectScriptFile(QString name, QString source);
void stopVirtmic(); void stopVirtmic();
void startVirtmic(QString target); void startVirtmic(QString target);
void toggleMute();
void toggleDeafen();
private Q_SLOTS: private Q_SLOTS:
void featurePermissionRequested(const QUrl &securityOrigin, void featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::Feature feature); QWebEnginePage::Feature feature);
void startStream(QString target, uint width, uint height, uint frameRate); void startStream(bool video, bool audio, uint width, uint height,
uint frameRate, QString target);
}; };
// Will immediately get destroyed again but is needed for navigation to // Will immediately get destroyed again but is needed for navigation to

View File

@@ -4,3 +4,4 @@ Q_LOGGING_CATEGORY(mainLog, "main");
Q_LOGGING_CATEGORY(discordLog, "discord"); Q_LOGGING_CATEGORY(discordLog, "discord");
Q_LOGGING_CATEGORY(userscriptLog, "userscript"); Q_LOGGING_CATEGORY(userscriptLog, "userscript");
Q_LOGGING_CATEGORY(virtmicLog, "virtmic"); 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(discordLog);
Q_DECLARE_LOGGING_CATEGORY(userscriptLog); Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
Q_DECLARE_LOGGING_CATEGORY(virtmicLog); Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
Q_DECLARE_LOGGING_CATEGORY(shortcutLog);

View File

@@ -1,6 +1,10 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "virtmic.h" #include "virtmic.h"
#ifdef KXMLGUI
#include <KAboutData>
#endif
#include <QApplication> #include <QApplication>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QLoggingCategory> #include <QLoggingCategory>
@@ -26,6 +30,10 @@ int main(int argc, char *argv[]) {
QCommandLineOption degubOption("remote-debugging", QCommandLineOption degubOption("remote-debugging",
"Open Chromium Remote Debugging on port 9222"); "Open Chromium Remote Debugging on port 9222");
parser.addOption(degubOption); parser.addOption(degubOption);
QCommandLineOption notifySendOption(
"notify-send", "Use notify-send instead of QT/KF5 notifications");
parser.addOption(notifySendOption);
parser.process(app); parser.process(app);
if (parser.isSet(virtmicOption)) { if (parser.isSet(virtmicOption)) {
@@ -41,7 +49,7 @@ int main(int argc, char *argv[]) {
"--remote-debugging-port=9222 " + "--remote-debugging-port=9222 " +
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS")); qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
MainWindow w; MainWindow w(parser.isSet(notifySendOption));
w.show(); w.show();
return app.exec(); return app.exec();

View File

@@ -22,7 +22,13 @@
#include <QWebEngineSettings> #include <QWebEngineSettings>
#include <QWidget> #include <QWidget>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { MainWindow *MainWindow::m_instance = nullptr;
MainWindow::MainWindow(bool useNotifySend, QWidget *parent)
: QMainWindow(parent) {
assert(MainWindow::m_instance == nullptr);
MainWindow::m_instance = this;
m_useNotifySend = useNotifySend;
setupWebView(); setupWebView();
resize(1000, 700); resize(1000, 700);
showMaximized(); showMaximized();
@@ -36,13 +42,26 @@ void MainWindow::setupWebView() {
m_webView = new QWebEngineView(this); m_webView = new QWebEngineView(this);
m_webView->setPage(page); m_webView->setPage(page);
#ifdef KNOTIFICATIONS if (m_useKF5Notifications || m_useNotifySend)
QWebEngineProfile::defaultProfile()->setNotificationPresenter( QWebEngineProfile::defaultProfile()->setNotificationPresenter(
[&](std::unique_ptr<QWebEngineNotification> notificationInfo) { [&](std::unique_ptr<QWebEngineNotification> notificationInfo) {
KNotification *notification = new KNotification("discordNotification"); if (m_useNotifySend) {
auto title = notificationInfo->title();
auto message = notificationInfo->message();
auto image_path =
QString("/tmp/discord-screenaudio-%1.png").arg(title);
notificationInfo->icon().save(image_path);
QProcess::execute("notify-send",
{"--icon", image_path, "--app-name",
"discord-screenaudio", title, message});
} else if (m_useKF5Notifications) {
#ifdef KNOTIFICATIONS
KNotification *notification =
new KNotification("discordNotification");
notification->setTitle(notificationInfo->title()); notification->setTitle(notificationInfo->title());
notification->setText(notificationInfo->message()); notification->setText(notificationInfo->message());
notification->setPixmap(QPixmap::fromImage(notificationInfo->icon())); notification->setPixmap(
QPixmap::fromImage(notificationInfo->icon()));
notification->setDefaultAction("View"); notification->setDefaultAction("View");
connect(notification, &KNotification::defaultActivated, connect(notification, &KNotification::defaultActivated,
[&, notificationInfo = std::move(notificationInfo)]() { [&, notificationInfo = std::move(notificationInfo)]() {
@@ -50,8 +69,9 @@ void MainWindow::setupWebView() {
activateWindow(); activateWindow();
}); });
notification->sendEvent(); notification->sendEvent();
});
#endif #endif
}
});
setCentralWidget(m_webView); setCentralWidget(m_webView);
} }
@@ -68,3 +88,5 @@ void MainWindow::fullScreenRequested(
} }
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); } void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
MainWindow *MainWindow::instance() { return m_instance; }

View File

@@ -14,7 +14,8 @@ class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
static MainWindow *instance();
private: private:
void setupWebView(); void setupWebView();
@@ -23,6 +24,13 @@ private:
DiscordPage *m_discordPage; DiscordPage *m_discordPage;
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
bool m_wasMaximized; bool m_wasMaximized;
static MainWindow *m_instance;
bool m_useNotifySend;
#ifdef KNOTIFICATIONS
bool m_useKF5Notifications = true;
#else
bool m_useKF5Notifications = false;
#endif
private Q_SLOTS: private Q_SLOTS:
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest); void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);

View File

@@ -12,53 +12,77 @@
StreamDialog::StreamDialog() : QWidget() { StreamDialog::StreamDialog() : QWidget() {
setAttribute(Qt::WA_QuitOnClose, false); setAttribute(Qt::WA_QuitOnClose, false);
{
auto layout = new QVBoxLayout(this); auto layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize); layout->setSizeConstraint(QLayout::SetFixedSize);
auto targetLabel = new QLabel(this); m_videoGroupBox = new QGroupBox(this);
targetLabel->setText("Which app do you want to stream sound from?"); m_videoGroupBox->setTitle("Video");
layout->addWidget(targetLabel); m_videoGroupBox->setCheckable(true);
layout->addWidget(m_videoGroupBox);
auto targetHBox = new QHBoxLayout(this); {
layout->addLayout(targetHBox); auto videoLayout = new QVBoxLayout(this);
m_videoGroupBox->setLayout(videoLayout);
auto resolutionLabel = new QLabel(this);
resolutionLabel->setText("Resolution");
videoLayout->addWidget(resolutionLabel);
m_resolutionComboBox = new QComboBox(this);
m_resolutionComboBox->addItem("2160p", "3840x2160");
m_resolutionComboBox->addItem("1440p", "2560x1440");
m_resolutionComboBox->addItem("1080p", "1920x1080");
m_resolutionComboBox->addItem("720p", "1280x720");
m_resolutionComboBox->addItem("480p", "854x480");
m_resolutionComboBox->addItem("360p", "640x360");
m_resolutionComboBox->addItem("240p", "426x240");
m_resolutionComboBox->setCurrentText("720p");
videoLayout->addWidget(m_resolutionComboBox);
auto framerateLabel = new QLabel(this);
framerateLabel->setText("Framerate");
videoLayout->addWidget(framerateLabel);
m_framerateComboBox = new QComboBox(this);
m_framerateComboBox->addItem("144 FPS", 144);
m_framerateComboBox->addItem("60 FPS", 60);
m_framerateComboBox->addItem("30 FPS", 30);
m_framerateComboBox->addItem("15 FPS", 15);
m_framerateComboBox->addItem("5 FPS", 5);
m_framerateComboBox->setCurrentText("30 FPS");
videoLayout->addWidget(m_framerateComboBox);
}
m_audioGroupBox = new QGroupBox(this);
m_audioGroupBox->setCheckable(true);
m_audioGroupBox->setTitle("Audio");
layout->addWidget(m_audioGroupBox);
{
auto audioLayout = new QVBoxLayout(this);
m_audioGroupBox->setLayout(audioLayout);
auto targetLabel = new QLabel(this);
targetLabel->setText("Audio Source");
audioLayout->addWidget(targetLabel);
{
auto targetLayout = new QHBoxLayout(this);
audioLayout->addLayout(targetLayout);
m_targetComboBox = new QComboBox(this); m_targetComboBox = new QComboBox(this);
updateTargets(); updateTargets();
targetHBox->addWidget(m_targetComboBox); targetLayout->addWidget(m_targetComboBox);
auto refreshTargetsButton = new QPushButton(this); auto refreshTargetsButton = new QPushButton(this);
refreshTargetsButton->setFixedSize(30, 30); refreshTargetsButton->setFixedSize(30, 30);
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh")); refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
connect(refreshTargetsButton, &QPushButton::clicked, this, connect(refreshTargetsButton, &QPushButton::clicked, this,
&StreamDialog::updateTargets); &StreamDialog::updateTargets);
targetHBox->addWidget(refreshTargetsButton); targetLayout->addWidget(refreshTargetsButton);
}
auto qualityLabel = new QLabel(this); }
qualityLabel->setText("Stream Quality");
layout->addWidget(qualityLabel);
auto qualityHBox = new QHBoxLayout(this);
layout->addLayout(qualityHBox);
m_qualityResolutionComboBox = new QComboBox(this);
m_qualityResolutionComboBox->addItem("2160p", "3840x2160");
m_qualityResolutionComboBox->addItem("1440p", "2560x1440");
m_qualityResolutionComboBox->addItem("1080p", "1920x1080");
m_qualityResolutionComboBox->addItem("720p", "1280x720");
m_qualityResolutionComboBox->addItem("480p", "854x480");
m_qualityResolutionComboBox->addItem("360p", "640x360");
m_qualityResolutionComboBox->addItem("240p", "426x240");
m_qualityResolutionComboBox->setCurrentText("720p");
qualityHBox->addWidget(m_qualityResolutionComboBox);
m_qualityFPSComboBox = new QComboBox(this);
m_qualityFPSComboBox->addItem("144 FPS", 144);
m_qualityFPSComboBox->addItem("60 FPS", 60);
m_qualityFPSComboBox->addItem("30 FPS", 30);
m_qualityFPSComboBox->addItem("15 FPS", 15);
m_qualityFPSComboBox->addItem("5 FPS", 5);
m_qualityFPSComboBox->setCurrentText("30 FPS");
qualityHBox->addWidget(m_qualityFPSComboBox);
auto button = new QPushButton(this); auto button = new QPushButton(this);
button->setText("Start Stream"); button->setText("Start Stream");
@@ -66,16 +90,18 @@ StreamDialog::StreamDialog() : QWidget() {
layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom); layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom);
setLayout(layout); setLayout(layout);
}
setWindowTitle("discord-screenaudio Stream Dialog"); setWindowTitle("discord-screenaudio Stream Dialog");
} }
void StreamDialog::startStream() { void StreamDialog::startStream() {
auto resolution = auto resolution = m_resolutionComboBox->currentData().toString().split('x');
m_qualityResolutionComboBox->currentData().toString().split('x'); emit requestedStreamStart(m_videoGroupBox->isChecked(),
emit requestedStreamStart(m_targetComboBox->currentText(), m_audioGroupBox->isChecked(),
resolution[0].toUInt(), resolution[1].toUInt(), resolution[0].toUInt(), resolution[1].toUInt(),
m_qualityFPSComboBox->currentData().toUInt()); m_framerateComboBox->currentData().toUInt(),
m_targetComboBox->currentText());
setHidden(true); setHidden(true);
} }
@@ -83,7 +109,6 @@ void StreamDialog::updateTargets() {
auto lastTarget = m_targetComboBox->currentText(); auto lastTarget = m_targetComboBox->currentText();
m_targetComboBox->clear(); m_targetComboBox->clear();
m_targetComboBox->addItem("[None]");
m_targetComboBox->addItem("[All Desktop Audio]"); m_targetComboBox->addItem("[All Desktop Audio]");
for (auto target : Virtmic::getTargets()) { for (auto target : Virtmic::getTargets()) {
m_targetComboBox->addItem(target); m_targetComboBox->addItem(target);

View File

@@ -2,6 +2,7 @@
#include <QComboBox> #include <QComboBox>
#include <QDialog> #include <QDialog>
#include <QGroupBox>
#include <QWidget> #include <QWidget>
class StreamDialog : public QWidget { class StreamDialog : public QWidget {
@@ -12,12 +13,14 @@ public:
private: private:
QComboBox *m_targetComboBox; QComboBox *m_targetComboBox;
QComboBox *m_qualityResolutionComboBox; QComboBox *m_resolutionComboBox;
QComboBox *m_qualityFPSComboBox; QComboBox *m_framerateComboBox;
QGroupBox *m_videoGroupBox;
QGroupBox *m_audioGroupBox;
Q_SIGNALS: Q_SIGNALS:
void requestedStreamStart(QString target, uint width, uint height, void requestedStreamStart(bool video, bool audio, uint width, uint height,
uint frameRate); uint frameRate, QString target);
public Q_SLOTS: public Q_SLOTS:
void updateTargets(); void updateTargets();