mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-06 05:01:25 -07:00
Provide asynchronous results via QFuture
Makes asynchronous logic to look more straightforward. Allows caller to choose blocking or non-blocking way of obtaining asynchronous results via the same interface. PR #22598.
This commit is contained in:
parent
33aaa867b5
commit
732b2bcbdb
15 changed files with 165 additions and 223 deletions
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2020-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -27,13 +27,14 @@
|
|||
*/
|
||||
|
||||
#include "filesearcher.h"
|
||||
#include "base/bittorrent/common.h"
|
||||
#include "base/bittorrent/infohash.h"
|
||||
|
||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath, const bool forceAppendExt)
|
||||
#include <QPromise>
|
||||
|
||||
#include "base/bittorrent/common.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const auto findInDir = [](const Path &dirPath, PathList &fileNames, const bool forceAppendExt) -> bool
|
||||
bool findInDir(const Path &dirPath, PathList &fileNames, const bool forceAppendExt)
|
||||
{
|
||||
bool found = false;
|
||||
for (Path &fileName : fileNames)
|
||||
|
@ -58,7 +59,13 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi
|
|||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void FileSearcher::search(const PathList &originalFileNames, const Path &savePath
|
||||
, const Path &downloadPath, const bool forceAppendExt, QPromise<FileSearchResult> promise)
|
||||
{
|
||||
promise.start();
|
||||
|
||||
Path usedPath = savePath;
|
||||
PathList adjustedFileNames = originalFileNames;
|
||||
|
@ -69,5 +76,6 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi
|
|||
findInDir(usedPath, adjustedFileNames, forceAppendExt);
|
||||
}
|
||||
|
||||
emit searchFinished(id, usedPath, adjustedFileNames);
|
||||
promise.addResult(FileSearchResult {.savePath = usedPath, .fileNames = adjustedFileNames});
|
||||
promise.finish();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2020-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -32,10 +32,13 @@
|
|||
|
||||
#include "base/path.h"
|
||||
|
||||
namespace BitTorrent
|
||||
template <typename T> class QPromise;
|
||||
|
||||
struct FileSearchResult
|
||||
{
|
||||
class TorrentID;
|
||||
}
|
||||
Path savePath;
|
||||
PathList fileNames;
|
||||
};
|
||||
|
||||
class FileSearcher final : public QObject
|
||||
{
|
||||
|
@ -43,12 +46,8 @@ class FileSearcher final : public QObject
|
|||
Q_DISABLE_COPY_MOVE(FileSearcher)
|
||||
|
||||
public:
|
||||
FileSearcher() = default;
|
||||
using QObject::QObject;
|
||||
|
||||
public slots:
|
||||
void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames
|
||||
, const Path &savePath, const Path &downloadPath, bool forceAppendExt);
|
||||
|
||||
signals:
|
||||
void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
void search(const PathList &originalFileNames, const Path &savePath
|
||||
, const Path &downloadPath, bool forceAppendExt, QPromise<FileSearchResult> promise);
|
||||
};
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include <QDeadlineTimer>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFuture>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
@ -69,6 +70,7 @@
|
|||
#include <QMutexLocker>
|
||||
#include <QNetworkAddressEntry>
|
||||
#include <QNetworkInterface>
|
||||
#include <QPromise>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
|
@ -622,7 +624,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||
m_fileSearcher = new FileSearcher;
|
||||
m_fileSearcher->moveToThread(m_ioThread.get());
|
||||
connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
||||
connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished);
|
||||
|
||||
m_torrentContentRemover = new TorrentContentRemover;
|
||||
m_torrentContentRemover->moveToThread(m_ioThread.get());
|
||||
|
@ -2376,31 +2377,6 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
|||
}
|
||||
}
|
||||
|
||||
void SessionImpl::fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(id);
|
||||
if (torrent)
|
||||
{
|
||||
torrent->fileSearchFinished(savePath, fileNames);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto loadingTorrentsIter = m_loadingTorrents.find(id);
|
||||
if (loadingTorrentsIter != m_loadingTorrents.end())
|
||||
{
|
||||
LoadTorrentParams ¶ms = loadingTorrentsIter.value();
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
|
||||
p.save_path = savePath.toString().toStdString();
|
||||
const TorrentInfo torrentInfo {*p.ti};
|
||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||
for (int i = 0; i < fileNames.size(); ++i)
|
||||
p.renamed_files[nativeIndexes[i]] = fileNames[i].toString().toStdString();
|
||||
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionImpl::torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage)
|
||||
{
|
||||
if (errorMessage.isEmpty())
|
||||
|
@ -2828,11 +2804,12 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
|
||||
p = source.ltAddTorrentParams();
|
||||
|
||||
bool isFindingIncompleteFiles = false;
|
||||
|
||||
const bool useAutoTMM = loadTorrentParams.useAutoTMM;
|
||||
const Path actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||
|
||||
bool needFindIncompleteFiles = false;
|
||||
PathList filePaths;
|
||||
|
||||
if (hasMetadata)
|
||||
{
|
||||
// Torrent that is being added with metadata is considered to be added as stopped
|
||||
|
@ -2847,7 +2824,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
|
||||
Q_ASSERT(addTorrentParams.filePaths.isEmpty() || (addTorrentParams.filePaths.size() == torrentInfo.filesCount()));
|
||||
|
||||
PathList filePaths = addTorrentParams.filePaths;
|
||||
filePaths = addTorrentParams.filePaths;
|
||||
if (filePaths.isEmpty())
|
||||
{
|
||||
filePaths = torrentInfo.filePaths();
|
||||
|
@ -2893,13 +2870,9 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
|
||||
if (!loadTorrentParams.hasFinishedStatus)
|
||||
{
|
||||
const Path actualDownloadPath = useAutoTMM
|
||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
||||
isFindingIncompleteFiles = true;
|
||||
needFindIncompleteFiles = true;
|
||||
}
|
||||
|
||||
if (!isFindingIncompleteFiles)
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < filePaths.size(); ++index)
|
||||
p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString();
|
||||
|
@ -2998,23 +2971,49 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
m_loadingTorrents.insert(id, loadTorrentParams);
|
||||
if (infoHash.isHybrid())
|
||||
m_hybridTorrentsByAltID.insert(altID, nullptr);
|
||||
if (!isFindingIncompleteFiles)
|
||||
|
||||
if (needFindIncompleteFiles)
|
||||
{
|
||||
const Path actualDownloadPath = useAutoTMM
|
||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||
findIncompleteFiles(actualSavePath, actualDownloadPath, filePaths).then(this
|
||||
, [this, id](const FileSearchResult &result)
|
||||
{
|
||||
const auto loadingTorrentsIter = m_loadingTorrents.find(id);
|
||||
Q_ASSERT(loadingTorrentsIter != m_loadingTorrents.end());
|
||||
if (loadingTorrentsIter == m_loadingTorrents.end()) [[unlikely]]
|
||||
return;
|
||||
|
||||
LoadTorrentParams ¶ms = loadingTorrentsIter.value();
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
|
||||
p.save_path = result.savePath.toString().toStdString();
|
||||
const TorrentInfo torrentInfo {*p.ti};
|
||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||
for (int i = 0; i < result.fileNames.size(); ++i)
|
||||
p.renamed_files[nativeIndexes[i]] = result.fileNames[i].toString().toStdString();
|
||||
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SessionImpl::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths) const
|
||||
QFuture<FileSearchResult> SessionImpl::findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths) const
|
||||
{
|
||||
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
||||
|
||||
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
||||
const PathList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||
QMetaObject::invokeMethod(m_fileSearcher, [=, this]
|
||||
QPromise<FileSearchResult> promise;
|
||||
QFuture<FileSearchResult> future = promise.future();
|
||||
QMetaObject::invokeMethod(m_fileSearcher, [=, this, promise = std::move(promise)]() mutable
|
||||
{
|
||||
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath, isAppendExtensionEnabled());
|
||||
m_fileSearcher->search(filePaths, savePath, downloadPath, isAppendExtensionEnabled(), std::move(promise));
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
void SessionImpl::enablePortMapping()
|
||||
|
|
|
@ -61,12 +61,16 @@ class QString;
|
|||
class QTimer;
|
||||
class QUrl;
|
||||
|
||||
template <typename T> class QFuture;
|
||||
|
||||
class BandwidthScheduler;
|
||||
class FileSearcher;
|
||||
class FilterParserThread;
|
||||
class FreeDiskSpaceChecker;
|
||||
class NativeSessionExtension;
|
||||
|
||||
struct FileSearchResult;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
enum class MoveStorageMode;
|
||||
|
@ -478,8 +482,7 @@ namespace BitTorrent
|
|||
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context);
|
||||
|
||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath
|
||||
, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
QFuture<FileSearchResult> findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
|
||||
void enablePortMapping();
|
||||
void disablePortMapping();
|
||||
|
@ -514,7 +517,6 @@ namespace BitTorrent
|
|||
void generateResumeData();
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||
void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage);
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -45,6 +45,8 @@ class QByteArray;
|
|||
class QDateTime;
|
||||
class QUrl;
|
||||
|
||||
template <typename T> class QFuture;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
enum class DownloadPriority;
|
||||
|
@ -275,10 +277,7 @@ namespace BitTorrent
|
|||
virtual bool isDHTDisabled() const = 0;
|
||||
virtual bool isPEXDisabled() const = 0;
|
||||
virtual bool isLSDDisabled() const = 0;
|
||||
virtual QList<PeerInfo> peers() const = 0;
|
||||
virtual QBitArray pieces() const = 0;
|
||||
virtual QBitArray downloadingPieces() const = 0;
|
||||
virtual QList<int> pieceAvailability() const = 0;
|
||||
virtual qreal distributedCopies() const = 0;
|
||||
virtual qreal maxRatio() const = 0;
|
||||
virtual int maxSeedingTime() const = 0;
|
||||
|
@ -325,10 +324,10 @@ namespace BitTorrent
|
|||
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
||||
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
||||
|
||||
virtual void fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHandler) const = 0;
|
||||
virtual void fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const = 0;
|
||||
virtual void fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const = 0;
|
||||
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
||||
virtual QFuture<QList<PeerInfo>> fetchPeerInfo() const = 0;
|
||||
virtual QFuture<QList<QUrl>> fetchURLSeeds() const = 0;
|
||||
virtual QFuture<QList<int>> fetchPieceAvailability() const = 0;
|
||||
virtual QFuture<QBitArray> fetchDownloadingPieces() const = 0;
|
||||
|
||||
TorrentID id() const;
|
||||
bool isRunning() const;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2022-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -34,6 +34,8 @@
|
|||
#include "abstractfilestorage.h"
|
||||
#include "downloadpriority.h"
|
||||
|
||||
template <typename T> class QFuture;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentContentHandler : public QObject, public AbstractFileStorage
|
||||
|
@ -52,8 +54,7 @@ namespace BitTorrent
|
|||
* This is not the same as torrrent availability, it is just a fraction of pieces
|
||||
* that can be downloaded right now. It varies between 0 to 1.
|
||||
*/
|
||||
virtual QList<qreal> availableFileFractions() const = 0;
|
||||
virtual void fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const = 0;
|
||||
virtual QFuture<QList<qreal>> fetchAvailableFileFractions() const = 0;
|
||||
|
||||
virtual void prioritizeFiles(const QList<DownloadPriority> &priorities) = 0;
|
||||
virtual void flushCache() const = 0;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -51,7 +51,9 @@
|
|||
#include <QByteArray>
|
||||
#include <QCache>
|
||||
#include <QDebug>
|
||||
#include <QFuture>
|
||||
#include <QPointer>
|
||||
#include <QPromise>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
@ -67,6 +69,7 @@
|
|||
#include "common.h"
|
||||
#include "downloadpriority.h"
|
||||
#include "extensiondata.h"
|
||||
#include "filesearcher.h"
|
||||
#include "loadtorrentparams.h"
|
||||
#include "ltqbitarray.h"
|
||||
#include "lttypecast.h"
|
||||
|
@ -1465,48 +1468,11 @@ bool TorrentImpl::isLSDDisabled() const
|
|||
return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_lsd);
|
||||
}
|
||||
|
||||
QList<PeerInfo> TorrentImpl::peers() const
|
||||
{
|
||||
std::vector<lt::peer_info> nativePeers;
|
||||
m_nativeHandle.get_peer_info(nativePeers);
|
||||
|
||||
QList<PeerInfo> peers;
|
||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||
|
||||
for (const lt::peer_info &peer : nativePeers)
|
||||
peers.append(PeerInfo(peer, pieces()));
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
QBitArray TorrentImpl::pieces() const
|
||||
{
|
||||
return m_pieces;
|
||||
}
|
||||
|
||||
QBitArray TorrentImpl::downloadingPieces() const
|
||||
{
|
||||
if (!hasMetadata())
|
||||
return {};
|
||||
|
||||
std::vector<lt::partial_piece_info> queue;
|
||||
m_nativeHandle.get_download_queue(queue);
|
||||
|
||||
QBitArray result {piecesCount()};
|
||||
for (const lt::partial_piece_info &info : queue)
|
||||
result.setBit(LT::toUnderlyingType(info.piece_index));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<int> TorrentImpl::pieceAvailability() const
|
||||
{
|
||||
std::vector<int> avail;
|
||||
m_nativeHandle.piece_availability(avail);
|
||||
|
||||
return {avail.cbegin(), avail.cend()};
|
||||
}
|
||||
|
||||
qreal TorrentImpl::distributedCopies() const
|
||||
{
|
||||
return m_nativeStatus.distributed_copies;
|
||||
|
@ -1751,12 +1717,6 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled)
|
|||
m_nativeHandle.prioritize_pieces(piecePriorities);
|
||||
}
|
||||
|
||||
void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames)
|
||||
{
|
||||
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
||||
endReceivedMetadataHandling(savePath, fileNames);
|
||||
}
|
||||
|
||||
TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo)
|
||||
{
|
||||
const auto it = std::find_if(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end()
|
||||
|
@ -2150,7 +2110,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
|||
// URL seed list have been changed by libtorrent for some reason, so we need to update cached one.
|
||||
// Unfortunately, URL seed list containing in "resume data" is generated according to different rules
|
||||
// than the list we usually cache, so we have to request it from the appropriate source.
|
||||
fetchURLSeeds([this](const QList<QUrl> &urlSeeds) { m_urlSeeds = urlSeeds; });
|
||||
fetchURLSeeds().then(this, [this](const QList<QUrl> &urlSeeds) { m_urlSeeds = urlSeeds; });
|
||||
}
|
||||
|
||||
if ((m_maintenanceJob == MaintenanceJob::HandleMetadata) && p->params.ti)
|
||||
|
@ -2197,7 +2157,12 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
|||
filePaths[i] = Path(it->second);
|
||||
}
|
||||
|
||||
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
||||
m_session->findIncompleteFiles(savePath(), downloadPath(), filePaths).then(this
|
||||
, [this](const FileSearchResult &result)
|
||||
{
|
||||
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
|
||||
endReceivedMetadataHandling(result.savePath, result.fileNames);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2930,9 +2895,9 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
|||
return {};
|
||||
}
|
||||
|
||||
void TorrentImpl::fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHandler) const
|
||||
QFuture<QList<PeerInfo>> TorrentImpl::fetchPeerInfo() const
|
||||
{
|
||||
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList<PeerInfo>
|
||||
return invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList<PeerInfo>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2947,13 +2912,12 @@ void TorrentImpl::fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHand
|
|||
catch (const std::exception &) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
, std::move(resultHandler));
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentImpl::fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const
|
||||
QFuture<QList<QUrl>> TorrentImpl::fetchURLSeeds() const
|
||||
{
|
||||
invokeAsync([nativeHandle = m_nativeHandle]() -> QList<QUrl>
|
||||
return invokeAsync([nativeHandle = m_nativeHandle]() -> QList<QUrl>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2967,13 +2931,12 @@ void TorrentImpl::fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler)
|
|||
catch (const std::exception &) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
, std::move(resultHandler));
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentImpl::fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const
|
||||
QFuture<QList<int>> TorrentImpl::fetchPieceAvailability() const
|
||||
{
|
||||
invokeAsync([nativeHandle = m_nativeHandle]() -> QList<int>
|
||||
return invokeAsync([nativeHandle = m_nativeHandle]() -> QList<int>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2984,13 +2947,12 @@ void TorrentImpl::fetchPieceAvailability(std::function<void (QList<int>)> result
|
|||
catch (const std::exception &) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
, std::move(resultHandler));
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const
|
||||
QFuture<QBitArray> TorrentImpl::fetchDownloadingPieces() const
|
||||
{
|
||||
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray
|
||||
return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -3009,13 +2971,12 @@ void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultH
|
|||
catch (const std::exception &) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
, std::move(resultHandler));
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const
|
||||
QFuture<QList<qreal>> TorrentImpl::fetchAvailableFileFractions() const
|
||||
{
|
||||
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList<qreal>
|
||||
return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList<qreal>
|
||||
{
|
||||
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0))
|
||||
return {};
|
||||
|
@ -3049,8 +3010,7 @@ void TorrentImpl::fetchAvailableFileFractions(std::function<void (QList<qreal>)>
|
|||
catch (const std::exception &) {}
|
||||
|
||||
return {};
|
||||
}
|
||||
, std::move(resultHandler));
|
||||
});
|
||||
}
|
||||
|
||||
void TorrentImpl::prioritizeFiles(const QList<DownloadPriority> &priorities)
|
||||
|
@ -3090,47 +3050,17 @@ void TorrentImpl::prioritizeFiles(const QList<DownloadPriority> &priorities)
|
|||
manageActualFilePaths();
|
||||
}
|
||||
|
||||
QList<qreal> TorrentImpl::availableFileFractions() const
|
||||
template <typename Func>
|
||||
QFuture<std::invoke_result_t<Func>> TorrentImpl::invokeAsync(Func &&func) const
|
||||
{
|
||||
Q_ASSERT(hasMetadata());
|
||||
|
||||
const int filesCount = this->filesCount();
|
||||
if (filesCount <= 0) return {};
|
||||
|
||||
const QList<int> piecesAvailability = pieceAvailability();
|
||||
// libtorrent returns empty array for seeding only torrents
|
||||
if (piecesAvailability.empty()) return QList<qreal>(filesCount, -1);
|
||||
|
||||
QList<qreal> res;
|
||||
res.reserve(filesCount);
|
||||
for (int i = 0; i < filesCount; ++i)
|
||||
QPromise<std::invoke_result_t<Func>> promise;
|
||||
const auto future = promise.future();
|
||||
m_session->invokeAsync([func = std::forward<Func>(func), promise = std::move(promise)]() mutable
|
||||
{
|
||||
const TorrentInfo::PieceRange filePieces = m_torrentInfo.filePieces(i);
|
||||
|
||||
int availablePieces = 0;
|
||||
for (const int piece : filePieces)
|
||||
availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0;
|
||||
|
||||
const qreal availability = filePieces.isEmpty()
|
||||
? 1 // the file has no pieces, so it is available by default
|
||||
: static_cast<qreal>(availablePieces) / filePieces.size();
|
||||
res.push_back(availability);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Func, typename Callback>
|
||||
void TorrentImpl::invokeAsync(Func func, Callback resultHandler) const
|
||||
{
|
||||
m_session->invokeAsync([session = m_session
|
||||
, func = std::move(func)
|
||||
, resultHandler = std::move(resultHandler)
|
||||
, thisTorrent = QPointer<const TorrentImpl>(this)]() mutable
|
||||
{
|
||||
session->invoke([result = func(), thisTorrent, resultHandler = std::move(resultHandler)]
|
||||
{
|
||||
if (thisTorrent)
|
||||
resultHandler(result);
|
||||
});
|
||||
promise.start();
|
||||
promise.addResult(func());
|
||||
promise.finish();
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
|
|
@ -203,10 +203,7 @@ namespace BitTorrent
|
|||
bool isDHTDisabled() const override;
|
||||
bool isPEXDisabled() const override;
|
||||
bool isLSDDisabled() const override;
|
||||
QList<PeerInfo> peers() const override;
|
||||
QBitArray pieces() const override;
|
||||
QBitArray downloadingPieces() const override;
|
||||
QList<int> pieceAvailability() const override;
|
||||
qreal distributedCopies() const override;
|
||||
qreal maxRatio() const override;
|
||||
int maxSeedingTime() const override;
|
||||
|
@ -220,7 +217,6 @@ namespace BitTorrent
|
|||
int connectionsCount() const override;
|
||||
int connectionsLimit() const override;
|
||||
qlonglong nextAnnounce() const override;
|
||||
QList<qreal> availableFileFractions() const override;
|
||||
|
||||
void setName(const QString &name) override;
|
||||
void setSequentialDownload(bool enable) override;
|
||||
|
@ -258,11 +254,11 @@ namespace BitTorrent
|
|||
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
||||
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
||||
|
||||
void fetchPeerInfo(std::function<void (QList<PeerInfo>)> resultHandler) const override;
|
||||
void fetchURLSeeds(std::function<void (QList<QUrl>)> resultHandler) const override;
|
||||
void fetchPieceAvailability(std::function<void (QList<int>)> resultHandler) const override;
|
||||
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
||||
void fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const override;
|
||||
QFuture<QList<PeerInfo>> fetchPeerInfo() const override;
|
||||
QFuture<QList<QUrl>> fetchURLSeeds() const override;
|
||||
QFuture<QList<int>> fetchPieceAvailability() const override;
|
||||
QFuture<QBitArray> fetchDownloadingPieces() const override;
|
||||
QFuture<QList<qreal>> fetchAvailableFileFractions() const override;
|
||||
|
||||
bool needSaveResumeData() const;
|
||||
|
||||
|
@ -278,7 +274,6 @@ namespace BitTorrent
|
|||
void requestResumeData(lt::resume_data_flags_t flags = {});
|
||||
void deferredRequestResumeData();
|
||||
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
|
||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||
TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash<lt::tcp::endpoint, QMap<int, int>> &updateInfo);
|
||||
void resetTrackerEntryStatuses();
|
||||
|
||||
|
@ -326,8 +321,8 @@ namespace BitTorrent
|
|||
|
||||
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
||||
|
||||
template <typename Func, typename Callback>
|
||||
void invokeAsync(Func func, Callback resultHandler) const;
|
||||
template <typename Func>
|
||||
QFuture<std::invoke_result_t<Func>> invokeAsync(Func &&func) const;
|
||||
|
||||
SessionImpl *const m_session = nullptr;
|
||||
lt::session *m_nativeSession = nullptr;
|
||||
|
|
|
@ -32,12 +32,14 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <QtVersionChecks>
|
||||
#include <QAction>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QFuture>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
@ -242,14 +244,13 @@ public:
|
|||
return QList<qreal>(filesCount(), 0);
|
||||
}
|
||||
|
||||
QList<qreal> availableFileFractions() const override
|
||||
QFuture<QList<qreal>> fetchAvailableFileFractions() const override
|
||||
{
|
||||
return QList<qreal>(filesCount(), 0);
|
||||
}
|
||||
|
||||
void fetchAvailableFileFractions(std::function<void (QList<qreal>)> resultHandler) const override
|
||||
{
|
||||
resultHandler(availableFileFractions());
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
|
||||
return QtFuture::makeReadyValueFuture(QList<qreal>(filesCount(), 0));
|
||||
#else
|
||||
return QtFuture::makeReadyFuture(QList<qreal>(filesCount(), 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void prioritizeFiles(const QList<BitTorrent::DownloadPriority> &priorities) override
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QFuture>
|
||||
#include <QHeaderView>
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
|
@ -406,7 +407,7 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
|
|||
return;
|
||||
|
||||
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QList<BitTorrent::PeerInfo> &peers)
|
||||
torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(torrent)](const QList<BitTorrent::PeerInfo> &peers)
|
||||
{
|
||||
if (torrent != m_properties->getCurrentTorrent())
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2022-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -32,6 +32,7 @@
|
|||
#include <QClipboard>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFuture>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
@ -471,15 +472,15 @@ void PropertiesWidget::loadDynamicData()
|
|||
|
||||
if (m_torrent->hasMetadata())
|
||||
{
|
||||
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||
|
||||
m_ui->labelTotalPiecesVal->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave()));
|
||||
|
||||
if (!m_torrent->isFinished() && !m_torrent->isStopped() && !m_torrent->isQueued() && !m_torrent->isChecking())
|
||||
{
|
||||
// Pieces availability
|
||||
showPiecesAvailability(true);
|
||||
m_torrent->fetchPieceAvailability([this, torrent = TorrentPtr(m_torrent)](const QList<int> &pieceAvailability)
|
||||
|
||||
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||
m_torrent->fetchPieceAvailability().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList<int> &pieceAvailability)
|
||||
{
|
||||
if (torrent == m_torrent)
|
||||
m_piecesAvailability->setAvailability(pieceAvailability);
|
||||
|
@ -496,10 +497,9 @@ void PropertiesWidget::loadDynamicData()
|
|||
qreal progress = m_torrent->progress() * 100.;
|
||||
m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + u'%');
|
||||
|
||||
m_torrent->fetchDownloadingPieces([this, torrent = TorrentPtr(m_torrent)](const QBitArray &downloadingPieces)
|
||||
m_torrent->fetchDownloadingPieces().then(this, [this](const QBitArray &downloadingPieces)
|
||||
{
|
||||
if (torrent == m_torrent)
|
||||
m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces);
|
||||
m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces);
|
||||
});
|
||||
}
|
||||
else
|
||||
|
@ -525,7 +525,7 @@ void PropertiesWidget::loadUrlSeeds()
|
|||
return;
|
||||
|
||||
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||
m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QList<QUrl> &urlSeeds)
|
||||
m_torrent->fetchURLSeeds().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList<QUrl> &urlSeeds)
|
||||
{
|
||||
if (torrent != m_torrent)
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2022-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006-2012 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <QFileIconProvider>
|
||||
#include <QFileInfo>
|
||||
#include <QFuture>
|
||||
#include <QIcon>
|
||||
#include <QMimeData>
|
||||
#include <QPointer>
|
||||
|
@ -219,7 +220,8 @@ void TorrentContentModel::updateFilesAvailability()
|
|||
Q_ASSERT(m_contentHandler && m_contentHandler->hasMetadata());
|
||||
|
||||
using HandlerPtr = QPointer<BitTorrent::TorrentContentHandler>;
|
||||
m_contentHandler->fetchAvailableFileFractions([this, handler = HandlerPtr(m_contentHandler)](const QList<qreal> &availableFileFractions)
|
||||
m_contentHandler->fetchAvailableFileFractions().then(this
|
||||
, [this, handler = HandlerPtr(m_contentHandler)](const QList<qreal> &availableFileFractions)
|
||||
{
|
||||
if (handler != m_contentHandler)
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2023-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
#include <QFuture>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QScopeGuard>
|
||||
|
@ -309,7 +310,7 @@ void TrackerListModel::populate()
|
|||
m_items->emplace_back(std::make_shared<Item>(u"** [LSD] **", privateTorrentMessage));
|
||||
|
||||
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||
m_torrent->fetchPeerInfo([this, torrent = TorrentPtr(m_torrent)](const QList<BitTorrent::PeerInfo> &peers)
|
||||
m_torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList<BitTorrent::PeerInfo> &peers)
|
||||
{
|
||||
if (torrent != m_torrent)
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "synccontroller.h"
|
||||
|
||||
#include <QFuture>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaObject>
|
||||
|
@ -745,7 +746,7 @@ void SyncController::torrentPeersAction()
|
|||
QVariantMap data;
|
||||
QVariantHash peers;
|
||||
|
||||
const QList<BitTorrent::PeerInfo> peersList = torrent->peers();
|
||||
const QList<BitTorrent::PeerInfo> peersList = torrent->fetchPeerInfo().takeResult();
|
||||
|
||||
bool resolvePeerCountries = Preferences::instance()->resolvePeerCountries();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2018-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -32,6 +32,7 @@
|
|||
#include <functional>
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QFuture>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
|
@ -180,9 +181,11 @@ namespace
|
|||
QJsonArray getStickyTrackers(const BitTorrent::Torrent *const torrent)
|
||||
{
|
||||
int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0;
|
||||
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers()))
|
||||
const QList<BitTorrent::PeerInfo> peersList = torrent->fetchPeerInfo().takeResult();
|
||||
for (const BitTorrent::PeerInfo &peer : peersList)
|
||||
{
|
||||
if (peer.isConnecting()) continue;
|
||||
if (peer.isConnecting())
|
||||
continue;
|
||||
|
||||
if (peer.isSeed())
|
||||
{
|
||||
|
@ -727,7 +730,7 @@ void TorrentsController::filesAction()
|
|||
{
|
||||
const QList<BitTorrent::DownloadPriority> priorities = torrent->filePriorities();
|
||||
const QList<qreal> fp = torrent->filesProgress();
|
||||
const QList<qreal> fileAvailability = torrent->availableFileFractions();
|
||||
const QList<qreal> fileAvailability = torrent->fetchAvailableFileFractions().takeResult();
|
||||
const BitTorrent::TorrentInfo info = torrent->info();
|
||||
for (const int index : asConst(fileIndexes))
|
||||
{
|
||||
|
@ -796,7 +799,7 @@ void TorrentsController::pieceStatesAction()
|
|||
for (int i = 0; i < states.size(); ++i)
|
||||
pieceStates.append(static_cast<int>(states[i]) * 2);
|
||||
|
||||
const QBitArray dlstates = torrent->downloadingPieces();
|
||||
const QBitArray dlstates = torrent->fetchDownloadingPieces().takeResult();
|
||||
for (int i = 0; i < states.size(); ++i)
|
||||
{
|
||||
if (dlstates[i])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue