diff --git a/CHANGELOG.md b/CHANGELOG.md index 77cd1f3d4..a62c787b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/util/StreamLink.cpp b/src/util/StreamLink.cpp index f83bc8cbc..3c2b3fc3b 100644 --- a/src/util/StreamLink.cpp +++ b/src/util/StreamLink.cpp @@ -15,118 +15,92 @@ #include #include #include +#include #include -namespace chatterino { - namespace { - const char *getBinaryName() +using namespace chatterino; + +QString getStreamlinkPath() +{ + if (getSettings()->streamlinkUseCustomPath) { -#ifdef _WIN32 - return "streamlink.exe"; -#else - return "streamlink"; -#endif + const QString path = getSettings()->streamlinkPath; + return path.trimmed() % "/" % STREAMLINK_BINARY_NAME; } - const char *getDefaultBinaryPath() + return STREAMLINK_BINARY_NAME.toString(); +} + +void showStreamlinkNotFoundError() +{ + static auto *msg = new QErrorMessage; + msg->setWindowTitle("Chatterino - streamlink not found"); + + if (getSettings()->streamlinkUseCustomPath) { -#ifdef _WIN32 - return "C:\\Program Files (x86)\\Streamlink\\bin\\streamlink.exe"; -#else - return "/usr/bin/streamlink"; -#endif + 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"); + } +} + +QProcess *createStreamlinkProcess() +{ + auto *p = new QProcess; + + const auto path = getStreamlinkPath(); + + if (Version::instance().isFlatpak()) + { + p->setProgram("flatpak-spawn"); + p->setArguments({"--host", path}); + } + else + { + p->setProgram(path); } - bool checkStreamlinkPath(const QString &path) - { - QFileInfo fileinfo(path); - - if (!fileinfo.exists()) + QObject::connect(p, &QProcess::errorOccurred, [=](auto err) { + if (err == QProcess::FailedToStart) { - return false; - // throw Exception(fS("Streamlink path ({}) is invalid, file does - // not exist", path)); - } - - return fileinfo.isExecutable(); - } - - void showStreamlinkNotFoundError() - { - 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"); + showStreamlinkNotFoundError(); } else { - 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; - - const QString path = []() -> QString { - if (getSettings()->streamlinkUseCustomPath) - { - const QString path = getSettings()->streamlinkPath; - return path.trimmed() + "/" + getBinaryName(); - } - - return {getBinaryName()}; - }(); - - if (Version::instance().isFlatpak()) - { - p->setProgram("flatpak-spawn"); - p->setArguments({"--host", path}); - } - 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( + &QProcess::finished), + [=](int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { p->deleteLater(); }); - QObject::connect( - p, - static_cast( - &QProcess::finished), - [=](int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { - p->deleteLater(); - }); - - return p; - } + return p; +} } // namespace +namespace chatterino { + void getStreamQualities(const QString &channelURL, std::function cb) { - auto p = createStreamlinkProcess(); + auto *p = createStreamlinkProcess(); QObject::connect( p, @@ -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)") @@ -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; @@ -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))); diff --git a/src/util/StreamLink.hpp b/src/util/StreamLink.hpp index 99dbb95b9..8ebcc617b 100644 --- a/src/util/StreamLink.hpp +++ b/src/util/StreamLink.hpp @@ -14,6 +14,12 @@ public: 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 diff --git a/src/widgets/settingspages/ExternalToolsPage.cpp b/src/widgets/settingspages/ExternalToolsPage.cpp index 6fb615d93..5619d460c 100644 --- a/src/widgets/settingspages/ExternalToolsPage.cpp +++ b/src/widgets/settingspages/ExternalToolsPage.cpp @@ -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 #include @@ -28,7 +29,7 @@ ExternalToolsPage::ExternalToolsPage() auto group = layout.emplace("Streamlink"); auto groupLayout = group.setLayoutType(); - 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 " @@ -36,7 +37,7 @@ ExternalToolsPage::ExternalToolsPage() description->setWordWrap(true); description->setStyleSheet("color: #bbb"); - auto links = new QLabel( + auto *links = new QLabel( formatRichNamedLink("https://streamlink.github.io/", "Website") + " " + formatRichNamedLink( @@ -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); @@ -84,7 +93,7 @@ ExternalToolsPage::ExternalToolsPage() auto group = layout.emplace("Custom stream player"); auto groupLayout = group.setLayoutType(); - 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 " @@ -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); } @@ -106,7 +115,7 @@ ExternalToolsPage::ExternalToolsPage() auto group = layout.emplace("Image Uploader"); auto groupLayout = group.setLayoutType(); - const auto description = new QLabel( + auto *description = new QLabel( "You can set custom host for uploading images, like " "imgur.com or s-ul.eu.
Check " + formatRichNamedLink("https://chatterino.com/help/image-uploader",