Add fallback to update mechanism

This brings a fallback version check to the update mechanism,
which should be as stable as it can be.
It will allow migrating to another primary mechanism without
having to have updated the older primary mechanism too.
This commit is contained in:
sledgehammer999 2025-06-29 22:25:34 +03:00
parent 5028f68d48
commit c47b981a56
No known key found for this signature in database
GPG key ID: 6E4A2D025B7CC9A2
2 changed files with 69 additions and 11 deletions

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"
@ -82,30 +85,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 QString 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();
@ -148,7 +155,7 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
qDebug("Detected version is %s", qUtf8Printable(version)); qDebug("Detected version is %s", qUtf8Printable(version));
if (isVersionMoreRecent(version)) if (isVersionMoreRecent(version))
{ {
m_newVersion = version; m_remoteVersion = version;
m_updateURL = updateLink; m_updateURL = updateLink;
} }
} }
@ -163,10 +170,55 @@ 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 auto ver = verJSON.toString();
if (isVersionMoreRecent(ver))
m_fallbackRemoteVersion = ver;
}
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
{
using Version = Utils::Version<4, 3>;
const auto remote = Version::fromString(m_remoteVersion);
const auto fallback = Version::fromString(m_fallbackRemoteVersion);
return fallback > remote;
} }

View file

@ -55,8 +55,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;
QString m_remoteVersion;
QString m_fallbackRemoteVersion;
QUrl m_updateURL; QUrl m_updateURL;
}; };