mirror-chatterino2/src/util/StreamLink.cpp

260 lines
6.5 KiB
C++
Raw Normal View History

2018-06-26 14:09:39 +02:00
#include "util/StreamLink.hpp"
2018-06-26 14:09:39 +02:00
#include "Application.hpp"
#include "Helpers.hpp"
2018-08-15 22:46:20 +02:00
#include "debug/Log.hpp"
2018-06-28 19:46:45 +02:00
#include "singletons/Settings.hpp"
2018-06-26 15:11:45 +02:00
#include "widgets/dialogs/QualityPopup.hpp"
#include <QErrorMessage>
#include <QFileInfo>
#include <QProcess>
#include <functional>
namespace chatterino {
namespace {
2018-08-15 22:46:20 +02:00
const char *getBinaryName()
{
#ifdef _WIN32
2018-08-15 22:46:20 +02:00
return "streamlink.exe";
#else
2018-08-15 22:46:20 +02:00
return "streamlink";
#endif
2018-08-15 22:46:20 +02:00
}
2018-08-15 22:46:20 +02:00
const char *getDefaultBinaryPath()
{
#ifdef _WIN32
2018-08-15 22:46:20 +02:00
return "C:\\Program Files (x86)\\Streamlink\\bin\\streamlink.exe";
#else
2018-08-15 22:46:20 +02:00
return "/usr/bin/streamlink";
#endif
}
2018-08-15 22:46:20 +02:00
QString getStreamlinkProgram()
{
auto app = getApp();
2018-10-21 13:43:02 +02:00
if (getSettings()->streamlinkUseCustomPath)
{
2018-08-15 22:46:20 +02:00
return getSettings()->streamlinkPath + "/" + getBinaryName();
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-15 22:46:20 +02:00
return getBinaryName();
}
}
2018-08-15 22:46:20 +02:00
bool checkStreamlinkPath(const QString &path)
{
QFileInfo fileinfo(path);
2018-10-21 13:43:02 +02:00
if (!fileinfo.exists())
{
2018-08-15 22:46:20 +02:00
return false;
// throw Exception(fS("Streamlink path ({}) is invalid, file does
// not exist", path));
}
2018-08-15 22:46:20 +02:00
return fileinfo.isExecutable();
}
2018-08-15 22:46:20 +02:00
void showStreamlinkNotFoundError()
{
static QErrorMessage *msg = new QErrorMessage;
auto app = getApp();
2018-10-21 13:43:02 +02:00
if (getSettings()->streamlinkUseCustomPath)
{
2018-08-15 22:46:20 +02:00
msg->showMessage(
"Unable to find Streamlink executable\nMake sure your custom "
"path "
"is pointing "
"to the DIRECTORY where the streamlink executable is located");
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-15 22:46:20 +02:00
msg->showMessage(
"Unable to find Streamlink executable.\nIf you have Streamlink "
"installed, you might need to enable the custom path option");
}
2018-08-15 22:46:20 +02:00
}
QProcess *createStreamlinkProcess()
{
auto p = new QProcess;
p->setProgram(getStreamlinkProgram());
2018-08-15 22:46:20 +02:00
QObject::connect(p, &QProcess::errorOccurred, [=](auto err) {
2018-10-21 13:43:02 +02:00
if (err == QProcess::FailedToStart)
{
2018-08-15 22:46:20 +02:00
showStreamlinkNotFoundError();
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-15 22:46:20 +02:00
log("Error occured {}", err);
}
2018-08-15 22:46:20 +02:00
p->deleteLater();
});
2018-08-15 22:46:20 +02:00
QObject::connect(
p, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
[=](int res) {
p->deleteLater(); //
});
return p;
}
} // namespace
2018-08-06 21:17:03 +02:00
void getStreamQualities(const QString &channelURL,
std::function<void(QStringList)> cb)
{
auto p = createStreamlinkProcess();
2018-08-06 21:17:03 +02:00
QObject::connect(
p, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
[=](int res) {
2018-10-21 13:43:02 +02:00
if (res != 0)
{
log("Got error code {}", res);
2018-08-06 21:17:03 +02:00
// return;
}
2018-08-06 21:17:03 +02:00
QString lastLine = QString(p->readAllStandardOutput());
lastLine = lastLine.trimmed().split('\n').last().trimmed();
2018-10-21 13:43:02 +02:00
if (lastLine.startsWith("Available streams: "))
{
2018-08-06 21:17:03 +02:00
QStringList options;
QStringList split =
lastLine.right(lastLine.length() - 19).split(", ");
2018-10-21 13:43:02 +02:00
for (int i = split.length() - 1; i >= 0; i--)
{
2018-08-06 21:17:03 +02:00
QString option = split.at(i);
2018-10-21 13:43:02 +02:00
if (option == "best)")
{
// As it turns out, sometimes, one quality option can
// be the best and worst quality at the same time.
// Since we start loop from the end, we can check
// that and act accordingly
option = split.at(--i);
// "900p60 (worst"
options << option.left(option.length() - 7);
2018-10-21 13:43:02 +02:00
}
else if (option.endsWith(" (worst)"))
{
2018-08-06 21:17:03 +02:00
options << option.left(option.length() - 8);
2018-10-21 13:43:02 +02:00
}
else if (option.endsWith(" (best)"))
{
2018-08-06 21:17:03 +02:00
options << option.left(option.length() - 7);
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-06 21:17:03 +02:00
options << option;
}
}
2018-08-06 21:17:03 +02:00
cb(options);
}
});
p->setArguments({channelURL, "--default-stream=KKona"});
p->start();
}
2018-08-06 21:17:03 +02:00
void openStreamlink(const QString &channelURL, const QString &quality,
QStringList extraArguments)
{
auto app = getApp();
QStringList arguments;
QString additionalOptions = getSettings()->streamlinkOpts.getValue();
2018-10-21 13:43:02 +02:00
if (!additionalOptions.isEmpty())
{
arguments << getSettings()->streamlinkOpts;
}
arguments.append(extraArguments);
arguments << channelURL;
2018-10-21 13:43:02 +02:00
if (!quality.isEmpty())
{
arguments << quality;
}
2018-08-06 21:17:03 +02:00
bool res = QProcess::startDetached(getStreamlinkProgram() + " " +
QString(arguments.join(' ')));
2018-10-21 13:43:02 +02:00
if (!res)
{
showStreamlinkNotFoundError();
}
}
2018-07-06 19:23:47 +02:00
void openStreamlinkForChannel(const QString &channel)
{
auto app = getApp();
QString channelURL = "twitch.tv/" + channel;
QString preferredQuality = getSettings()->preferredQuality;
preferredQuality = preferredQuality.toLower();
2018-10-21 13:43:02 +02:00
if (preferredQuality == "choose")
{
2018-07-06 19:23:47 +02:00
getStreamQualities(channelURL, [=](QStringList qualityOptions) {
2018-06-26 17:06:17 +02:00
QualityPopup::showDialog(channel, qualityOptions);
});
return;
}
QStringList args;
// Quality converted from Chatterino format to Streamlink format
QString quality;
// Streamlink qualities to exclude
QString exclude;
2018-10-21 13:43:02 +02:00
if (preferredQuality == "high")
{
exclude = ">720p30";
quality = "high,best";
2018-10-21 13:43:02 +02:00
}
else if (preferredQuality == "medium")
{
exclude = ">540p30";
quality = "medium,best";
2018-10-21 13:43:02 +02:00
}
else if (preferredQuality == "low")
{
exclude = ">360p30";
quality = "low,best";
2018-10-21 13:43:02 +02:00
}
else if (preferredQuality == "audio only")
{
quality = "audio,audio_only";
2018-10-21 13:43:02 +02:00
}
else
{
quality = "best";
}
2018-10-21 13:43:02 +02:00
if (!exclude.isEmpty())
{
args << "--stream-sorting-excludes" << exclude;
}
2018-07-06 19:23:47 +02:00
openStreamlink(channelURL, quality, args);
}
} // namespace chatterino