Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Streamlink documentation #5076

Merged
merged 14 commits into from
Jan 12, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Minor: The whisper highlight color can now be configured through the settings. (#5053)
- Minor: Added missing periods at various moderator messages and commands. (#5061)
- Minor: Improved color selection and display. (#5057)
- Minor: Improved Streamlink documentation in the settings dialog. (#5076)
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
- Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848)
- Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834)
Expand Down
138 changes: 56 additions & 82 deletions src/util/StreamLink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,118 +15,92 @@
#include <QErrorMessage>
#include <QFileInfo>
#include <QProcess>
#include <QStringBuilder>

#include <functional>

namespace chatterino {

namespace {

const char *getBinaryName()
{
#ifdef _WIN32
return "streamlink.exe";
#else
return "streamlink";
#endif
}
using namespace chatterino;

const char *getDefaultBinaryPath()
QString getStreamlinkPath()
{
if (getSettings()->streamlinkUseCustomPath)
{
#ifdef _WIN32
return "C:\\Program Files (x86)\\Streamlink\\bin\\streamlink.exe";
#else
return "/usr/bin/streamlink";
#endif
const QString path = getSettings()->streamlinkPath;
return path.trimmed() % "/" % STREAMLINK_BINARY_NAME;
}

bool checkStreamlinkPath(const QString &path)
{
QFileInfo fileinfo(path);
return STREAMLINK_BINARY_NAME.toString();
}

if (!fileinfo.exists())
{
return false;
// throw Exception(fS("Streamlink path ({}) is invalid, file does
// not exist", path));
}
void showStreamlinkNotFoundError()
{
static auto *msg = new QErrorMessage;
pajlada marked this conversation as resolved.
Show resolved Hide resolved
msg->setWindowTitle("Chatterino - streamlink not found");

return fileinfo.isExecutable();
if (getSettings()->streamlinkUseCustomPath)
{
msg->showMessage("Unable to find Streamlink executable\nMake sure "
"your custom path is pointing to the DIRECTORY "
"where the streamlink executable is located");
}

void showStreamlinkNotFoundError()
else
{
static QErrorMessage *msg = new QErrorMessage;
msg->setWindowTitle("Chatterino - streamlink not found");

if (getSettings()->streamlinkUseCustomPath)
{
msg->showMessage("Unable to find Streamlink executable\nMake sure "
"your custom path is pointing to the DIRECTORY "
"where the streamlink executable is located");
}
else
{
msg->showMessage(
"Unable to find Streamlink executable.\nIf you have Streamlink "
"installed, you might need to enable the custom path option");
}
msg->showMessage(
"Unable to find Streamlink executable.\nIf you have Streamlink "
"installed, you might need to enable the custom path option");
}
}

QProcess *createStreamlinkProcess()
{
auto *p = new QProcess;
QProcess *createStreamlinkProcess()
{
auto *p = new QProcess;

const QString path = []() -> QString {
if (getSettings()->streamlinkUseCustomPath)
{
const QString path = getSettings()->streamlinkPath;
return path.trimmed() + "/" + getBinaryName();
}
const auto path = getStreamlinkPath();

return {getBinaryName()};
}();
if (Version::instance().isFlatpak())
{
p->setProgram("flatpak-spawn");
p->setArguments({"--host", path});
}
else
{
p->setProgram(path);
}

if (Version::instance().isFlatpak())
QObject::connect(p, &QProcess::errorOccurred, [=](auto err) {
if (err == QProcess::FailedToStart)
{
p->setProgram("flatpak-spawn");
p->setArguments({"--host", path});
showStreamlinkNotFoundError();
}
else
{
p->setProgram(path);
qCWarning(chatterinoStreamlink) << "Error occurred" << err;
}

QObject::connect(p, &QProcess::errorOccurred, [=](auto err) {
if (err == QProcess::FailedToStart)
{
showStreamlinkNotFoundError();
}
else
{
qCWarning(chatterinoStreamlink) << "Error occurred" << err;
}
p->deleteLater();
});

QObject::connect(
p,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
&QProcess::finished),
[=](int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
p->deleteLater();
});

QObject::connect(
p,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(
&QProcess::finished),
[=](int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
p->deleteLater();
});

return p;
}
return p;
}

} // namespace

namespace chatterino {

void getStreamQualities(const QString &channelURL,
std::function<void(QStringList)> cb)
{
auto p = createStreamlinkProcess();
auto *p = createStreamlinkProcess();

QObject::connect(
p,
Expand All @@ -146,7 +120,7 @@ void getStreamQualities(const QString &channelURL,
QStringList split =
lastLine.right(lastLine.length() - 19).split(", ");

for (int i = split.length() - 1; i >= 0; i--)
for (auto i = split.length() - 1; i >= 0; i--)
{
QString option = split.at(i);
if (option == "best)")
Expand Down Expand Up @@ -186,7 +160,7 @@ void getStreamQualities(const QString &channelURL,
void openStreamlink(const QString &channelURL, const QString &quality,
QStringList extraArguments)
{
auto proc = createStreamlinkProcess();
auto *proc = createStreamlinkProcess();
auto arguments = proc->arguments()
<< extraArguments << channelURL << quality;

Expand Down Expand Up @@ -214,8 +188,8 @@ void openStreamlinkForChannel(const QString &channel)
getApp()->windows->getMainWindow().getNotebook().getSelectedPage());
if (currentPage != nullptr)
{
if (auto currentSplit = currentPage->getSelectedSplit();
currentSplit != nullptr)
auto *currentSplit = currentPage->getSelectedSplit();
if (currentSplit != nullptr)
{
currentSplit->getChannel()->addMessage(
makeSystemMessage(INFO_TEMPLATE.arg(channel)));
Expand Down
6 changes: 6 additions & 0 deletions src/util/StreamLink.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ class Exception : public std::runtime_error
using std::runtime_error::runtime_error;
};

#ifdef Q_OS_WIN
constexpr inline QStringView STREAMLINK_BINARY_NAME = u"streamlink.exe";
#else
constexpr inline QStringView STREAMLINK_BINARY_NAME = u"streamlink";
#endif

// Open streamlink for given channel, quality and extra arguments
// the "Additional arguments" are fetched and added at the beginning of the
// streamlink call
Expand Down
25 changes: 17 additions & 8 deletions src/widgets/settingspages/ExternalToolsPage.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include "ExternalToolsPage.hpp"
#include "widgets/settingspages/ExternalToolsPage.hpp"

#include "singletons/Settings.hpp"
#include "util/Helpers.hpp"
#include "util/LayoutCreator.hpp"
#include "util/RemoveScrollAreaBackground.hpp"
#include "util/StreamLink.hpp"

#include <QFormLayout>
#include <QGroupBox>
Expand All @@ -28,15 +29,15 @@ ExternalToolsPage::ExternalToolsPage()
auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>();

auto description = new QLabel(
auto *description = new QLabel(
"Streamlink is a command-line utility that pipes video streams "
"from various "
"services into a video player, such as VLC. Make sure to edit the "
"configuration file before you use it!");
description->setWordWrap(true);
description->setStyleSheet("color: #bbb");

auto links = new QLabel(
auto *links = new QLabel(
formatRichNamedLink("https://streamlink.github.io/", "Website") +
" " +
formatRichNamedLink(
Expand All @@ -54,13 +55,21 @@ ExternalToolsPage::ExternalToolsPage()
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);

auto customPathCb =
auto *customPathCb =
this->createCheckBox("Use custom path (Enable if using "
"non-standard streamlink installation path)",
getSettings()->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);

auto customPath = this->createLineEdit(getSettings()->streamlinkPath);
auto *note = new QLabel(
QStringLiteral(
"Chatterino expects the executable to be called \"%1\".")
.arg(STREAMLINK_BINARY_NAME));
note->setWordWrap(true);
note->setStyleSheet("color: #bbb");
groupLayout->setWidget(3, QFormLayout::SpanningRole, note);

auto *customPath = this->createLineEdit(getSettings()->streamlinkPath);
customPath->setPlaceholderText(
"Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
Expand All @@ -84,7 +93,7 @@ ExternalToolsPage::ExternalToolsPage()
auto group = layout.emplace<QGroupBox>("Custom stream player");
auto groupLayout = group.setLayoutType<QFormLayout>();

const auto description = new QLabel(
auto *description = new QLabel(
"You can open Twitch streams directly in any video player that "
"has built-in Twitch support and has own URI Scheme.\nE.g.: "
"IINA for macOS and Potplayer (with extension) for "
Expand All @@ -96,7 +105,7 @@ ExternalToolsPage::ExternalToolsPage()

groupLayout->setWidget(0, QFormLayout::SpanningRole, description);

auto lineEdit = this->createLineEdit(getSettings()->customURIScheme);
auto *lineEdit = this->createLineEdit(getSettings()->customURIScheme);
lineEdit->setPlaceholderText("custom-player-scheme://");
groupLayout->addRow("Custom stream player URI Scheme:", lineEdit);
}
Expand All @@ -106,7 +115,7 @@ ExternalToolsPage::ExternalToolsPage()
auto group = layout.emplace<QGroupBox>("Image Uploader");
auto groupLayout = group.setLayoutType<QFormLayout>();

const auto description = new QLabel(
auto *description = new QLabel(
"You can set custom host for uploading images, like "
"imgur.com or s-ul.eu.<br>Check " +
formatRichNamedLink("https://chatterino.com/help/image-uploader",
Expand Down
Loading