Skip to content

Commit

Permalink
Improve Streamlink documentation (Chatterino#5076)
Browse files Browse the repository at this point in the history
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
  • Loading branch information
fraxxio and pajlada authored Jan 12, 2024
1 parent 5c9747e commit 06f950a
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 90 deletions.
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;
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

0 comments on commit 06f950a

Please sign in to comment.