Compare commits
23 Commits
v1.0.0-rc.
...
v1.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1b35e3ca8 | ||
|
|
a773795e38 | ||
|
|
c8695ca5ff | ||
|
|
bc23d3423a | ||
|
|
f5217dfdfa | ||
|
|
6ef16b9625 | ||
|
|
b62f1bab1f | ||
|
|
5f9211f98f | ||
|
|
cfbb4152c3 | ||
|
|
fa83e2d9a8 | ||
|
|
58ecbbc6f4 | ||
|
|
77300ed178 | ||
|
|
99df18ef89 | ||
|
|
6fc6140ce2 | ||
|
|
84c12a752e | ||
|
|
1199381803 | ||
|
|
bce32e2ba1 | ||
|
|
bc59458e19 | ||
|
|
6492cb9a35 | ||
|
|
a814d9cc1b | ||
|
|
dd107c6767 | ||
|
|
50be25d7db | ||
|
|
fd894fb63e |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "submodules/rohrkabel"]
|
||||||
|
path = submodules/rohrkabel
|
||||||
|
url = https://github.com/Soundux/rohrkabel
|
||||||
@@ -22,18 +22,32 @@ set(discord-screenaudio_SRC
|
|||||||
resources.qrc
|
resources.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
include(FetchContent)
|
# Adapted from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html
|
||||||
|
find_package(Git QUIET)
|
||||||
|
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||||
|
option(GIT_SUBMODULE "Check submodules during build" ON)
|
||||||
|
if(GIT_SUBMODULE)
|
||||||
|
message(STATUS "Updating submodules")
|
||||||
|
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive --checkout
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||||
|
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||||
|
message(FATAL_ERROR "`git submodule update --init --recursive --checkout` failed with ${GIT_SUBMOD_RESULT}, please provide the submodules manually")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
FetchContent_Declare(
|
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/submodules/rohrkabel/CMakeLists.txt")
|
||||||
rohrkabel
|
message(FATAL_ERROR "Rohrkabel was not found since you are not in a Git checkout or have GIT_SUBMODULE disabled. Please provide rohrkabel manually to `./submodules/rohrkabel`.")
|
||||||
GIT_REPOSITORY "https://github.com/Soundux/rohrkabel"
|
endif()
|
||||||
GIT_TAG "d87403f48d3a95aa4bcf4cd60112d9e4bb090d5d"
|
|
||||||
)
|
add_subdirectory(submodules/rohrkabel)
|
||||||
FetchContent_MakeAvailable(rohrkabel)
|
|
||||||
|
|
||||||
add_executable(discord-screenaudio ${discord-screenaudio_SRC})
|
add_executable(discord-screenaudio ${discord-screenaudio_SRC})
|
||||||
|
|
||||||
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
|
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
|
||||||
|
|
||||||
install(TARGETS discord-screenaudio DESTINATION bin)
|
install(TARGETS discord-screenaudio DESTINATION bin)
|
||||||
|
install(PROGRAMS assets/discord.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps)
|
||||||
install(PROGRAMS assets/discord-screenaudio.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
install(PROGRAMS assets/discord-screenaudio.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||||
|
install(PROGRAMS assets/de.shorsh.discord-screenaudio.metainfo.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -9,6 +9,19 @@ of [@edisionnano](https://github.com/edisionnano) and the
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
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
|
||||||
|
case you want to stream something, maybe used with a second account. For
|
||||||
|
anything else, this client has way too many things that work less well than in
|
||||||
|
the original client.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
- Only works with **PipeWire**
|
||||||
|
- Only works on **X11**
|
||||||
|
- Can only share primary screen (no other screens or specific applications) (see
|
||||||
|
[#1](https://github.com/maltejur/discord-screenaudio/issues/1))
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If you are using Arch, you can build and install
|
If you are using Arch, you can build and install
|
||||||
@@ -30,7 +43,14 @@ On Debian:
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
To build the program, run this in the source directory:
|
First, clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/maltejur/discord-screenaudio.git
|
||||||
|
cd discord-screenaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, to build the program, run this in the source directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -B build
|
cmake -B build
|
||||||
|
|||||||
38
assets/de.shorsh.discord-screenaudio.metainfo.xml
Normal file
38
assets/de.shorsh.discord-screenaudio.metainfo.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>de.shorsh.discord-screenaudio</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0+</project_license>
|
||||||
|
<name>discord-screenaudio</name>
|
||||||
|
<releases>
|
||||||
|
<release version="v1.0.0-rc.9" />
|
||||||
|
</releases>
|
||||||
|
|
||||||
|
<summary>
|
||||||
|
A very WIP custom discord client that supports streaming with audio on
|
||||||
|
Linux.
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
<p>A very WIP custom discord client that supports streaming with audio on Linux, made possible by the great work of @edisionnano and the Rohrkabel library by @Curve.</p>
|
||||||
|
<p>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 case you want to stream something, maybe used with a second account. For anything else, this client has way too many things that work less well than in the original client.</p>
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<launchable type="desktop-id">
|
||||||
|
discord-screenaudio.desktop
|
||||||
|
</launchable>
|
||||||
|
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>
|
||||||
|
https://user-images.githubusercontent.com/48161361/179571245-11ea05f3-fb5e-4aef-9132-2736e122ef04.png
|
||||||
|
</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
|
||||||
|
<url type="homepage">https://github.com/maltejur/discord-screenaudio</url>
|
||||||
|
|
||||||
|
<provides>
|
||||||
|
<binary>discord-screenaudio</binary>
|
||||||
|
</provides>
|
||||||
|
</component>
|
||||||
BIN
assets/discord.png
Normal file
BIN
assets/discord.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
@@ -81,11 +81,16 @@ setInterval(() => {
|
|||||||
});
|
});
|
||||||
hiddenElements.length = 0;
|
hiddenElements.length = 0;
|
||||||
} else {
|
} else {
|
||||||
for (const el of document.querySelectorAll(
|
for (const el of [
|
||||||
'[aria-label="Share Your Screen"]'
|
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;
|
||||||
|
el.classList.add("discord-screenaudio-cloned");
|
||||||
elClone = el.cloneNode(true);
|
elClone = el.cloneNode(true);
|
||||||
elClone.ariaLabel = "Share Your Screen with Audio";
|
|
||||||
elClone.title = "Share Your Screen with Audio";
|
elClone.title = "Share Your Screen with Audio";
|
||||||
elClone.addEventListener("click", () => {
|
elClone.addEventListener("click", () => {
|
||||||
console.log("!discord-screenaudio-start-stream");
|
console.log("!discord-screenaudio-start-stream");
|
||||||
@@ -117,7 +122,7 @@ setInterval(() => {
|
|||||||
) {
|
) {
|
||||||
for (const el of document.getElementsByClassName("info-3pQQBb")) {
|
for (const el of document.getElementsByClassName("info-3pQQBb")) {
|
||||||
const aboutEl = document.createElement("div");
|
const aboutEl = document.createElement("div");
|
||||||
aboutEl.innerText = "discord-screenaudio v1.0.0-rc.5";
|
aboutEl.innerText = "discord-screenaudio v1.0.0-rc.9";
|
||||||
aboutEl.style.fontSize = "12px";
|
aboutEl.style.fontSize = "12px";
|
||||||
aboutEl.style.color = "var(--text-muted)";
|
aboutEl.style.color = "var(--text-muted)";
|
||||||
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
||||||
@@ -128,4 +133,8 @@ setInterval(() => {
|
|||||||
// Remove stream settings if stream is active
|
// Remove stream settings if stream is active
|
||||||
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();
|
||||||
}, 1000);
|
}, 500);
|
||||||
|
|
||||||
|
// Fix for broken discord notifications after restart
|
||||||
|
// (https://github.com/maltejur/discord-screenaudio/issues/17)
|
||||||
|
Notification.requestPermission();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|||||||
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
||||||
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
||||||
false);
|
false);
|
||||||
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
|
||||||
|
|
||||||
setUrl(QUrl("https://discord.com/app"));
|
setUrl(QUrl("https://discord.com/app"));
|
||||||
|
|
||||||
@@ -63,6 +64,15 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
|||||||
// Allow every permission asked
|
// Allow every permission asked
|
||||||
setFeaturePermission(securityOrigin, feature,
|
setFeaturePermission(securityOrigin, feature,
|
||||||
QWebEnginePage::PermissionGrantedByUser);
|
QWebEnginePage::PermissionGrantedByUser);
|
||||||
|
|
||||||
|
if (feature == QWebEnginePage::Feature::MediaAudioCapture) {
|
||||||
|
if (m_virtmicProcess.state() == QProcess::NotRunning) {
|
||||||
|
qDebug() << "[virtmic] Starting Virtmic with no target to make sure "
|
||||||
|
"Discord can find all the audio devices";
|
||||||
|
m_virtmicProcess.start(QApplication::arguments()[0],
|
||||||
|
{"--virtmic", "None"});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
||||||
@@ -76,10 +86,23 @@ bool DiscordPage::acceptNavigationRequest(const QUrl &url,
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void DiscordPage::stopVirtmic() {
|
void DiscordPage::stopVirtmic() {
|
||||||
if (m_virtmicProcess.state() == QProcess::Running) {
|
if (m_virtmicProcess.state() == QProcess::Running) {
|
||||||
qDebug() << "[virtmic] Stopping Virtmic";
|
qDebug() << "[virtmic] Stopping Virtmic";
|
||||||
m_virtmicProcess.kill();
|
m_virtmicProcess.kill();
|
||||||
|
m_virtmicProcess.waitForFinished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QWebEngineFullScreenRequest>
|
||||||
#include <QWebEnginePage>
|
#include <QWebEnginePage>
|
||||||
|
|
||||||
class DiscordPage : public QWebEnginePage {
|
class DiscordPage : public QWebEnginePage {
|
||||||
@@ -18,6 +19,7 @@ private:
|
|||||||
bool acceptNavigationRequest(const QUrl &url,
|
bool acceptNavigationRequest(const QUrl &url,
|
||||||
QWebEnginePage::NavigationType type,
|
QWebEnginePage::NavigationType type,
|
||||||
bool isMainFrame) override;
|
bool isMainFrame) override;
|
||||||
|
QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type) override;
|
||||||
void
|
void
|
||||||
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
||||||
const QString &message, int lineNumber,
|
const QString &message, int lineNumber,
|
||||||
@@ -31,3 +33,15 @@ private Q_SLOTS:
|
|||||||
QWebEnginePage::Feature feature);
|
QWebEnginePage::Feature feature);
|
||||||
void startStream(QString target, uint width, uint height, uint frameRate);
|
void startStream(QString target, uint width, uint height, uint frameRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Will immediately get destroyed again but is needed for navigation to
|
||||||
|
// target="_blank" links, since QWebEnginePage::newWindowRequested is
|
||||||
|
// only available sinec Qt 6.3.
|
||||||
|
class ExternalPage : public QWebEnginePage {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool acceptNavigationRequest(const QUrl &url,
|
||||||
|
QWebEnginePage::NavigationType type,
|
||||||
|
bool isMainFrame) override;
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
QApplication::setApplicationName("discord-screenaudio");
|
QApplication::setApplicationName("discord-screenaudio");
|
||||||
QApplication::setApplicationVersion("1.0.0-rc.5");
|
QApplication::setApplicationVersion("1.0.0-rc.9");
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(
|
parser.setApplicationDescription(
|
||||||
|
|||||||
@@ -23,8 +23,25 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setupWebView() {
|
void MainWindow::setupWebView() {
|
||||||
m_webView = new QWebEngineView(this);
|
|
||||||
auto page = new DiscordPage(this);
|
auto page = new DiscordPage(this);
|
||||||
|
connect(page, &QWebEnginePage::fullScreenRequested, this,
|
||||||
|
&MainWindow::fullScreenRequested);
|
||||||
|
|
||||||
|
m_webView = new QWebEngineView(this);
|
||||||
m_webView->setPage(page);
|
m_webView->setPage(page);
|
||||||
|
|
||||||
setCentralWidget(m_webView);
|
setCentralWidget(m_webView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::fullScreenRequested(
|
||||||
|
QWebEngineFullScreenRequest fullScreenRequest) {
|
||||||
|
fullScreenRequest.accept();
|
||||||
|
if (fullScreenRequest.toggleOn()) {
|
||||||
|
m_wasMaximized = isMaximized();
|
||||||
|
showFullScreen();
|
||||||
|
} else {
|
||||||
|
m_wasMaximized ? showMaximized() : showNormal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
|
||||||
|
|||||||
@@ -21,4 +21,9 @@ private:
|
|||||||
QWebEngineView *m_webView;
|
QWebEngineView *m_webView;
|
||||||
QWebEngineProfile *prepareProfile();
|
QWebEngineProfile *prepareProfile();
|
||||||
DiscordPage *m_discordPage;
|
DiscordPage *m_discordPage;
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
bool m_wasMaximized;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#include <rohrkabel/loop/main.hpp>
|
||||||
|
#include <rohrkabel/registry/registry.hpp>
|
||||||
|
|
||||||
namespace Virtmic {
|
namespace Virtmic {
|
||||||
|
|
||||||
QVector<QString> getTargets() {
|
QVector<QString> getTargets() {
|
||||||
@@ -24,7 +27,7 @@ QVector<QString> getTargets() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
core.sync();
|
core.update();
|
||||||
|
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
@@ -81,14 +84,21 @@ void start(QString _target) {
|
|||||||
|
|
||||||
std::string target = _target.toLatin1().toStdString();
|
std::string target = _target.toLatin1().toStdString();
|
||||||
|
|
||||||
auto virtual_mic =
|
auto virtual_mic = core.create("adapter",
|
||||||
core.create("adapter",
|
|
||||||
{{"node.name", "discord-screenaudio-virtmic"},
|
{{"node.name", "discord-screenaudio-virtmic"},
|
||||||
{"media.class", "Audio/Source/Virtual"},
|
{"media.class", "Audio/Source/Virtual"},
|
||||||
{"factory.name", "support.null-audio-sink"},
|
{"factory.name", "support.null-audio-sink"},
|
||||||
{"audio.channels", "2"},
|
{"audio.channels", "2"},
|
||||||
{"audio.position", "FL,FR"}},
|
{"audio.position", "FL,FR"}},
|
||||||
pipewire::node::type, pipewire::node::version, false);
|
pipewire::node::type, pipewire::node::version,
|
||||||
|
pipewire::update_strategy::none);
|
||||||
|
|
||||||
|
if (target == "None") {
|
||||||
|
while (true) {
|
||||||
|
main_loop.run();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto reg_events = reg.listen<pipewire::registry_listener>();
|
auto reg_events = reg.listen<pipewire::registry_listener>();
|
||||||
reg_events.on<pipewire::registry_event::global>(
|
reg_events.on<pipewire::registry_event::global>(
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <rohrkabel/registry/registry.hpp>
|
|
||||||
|
|
||||||
namespace Virtmic {
|
namespace Virtmic {
|
||||||
|
|
||||||
|
|||||||
1
submodules/rohrkabel
Submodule
1
submodules/rohrkabel
Submodule
Submodule submodules/rohrkabel added at 04bfb921c4
Reference in New Issue
Block a user