Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7bff3f428 | ||
|
|
34e23f7187 | ||
|
|
c325dd6798 | ||
|
|
b42fde85d1 | ||
|
|
cc379ada85 | ||
|
|
fdb6145bbd | ||
|
|
8bd4d1896a | ||
|
|
1a2d09fe3d | ||
|
|
f01c941c90 | ||
|
|
95d391e869 | ||
|
|
af107168db |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/build
|
||||
.vscode
|
||||
|
||||
@@ -30,6 +30,7 @@ set(discord-screenaudio_SRC
|
||||
src/virtmic.cpp
|
||||
src/discordpage.cpp
|
||||
src/streamdialog.cpp
|
||||
src/log.cpp
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
<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>
|
||||
<p>Technically this could be against Discord's TOS, so be warned. Discord probably won't ban you for using this, but if they do I told you and it's not my fault.</p>
|
||||
<p>Known issues:</p>
|
||||
<ul><li>Only works with PipeWire</li><li>Can only share primary screen on X11</li></ul>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">
|
||||
|
||||
@@ -3,12 +3,25 @@
|
||||
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 devices = await navigator.mediaDevices.enumerateDevices();
|
||||
let audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
|
||||
let audioDevice;
|
||||
while (audioDevice === undefined) {
|
||||
let devices = await navigator.mediaDevices.enumerateDevices();
|
||||
audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
|
||||
if (!audioDevice)
|
||||
console.log(
|
||||
`dsa: Did not find '${nameOfAudioDevice}', trying again in 100ms`
|
||||
);
|
||||
await sleep(100);
|
||||
}
|
||||
console.log(`dsa: Found '${nameOfAudioDevice}'`);
|
||||
return audioDevice;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "discordpage.h"
|
||||
#include "log.h"
|
||||
#include "virtmic.h"
|
||||
|
||||
#include <QApplication>
|
||||
@@ -43,7 +44,7 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
||||
}
|
||||
|
||||
void DiscordPage::injectScript(QString source) {
|
||||
qDebug() << "[main ] Injecting " << source;
|
||||
qDebug(mainLog) << "Injecting " << source;
|
||||
|
||||
QFile userscript(source);
|
||||
|
||||
@@ -87,8 +88,8 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
||||
|
||||
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";
|
||||
qDebug(virtmicLog) << "Starting Virtmic with no target to make sure "
|
||||
"Discord can find all the audio devices";
|
||||
m_virtmicProcess.start(QApplication::arguments()[0],
|
||||
{"--virtmic", "None"});
|
||||
}
|
||||
@@ -119,7 +120,7 @@ QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) {
|
||||
|
||||
void DiscordPage::stopVirtmic() {
|
||||
if (m_virtmicProcess.state() == QProcess::Running) {
|
||||
qDebug() << "[virtmic] Stopping Virtmic";
|
||||
qDebug(virtmicLog) << "Stopping Virtmic";
|
||||
m_virtmicProcess.kill();
|
||||
m_virtmicProcess.waitForFinished();
|
||||
}
|
||||
@@ -127,7 +128,7 @@ void DiscordPage::stopVirtmic() {
|
||||
|
||||
void DiscordPage::startVirtmic(QString target) {
|
||||
if (target != "None") {
|
||||
qDebug() << "[virtmic] Starting Virtmic with target" << target;
|
||||
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
|
||||
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
||||
}
|
||||
}
|
||||
@@ -143,8 +144,10 @@ void DiscordPage::javaScriptConsoleMessage(
|
||||
m_streamDialog.updateTargets();
|
||||
} else if (message == "!discord-screenaudio-stream-stopped") {
|
||||
stopVirtmic();
|
||||
} else if (message.startsWith("dsa: ")) {
|
||||
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
|
||||
} else {
|
||||
qDebug() << "[discord]" << message;
|
||||
qDebug(discordLog) << message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
src/log.cpp
Normal file
6
src/log.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "log.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(mainLog, "main");
|
||||
Q_LOGGING_CATEGORY(discordLog, "discord");
|
||||
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
|
||||
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");
|
||||
8
src/log.h
Normal file
8
src/log.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(mainLog);
|
||||
Q_DECLARE_LOGGING_CATEGORY(discordLog);
|
||||
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
|
||||
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
|
||||
15
src/main.cpp
15
src/main.cpp
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QApplication app(argc, argv);
|
||||
@@ -12,6 +13,8 @@ int main(int argc, char *argv[]) {
|
||||
QApplication::setApplicationVersion(DISCORD_SCEENAUDIO_VERSION_FULL);
|
||||
QApplication::setDesktopFileName("de.shorsh.discord-screenaudio");
|
||||
|
||||
qSetMessagePattern("[%{category}] %{message}");
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(
|
||||
"Custom Discord client with the ability to stream audio on Linux");
|
||||
@@ -20,10 +23,9 @@ int main(int argc, char *argv[]) {
|
||||
QCommandLineOption virtmicOption("virtmic", "Start the Virtual Microphone",
|
||||
"target");
|
||||
parser.addOption(virtmicOption);
|
||||
#ifdef DEBUG
|
||||
parser.addOption(QCommandLineOption(
|
||||
"remote-debugging-port", "Chromium Remote Debugging Port", "port"));
|
||||
#endif
|
||||
QCommandLineOption degubOption("remote-debugging",
|
||||
"Open Chromium Remote Debugging on port 9222");
|
||||
parser.addOption(degubOption);
|
||||
parser.process(app);
|
||||
|
||||
if (parser.isSet(virtmicOption)) {
|
||||
@@ -34,6 +36,11 @@ int main(int argc, char *argv[]) {
|
||||
"--enable-features=WebRTCPipeWireCapturer " +
|
||||
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||
|
||||
if (parser.isSet(degubOption))
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS",
|
||||
"--remote-debugging-port=9222 " +
|
||||
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
|
||||
@@ -19,9 +19,19 @@ StreamDialog::StreamDialog() : QWidget() {
|
||||
targetLabel->setText("Which app do you want to stream sound from?");
|
||||
layout->addWidget(targetLabel);
|
||||
|
||||
auto targetHBox = new QHBoxLayout(this);
|
||||
layout->addLayout(targetHBox);
|
||||
|
||||
m_targetComboBox = new QComboBox(this);
|
||||
updateTargets();
|
||||
layout->addWidget(m_targetComboBox);
|
||||
targetHBox->addWidget(m_targetComboBox);
|
||||
|
||||
auto refreshTargetsButton = new QPushButton(this);
|
||||
refreshTargetsButton->setFixedSize(30, 30);
|
||||
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
|
||||
connect(refreshTargetsButton, &QPushButton::clicked, this,
|
||||
&StreamDialog::updateTargets);
|
||||
targetHBox->addWidget(refreshTargetsButton);
|
||||
|
||||
auto qualityLabel = new QLabel(this);
|
||||
qualityLabel->setText("Stream Quality");
|
||||
@@ -70,9 +80,17 @@ void StreamDialog::startStream() {
|
||||
}
|
||||
|
||||
void StreamDialog::updateTargets() {
|
||||
auto lastTarget = m_targetComboBox->currentText();
|
||||
|
||||
m_targetComboBox->clear();
|
||||
m_targetComboBox->addItem("None");
|
||||
m_targetComboBox->addItem("[None]");
|
||||
m_targetComboBox->addItem("[All Desktop Audio]");
|
||||
for (auto target : Virtmic::getTargets()) {
|
||||
m_targetComboBox->addItem(target);
|
||||
}
|
||||
|
||||
if (m_targetComboBox->findText(lastTarget) != -1)
|
||||
m_targetComboBox->setCurrentText(lastTarget);
|
||||
else
|
||||
m_targetComboBox->setCurrentText("[All Desktop Audio]");
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "virtmic.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <rohrkabel/loop/main.hpp>
|
||||
#include <rohrkabel/registry/registry.hpp>
|
||||
|
||||
namespace Virtmic {
|
||||
|
||||
const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"};
|
||||
|
||||
QVector<QString> getTargets() {
|
||||
auto main_loop = pipewire::main_loop();
|
||||
auto context = pipewire::context(main_loop);
|
||||
@@ -19,11 +22,11 @@ QVector<QString> getTargets() {
|
||||
if (global.type == pipewire::node::type) {
|
||||
auto node = reg.bind<pipewire::node>(global.id);
|
||||
auto info = node.info();
|
||||
auto name = QString::fromStdString(info.props["application.name"]);
|
||||
|
||||
if (info.props.count("node.name")) {
|
||||
auto name = QString::fromStdString(info.props["node.name"]);
|
||||
if (!targets.contains(name))
|
||||
targets.append(name);
|
||||
if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
|
||||
!targets.contains(name)) {
|
||||
targets.append(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -64,20 +67,24 @@ void start(QString _target) {
|
||||
continue;
|
||||
|
||||
auto &parent = nodes.at(parent_id);
|
||||
auto name = parent.props["application.name"];
|
||||
|
||||
if (parent.props["node.name"].find(target) != std::string::npos) {
|
||||
std::cout << "[virtmic] "
|
||||
<< "Link : " << target << ":" << port_id << " -> ";
|
||||
|
||||
if (port.info().props["audio.channel"] == "FL") {
|
||||
links.emplace(port_id, core.create<pipewire::link_factory>(
|
||||
{virt_fl->info().id, port_id}));
|
||||
std::cout << "[virtmic] " << virt_fl->info().id << std::endl;
|
||||
} else {
|
||||
links.emplace(port_id, core.create<pipewire::link_factory>(
|
||||
{virt_fr->info().id, port_id}));
|
||||
std::cout << "[virtmic] " << virt_fr->info().id << std::endl;
|
||||
}
|
||||
if (name == target ||
|
||||
(target == "[All Desktop Audio]" &&
|
||||
!EXCLUDE_TARGETS.contains(QString::fromStdString(name)))) {
|
||||
auto fl = port.info().props["audio.channel"] == "FL";
|
||||
links.emplace(
|
||||
port_id,
|
||||
core.create<pipewire::link_factory>(
|
||||
{fl ? virt_fl->info().id : virt_fr->info().id, port_id}));
|
||||
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
|
||||
.arg(QString::fromStdString(
|
||||
parent.props["application.name"]))
|
||||
.arg(port_id)
|
||||
.arg(fl ? virt_fl->info().id
|
||||
: virt_fr->info().id)
|
||||
.toUtf8()
|
||||
.data();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -93,7 +100,7 @@ void start(QString _target) {
|
||||
pipewire::node::type, pipewire::node::version,
|
||||
pipewire::update_strategy::none);
|
||||
|
||||
if (target == "None") {
|
||||
if (target == "[None]") {
|
||||
while (true) {
|
||||
main_loop.run();
|
||||
}
|
||||
@@ -105,9 +112,13 @@ void start(QString _target) {
|
||||
[&](const pipewire::global &global) {
|
||||
if (global.type == pipewire::node::type) {
|
||||
auto node = reg.bind<pipewire::node>(global.id);
|
||||
std::cout << "[virtmic] "
|
||||
<< "Added : " << node.info().props["node.name"]
|
||||
<< std::endl;
|
||||
if (!node.info().props.count("application.name"))
|
||||
return;
|
||||
qDebug(virtmicLog) << QString("Added: %1")
|
||||
.arg(QString::fromStdString(
|
||||
node.info().props["application.name"]))
|
||||
.toUtf8()
|
||||
.data();
|
||||
|
||||
if (!nodes.count(global.id)) {
|
||||
nodes.emplace(global.id, node.info());
|
||||
@@ -141,8 +152,11 @@ void start(QString _target) {
|
||||
[&](const std::uint32_t id) {
|
||||
if (nodes.count(id)) {
|
||||
auto info = nodes.at(id);
|
||||
std::cout << "[virtmic] "
|
||||
<< "Removed: " << info.props["node.name"] << std::endl;
|
||||
qDebug(virtmicLog) << QString("Removed: %1")
|
||||
.arg(QString::fromStdString(
|
||||
info.props["application.name"].data()))
|
||||
.toUtf8()
|
||||
.data();
|
||||
nodes.erase(id);
|
||||
}
|
||||
if (ports.count(id)) {
|
||||
|
||||
@@ -138,11 +138,12 @@ function(determine_version source_dir var_prefix)
|
||||
# | exact tag | 0.9.0 |
|
||||
# | exact tag, dirty | 0.9.0+git20180604 |
|
||||
# | after tag | 0.9.0+git20180604.1.085039f |
|
||||
# | no tag, version.txt | 0.9.0+git20180604.2ee02af |
|
||||
# | no tag, version.txt | 0.9.0 |
|
||||
# | no tag, no version.txt| 0.0.0+git20180604.2ee02af |
|
||||
string(TIMESTAMP date "%Y%m%d" UTC)
|
||||
set(version_suffix "")
|
||||
if (NOT ${local_prefix}_VERSION_NCOMMITS EQUAL 0)
|
||||
if (has_version_txt)
|
||||
elseif (NOT ${local_prefix}_VERSION_NCOMMITS EQUAL 0)
|
||||
# 0.9.0+git20150604.4.289818b
|
||||
string(APPEND version_suffix "+git${date}.${${local_prefix}_VERSION_NCOMMITS}.${${local_prefix}_VERSION_GIT_REV}")
|
||||
elseif ((NOT has_release_tag) AND ((NOT has_version_txt) OR ("${base_version}" STREQUAL "0.0.0") OR (NOT "${revision}" STREQUAL "")))
|
||||
|
||||
Reference in New Issue
Block a user