Backport changes to v5.1.x branch

PR #22988.
This commit is contained in:
Vladimir Golovnev
2025-11-04 16:25:31 +03:00
committed by GitHub
24 changed files with 185 additions and 86 deletions

View File

@@ -191,6 +191,11 @@ jobs:
name: qBittorrent-CI_Windows-x64_libtorrent-${{ matrix.libt_version }}
path: upload
- name: Install NSIS
uses: repolevedavaj/install-nsis@265e893c16602d8ccfb0a9ca44173b084078917c # v1.0.3
with:
nsis-version: '3.11'
- name: Create installer
run: |
7z x -o"dist/windows/" "dist/windows/NSISPlugins.zip"

View File

@@ -86,6 +86,19 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
PROPERTIES
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
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

View File

@@ -147,12 +147,14 @@ namespace
QString value(const QString &arg) const
{
QStringList parts = arg.split(u'=');
if (parts.size() == 2)
return Utils::String::unquote(parts[1], u"'\""_s);
const qsizetype index = arg.indexOf(u'=');
if (index == -1)
throw CommandLineParameterError(QCoreApplication::translate("CMD Options", "Parameter '%1' must follow syntax '%1=%2'",
"e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'")
.arg(fullParameter(), u"<value>"_s));
const QStringView val = QStringView(arg).sliced(index + 1);
return Utils::String::unquote(val, u"'\""_s).toString();
}
QString value(const QProcessEnvironment &env, const QString &defaultValue = {}) const
@@ -168,7 +170,7 @@ namespace
friend bool operator==(const StringOption &option, const QString &arg)
{
return arg.startsWith(option.parameterAssignment());
return (arg == option.fullParameter()) || arg.startsWith(option.parameterAssignment());
}
private:

View File

