This commit is contained in:
Vladimir Golovnev 2025-08-09 17:42:02 +03:00 committed by GitHub
commit c67328cf17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 97 additions and 39 deletions

View file

@ -86,6 +86,19 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
PROPERTIES PROPERTIES
MACOSX_PACKAGE_LOCATION Resources MACOSX_PACKAGE_LOCATION Resources
) )
# Generate lproj folders for the translations
foreach(TS_FILE IN LISTS QBT_TS_FILES)
string(FIND "${TS_FILE}" "_" POS)
math(EXPR START "${POS} + 1")
string(SUBSTRING "${TS_FILE}" ${START} -1 LPROJ_FOLDER)
string(REPLACE ".ts" ".lproj" LPROJ_FOLDER "${LPROJ_FOLDER}")
# @ is not valid as a language code for a lproj folder on MacOS
string(REPLACE "@" "-" LPROJ_FOLDER "${LPROJ_FOLDER}")
add_custom_command(TARGET qbt_app POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_FILE_DIR:qbt_app>/../Resources/${LPROJ_FOLDER}"
)
endforeach()
# provide variables for substitution in dist/mac/Info.plist # provide variables for substitution in dist/mac/Info.plist
get_target_property(EXECUTABLE_NAME qbt_app OUTPUT_NAME) get_target_property(EXECUTABLE_NAME qbt_app OUTPUT_NAME)
# This variable name should be changed once qmake is no longer used. Refer to the discussion in PR #14813 # This variable name should be changed once qmake is no longer used. Refer to the discussion in PR #14813

View file

@ -87,7 +87,7 @@ namespace
QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr; QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr;
SearchPluginManager::SearchPluginManager() SearchPluginManager::SearchPluginManager()
: m_updateUrl(u"https://searchplugins.qbittorrent.org/nova3/engines/"_s) : m_updateUrl(u"https://raw.githubusercontent.com/qbittorrent/search-plugins/refs/heads/master/nova3/engines/"_s)
{ {
Q_ASSERT(!m_instance); // only one instance is allowed Q_ASSERT(!m_instance); // only one instance is allowed
m_instance = this; m_instance = this;

View file

@ -104,7 +104,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const Path &path)
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot)) if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
continue; continue;
const QStringList tmpFileList = dir.entryList(QDir::Files); const QStringList tmpFileList = dir.entryList(QDir::Files | QDir::Hidden);
// deleteFilesList contains unwanted files, usually created by the OS // deleteFilesList contains unwanted files, usually created by the OS
// temp files on linux usually end with '~', e.g. `filename~` // temp files on linux usually end with '~', e.g. `filename~`

View file

