Backport changes to v4.6.x branch

PR #21035.
This commit is contained in:
Vladimir Golovnev 2024-08-16 07:12:35 +03:00 committed by GitHub
commit 742bc410f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 99 additions and 30 deletions

View file

@ -425,6 +425,8 @@ Path TorrentImpl::savePath() const
void TorrentImpl::setSavePath(const Path &path) void TorrentImpl::setSavePath(const Path &path)
{ {
Q_ASSERT(!isAutoTMMEnabled()); Q_ASSERT(!isAutoTMMEnabled());
if (Q_UNLIKELY(isAutoTMMEnabled()))
return;
const Path basePath = m_session->useCategoryPathsInManualMode() const Path basePath = m_session->useCategoryPathsInManualMode()
? m_session->categorySavePath(category()) : m_session->savePath(); ? m_session->categorySavePath(category()) : m_session->savePath();
@ -452,6 +454,8 @@ Path TorrentImpl::downloadPath() const
void TorrentImpl::setDownloadPath(const Path &path) void TorrentImpl::setDownloadPath(const Path &path)
{ {
Q_ASSERT(!isAutoTMMEnabled()); Q_ASSERT(!isAutoTMMEnabled());
if (Q_UNLIKELY(isAutoTMMEnabled()))
return;
const Path basePath = m_session->useCategoryPathsInManualMode() const Path basePath = m_session->useCategoryPathsInManualMode()
? m_session->categoryDownloadPath(category()) : m_session->downloadPath(); ? m_session->categoryDownloadPath(category()) : m_session->downloadPath();
@ -1364,11 +1368,13 @@ QBitArray TorrentImpl::pieces() const
QBitArray TorrentImpl::downloadingPieces() const QBitArray TorrentImpl::downloadingPieces() const
{ {
QBitArray result(piecesCount()); if (!hasMetadata())
return {};
std::vector<lt::partial_piece_info> queue; std::vector<lt::partial_piece_info> queue;
m_nativeHandle.get_download_queue(queue); m_nativeHandle.get_download_queue(queue);
QBitArray result {piecesCount()};
for (const lt::partial_piece_info &info : queue) for (const lt::partial_piece_info &info : queue)
result.setBit(LT::toUnderlyingType(info.piece_index)); result.setBit(LT::toUnderlyingType(info.piece_index));
@ -1821,8 +1827,17 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageContext cont
{ {
if (!hasMetadata()) if (!hasMetadata())
{ {
m_savePath = newPath; if (context == MoveStorageContext::ChangeSavePath)
m_session->handleTorrentSavePathChanged(this); {
m_savePath = newPath;
m_session->handleTorrentSavePathChanged(this);
}
else if (context == MoveStorageContext::ChangeDownloadPath)
{
m_downloadPath = newPath;
m_session->handleTorrentSavePathChanged(this);
}
return; return;
} }

View file

@ -51,7 +51,7 @@
namespace namespace
{ {
// Disguise as Firefox to avoid web server banning // Disguise as Firefox to avoid web server banning
const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"; const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0";
} }
class Net::DownloadManager::NetworkCookieJar final : public QNetworkCookieJar class Net::DownloadManager::NetworkCookieJar final : public QNetworkCookieJar

View file

@ -56,6 +56,19 @@
#include "gui/macutilities.h" #include "gui/macutilities.h"
#endif #endif
namespace
{
QList<QPersistentModelIndex> toPersistentIndexes(const QModelIndexList &indexes)
{
QList<QPersistentModelIndex> persistentIndexes;
persistentIndexes.reserve(indexes.size());
for (const QModelIndex &index : indexes)
persistentIndexes.append(index);
return persistentIndexes;
}
}
TorrentContentWidget::TorrentContentWidget(QWidget *parent) TorrentContentWidget::TorrentContentWidget(QWidget *parent)
: QTreeView(parent) : QTreeView(parent)
{ {
@ -219,9 +232,9 @@ void TorrentContentWidget::keyPressEvent(QKeyEvent *event)
const Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked) const Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked)
? Qt::Unchecked : Qt::Checked; ? Qt::Unchecked : Qt::Checked;
const QModelIndexList selection = selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME); const QList<QPersistentModelIndex> selection = toPersistentIndexes(selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME));
for (const QModelIndex &index : selection) for (const QPersistentModelIndex &index : selection)
model()->setData(index, state, Qt::CheckStateRole); model()->setData(index, state, Qt::CheckStateRole);
} }
@ -248,10 +261,10 @@ void TorrentContentWidget::renameSelectedFile()
void TorrentContentWidget::applyPriorities(const BitTorrent::DownloadPriority priority) void TorrentContentWidget::applyPriorities(const BitTorrent::DownloadPriority priority)
{ {
const QModelIndexList selectedRows = selectionModel()->selectedRows(0); const QList<QPersistentModelIndex> selectedRows = toPersistentIndexes(selectionModel()->selectedRows(Priority));
for (const QModelIndex &index : selectedRows) for (const QPersistentModelIndex &index : selectedRows)
{ {
model()->setData(index.sibling(index.row(), Priority), static_cast<int>(priority)); model()->setData(index, static_cast<int>(priority));
} }
} }
@ -261,7 +274,7 @@ void TorrentContentWidget::applyPrioritiesByOrder()
// a download priority that will apply to each item. The number of groups depends on how // a download priority that will apply to each item. The number of groups depends on how
// many "download priority" are available to be assigned // many "download priority" are available to be assigned
const QModelIndexList selectedRows = selectionModel()->selectedRows(0); const QList<QPersistentModelIndex> selectedRows = toPersistentIndexes(selectionModel()->selectedRows(Priority));
const qsizetype priorityGroups = 3; const qsizetype priorityGroups = 3;
const auto priorityGroupSize = std::max<qsizetype>((selectedRows.length() / priorityGroups), 1); const auto priorityGroupSize = std::max<qsizetype>((selectedRows.length() / priorityGroups), 1);
@ -283,8 +296,8 @@ void TorrentContentWidget::applyPrioritiesByOrder()
break; break;
} }
const QModelIndex &index = selectedRows[i]; const QPersistentModelIndex &index = selectedRows[i];
model()->setData(index.sibling(index.row(), Priority), static_cast<int>(priority)); model()->setData(index, static_cast<int>(priority));
} }
} }

View file

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2023-2024 Vladimir Golovnev <glassez@yandex.ru>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -37,6 +37,7 @@
#include "base/global.h" #include "base/global.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "flowlayout.h" #include "flowlayout.h"
#include "utils.h"
#include "ui_torrenttagsdialog.h" #include "ui_torrenttagsdialog.h"
@ -52,10 +53,10 @@ TorrentTagsDialog::TorrentTagsDialog(const TagSet &initialTags, QWidget *parent)
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
auto *tagsLayout = new FlowLayout(m_ui->scrollArea); auto *tagsLayout = new FlowLayout(m_ui->scrollArea->widget());
for (const QString &tag : asConst(initialTags.united(BitTorrent::Session::instance()->tags()))) for (const QString &tag : asConst(initialTags.united(BitTorrent::Session::instance()->tags())))
{ {
auto *tagWidget = new QCheckBox(tag); auto *tagWidget = new QCheckBox(Utils::Gui::tagToWidgetText(tag));
if (initialTags.contains(tag)) if (initialTags.contains(tag))
tagWidget->setChecked(true); tagWidget->setChecked(true);
tagsLayout->addWidget(tagWidget); tagsLayout->addWidget(tagWidget);
@ -78,12 +79,12 @@ TorrentTagsDialog::~TorrentTagsDialog()
TagSet TorrentTagsDialog::tags() const TagSet TorrentTagsDialog::tags() const
{ {
TagSet tags; TagSet tags;
auto *layout = m_ui->scrollArea->layout(); auto *layout = m_ui->scrollArea->widget()->layout();
for (int i = 0; i < (layout->count() - 1); ++i) for (int i = 0; i < (layout->count() - 1); ++i)
{ {
const auto *tagWidget = static_cast<QCheckBox *>(layout->itemAt(i)->widget()); const auto *tagWidget = static_cast<QCheckBox *>(layout->itemAt(i)->widget());
if (tagWidget->isChecked()) if (tagWidget->isChecked())
tags.insert(tagWidget->text()); tags.insert(Utils::Gui::widgetTextToTag(tagWidget->text()));
} }
return tags; return tags;
@ -111,9 +112,9 @@ void TorrentTagsDialog::addNewTag()
} }
else else
{ {
auto *layout = m_ui->scrollArea->layout(); auto *layout = m_ui->scrollArea->widget()->layout();
auto *btn = layout->takeAt(layout->count() - 1); auto *btn = layout->takeAt(layout->count() - 1);
auto *tagWidget = new QCheckBox(tag); auto *tagWidget = new QCheckBox(Utils::Gui::tagToWidgetText(tag));
tagWidget->setChecked(true); tagWidget->setChecked(true);
layout->addWidget(tagWidget); layout->addWidget(tagWidget);
layout->addItem(btn); layout->addItem(btn);

View file

@ -235,10 +235,7 @@ void StatusFilterWidget::applyFilter(int row)
void StatusFilterWidget::handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents) void StatusFilterWidget::handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents)
{ {
for (const BitTorrent::Torrent *torrent : torrents) update(torrents);
updateTorrentStatus(torrent);
updateTexts();
} }
void StatusFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) void StatusFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent)
@ -273,6 +270,12 @@ void StatusFilterWidget::torrentAboutToBeDeleted(BitTorrent::Torrent *const torr
m_nbStalled = m_nbStalledUploading + m_nbStalledDownloading; m_nbStalled = m_nbStalledUploading + m_nbStalledDownloading;
updateTexts(); updateTexts();
if (Preferences::instance()->getHideZeroStatusFilters())
{
hideZeroItems();
updateGeometry();
}
} }
void StatusFilterWidget::configure() void StatusFilterWidget::configure()

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -1191,7 +1192,7 @@ void TransferListWidget::displayListMenu()
for (const QString &tag : asConst(tags)) for (const QString &tag : asConst(tags))
{ {
auto *action = new TriStateAction(tag, tagsMenu); auto *action = new TriStateAction(Utils::Gui::tagToWidgetText(tag), tagsMenu);
action->setCloseOnInteraction(false); action->setCloseOnInteraction(false);
const Qt::CheckState initialState = tagsInAll.contains(tag) ? Qt::Checked const Qt::CheckState initialState = tagsInAll.contains(tag) ? Qt::Checked

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017 Mike Tzou * Copyright (C) 2017 Mike Tzou
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -218,3 +219,29 @@ void Utils::Gui::openFolderSelect(const Path &path)
openPath(path.parentPath()); openPath(path.parentPath());
#endif #endif
} }
QString Utils::Gui::tagToWidgetText(const QString &tag)
{
return QString(tag).replace(u'&', u"&&"_s);
}
QString Utils::Gui::widgetTextToTag(const QString &text)
{
// replace pairs of '&' with single '&' and remove non-paired occurrences of '&'
QString cleanedText;
cleanedText.reserve(text.size());
bool amp = false;
for (const QChar c : text)
{
if (c == u'&')
{
amp = !amp;
if (amp)
continue;
}
cleanedText.append(c);
}
return cleanedText;
}

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017 Mike Tzou * Copyright (C) 2017 Mike Tzou
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -34,6 +35,7 @@ class QIcon;
class QPixmap; class QPixmap;
class QPoint; class QPoint;
class QSize; class QSize;
class QString;
class QWidget; class QWidget;
namespace Utils::Gui namespace Utils::Gui
@ -51,4 +53,7 @@ namespace Utils::Gui
void openPath(const Path &path); void openPath(const Path &path);
void openFolderSelect(const Path &path); void openFolderSelect(const Path &path);
QString tagToWidgetText(const QString &tag);
QString widgetTextToTag(const QString &text);
} }

View file

@ -1,4 +1,4 @@
#VERSION: 1.43 #VERSION: 1.44
# Author: # Author:
# Christophe DUMEZ (chris@qbittorrent.org) # Christophe DUMEZ (chris@qbittorrent.org)
@ -40,7 +40,7 @@ import urllib.parse
import urllib.request import urllib.request
# Some sites blocks default python User-agent # Some sites blocks default python User-agent
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0' user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0'
headers = {'User-Agent': user_agent} headers = {'User-Agent': user_agent}
# SOCKS5 Proxy support # SOCKS5 Proxy support
if "sock_proxy" in os.environ and len(os.environ["sock_proxy"].strip()) > 0: if "sock_proxy" in os.environ and len(os.environ["sock_proxy"].strip()) > 0:

View file

@ -670,6 +670,7 @@ window.addEvent('load', function() {
torrentsTable.clear(); torrentsTable.clear();
category_list = {}; category_list = {};
tagList = {}; tagList = {};
trackerList.clear();
} }
if (response['rid']) { if (response['rid']) {
syncMainDataLastResponseId = response['rid']; syncMainDataLastResponseId = response['rid'];

View file

@ -597,6 +597,9 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
rulesList[rule].torrentParams.save_path = $('saveToText').value; rulesList[rule].torrentParams.save_path = $('saveToText').value;
rulesList[rule].torrentParams.use_auto_tmm = false; rulesList[rule].torrentParams.use_auto_tmm = false;
} }
else {
rulesList[rule].torrentParams.save_path = "";
}
switch ($('addPausedCombobox').value) { switch ($('addPausedCombobox').value) {
case 'default': case 'default':
@ -746,13 +749,13 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
$('lastMatchText').textContent = 'QBT_TR(Last Match: Unknown)QBT_TR[CONTEXT=AutomatedRssDownloader]'; $('lastMatchText').textContent = 'QBT_TR(Last Match: Unknown)QBT_TR[CONTEXT=AutomatedRssDownloader]';
} }
if (rulesList[ruleName].torrentParams.stopped === null) if ((rulesList[ruleName].torrentParams.stopped === undefined) || (rulesList[ruleName].torrentParams.stopped === null))
$('addPausedCombobox').value = 'default'; $("addStoppedCombobox").value = "default";
else else
$('addPausedCombobox').value = rulesList[ruleName].torrentParams.stopped ? 'always' : 'never'; $('addPausedCombobox').value = rulesList[ruleName].torrentParams.stopped ? 'always' : 'never';
if (rulesList[ruleName].torrentParams.content_layout === null) if ((rulesList[ruleName].torrentParams.content_layout === undefined) || (rulesList[ruleName].torrentParams.content_layout === null))
$('contentLayoutCombobox').value = 'Default'; $("contentLayoutCombobox").value = "Default";
else else
$('contentLayoutCombobox').value = rulesList[ruleName].torrentParams.content_layout; $('contentLayoutCombobox').value = rulesList[ruleName].torrentParams.content_layout;