Backport changes to v5.1.x branch
Some checks are pending
CI - File health / Check (push) Waiting to run
CI - macOS / Build (push) Waiting to run
CI - Python / Check (push) Waiting to run
CI - Ubuntu / Build (push) Waiting to run
CI - WebUI / Check (push) Waiting to run
CI - Windows / Build (push) Waiting to run

PR #22905.
This commit is contained in:
Vladimir Golovnev 2025-07-01 17:18:53 +03:00 committed by GitHub
commit 22df0b45c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 98 additions and 36 deletions

View file

@ -47,6 +47,9 @@ find_package(Boost ${minBoostVersion} REQUIRED)
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED) find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
find_package(ZLIB ${minZlibVersion} REQUIRED) find_package(ZLIB ${minZlibVersion} REQUIRED)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools) find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
if (Qt6_FOUND AND (Qt6_VERSION VERSION_GREATER_EQUAL 6.10))
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS CorePrivate)
endif()
if (DBUS) if (DBUS)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus) find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus)
set_package_properties(Qt6DBus PROPERTIES set_package_properties(Qt6DBus PROPERTIES

View file

@ -487,13 +487,13 @@ void SearchPluginManager::updateNova()
const Path enginePath = engineLocation(); const Path enginePath = engineLocation();
QFile packageFile {(enginePath / Path(u"__init__.py"_s)).data()}; QFile packageFile {(enginePath / Path(u"__init__.py"_s)).data()};
packageFile.open(QIODevice::WriteOnly); if (packageFile.open(QIODevice::WriteOnly))
packageFile.close(); packageFile.close();
Utils::Fs::mkdir(enginePath / Path(u"engines"_s)); Utils::Fs::mkdir(enginePath / Path(u"engines"_s));
QFile packageFile2 {(enginePath / Path(u"engines/__init__.py"_s)).data()}; QFile packageFile2 {(enginePath / Path(u"engines/__init__.py"_s)).data()};
packageFile2.open(QIODevice::WriteOnly); if (packageFile2.open(QIODevice::WriteOnly))
packageFile2.close(); packageFile2.close();
// Copy search plugin files (if necessary) // Copy search plugin files (if necessary)

View file

@ -1161,7 +1161,7 @@ void MainWindow::closeEvent(QCloseEvent *e)
if (!m_forceExit) if (!m_forceExit)
{ {
hide(); hide();
e->accept(); e->ignore();
return; return;
} }
#else #else
@ -1660,11 +1660,11 @@ void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool i
updater->deleteLater(); updater->deleteLater();
}; };
const QString newVersion = updater->getNewVersion(); const auto newVersion = updater->getNewVersion();
if (!newVersion.isEmpty()) if (newVersion.isValid())
{ {
const QString msg {tr("A new version is available.") + u"<br/>" const QString msg {tr("A new version is available.") + u"<br/>"
+ tr("Do you want to download %1?").arg(newVersion) + u"<br/><br/>" + tr("Do you want to download %1?").arg(newVersion.toString()) + u"<br/><br/>"
+ u"<a href=\"https://www.qbittorrent.org/news\">%1</a>"_s.arg(tr("Open changelog..."))}; + u"<a href=\"https://www.qbittorrent.org/news\">%1</a>"_s.arg(tr("Open changelog..."))};
auto *msgBox = new QMessageBox {QMessageBox::Question, tr("qBittorrent Update Available"), msg auto *msgBox = new QMessageBox {QMessageBox::Question, tr("qBittorrent Update Available"), msg
, (QMessageBox::Yes | QMessageBox::No), this}; , (QMessageBox::Yes | QMessageBox::No), this};

View file

@ -35,10 +35,13 @@
#include <QtSystemDetection> #include <QtSystemDetection>
#include <QDebug> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
#include <QJsonDocument>
#include <QJsonValue>
#include <QRegularExpression> #include <QRegularExpression>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include "base/global.h" #include "base/global.h"
#include "base/logger.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/utils/version.h" #include "base/utils/version.h"
@ -46,23 +49,20 @@
namespace namespace
{ {
bool isVersionMoreRecent(const QString &remoteVersion) bool isVersionMoreRecent(const ProgramUpdater::Version &remoteVersion)
{ {
using Version = Utils::Version<4, 3>; if (!remoteVersion.isValid())
const auto newVersion = Version::fromString(remoteVersion);
if (!newVersion.isValid())
return false; return false;
const Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD}; const ProgramUpdater::Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD};
if (newVersion == currentVersion) if (remoteVersion == currentVersion)
{ {
const bool isDevVersion = QStringLiteral(QBT_VERSION_STATUS).contains( const bool isDevVersion = QStringLiteral(QBT_VERSION_STATUS).contains(
QRegularExpression(u"(alpha|beta|rc)"_s)); QRegularExpression(u"(alpha|beta|rc)"_s));
if (isDevVersion) if (isDevVersion)
return true; return true;
} }
return (newVersion > currentVersion); return (remoteVersion > currentVersion);
} }
QString buildVariant() QString buildVariant()
@ -82,30 +82,34 @@ namespace
void ProgramUpdater::checkForUpdates() const void ProgramUpdater::checkForUpdates() const
{ {
const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)");
const auto RSS_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s; const auto RSS_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s;
const auto FALLBACK_URL = u"https://www.qbittorrent.org/versions.json"_s;
// Don't change this User-Agent. In case our updater goes haywire, // Don't change this User-Agent. In case our updater goes haywire,
// the filehost can identify it and contact us. // the filehost can identify it and contact us.
Net::DownloadManager::instance()->download( Net::DownloadManager::instance()->download(Net::DownloadRequest(RSS_URL).userAgent(USER_AGENT)
Net::DownloadRequest(RSS_URL).userAgent(QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)"))
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished); , Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished);
Net::DownloadManager::instance()->download(Net::DownloadRequest(FALLBACK_URL).userAgent(USER_AGENT)
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::fallbackDownloadFinished);
m_hasCompletedOneReq = false;
} }
QString ProgramUpdater::getNewVersion() const ProgramUpdater::Version ProgramUpdater::getNewVersion() const
{ {
return m_newVersion; return shouldUseFallback() ? m_fallbackRemoteVersion : m_remoteVersion;
} }
void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
{ {
if (result.status != Net::DownloadStatus::Success) if (result.status != Net::DownloadStatus::Success)
{ {
qDebug() << "Downloading the new qBittorrent updates RSS failed:" << result.errorString; LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING);
emit updateCheckFinished(); handleFinishedRequest();
return; return;
} }
qDebug("Finished downloading the new qBittorrent updates RSS");
const auto getStringValue = [](QXmlStreamReader &xml) -> QString const auto getStringValue = [](QXmlStreamReader &xml) -> QString
{ {
xml.readNext(); xml.readNext();
@ -146,9 +150,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
if (!version.isEmpty()) if (!version.isEmpty())
{ {
qDebug("Detected version is %s", qUtf8Printable(version)); qDebug("Detected version is %s", qUtf8Printable(version));
if (isVersionMoreRecent(version)) const ProgramUpdater::Version tmpVer {version};
if (isVersionMoreRecent(tmpVer))
{ {
m_newVersion = version; m_remoteVersion = tmpVer;
m_updateURL = updateLink; m_updateURL = updateLink;
} }
} }
@ -163,10 +168,50 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
} }
} }
emit updateCheckFinished(); handleFinishedRequest();
}
void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
{
if (result.status != Net::DownloadStatus::Success)
{
LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING);
handleFinishedRequest();
return;
}
const auto json = QJsonDocument::fromJson(result.data);
#if defined(Q_OS_MACOS)
const QString platformKey = u"macos"_s;
#elif defined(Q_OS_WIN)
const QString platformKey = u"win"_s;
#endif
if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString())
{
const ProgramUpdater::Version tmpVer {verJSON.toString()};
if (isVersionMoreRecent(tmpVer))
m_fallbackRemoteVersion = tmpVer;
}
handleFinishedRequest();
} }
bool ProgramUpdater::updateProgram() const bool ProgramUpdater::updateProgram() const
{ {
return QDesktopServices::openUrl(m_updateURL); return QDesktopServices::openUrl(shouldUseFallback() ? u"https://www.qbittorrent.org/download"_s : m_updateURL);
}
void ProgramUpdater::handleFinishedRequest()
{
if (m_hasCompletedOneReq)
emit updateCheckFinished();
else
m_hasCompletedOneReq = true;
}
bool ProgramUpdater::shouldUseFallback() const
{
return m_fallbackRemoteVersion > m_remoteVersion;
} }