@ -45,7 +45,7 @@ namespace
RandomLayer() RandomLayer()
{ {
if (::getrandom(nullptr, 0, 0) < 0) if (unsigned char buf = 0; ::getrandom(&buf, sizeof(buf), 0) < 0)
{ {
if (errno == ENOSYS) if (errno == ENOSYS)
{ {

View file

@ -1660,7 +1660,7 @@ void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool i
updater->deleteLater(); updater->deleteLater();
}; };
const auto newVersion = updater->getNewVersion(); const ProgramUpdater::Version newVersion = updater->getNewVersion();
if (newVersion.isValid()) 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/>"

View file

@ -29,6 +29,8 @@
#include "programupdater.h" #include "programupdater.h"
#include <algorithm>
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
#include <QtCore/qconfig.h> #include <QtCore/qconfig.h>
@ -44,7 +46,6 @@
#include "base/logger.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/version.h" #include "base/version.h"
namespace namespace
@ -80,32 +81,50 @@ namespace
} }
} }
void ProgramUpdater::checkForUpdates() const void ProgramUpdater::checkForUpdates()
{ {
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 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::DownloadRequest(RSS_URL).userAgent(USER_AGENT) const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)");
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished); const auto FOSSHUB_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s;
Net::DownloadManager::instance()->download(Net::DownloadRequest(FALLBACK_URL).userAgent(USER_AGENT) const auto QBT_MAIN_URL = u"https://www.qbittorrent.org/versions.json"_s;
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::fallbackDownloadFinished); const auto QBT_BACKUP_URL = u"https://qbittorrent.github.io/qBittorrent-website/versions.json"_s;
m_hasCompletedOneReq = false; Net::DownloadManager *netManager = Net::DownloadManager::instance();
const bool useProxy = Preferences::instance()->useProxyForGeneralPurposes();
m_pendingRequestCount = 3;
netManager->download(Net::DownloadRequest(FOSSHUB_URL).userAgent(USER_AGENT), useProxy, this, &ProgramUpdater::rssDownloadFinished);
// don't use the custom user agent for the following requests, disguise as a normal browser instead
netManager->download(Net::DownloadRequest(QBT_MAIN_URL), useProxy, this, [this](const Net::DownloadResult &result)
{
fallbackDownloadFinished(result, m_qbtMainVersion);
});
netManager->download(Net::DownloadRequest(QBT_BACKUP_URL), useProxy, this, [this](const Net::DownloadResult &result)
{
fallbackDownloadFinished(result, m_qbtBackupVersion);
});
} }
ProgramUpdater::Version ProgramUpdater::getNewVersion() const ProgramUpdater::Version ProgramUpdater::getNewVersion() const
{ {
return shouldUseFallback() ? m_fallbackRemoteVersion : m_remoteVersion; switch (getLatestRemoteSource())
{
case RemoteSource::Fosshub:
return m_fosshubVersion;
case RemoteSource::QbtMain:
return m_qbtMainVersion;
case RemoteSource::QbtBackup:
return m_qbtBackupVersion;
}
Q_UNREACHABLE();
} }
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)
{ {
LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING); LogMsg(tr("Failed to download the program update info. URL: \"%1\". Error: \"%2\"").arg(result.url, result.errorString) , Log::WARNING);
handleFinishedRequest(); handleFinishedRequest();
return; return;
} }
@ -150,10 +169,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));
const ProgramUpdater::Version tmpVer {version}; const Version tmpVer {version};
if (isVersionMoreRecent(tmpVer)) if (isVersionMoreRecent(tmpVer))
{ {
m_remoteVersion = tmpVer; m_fosshubVersion = tmpVer;
m_updateURL = updateLink; m_updateURL = updateLink;
} }
} }
@ -171,11 +190,13 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
handleFinishedRequest(); handleFinishedRequest();
} }
void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result) void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result, Version &version)
{ {
version = {};
if (result.status != Net::DownloadStatus::Success) 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); LogMsg(tr("Failed to download the program update info. URL: \"%1\". Error: \"%2\"").arg(result.url, result.errorString) , Log::WARNING);
handleFinishedRequest(); handleFinishedRequest();
return; return;
} }
@ -190,9 +211,9 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString()) if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString())
{ {
const ProgramUpdater::Version tmpVer {verJSON.toString()}; const Version tmpVer {verJSON.toString()};
if (isVersionMoreRecent(tmpVer)) if (isVersionMoreRecent(tmpVer))
m_fallbackRemoteVersion = tmpVer; version = tmpVer;
} }
handleFinishedRequest(); handleFinishedRequest();
@ -200,18 +221,33 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
bool ProgramUpdater::updateProgram() const bool ProgramUpdater::updateProgram() const
{ {
return QDesktopServices::openUrl(shouldUseFallback() ? u"https://www.qbittorrent.org/download"_s : m_updateURL); switch (getLatestRemoteSource())
{
case RemoteSource::Fosshub:
return QDesktopServices::openUrl(m_updateURL);
case RemoteSource::QbtMain:
return QDesktopServices::openUrl(u"https://www.qbittorrent.org/download"_s);
case RemoteSource::QbtBackup:
return QDesktopServices::openUrl(u"https://qbittorrent.github.io/qBittorrent-website/download"_s);
}
Q_UNREACHABLE();
} }
void ProgramUpdater::handleFinishedRequest() void ProgramUpdater::handleFinishedRequest()
{ {
if (m_hasCompletedOneReq) --m_pendingRequestCount;
if (m_pendingRequestCount == 0)
emit updateCheckFinished(); emit updateCheckFinished();
else
m_hasCompletedOneReq = true;
} }
bool ProgramUpdater::shouldUseFallback() const ProgramUpdater::RemoteSource ProgramUpdater::getLatestRemoteSource() const
{ {
return m_fallbackRemoteVersion > m_remoteVersion; const Version max = std::max({m_fosshubVersion, m_qbtMainVersion, m_qbtBackupVersion});
if (max == m_fosshubVersion)
return RemoteSource::Fosshub;
if (max == m_qbtMainVersion)
return RemoteSource::QbtMain;
if (max == m_qbtBackupVersion)
return RemoteSource::QbtBackup;
Q_UNREACHABLE();
} }