@@ -3151,7 +3151,7 @@ void SessionImpl::exportTorrentFile(const Torrent *torrent, const Path &folderPa
while (newTorrentPath.exists())
{
// Append number to torrent name to make it unique
torrentExportFilename = u"%1 %2.torrent"_s.arg(validName).arg(++counter);
torrentExportFilename = u"%1 (%2).torrent"_s.arg(validName).arg(++counter);
newTorrentPath = folderPath / Path(torrentExportFilename);
}
@@ -5651,6 +5651,25 @@ void SessionImpl::fetchPendingAlerts(const lt::time_duration time)
m_nativeSession->pop_alerts(&m_alerts);
}
void SessionImpl::endAlertSequence(const int alertType, const qsizetype alertCount)
{
qDebug() << "End alert sequence. Alert:" << lt::alert_name(alertType) << "Count:" << alertCount;
if (alertType == lt::add_torrent_alert::alert_type)
{
emit addTorrentAlertsReceived(alertCount);
if (!m_loadedTorrents.isEmpty())
{
if (isRestored())
m_torrentsQueueChanged = true;
emit torrentsLoaded(m_loadedTorrents);
m_loadedTorrents.clear();
}
}
}
TorrentContentLayout SessionImpl::torrentContentLayout() const
{
return m_torrentContentLayout;
@@ -5667,28 +5686,26 @@ void SessionImpl::readAlerts()
fetchPendingAlerts();
Q_ASSERT(m_loadedTorrents.isEmpty());
Q_ASSERT(m_receivedAddTorrentAlertsCount == 0);
if (!isRestored())
m_loadedTorrents.reserve(MAX_PROCESSING_RESUMEDATA_COUNT);
int previousAlertType = -1;
qsizetype alertSequenceSize = 0;
for (const lt::alert *a : m_alerts)
{
const int alertType = a->type();
if ((alertType != previousAlertType) && (previousAlertType != -1))
{
endAlertSequence(previousAlertType, alertSequenceSize);
alertSequenceSize = 0;
}
handleAlert(a);
if (m_receivedAddTorrentAlertsCount > 0)
{
emit addTorrentAlertsReceived(m_receivedAddTorrentAlertsCount);
m_receivedAddTorrentAlertsCount = 0;
if (!m_loadedTorrents.isEmpty())
{
if (isRestored())
m_torrentsQueueChanged = true;
emit torrentsLoaded(m_loadedTorrents);
m_loadedTorrents.clear();
}
++alertSequenceSize;
previousAlertType = alertType;
}
endAlertSequence(previousAlertType, alertSequenceSize);
// Some torrents may become "finished" after different alerts handling.
processPendingFinishedTorrents();
@@ -5696,8 +5713,6 @@ void SessionImpl::readAlerts()
void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
{
++m_receivedAddTorrentAlertsCount;
if (alert->error)
{
const QString msg = QString::fromStdString(alert->message());

View File

@@ -607,6 +607,7 @@ namespace BitTorrent
void populateAdditionalTrackersFromURL();
void fetchPendingAlerts(lt::time_duration time = lt::time_duration::zero());
void endAlertSequence(int alertType, qsizetype alertCount);
void moveTorrentStorage(const MoveStorageJob &job) const;
void handleMoveTorrentStorageJobFinished(const Path &newPath);

View File

@@ -60,7 +60,7 @@ namespace Http
inline const QString HEADER_X_CONTENT_TYPE_OPTIONS = u"x-content-type-options"_s;
inline const QString HEADER_X_FORWARDED_FOR = u"x-forwarded-for"_s;
inline const QString HEADER_X_FORWARDED_HOST = u"x-forwarded-host"_s;
inline const QString HEADER_X_FORWARDED_PROTO = u"X-forwarded-proto"_s;
inline const QString HEADER_X_FORWARDED_PROTO = u"x-forwarded-proto"_s;
inline const QString HEADER_X_FRAME_OPTIONS = u"x-frame-options"_s;
inline const QString HEADER_X_XSS_PROTECTION = u"x-xss-protection"_s;

View File

@@ -87,7 +87,7 @@ namespace
QPointer<SearchPluginManager> SearchPluginManager::m_instance = nullptr;
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
m_instance = this;

View File

@@ -104,7 +104,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const Path &path)
if (!dir.isEmpty(QDir::Dirs | QDir::NoDotAndDotDot))
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
// temp files on linux usually end with '~', e.g. `filename~`

View File

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

View File

@@ -508,7 +508,7 @@ void AddNewTorrentDialog::setCurrentContext(const std::shared_ptr<Context> conte
{
m_ui->lblMetaLoading->setVisible(false);
m_ui->progMetaLoading->setVisible(false);
m_ui->buttonSave->setVisible(false);
m_ui->buttonSave->setVisible(true);
setupTreeview();
}
else
@@ -520,6 +520,7 @@ void AddNewTorrentDialog::setCurrentContext(const std::shared_ptr<Context> conte
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable"));
updateDiskSpaceLabel();
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
m_ui->buttonSave->setVisible(false);
}
TMMChanged(m_ui->comboTMM->currentIndex());

View File

@@ -38,19 +38,28 @@
HidableTabWidget::HidableTabWidget(QWidget *parent)
: QTabWidget(parent)
{
// Skip single tab in keyboard navigation (no point navigating to it)
tabBar()->setFocusPolicy(Qt::NoFocus);
}
void HidableTabWidget::tabInserted(const int index)
{
QTabWidget::tabInserted(index);
tabBar()->setVisible(count() != 1);
tabsCountChanged();
}
void HidableTabWidget::tabRemoved(const int index)
{
//QTabWidget::tabInserted(index);
QTabWidget::tabRemoved(index);
tabBar()->setVisible(count() != 1);
tabsCountChanged();
}
void HidableTabWidget::tabsCountChanged()
{
const qsizetype tabsCount = count();
tabBar()->setVisible(tabsCount != 1);
// Skip single tab in keyboard navigation (no point navigating to it)
tabBar()->setFocusPolicy((tabsCount > 1) ? Qt::StrongFocus : Qt::NoFocus);
}
#ifdef Q_OS_MACOS

View File

@@ -42,6 +42,7 @@ public:
private:
void tabInserted(int index) override;
void tabRemoved(int index) override;
void tabsCountChanged();
#ifdef Q_OS_MACOS
void paintEvent(QPaintEvent *event) override;

View File

@@ -1660,7 +1660,7 @@ void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool i
updater->deleteLater();
};
const auto newVersion = updater->getNewVersion();
const ProgramUpdater::Version newVersion = updater->getNewVersion();
if (newVersion.isValid())
{
const QString msg {tr("A new version is available.") + u"<br/>"

View File

@@ -29,6 +29,8 @@
#include "programupdater.h"
#include <algorithm>
#include <libtorrent/version.hpp>
#include <QtCore/qconfig.h>
@@ -44,7 +46,6 @@
#include "base/logger.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
#include "base/utils/version.h"
#include "base/version.h"
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,
// the filehost can identify it and contact us.
Net::DownloadManager::instance()->download(Net::DownloadRequest(RSS_URL).userAgent(USER_AGENT)
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished);
Net::DownloadManager::instance()->download(Net::DownloadRequest(FALLBACK_URL).userAgent(USER_AGENT)
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::fallbackDownloadFinished);
const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)");
const auto FOSSHUB_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s;
const auto QBT_MAIN_URL = u"https://www.qbittorrent.org/versions.json"_s;
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
{
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)
{
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();
return;
}
@@ -150,10 +169,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
if (!version.isEmpty())
{
qDebug("Detected version is %s", qUtf8Printable(version));
const ProgramUpdater::Version tmpVer {version};
const Version tmpVer {version};
if (isVersionMoreRecent(tmpVer))
{
m_remoteVersion = tmpVer;
m_fosshubVersion = tmpVer;
m_updateURL = updateLink;
}
}
@@ -171,11 +190,13 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result)
handleFinishedRequest();
}
void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result, Version &version)
{
version = {};
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();
return;
}
@@ -190,9 +211,9 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
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))
m_fallbackRemoteVersion = tmpVer;
version = tmpVer;
}
handleFinishedRequest();
@@ -200,18 +221,33 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result)
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()
{
if (m_hasCompletedOneReq)
--m_pendingRequestCount;
if (m_pendingRequestCount == 0)
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)
public:
using QObject::QObject;
using Version = Utils::Version<4, 3>;
void checkForUpdates() const;
using QObject::QObject;
void checkForUpdates();
Version getNewVersion() const;
bool updateProgram() const;
@@ -57,14 +58,22 @@ signals:
private slots:
void rssDownloadFinished(const Net::DownloadResult &result);
void fallbackDownloadFinished(const Net::DownloadResult &result);
void fallbackDownloadFinished(const Net::DownloadResult &result, Version &version);
private:
void handleFinishedRequest();
bool shouldUseFallback() const;
enum class RemoteSource
{
Fosshub,
QbtMain,
QbtBackup
};
mutable bool m_hasCompletedOneReq = false;
Version m_remoteVersion;
Version m_fallbackRemoteVersion;
void handleFinishedRequest();
RemoteSource getLatestRemoteSource() const;
int m_pendingRequestCount = 0;
Version m_fosshubVersion;
Version m_qbtMainVersion;
Version m_qbtBackupVersion;
QUrl m_updateURL;
};