View file

@ -30,9 +30,10 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QString>
#include <QUrl> #include <QUrl>
#include "base/utils/version.h"
namespace Net namespace Net
{ {
struct DownloadResult; struct DownloadResult;
@ -45,9 +46,10 @@ class ProgramUpdater final : public QObject
public: public:
using QObject::QObject; using QObject::QObject;
using Version = Utils::Version<4, 3>;
void checkForUpdates() const; void checkForUpdates() const;
QString getNewVersion() const; Version getNewVersion() const;
bool updateProgram() const; bool updateProgram() const;
signals: signals:
@ -55,8 +57,14 @@ signals:
private slots: private slots:
void rssDownloadFinished(const Net::DownloadResult &result); void rssDownloadFinished(const Net::DownloadResult &result);
void fallbackDownloadFinished(const Net::DownloadResult &result);
private: private:
QString m_newVersion; void handleFinishedRequest();
bool shouldUseFallback() const;
mutable bool m_hasCompletedOneReq = false;
Version m_remoteVersion;
Version m_fallbackRemoteVersion;
QUrl m_updateURL; QUrl m_updateURL;
}; };

View file

@ -80,8 +80,14 @@ inline QHash<QString, UIThemeColor> defaultUIThemeColors()
{u"TransferList.StoppedUploading"_s, {Color::Primer::Light::doneFg, Color::Primer::Dark::doneFg}}, {u"TransferList.StoppedUploading"_s, {Color::Primer::Light::doneFg, Color::Primer::Dark::doneFg}},
{u"TransferList.Moving"_s, {Color::Primer::Light::successFg, Color::Primer::Dark::successFg}}, {u"TransferList.Moving"_s, {Color::Primer::Light::successFg, Color::Primer::Dark::successFg}},
{u"TransferList.MissingFiles"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, {u"TransferList.MissingFiles"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}},
{u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, {u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}
};
}
// Palette isn't customizable in default theme
inline QHash<QString, UIThemeColor> defaultPaletteColors()
{
return {
{u"Palette.Window"_s, {{}, {}}}, {u"Palette.Window"_s, {{}, {}}},
{u"Palette.WindowText"_s, {{}, {}}}, {u"Palette.WindowText"_s, {{}, {}}},
{u"Palette.Base"_s, {{}, {}}}, {u"Palette.Base"_s, {{}, {}}},

View file

@ -273,8 +273,6 @@ void UIThemeDialog::loadColors()
int row = 2; int row = 2;
for (const QString &id : colorIDs) for (const QString &id : colorIDs)
{ {
if (id == u"Log.Normal")
qDebug() << "!!!!!!!";
m_ui->colorsLayout->addWidget(new QLabel(id), row, 0); m_ui->colorsLayout->addWidget(new QLabel(id), row, 0);
const UIThemeColor &defaultColor = defaultColors.value(id); const UIThemeColor &defaultColor = defaultColors.value(id);

View file

@ -105,6 +105,8 @@ DefaultThemeSource::DefaultThemeSource()
, m_colors {defaultUIThemeColors()} , m_colors {defaultUIThemeColors()}
{ {
loadColors(); loadColors();
// Palette isn't customizable in default theme
m_colors.insert(defaultPaletteColors());
} }
QByteArray DefaultThemeSource::readStyleSheet() QByteArray DefaultThemeSource::readStyleSheet()

View file

@ -673,12 +673,12 @@ void AppController::setPreferencesAction()
if (hasKey(u"autorun_on_torrent_added_enabled"_s)) if (hasKey(u"autorun_on_torrent_added_enabled"_s))
pref->setAutoRunOnTorrentAddedEnabled(it.value().toBool()); pref->setAutoRunOnTorrentAddedEnabled(it.value().toBool());
if (hasKey(u"autorun_on_torrent_added_program"_s)) if (hasKey(u"autorun_on_torrent_added_program"_s))
pref->setAutoRunOnTorrentAddedProgram(it.value().toString()); pref->setAutoRunOnTorrentAddedProgram(it.value().toString().trimmed());
// Run an external program on torrent finished // Run an external program on torrent finished
if (hasKey(u"autorun_enabled"_s)) if (hasKey(u"autorun_enabled"_s))
pref->setAutoRunOnTorrentFinishedEnabled(it.value().toBool()); pref->setAutoRunOnTorrentFinishedEnabled(it.value().toBool());
if (hasKey(u"autorun_program"_s)) if (hasKey(u"autorun_program"_s))
pref->setAutoRunOnTorrentFinishedProgram(it.value().toString()); pref->setAutoRunOnTorrentFinishedProgram(it.value().toString().trimmed());
// Connection // Connection
// Listening Port // Listening Port