Add brief description to Streamlink settings page

Add links to Streamlink website and download page to Streamlink settings page
Make streamlink custom path usage more explicit
Change how streamlink runs, it now works perfectly on Linux so that if you don't have a custom path set, it will try to just run "streamlink" in your PATH variable

This needs testing on Windows again
This commit is contained in:
Rasmus Karlsson 2018-05-06 16:33:00 +02:00
parent aba8e1a18f
commit 49458e4fac
3 changed files with 115 additions and 46 deletions

View file

@ -61,10 +61,6 @@ public:
"/behaviour/autocompletion/onlyFetchChattersForSmallerStreamers", true}; "/behaviour/autocompletion/onlyFetchChattersForSmallerStreamers", true};
IntSetting smallStreamerLimit = {"/behaviour/autocompletion/smallStreamerLimit", 1000}; IntSetting smallStreamerLimit = {"/behaviour/autocompletion/smallStreamerLimit", 1000};
// Streamlink
QStringSetting streamlinkPath = {"/behaviour/streamlink/path", ""};
QStringSetting preferredQuality = {"/behaviour/streamlink/quality", "Choose"};
QStringSetting streamlinkOpts = {"/behaviour/streamlink/options", ""};
BoolSetting pauseChatHover = {"/behaviour/pauseChatHover", false}; BoolSetting pauseChatHover = {"/behaviour/pauseChatHover", false};
/// Commands /// Commands
@ -114,6 +110,13 @@ public:
BoolSetting inlineWhispers = {"/whispers/enableInlineWhispers", true}; BoolSetting inlineWhispers = {"/whispers/enableInlineWhispers", true};
/// External tools
// Streamlink
BoolSetting streamlinkUseCustomPath = {"/external/streamlink/useCustomPath", false};
QStringSetting streamlinkPath = {"/external/streamlink/customPath", ""};
QStringSetting preferredQuality = {"/external/streamlink/quality", "Choose"};
QStringSetting streamlinkOpts = {"/external/streamlink/options", ""};
void updateWordTypeMask(); void updateWordTypeMask();
void saveSnapshot(); void saveSnapshot();

View file

@ -5,6 +5,7 @@
#include "singletons/settingsmanager.hpp" #include "singletons/settingsmanager.hpp"
#include "widgets/qualitypopup.hpp" #include "widgets/qualitypopup.hpp"
#include <QErrorMessage>
#include <QFileInfo> #include <QFileInfo>
#include <QProcess> #include <QProcess>
@ -33,6 +34,17 @@ const char *GetDefaultBinaryPath()
#endif #endif
} }
QString getStreamlinkProgram()
{
auto app = getApp();
if (app->settings->streamlinkUseCustomPath) {
return app->settings->streamlinkPath + "/" + GetBinaryName();
} else {
return GetBinaryName();
}
}
bool CheckStreamlinkPath(const QString &path) bool CheckStreamlinkPath(const QString &path)
{ {
QFileInfo fileinfo(path); QFileInfo fileinfo(path);
@ -42,49 +54,57 @@ bool CheckStreamlinkPath(const QString &path)
// throw Exception(fS("Streamlink path ({}) is invalid, file does not exist", path)); // throw Exception(fS("Streamlink path ({}) is invalid, file does not exist", path));
} }
if (fileinfo.isDir() || !fileinfo.isExecutable()) { return fileinfo.isExecutable();
return false;
} }
return true; void showStreamlinkNotFoundError()
}
// TODO: Make streamlink binary finder smarter
QString GetStreamlinkBinaryPath()
{ {
static QErrorMessage *msg = new QErrorMessage;
auto app = getApp(); auto app = getApp();
if (app->settings->streamlinkUseCustomPath) {
QString settingPath = app->settings->streamlinkPath; msg->showMessage(
"Unable to find Streamlink executable\nMake sure your custom path is pointing "
QStringList paths; "to the DIRECTORY where the streamlink executable is located");
paths << settingPath; } else {
paths << GetDefaultBinaryPath(); msg->showMessage("Unable to find Streamlink executable.\nIf you have Streamlink "
#ifdef _WIN32 "installed, you might need to enable the custom path option");
paths << settingPath + "\\" + GetBinaryName();
paths << settingPath + "\\bin\\" + GetBinaryName();
#else
paths << "/usr/local/bin/streamlink";
paths << "/bin/streamlink";
#endif
for (const auto &path : paths) {
if (CheckStreamlinkPath(path)) {
return path;
} }
} }
throw Exception("Unable to find streamlink binary. Install streamlink or set the binary path " QProcess *createStreamlinkProcess()
"in the settings dialog."); {
auto p = new QProcess;
p->setProgram(getStreamlinkProgram());
QObject::connect(p, &QProcess::errorOccurred, [=](auto err) {
if (err == QProcess::FailedToStart) {
showStreamlinkNotFoundError();
} else {
qDebug() << "Error occured: " << err; //
} }
p->deleteLater();
});
QObject::connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [=](int res) {
p->deleteLater(); //
});
return p;
}
} // namespace
void GetStreamQualities(const QString &channelURL, std::function<void(QStringList)> cb) void GetStreamQualities(const QString &channelURL, std::function<void(QStringList)> cb)
{ {
QString path = GetStreamlinkBinaryPath(); auto p = createStreamlinkProcess();
// XXX: Memory leak QObject::connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [=](int res) {
QProcess *p = new QProcess(); if (res != 0) {
qDebug() << "Got error code" << res;
QObject::connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [=](int) { // return;
}
QString lastLine = QString(p->readAllStandardOutput()); QString lastLine = QString(p->readAllStandardOutput());
lastLine = lastLine.trimmed().split('\n').last().trimmed(); lastLine = lastLine.trimmed().split('\n').last().trimmed();
if (lastLine.startsWith("Available streams: ")) { if (lastLine.startsWith("Available streams: ")) {
@ -106,17 +126,15 @@ void GetStreamQualities(const QString &channelURL, std::function<void(QStringLis
} }
}); });
p->start(path, {channelURL, "--default-stream=KKona"}); p->setArguments({channelURL, "--default-stream=KKona"});
}
} // namespace p->start();
}
void OpenStreamlink(const QString &channelURL, const QString &quality, QStringList extraArguments) void OpenStreamlink(const QString &channelURL, const QString &quality, QStringList extraArguments)
{ {
auto app = getApp(); auto app = getApp();
QString path = GetStreamlinkBinaryPath();
QStringList arguments; QStringList arguments;
QString additionalOptions = app->settings->streamlinkOpts.getValue(); QString additionalOptions = app->settings->streamlinkOpts.getValue();
@ -132,7 +150,15 @@ void OpenStreamlink(const QString &channelURL, const QString &quality, QStringLi
arguments << quality; arguments << quality;
} }
QProcess::startDetached(path, arguments); auto p = createStreamlinkProcess();
p->setArguments(arguments);
bool res = p->startDetached();
if (!res) {
showStreamlinkNotFoundError();
}
} }
void Start(const QString &channel) void Start(const QString &channel)