View File

@@ -819,21 +819,21 @@ void TransferListWidget::exportTorrent()
bool hasError = false;
for (const BitTorrent::Torrent *torrent : torrents)
{
const QString validName = Utils::Fs::toValidFileName(torrent->name(), u"_"_s);
const Path filePath = savePath / Path(validName + u".torrent");
if (filePath.exists())
const QString validName = Utils::Fs::toValidFileName(torrent->name());
QString torrentExportFilename = u"%1.torrent"_s.arg(validName);
Path newTorrentPath = savePath / Path(torrentExportFilename);
int counter = 0;
while (newTorrentPath.exists())
{
LogMsg(errorMsg.arg(torrent->name(), filePath.toString(), tr("A file with the same name already exists")) , Log::WARNING);
hasError = true;
continue;
// Append number to torrent name to make it unique
torrentExportFilename = u"%1 (%2).torrent"_s.arg(validName).arg(++counter);
newTorrentPath = savePath / Path(torrentExportFilename);
}
const nonstd::expected<void, QString> result = torrent->exportToFile(filePath);
if (!result)
if (const nonstd::expected<void, QString> result = torrent->exportToFile(newTorrentPath); !result)
{
LogMsg(errorMsg.arg(torrent->name(), filePath.toString(), result.error()) , Log::WARNING);
LogMsg(errorMsg.arg(torrent->name(), newTorrentPath.toString(), result.error()) , Log::WARNING);
hasError = true;
continue;
}
}
@@ -1285,9 +1285,16 @@ void TransferListWidget::displayListMenu()
listMenu->popup(QCursor::pos());
}
void TransferListWidget::currentChanged(const QModelIndex &current, const QModelIndex&)
void TransferListWidget::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
qDebug("CURRENT CHANGED");
// Call base class to ensure Qt's accessibility system is notified of focus changes.
// This is critical for screen readers to announce the currently selected torrent.
// Without this call, users relying on assistive technologies cannot effectively
// navigate the torrent list with keyboard arrow keys.
QTreeView::currentChanged(current, previous);
BitTorrent::Torrent *torrent = nullptr;
if (current.isValid())
{

View File

@@ -110,7 +110,7 @@ private slots:
void torrentDoubleClicked();
void displayListMenu();
void displayColumnHeaderMenu();
void currentChanged(const QModelIndex &current, const QModelIndex&) override;
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
void setSelectedTorrentsSuperSeeding(bool enabled) const;
void setSelectedTorrentsSequentialDownload(bool enabled) const;
void setSelectedFirstLastPiecePrio(bool enabled) const;

View File

@@ -6,7 +6,7 @@
</head>
<body>
<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)
</p>
<ul>

