mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-19 04:49:47 -07:00
Merge pull request #18034 from glassez/fetch-async
Fetch data asynchronously
This commit is contained in:
commit
84fabf14c8
10 changed files with 427 additions and 133 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2022 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
|
||||||
|
@ -31,16 +31,15 @@
|
||||||
#include <QBitArray>
|
#include <QBitArray>
|
||||||
|
|
||||||
#include "base/bittorrent/ltqbitarray.h"
|
#include "base/bittorrent/ltqbitarray.h"
|
||||||
#include "base/bittorrent/torrent.h"
|
|
||||||
#include "base/net/geoipmanager.h"
|
#include "base/net/geoipmanager.h"
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "peeraddress.h"
|
#include "peeraddress.h"
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
PeerInfo::PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo)
|
PeerInfo::PeerInfo(const lt::peer_info &nativeInfo, const QBitArray &allPieces)
|
||||||
: m_nativeInfo(nativeInfo)
|
: m_nativeInfo(nativeInfo)
|
||||||
, m_relevance(calcRelevance(torrent))
|
, m_relevance(calcRelevance(allPieces))
|
||||||
{
|
{
|
||||||
determineFlags();
|
determineFlags();
|
||||||
}
|
}
|
||||||
|
@ -246,9 +245,8 @@ QString PeerInfo::connectionType() const
|
||||||
: u"Web"_qs;
|
: u"Web"_qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal PeerInfo::calcRelevance(const Torrent *torrent) const
|
qreal PeerInfo::calcRelevance(const QBitArray &allPieces) const
|
||||||
{
|
{
|
||||||
const QBitArray allPieces = torrent->pieces();
|
|
||||||
const int localMissing = allPieces.count(false);
|
const int localMissing = allPieces.count(false);
|
||||||
if (localMissing <= 0)
|
if (localMissing <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2022 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
|
||||||
|
@ -36,7 +36,6 @@ class QBitArray;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class Torrent;
|
|
||||||
struct PeerAddress;
|
struct PeerAddress;
|
||||||
|
|
||||||
class PeerInfo
|
class PeerInfo
|
||||||
|
@ -45,7 +44,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PeerInfo() = default;
|
PeerInfo() = default;
|
||||||
PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo);
|
PeerInfo(const lt::peer_info &nativeInfo, const QBitArray &allPieces);
|
||||||
|
|
||||||
bool fromDHT() const;
|
bool fromDHT() const;
|
||||||
bool fromPeX() const;
|
bool fromPeX() const;
|
||||||
|
@ -93,7 +92,7 @@ namespace BitTorrent
|
||||||
int downloadingPieceIndex() const;
|
int downloadingPieceIndex() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qreal calcRelevance(const Torrent *torrent) const;
|
qreal calcRelevance(const QBitArray &allPieces) const;
|
||||||
void determineFlags();
|
void determineFlags();
|
||||||
|
|
||||||
lt::peer_info m_nativeInfo = {};
|
lt::peer_info m_nativeInfo = {};
|
||||||
|
|
|
@ -627,6 +627,8 @@ SessionImpl::~SessionImpl()
|
||||||
// we delete lt::session
|
// we delete lt::session
|
||||||
delete Net::PortForwarder::instance();
|
delete Net::PortForwarder::instance();
|
||||||
|
|
||||||
|
// We must stop "async worker" only after deletion
|
||||||
|
// of all the components that could potentially use it
|
||||||
m_asyncWorker->clear();
|
m_asyncWorker->clear();
|
||||||
m_asyncWorker->waitForDone();
|
m_asyncWorker->waitForDone();
|
||||||
|
|
||||||
|
|
|
@ -443,7 +443,7 @@ namespace BitTorrent
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
void invoke(Func &&func)
|
void invoke(Func &&func)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, std::forward<Func>(func));
|
QMetaObject::invokeMethod(this, std::forward<Func>(func), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void invokeAsync(std::function<void ()> func);
|
void invokeAsync(std::function<void ()> func);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2022 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
|
||||||
|
@ -106,9 +106,10 @@ namespace BitTorrent
|
||||||
uint qHash(TorrentState key, uint seed = 0);
|
uint qHash(TorrentState key, uint seed = 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Torrent : public AbstractFileStorage
|
class Torrent : public QObject, public AbstractFileStorage
|
||||||
{
|
{
|
||||||
Q_GADGET
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(Torrent)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class StopCondition
|
enum class StopCondition
|
||||||
|
@ -128,7 +129,7 @@ namespace BitTorrent
|
||||||
static const qreal MAX_RATIO;
|
static const qreal MAX_RATIO;
|
||||||
static const int MAX_SEEDING_TIME;
|
static const int MAX_SEEDING_TIME;
|
||||||
|
|
||||||
virtual ~Torrent() = default;
|
using QObject::QObject;
|
||||||
|
|
||||||
virtual InfoHash infoHash() const = 0;
|
virtual InfoHash infoHash() const = 0;
|
||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
|
@ -309,7 +310,7 @@ namespace BitTorrent
|
||||||
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
|
||||||
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
|
||||||
virtual void clearPeers() = 0;
|
virtual void clearPeers() = 0;
|
||||||
virtual bool setMetadata(const TorrentInfo &torrentInfo) = 0;
|
virtual void setMetadata(const TorrentInfo &torrentInfo) = 0;
|
||||||
|
|
||||||
virtual StopCondition stopCondition() const = 0;
|
virtual StopCondition stopCondition() const = 0;
|
||||||
virtual void setStopCondition(StopCondition stopCondition) = 0;
|
virtual void setStopCondition(StopCondition stopCondition) = 0;
|
||||||
|
@ -318,6 +319,19 @@ namespace BitTorrent
|
||||||
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
||||||
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
||||||
|
|
||||||
|
virtual void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const = 0;
|
||||||
|
virtual void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const = 0;
|
||||||
|
/**
|
||||||
|
* @brief fraction of file pieces that are available at least from one peer
|
||||||
|
*
|
||||||
|
* 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 void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const = 0;
|
||||||
|
|
||||||
TorrentID id() const;
|
TorrentID id() const;
|
||||||
bool isResumed() const;
|
bool isResumed() const;
|
||||||
qlonglong remainingSize() const;
|
qlonglong remainingSize() const;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
@ -236,7 +237,7 @@ namespace
|
||||||
|
|
||||||
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||||
: QObject(session)
|
: Torrent(session)
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_nativeSession(nativeSession)
|
, m_nativeSession(nativeSession)
|
||||||
, m_nativeHandle(nativeHandle)
|
, m_nativeHandle(nativeHandle)
|
||||||
|
@ -620,50 +621,86 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
|
||||||
|
|
||||||
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
|
, nativeHandle = m_nativeHandle
|
||||||
QVector<QUrl> addedUrlSeeds;
|
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||||
addedUrlSeeds.reserve(urlSeeds.size());
|
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
|
||||||
{
|
{
|
||||||
const std::string nativeUrl = url.toString().toStdString();
|
try
|
||||||
if (currentSeeds.find(nativeUrl) == currentSeeds.end())
|
|
||||||
{
|
{
|
||||||
m_nativeHandle.add_url_seed(nativeUrl);
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
addedUrlSeeds << url;
|
QVector<QUrl> currentSeeds;
|
||||||
}
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
}
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
if (!addedUrlSeeds.isEmpty())
|
QVector<QUrl> addedUrlSeeds;
|
||||||
{
|
addedUrlSeeds.reserve(urlSeeds.size());
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds);
|
for (const QUrl &url : urlSeeds)
|
||||||
}
|
{
|
||||||
|
if (!currentSeeds.contains(url))
|
||||||
|
{
|
||||||
|
nativeHandle.add_url_seed(url.toString().toStdString());
|
||||||
|
addedUrlSeeds.append(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->invoke([session, thisTorrent, addedUrlSeeds]
|
||||||
|
{
|
||||||
|
if (!thisTorrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!addedUrlSeeds.isEmpty())
|
||||||
|
{
|
||||||
|
session->handleTorrentNeedSaveResumeData(thisTorrent);
|
||||||
|
session->handleTorrentUrlSeedsAdded(thisTorrent, addedUrlSeeds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
m_session->invokeAsync([urlSeeds, session = m_session
|
||||||
|
, nativeHandle = m_nativeHandle
|
||||||
QVector<QUrl> removedUrlSeeds;
|
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||||
removedUrlSeeds.reserve(urlSeeds.size());
|
|
||||||
|
|
||||||
for (const QUrl &url : urlSeeds)
|
|
||||||
{
|
{
|
||||||
const std::string nativeUrl = url.toString().toStdString();
|
try
|
||||||
if (currentSeeds.find(nativeUrl) != currentSeeds.end())
|
|
||||||
{
|
{
|
||||||
m_nativeHandle.remove_url_seed(nativeUrl);
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||||
removedUrlSeeds << url;
|
QVector<QUrl> currentSeeds;
|
||||||
}
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size()));
|
||||||
}
|
for (const std::string &urlSeed : nativeSeeds)
|
||||||
|
currentSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
|
||||||
if (!removedUrlSeeds.isEmpty())
|
QVector<QUrl> removedUrlSeeds;
|
||||||
{
|
removedUrlSeeds.reserve(urlSeeds.size());
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds);
|
for (const QUrl &url : urlSeeds)
|
||||||
}
|
{
|
||||||
|
if (currentSeeds.contains(url))
|
||||||
|
{
|
||||||
|
nativeHandle.remove_url_seed(url.toString().toStdString());
|
||||||
|
removedUrlSeeds.append(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->invoke([session, thisTorrent, removedUrlSeeds]
|
||||||
|
{
|
||||||
|
if (!thisTorrent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!removedUrlSeeds.isEmpty())
|
||||||
|
{
|
||||||
|
session->handleTorrentNeedSaveResumeData(thisTorrent);
|
||||||
|
session->handleTorrentUrlSeedsRemoved(thisTorrent, removedUrlSeeds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::clearPeers()
|
void TorrentImpl::clearPeers()
|
||||||
|
@ -1282,7 +1319,7 @@ QVector<PeerInfo> TorrentImpl::peers() const
|
||||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
|
|
||||||
for (const lt::peer_info &peer : nativePeers)
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
peers << PeerInfo(this, peer);
|
peers.append(PeerInfo(peer, pieces()));
|
||||||
|
|
||||||
return peers;
|
return peers;
|
||||||
}
|
}
|
||||||
|
@ -2025,7 +2062,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
||||||
const int fileIndex = m_indexMap.value(p->index, -1);
|
const int fileIndex = m_indexMap.value(p->index, -1);
|
||||||
Q_ASSERT(fileIndex >= 0);
|
Q_ASSERT(fileIndex >= 0);
|
||||||
|
|
||||||
m_completedFiles[fileIndex] = true;
|
m_completedFiles.setBit(fileIndex);
|
||||||
|
|
||||||
if (m_session->isAppendExtensionEnabled())
|
if (m_session->isAppendExtensionEnabled())
|
||||||
{
|
{
|
||||||
|
@ -2185,17 +2222,24 @@ lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||||
return m_nativeHandle;
|
return m_nativeHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
|
void TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
|
||||||
{
|
{
|
||||||
if (hasMetadata())
|
if (hasMetadata())
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
|
m_session->invokeAsync([nativeHandle = m_nativeHandle, torrentInfo]
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
return m_nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
|
nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
|
||||||
#else
|
#else
|
||||||
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo();
|
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo();
|
||||||
return m_nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size()));
|
nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size()));
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Torrent::StopCondition TorrentImpl::stopCondition() const
|
Torrent::StopCondition TorrentImpl::stopCondition() const
|
||||||
|
@ -2415,6 +2459,171 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QVector<PeerInfo>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<lt::peer_info> nativePeers;
|
||||||
|
nativeHandle.get_peer_info(nativePeers);
|
||||||
|
QVector<PeerInfo> peers;
|
||||||
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||||
|
for (const lt::peer_info &peer : nativePeers)
|
||||||
|
peers.append(PeerInfo(peer, allPieces));
|
||||||
|
return peers;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<QUrl>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const std::set<std::string> currentSeeds = nativeHandle.url_seeds();
|
||||||
|
QVector<QUrl> urlSeeds;
|
||||||
|
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
|
||||||
|
for (const std::string &urlSeed : currentSeeds)
|
||||||
|
urlSeeds.append(QString::fromStdString(urlSeed));
|
||||||
|
return urlSeeds;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo
|
||||||
|
, completedFiles = m_completedFiles]() -> QVector<qreal>
|
||||||
|
{
|
||||||
|
if (!torrentInfo.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const int filesCount = torrentInfo.filesCount();
|
||||||
|
if (completedFiles.count(true) == filesCount)
|
||||||
|
return QVector<qreal>(filesCount, 1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const std::vector<int64_t> fp = nativeHandle.file_progress(lt::torrent_handle::piece_granularity);
|
||||||
|
#else
|
||||||
|
std::vector<int64_t> fp;
|
||||||
|
nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||||
|
QVector<qreal> result;
|
||||||
|
result.reserve(filesCount);
|
||||||
|
for (int i = 0; i < filesCount; ++i)
|
||||||
|
{
|
||||||
|
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])];
|
||||||
|
const qlonglong size = torrentInfo.fileSize(i);
|
||||||
|
if ((size <= 0) || (progress == size))
|
||||||
|
result.append(1);
|
||||||
|
else
|
||||||
|
result.append(progress / static_cast<qreal>(size));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<int> piecesAvailability;
|
||||||
|
nativeHandle.piece_availability(piecesAvailability);
|
||||||
|
return QVector<int>(piecesAvailability.cbegin(), piecesAvailability.cend());
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const std::vector<lt::partial_piece_info> queue = nativeHandle.get_download_queue();
|
||||||
|
#else
|
||||||
|
std::vector<lt::partial_piece_info> queue;
|
||||||
|
nativeHandle.get_download_queue(queue);
|
||||||
|
#endif
|
||||||
|
QBitArray result;
|
||||||
|
result.resize(torrentInfo.piecesCount());
|
||||||
|
for (const lt::partial_piece_info &info : queue)
|
||||||
|
result.setBit(LT::toUnderlyingType(info.piece_index));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const
|
||||||
|
{
|
||||||
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QVector<qreal>
|
||||||
|
{
|
||||||
|
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<int> piecesAvailability;
|
||||||
|
nativeHandle.piece_availability(piecesAvailability);
|
||||||
|
const int filesCount = torrentInfo.filesCount();
|
||||||
|
// libtorrent returns empty array for seeding only torrents
|
||||||
|
if (piecesAvailability.empty())
|
||||||
|
return QVector<qreal>(filesCount, -1);
|
||||||
|
|
||||||
|
QVector<qreal> result;
|
||||||
|
result.reserve(filesCount);
|
||||||
|
for (int i = 0; i < filesCount; ++i)
|
||||||
|
{
|
||||||
|
const TorrentInfo::PieceRange filePieces = 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();
|
||||||
|
result.append(availability);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (const std::exception &) {}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
, std::move(resultHandler));
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
||||||
{
|
{
|
||||||
if (!hasMetadata()) return;
|
if (!hasMetadata()) return;
|
||||||
|
@ -2478,3 +2687,19 @@ QVector<qreal> TorrentImpl::availableFileFractions() const
|
||||||
}
|
}
|
||||||
return res;
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -80,10 +80,10 @@ namespace BitTorrent
|
||||||
lt::operation_t operation;
|
lt::operation_t operation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TorrentImpl final : public QObject, public Torrent
|
class TorrentImpl final : public Torrent
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
||||||
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentImpl)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||||
|
@ -227,7 +227,7 @@ namespace BitTorrent
|
||||||
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
|
||||||
bool connectPeer(const PeerAddress &peerAddress) override;
|
bool connectPeer(const PeerAddress &peerAddress) override;
|
||||||
void clearPeers() override;
|
void clearPeers() override;
|
||||||
bool setMetadata(const TorrentInfo &torrentInfo) override;
|
void setMetadata(const TorrentInfo &torrentInfo) override;
|
||||||
|
|
||||||
StopCondition stopCondition() const override;
|
StopCondition stopCondition() const override;
|
||||||
void setStopCondition(StopCondition stopCondition) override;
|
void setStopCondition(StopCondition stopCondition) override;
|
||||||
|
@ -236,6 +236,13 @@ namespace BitTorrent
|
||||||
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
||||||
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
nonstd::expected<void, QString> exportToFile(const Path &path) const override;
|
||||||
|
|
||||||
|
void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override;
|
||||||
|
void fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const override;
|
||||||
|
void fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const override;
|
||||||
|
void fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const override;
|
||||||
|
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
|
||||||
|
void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override;
|
||||||
|
|
||||||
bool needSaveResumeData() const;
|
bool needSaveResumeData() const;
|
||||||
|
|
||||||
// Session interface
|
// Session interface
|
||||||
|
@ -292,6 +299,9 @@ namespace BitTorrent
|
||||||
|
|
||||||
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
||||||
|
|
||||||
|
template <typename Func, typename Callback>
|
||||||
|
void invokeAsync(Func func, Callback resultHandler) const;
|
||||||
|
|
||||||
SessionImpl *const m_session = nullptr;
|
SessionImpl *const m_session = nullptr;
|
||||||
lt::session *m_nativeSession = nullptr;
|
lt::session *m_nativeSession = nullptr;
|
||||||
lt::torrent_handle m_nativeHandle;
|
lt::torrent_handle m_nativeHandle;
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
@ -392,38 +393,47 @@ void PeerListWidget::saveSettings() const
|
||||||
|
|
||||||
void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
|
void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
|
||||||
{
|
{
|
||||||
if (!torrent) return;
|
if (!torrent)
|
||||||
|
return;
|
||||||
|
|
||||||
const QVector<BitTorrent::PeerInfo> peers = torrent->peers();
|
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||||
QSet<PeerEndpoint> existingPeers;
|
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &peers)
|
||||||
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
|
|
||||||
existingPeers << i.key();
|
|
||||||
|
|
||||||
for (const BitTorrent::PeerInfo &peer : peers)
|
|
||||||
{
|
{
|
||||||
if (peer.address().ip.isNull()) continue;
|
if (torrent != m_properties->getCurrentTorrent())
|
||||||
|
return;
|
||||||
|
|
||||||
bool isNewPeer = false;
|
QSet<PeerEndpoint> existingPeers;
|
||||||
updatePeer(torrent, peer, isNewPeer);
|
existingPeers.reserve(m_peerItems.size());
|
||||||
if (!isNewPeer)
|
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
|
||||||
|
existingPeers.insert(i.key());
|
||||||
|
|
||||||
|
for (const BitTorrent::PeerInfo &peer : peers)
|
||||||
{
|
{
|
||||||
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
|
if (peer.address().ip.isNull())
|
||||||
existingPeers.remove(peerEndpoint);
|
continue;
|
||||||
|
|
||||||
|
bool isNewPeer = false;
|
||||||
|
updatePeer(torrent, peer, isNewPeer);
|
||||||
|
if (!isNewPeer)
|
||||||
|
{
|
||||||
|
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
|
||||||
|
existingPeers.remove(peerEndpoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove peers that are gone
|
// Remove peers that are gone
|
||||||
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers))
|
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers))
|
||||||
{
|
{
|
||||||
QStandardItem *item = m_peerItems.take(peerEndpoint);
|
QStandardItem *item = m_peerItems.take(peerEndpoint);
|
||||||
|
|
||||||
QSet<QStandardItem *> &items = m_itemsByIP[peerEndpoint.address.ip];
|
QSet<QStandardItem *> &items = m_itemsByIP[peerEndpoint.address.ip];
|
||||||
items.remove(item);
|
items.remove(item);
|
||||||
if (items.isEmpty())
|
if (items.isEmpty())
|
||||||
m_itemsByIP.remove(peerEndpoint.address.ip);
|
m_itemsByIP.remove(peerEndpoint.address.ip);
|
||||||
|
|
||||||
m_listModel->removeRow(item->row());
|
m_listModel->removeRow(item->row());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer)
|
void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer)
|
||||||
|
@ -433,7 +443,7 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor
|
||||||
const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter;
|
const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter;
|
||||||
|
|
||||||
const auto setModelData =
|
const auto setModelData =
|
||||||
[this] (const int row, const int column, const QString &displayData
|
[this](const int row, const int column, const QString &displayData
|
||||||
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {}
|
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {}
|
||||||
, const QString &toolTip = {})
|
, const QString &toolTip = {})
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QPointer>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
|
@ -498,13 +499,20 @@ void PropertiesWidget::loadDynamicData()
|
||||||
|
|
||||||
if (m_torrent->hasMetadata())
|
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()));
|
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->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking())
|
if (!m_torrent->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking())
|
||||||
{
|
{
|
||||||
// Pieces availability
|
// Pieces availability
|
||||||
showPiecesAvailability(true);
|
showPiecesAvailability(true);
|
||||||
m_piecesAvailability->setAvailability(m_torrent->pieceAvailability());
|
m_torrent->fetchPieceAvailability([this, torrent = TorrentPtr(m_torrent)](const QVector<int> &pieceAvailability)
|
||||||
|
{
|
||||||
|
if (torrent == m_torrent)
|
||||||
|
m_piecesAvailability->setAvailability(pieceAvailability);
|
||||||
|
});
|
||||||
|
|
||||||
m_ui->labelAverageAvailabilityVal->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
|
m_ui->labelAverageAvailabilityVal->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -515,7 +523,12 @@ void PropertiesWidget::loadDynamicData()
|
||||||
// Progress
|
// Progress
|
||||||
qreal progress = m_torrent->progress() * 100.;
|
qreal progress = m_torrent->progress() * 100.;
|
||||||
m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + u'%');
|
m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + u'%');
|
||||||
m_downloadedPieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
|
|
||||||
|
m_torrent->fetchDownloadingPieces([this, torrent = TorrentPtr(m_torrent)](const QBitArray &downloadingPieces)
|
||||||
|
{
|
||||||
|
if (torrent == m_torrent)
|
||||||
|
m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -538,6 +551,19 @@ void PropertiesWidget::loadDynamicData()
|
||||||
qDebug("Updating priorities in files tab");
|
qDebug("Updating priorities in files tab");
|
||||||
m_ui->filesList->setUpdatesEnabled(false);
|
m_ui->filesList->setUpdatesEnabled(false);
|
||||||
|
|
||||||
|
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||||
|
m_torrent->fetchFilesProgress([this, torrent = TorrentPtr(m_torrent)](const QVector<qreal> &filesProgress)
|
||||||
|
{
|
||||||
|
if (torrent == m_torrent)
|
||||||
|
m_propListModel->model()->updateFilesProgress(filesProgress);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_torrent->fetchAvailableFileFractions([this, torrent = TorrentPtr(m_torrent)](const QVector<qreal> &availableFileFractions)
|
||||||
|
{
|
||||||
|
if (torrent == m_torrent)
|
||||||
|
m_propListModel->model()->updateFilesAvailability(availableFileFractions);
|
||||||
|
});
|
||||||
|
|
||||||
// Load torrent content if not yet done so
|
// Load torrent content if not yet done so
|
||||||
const bool isContentInitialized = m_propListModel->model()->hasIndex(0, 0);
|
const bool isContentInitialized = m_propListModel->model()->hasIndex(0, 0);
|
||||||
if (!isContentInitialized)
|
if (!isContentInitialized)
|
||||||
|
@ -546,9 +572,6 @@ void PropertiesWidget::loadDynamicData()
|
||||||
m_propListModel->model()->setupModelData(*m_torrent);
|
m_propListModel->model()->setupModelData(*m_torrent);
|
||||||
// Load file priorities
|
// Load file priorities
|
||||||
m_propListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
|
m_propListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
|
||||||
// Update file progress/availability
|
|
||||||
m_propListModel->model()->updateFilesProgress(m_torrent->filesProgress());
|
|
||||||
m_propListModel->model()->updateFilesAvailability(m_torrent->availableFileFractions());
|
|
||||||
|
|
||||||
// Expand single-item folders recursively.
|
// Expand single-item folders recursively.
|
||||||
// This will trigger sorting and filtering so do it after all relevant data is loaded.
|
// This will trigger sorting and filtering so do it after all relevant data is loaded.
|
||||||
|
@ -563,8 +586,6 @@ void PropertiesWidget::loadDynamicData()
|
||||||
{
|
{
|
||||||
// Torrent content was loaded already, only make some updates
|
// Torrent content was loaded already, only make some updates
|
||||||
|
|
||||||
m_propListModel->model()->updateFilesProgress(m_torrent->filesProgress());
|
|
||||||
m_propListModel->model()->updateFilesAvailability(m_torrent->availableFileFractions());
|
|
||||||
// XXX: We don't update file priorities regularly for performance
|
// XXX: We don't update file priorities regularly for performance
|
||||||
// reasons. This means that priorities will not be updated if
|
// reasons. This means that priorities will not be updated if
|
||||||
// set from the Web UI.
|
// set from the Web UI.
|
||||||
|
@ -583,15 +604,21 @@ void PropertiesWidget::loadUrlSeeds()
|
||||||
if (!m_torrent)
|
if (!m_torrent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_ui->listWebSeeds->clear();
|
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||||
qDebug("Loading URL seeds");
|
m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QVector<QUrl> &urlSeeds)
|
||||||
const QVector<QUrl> hcSeeds = m_torrent->urlSeeds();
|
|
||||||
// Add url seeds
|
|
||||||
for (const QUrl &hcSeed : hcSeeds)
|
|
||||||
{
|
{
|
||||||
qDebug("Loading URL seed: %s", qUtf8Printable(hcSeed.toString()));
|
if (torrent != m_torrent)
|
||||||
new QListWidgetItem(hcSeed.toString(), m_ui->listWebSeeds);
|
return;
|
||||||
}
|
|
||||||
|
m_ui->listWebSeeds->clear();
|
||||||
|
qDebug("Loading URL seeds");
|
||||||
|
// Add url seeds
|
||||||
|
for (const QUrl &urlSeed : urlSeeds)
|
||||||
|
{
|
||||||
|
qDebug("Loading URL seed: %s", qUtf8Printable(urlSeed.toString()));
|
||||||
|
new QListWidgetItem(urlSeed.toString(), m_ui->listWebSeeds);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Path PropertiesWidget::getFullPath(const QModelIndex &index) const
|
Path PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPointer>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTreeWidgetItem>
|
#include <QTreeWidgetItem>
|
||||||
|
@ -310,42 +311,50 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::Torrent *torrent)
|
||||||
m_LSDItem->setText(COL_MSG, privateMsg);
|
m_LSDItem->setText(COL_MSG, privateMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: libtorrent should provide this info...
|
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||||
// Count peers from DHT, PeX, LSD
|
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &peers)
|
||||||
uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
|
|
||||||
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers()))
|
|
||||||
{
|
{
|
||||||
if (peer.isConnecting()) continue;
|
if (torrent != m_properties->getCurrentTorrent())
|
||||||
|
return;
|
||||||
|
|
||||||
if (peer.fromDHT())
|
// XXX: libtorrent should provide this info...
|
||||||
|
// Count peers from DHT, PeX, LSD
|
||||||
|
uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
|
||||||
|
for (const BitTorrent::PeerInfo &peer : peers)
|
||||||
{
|
{
|
||||||
if (peer.isSeed())
|
if (peer.isConnecting())
|
||||||
++seedsDHT;
|
continue;
|
||||||
else
|
|
||||||
++peersDHT;
|
|
||||||
}
|
|
||||||
if (peer.fromPeX())
|
|
||||||
{
|
|
||||||
if (peer.isSeed())
|
|
||||||
++seedsPeX;
|
|
||||||
else
|
|
||||||
++peersPeX;
|
|
||||||
}
|
|
||||||
if (peer.fromLSD())
|
|
||||||
{
|
|
||||||
if (peer.isSeed())
|
|
||||||
++seedsLSD;
|
|
||||||
else
|
|
||||||
++peersLSD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_DHTItem->setText(COL_SEEDS, QString::number(seedsDHT));
|
if (peer.fromDHT())
|
||||||
m_DHTItem->setText(COL_LEECHES, QString::number(peersDHT));
|
{
|
||||||
m_PEXItem->setText(COL_SEEDS, QString::number(seedsPeX));
|
if (peer.isSeed())
|
||||||
m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX));
|
++seedsDHT;
|
||||||
m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
|
else
|
||||||
m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD));
|
++peersDHT;
|
||||||
|
}
|
||||||
|
if (peer.fromPeX())
|
||||||
|
{
|
||||||
|
if (peer.isSeed())
|
||||||
|
++seedsPeX;
|
||||||
|
else
|
||||||
|
++peersPeX;
|
||||||
|
}
|
||||||
|
if (peer.fromLSD())
|
||||||
|
{
|
||||||
|
if (peer.isSeed())
|
||||||
|
++seedsLSD;
|
||||||
|
else
|
||||||
|
++peersLSD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_DHTItem->setText(COL_SEEDS, QString::number(seedsDHT));
|
||||||
|
m_DHTItem->setText(COL_LEECHES, QString::number(peersDHT));
|
||||||
|
m_PEXItem->setText(COL_SEEDS, QString::number(seedsPeX));
|
||||||
|
m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX));
|
||||||
|
m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
|
||||||
|
m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackerListWidget::loadTrackers()
|
void TrackerListWidget::loadTrackers()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue