From 5abf458e69ff644b60a205814e3da2b22febf0a8 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sat, 13 Dec 2025 14:22:20 +0800 Subject: [PATCH] Calculate torrent pieces asynchronously So the GUI won't hang when the calculation took a long time. Note that it is not possible to cancel the calculation so it will always run until finish in the background. Supersedes #23497. PR #23584. --- src/gui/torrentcreatordialog.cpp | 88 ++++++++++++++++++++++++++------ src/gui/torrentcreatordialog.h | 9 +++- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/gui/torrentcreatordialog.cpp b/src/gui/torrentcreatordialog.cpp index 71405ca8e..a7a1159f7 100644 --- a/src/gui/torrentcreatordialog.cpp +++ b/src/gui/torrentcreatordialog.cpp @@ -31,13 +31,17 @@ #include "torrentcreatordialog.h" +#include + #include #include #include #include +#include #include #include "base/bittorrent/session.h" +#include "base/bittorrent/torrentcreator.h" #include "base/bittorrent/torrentdescriptor.h" #include "base/global.h" #include "base/utils/fs.h" @@ -57,6 +61,37 @@ namespace #else const QFileDialog::Options FILE_DIALOG_OPTIONS {}; #endif + + class PieceCalculationThread final : public QThread + { + Q_OBJECT + Q_DISABLE_COPY_MOVE(PieceCalculationThread) + + public: + using CalcFunc = std::function; + + explicit PieceCalculationThread(CalcFunc calc, QObject *parent = nullptr) + : QThread(parent) + , m_calc {std::move(calc)} + { + } + + ~PieceCalculationThread() override + { + wait(); + } + + signals: + void resultReady(int pieces); + + private: + void run() override + { + emit resultReady(m_calc()); + } + + CalcFunc m_calc; + }; } TorrentCreatorDialog::TorrentCreatorDialog(QWidget *parent, const Path &defaultPath) @@ -98,7 +133,7 @@ TorrentCreatorDialog::TorrentCreatorDialog(QWidget *parent, const Path &defaultP connect(m_ui->addFolderButton, &QPushButton::clicked, this, &TorrentCreatorDialog::onAddFolderButtonClicked); connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &TorrentCreatorDialog::onCreateButtonClicked); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_ui->buttonCalcTotalPieces, &QPushButton::clicked, this, &TorrentCreatorDialog::updatePiecesCount); + connect(m_ui->buttonCalcTotalPieces, &QPushButton::clicked, this, &TorrentCreatorDialog::onCalculatePiecesButtonClicked); connect(m_ui->checkStartSeeding, &QCheckBox::clicked, m_ui->checkIgnoreShareLimits, &QWidget::setEnabled); loadSettings(); @@ -192,6 +227,41 @@ void TorrentCreatorDialog::dragEnterEvent(QDragEnterEvent *event) event->acceptProposedAction(); } +void TorrentCreatorDialog::onCalculatePiecesButtonClicked() +{ + m_ui->buttonCalcTotalPieces->setEnabled(false); + m_ui->labelTotalPieces->setText(tr("Calculating...")); + +#ifdef QBT_USES_LIBTORRENT2 + PieceCalculationThread::CalcFunc calc = [path = m_ui->textInputPath->selectedPath() + , pieceSize = getPieceSize() + , torrentFormat = getTorrentFormat()]() -> int + { + return BitTorrent::TorrentCreator::calculateTotalPieces(path, pieceSize, torrentFormat); + }; +#else + PieceCalculationThread::CalcFunc calc = [path = m_ui->textInputPath->selectedPath() + , pieceSize = getPieceSize() + , isAlignmentOptimized = m_ui->checkOptimizeAlignment->isChecked() + , paddedFileSizeLimit = getPaddedFileSizeLimit()]() -> int + { + return BitTorrent::TorrentCreator::calculateTotalPieces(path, pieceSize, isAlignmentOptimized, paddedFileSizeLimit); + }; +#endif + + // since the calculation (in libtorrent) cannot be interrupted, always let it run to completion and + // not managed by `parent` + auto *thread = new PieceCalculationThread(std::move(calc), nullptr); + thread->setObjectName("PieceCalculationThread thread"); + connect(thread, &PieceCalculationThread::finished, thread, &QObject::deleteLater); + connect(thread, &PieceCalculationThread::resultReady, this, [this](const int pieces) + { + m_ui->labelTotalPieces->setText(QString::number(pieces)); + m_ui->buttonCalcTotalPieces->setEnabled(true); + }); + thread->start(); +} + // Main function that create a .torrent file void TorrentCreatorDialog::onCreateButtonClicked() { @@ -298,20 +368,6 @@ void TorrentCreatorDialog::updateProgressBar(int progress) m_ui->progressBar->setValue(progress); } -void TorrentCreatorDialog::updatePiecesCount() -{ - const Path path = m_ui->textInputPath->selectedPath(); -#ifdef QBT_USES_LIBTORRENT2 - const int count = BitTorrent::TorrentCreator::calculateTotalPieces( - path, getPieceSize(), getTorrentFormat()); -#else - const bool isAlignmentOptimized = m_ui->checkOptimizeAlignment->isChecked(); - const int count = BitTorrent::TorrentCreator::calculateTotalPieces(path - , getPieceSize(), isAlignmentOptimized, getPaddedFileSizeLimit()); -#endif - m_ui->labelTotalPieces->setText(QString::number(count)); -} - void TorrentCreatorDialog::setInteractionEnabled(const bool enabled) const { m_ui->textInputPath->setEnabled(enabled); @@ -382,3 +438,5 @@ void TorrentCreatorDialog::loadSettings() if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid()) resize(dialogSize); } + +#include "torrentcreatordialog.moc" diff --git a/src/gui/torrentcreatordialog.h b/src/gui/torrentcreatordialog.h index 283978184..bca27b276 100644 --- a/src/gui/torrentcreatordialog.h +++ b/src/gui/torrentcreatordialog.h @@ -33,10 +33,15 @@ #include #include -#include "base/bittorrent/torrentcreator.h" #include "base/path.h" #include "base/settingvalue.h" +namespace BitTorrent +{ + enum class TorrentFormat; + struct TorrentCreatorResult; +} + namespace Ui { class TorrentCreatorDialog; @@ -54,7 +59,7 @@ public: private slots: void updateProgressBar(int progress); - void updatePiecesCount(); + void onCalculatePiecesButtonClicked(); void onCreateButtonClicked(); void onAddFileButtonClicked(); void onAddFolderButtonClicked();