View File

@@ -1210,7 +1210,7 @@ void AppController::getDirectoryContentAction()
QJsonArray ret;
QDirIterator it {dirPath, (QDir::NoDotAndDotDot | parseDirectoryContentMode(visibility))};
while (it.hasNext())
ret.append(it.next());
ret.append(Path(it.next()).toString());
setResult(ret);
}

View File

@@ -349,7 +349,7 @@ window.qBittorrent.DynamicTable ??= (() => {
}
},
setupDynamicTableHeaderContextMenuClass: function() {
setupDynamicTableHeaderContextMenuClass: () => {
DynamicTableHeaderContextMenuClass ??= class extends window.qBittorrent.ContextMenu.ContextMenu {
updateMenuItems() {
for (let i = 0; i < this.dynamicTable.columns.length; ++i) {
@@ -2316,7 +2316,7 @@ window.qBittorrent.DynamicTable ??= (() => {
this.updateGlobalCheckbox();
},
_sortNodesByColumn: function(nodes, column) {
_sortNodesByColumn: (nodes, column) => {
nodes.sort((row1, row2) => {
// list folders before files when sorting by name
if (column.name === "original") {
@@ -2633,7 +2633,7 @@ window.qBittorrent.DynamicTable ??= (() => {
this.columns["availability"].updateTd = displayPercentage;
},
_sortNodesByColumn: function(nodes, column) {
_sortNodesByColumn: (nodes, column) => {
nodes.sort((row1, row2) => {
// list folders before files when sorting by name
if (column.name === "name") {
@@ -3012,7 +3012,7 @@ window.qBittorrent.DynamicTable ??= (() => {
this.newColumn("checked", "", "", 30, true);
this.newColumn("name", "", "", -1, true);
this.columns["checked"].updateTd = function(td, row) {
this.columns["checked"].updateTd = (td, row) => {
if ($(`cbRssDlRule${row.rowId}`) === null) {
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
@@ -3110,7 +3110,7 @@ window.qBittorrent.DynamicTable ??= (() => {
this.newColumn("checked", "", "", 30, true);
this.newColumn("name", "", "", -1, true);
this.columns["checked"].updateTd = function(td, row) {
this.columns["checked"].updateTd = (td, row) => {
if ($(`cbRssDlFeed${row.rowId}`) === null) {
const checkbox = document.createElement("input");
checkbox.type = "checkbox";

View File

@@ -103,7 +103,7 @@ window.qBittorrent.FileTree ??= (() => {
return nodes;
},
_getArrayOfNodes: function(node, array) {
_getArrayOfNodes: (node, array) => {
array.push(node);
node.children.each((child) => {
this._getArrayOfNodes(child, array);

View File

@@ -73,7 +73,7 @@ window.qBittorrent.pathAutofill ??= (() => {
.catch(error => {});
};
function attachPathAutofill() {
const attachPathAutofill = () => {
const directoryInputs = document.querySelectorAll(".pathDirectory:not(.pathAutoFillInitialized)");
for (const input of directoryInputs) {
input.addEventListener("input", function() { showPathSuggestions(this, "dirs"); });

View File

@@ -773,7 +773,7 @@ window.qBittorrent.Search ??= (() => {
$("numSearchResultsVisible").textContent = searchResultsTable.getFilteredAndSortedRows().length;
};
const loadSearchResultsData = function(searchId) {
const loadSearchResultsData = (searchId) => {
const state = searchState.get(searchId);
const url = new URL("api/v2/search/results", window.location);
url.search = new URLSearchParams({

View File

@@ -73,7 +73,7 @@
<div id="aboutTranslatorsContent" class="aboutTabContent invisible">
<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)
</p>
<ul>