View file

@ -45,10 +45,11 @@ class ProgramUpdater final : public QObject
Q_DISABLE_COPY_MOVE(ProgramUpdater) Q_DISABLE_COPY_MOVE(ProgramUpdater)
public: public:
using QObject::QObject;
using Version = Utils::Version<4, 3>; using Version = Utils::Version<4, 3>;
void checkForUpdates() const; using QObject::QObject;
void checkForUpdates();
Version getNewVersion() const; Version getNewVersion() const;
bool updateProgram() const; bool updateProgram() const;
@ -57,14 +58,22 @@ signals:
private slots: private slots:
void rssDownloadFinished(const Net::DownloadResult &result); void rssDownloadFinished(const Net::DownloadResult &result);
void fallbackDownloadFinished(const Net::DownloadResult &result); void fallbackDownloadFinished(const Net::DownloadResult &result, Version &version);
private: private:
void handleFinishedRequest(); enum class RemoteSource
bool shouldUseFallback() const; {
Fosshub,
QbtMain,
QbtBackup
};
mutable bool m_hasCompletedOneReq = false; void handleFinishedRequest();
Version m_remoteVersion; RemoteSource getLatestRemoteSource() const;
Version m_fallbackRemoteVersion;
int m_pendingRequestCount = 0;
Version m_fosshubVersion;
Version m_qbtMainVersion;
Version m_qbtBackupVersion;
QUrl m_updateURL; QUrl m_updateURL;
}; };

View file

@ -6,7 +6,7 @@
</head> </head>
<body> <body>
<p>I would like to thank the people who volunteered to translate qBittorrent.<br> <p>I would like to thank the people who volunteered to translate qBittorrent.<br>
Most of them translated via <a href="https://www.transifex.com/sledgehammer999/qbittorrent">Transifex</a> and some of them are mentioned below:<br> Most of them translated via <a href="https://explore.transifex.com/sledgehammer999/qbittorrent/">Transifex</a> and some of them are mentioned below:<br>
(the list might not be up to date) (the list might not be up to date)
</p> </p>
<ul> <ul>

View file

@ -73,7 +73,7 @@
<div id="aboutTranslatorsContent" class="aboutTabContent invisible"> <div id="aboutTranslatorsContent" class="aboutTabContent invisible">
<p>I would like to thank the people who volunteered to translate qBittorrent.<br> <p>I would like to thank the people who volunteered to translate qBittorrent.<br>
Most of them translated via <a target="_blank" href="https://www.transifex.com/sledgehammer999/qbittorrent/">Transifex</a> and some of them are mentioned below:<br> Most of them translated via <a target="_blank" href="https://explore.transifex.com/sledgehammer999/qbittorrent/">Transifex</a> and some of them are mentioned below:<br>
(the list might not be up to date) (the list might not be up to date)
</p> </p>
<ul> <ul>