View file

@ -11,6 +11,15 @@ namespace chatterino {
namespace widgets { namespace widgets {
namespace settingspages { namespace settingspages {
namespace {
QString CreateLink(const QString &url, const QString &name)
{
return QString("<a href=\"" + url + "\"><span style=\"color: white;\">" + name + "</span></a>");
}
} // namespace
ExternalToolsPage::ExternalToolsPage() ExternalToolsPage::ExternalToolsPage()
: SettingsPage("External tools", "") : SettingsPage("External tools", "")
{ {
@ -22,13 +31,44 @@ ExternalToolsPage::ExternalToolsPage()
{ {
auto group = layout.emplace<QGroupBox>("Streamlink"); auto group = layout.emplace<QGroupBox>("Streamlink");
auto groupLayout = group.setLayoutType<QFormLayout>(); auto groupLayout = group.setLayoutType<QFormLayout>();
groupLayout->addRow("Streamlink path:",
this->createLineEdit(app->settings->streamlinkPath)); 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(
CreateLink("https://streamlink.github.io/", "Website") + " " +
CreateLink("https://github.com/streamlink/streamlink/releases/latest", "Download"));
links->setTextFormat(Qt::RichText);
links->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::LinksAccessibleByKeyboard |
Qt::LinksAccessibleByKeyboard);
links->setOpenExternalLinks(true);
groupLayout->setWidget(0, QFormLayout::SpanningRole, description);
groupLayout->setWidget(1, QFormLayout::SpanningRole, links);
auto customPathCb = this->createCheckBox(
"Use custom path (Enable if using non-standard streamlink installation path)",
app->settings->streamlinkUseCustomPath);
groupLayout->setWidget(2, QFormLayout::SpanningRole, customPathCb);
auto customPath = this->createLineEdit(app->settings->streamlinkPath);
customPath->setPlaceholderText("Path to folder where Streamlink executable can be found");
groupLayout->addRow("Custom streamlink path:", customPath);
groupLayout->addRow( groupLayout->addRow(
"Prefered quality:", "Preferred quality:",
this->createComboBox({STREAMLINK_QUALITY}, app->settings->preferredQuality)); this->createComboBox({STREAMLINK_QUALITY}, app->settings->preferredQuality));
groupLayout->addRow("Additional options:", groupLayout->addRow("Additional options:",
this->createLineEdit(app->settings->streamlinkOpts)); this->createLineEdit(app->settings->streamlinkOpts));
app->settings->streamlinkUseCustomPath.connect(
[=](const auto &value, auto) {
customPath->setEnabled(value); //
},
this->managedConnections);
} }
layout->addStretch(1); layout->addStretch(1);