mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-26 22:44:36 +00:00
Allow to fetch data asynchronously
This commit is contained in:
parent
c3936cd4b6
commit
991c30943a
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -31,16 +31,15 @@
|
||||
#include <QBitArray>
|
||||
|
||||
#include "base/bittorrent/ltqbitarray.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/net/geoipmanager.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "peeraddress.h"
|
||||
|
||||
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_relevance(calcRelevance(torrent))
|
||||
, m_relevance(calcRelevance(allPieces))
|
||||
{
|
||||
determineFlags();
|
||||
}
|
||||
@ -246,9 +245,8 @@ QString PeerInfo::connectionType() const
|
||||
: 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);
|
||||
if (localMissing <= 0)
|
||||
return 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -36,7 +36,6 @@ class QBitArray;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class Torrent;
|
||||
struct PeerAddress;
|
||||
|
||||
class PeerInfo
|
||||
@ -45,7 +44,7 @@ namespace BitTorrent
|
||||
|
||||
public:
|
||||
PeerInfo() = default;
|
||||
PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo);
|
||||
PeerInfo(const lt::peer_info &nativeInfo, const QBitArray &allPieces);
|
||||
|
||||
bool fromDHT() const;
|
||||
bool fromPeX() const;
|
||||
@ -93,7 +92,7 @@ namespace BitTorrent
|
||||
int downloadingPieceIndex() const;
|
||||
|
||||
private:
|
||||
qreal calcRelevance(const Torrent *torrent) const;
|
||||
qreal calcRelevance(const QBitArray &allPieces) const;
|
||||
void determineFlags();
|
||||
|
||||
lt::peer_info m_nativeInfo = {};
|
||||
|
@ -443,7 +443,7 @@ namespace BitTorrent
|
||||
template <typename 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);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@ -106,9 +106,10 @@ namespace BitTorrent
|
||||
uint qHash(TorrentState key, uint seed = 0);
|
||||
#endif
|
||||
|
||||
class Torrent : public AbstractFileStorage
|
||||
class Torrent : public QObject, public AbstractFileStorage
|
||||
{
|
||||
Q_GADGET
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Torrent)
|
||||
|
||||
public:
|
||||
enum class StopCondition
|
||||
@ -128,7 +129,7 @@ namespace BitTorrent
|
||||
static const qreal MAX_RATIO;
|
||||
static const int MAX_SEEDING_TIME;
|
||||
|
||||
virtual ~Torrent() = default;
|
||||
using QObject::QObject;
|
||||
|
||||
virtual InfoHash infoHash() const = 0;
|
||||
virtual QString name() const = 0;
|
||||
@ -318,6 +319,19 @@ 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 (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;
|
||||
bool isResumed() const;
|
||||
qlonglong remainingSize() const;
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
@ -236,7 +237,7 @@ namespace
|
||||
|
||||
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||
: QObject(session)
|
||||
: Torrent(session)
|
||||
, m_session(session)
|
||||
, m_nativeSession(nativeSession)
|
||||
, m_nativeHandle(nativeHandle)
|
||||
@ -609,50 +610,86 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
|
||||
|
||||
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
||||
{
|
||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
||||
|
||||
QVector<QUrl> addedUrlSeeds;
|
||||
addedUrlSeeds.reserve(urlSeeds.size());
|
||||
|
||||
for (const QUrl &url : urlSeeds)
|
||||
m_session->invokeAsync([urlSeeds, session = m_session
|
||||
, nativeHandle = m_nativeHandle
|
||||
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||
{
|
||||
const std::string nativeUrl = url.toString().toStdString();
|
||||
if (currentSeeds.find(nativeUrl) == currentSeeds.end())
|
||||
try
|
||||
{
|
||||
m_nativeHandle.add_url_seed(nativeUrl);
|
||||
addedUrlSeeds << url;
|
||||
}
|
||||
}
|
||||
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||
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())
|
||||
{
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds);
|
||||
}
|
||||
QVector<QUrl> addedUrlSeeds;
|
||||
addedUrlSeeds.reserve(urlSeeds.size());
|
||||
|
||||
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)
|
||||
{
|
||||
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
||||
|
||||
QVector<QUrl> removedUrlSeeds;
|
||||
removedUrlSeeds.reserve(urlSeeds.size());
|
||||
|
||||
for (const QUrl &url : urlSeeds)
|
||||
m_session->invokeAsync([urlSeeds, session = m_session
|
||||
, nativeHandle = m_nativeHandle
|
||||
, thisTorrent = QPointer<TorrentImpl>(this)]
|
||||
{
|
||||
const std::string nativeUrl = url.toString().toStdString();
|
||||
if (currentSeeds.find(nativeUrl) != currentSeeds.end())
|
||||
try
|
||||
{
|
||||
m_nativeHandle.remove_url_seed(nativeUrl);
|
||||
removedUrlSeeds << url;
|
||||
}
|
||||
}
|
||||
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds();
|
||||
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())
|
||||
{
|
||||
m_session->handleTorrentNeedSaveResumeData(this);
|
||||
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds);
|
||||
}
|
||||
QVector<QUrl> removedUrlSeeds;
|
||||
removedUrlSeeds.reserve(urlSeeds.size());
|
||||
|
||||
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()
|
||||
@ -1271,7 +1308,7 @@ QVector<PeerInfo> TorrentImpl::peers() const
|
||||
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
|
||||
|
||||
for (const lt::peer_info &peer : nativePeers)
|
||||
peers << PeerInfo(this, peer);
|
||||
peers.append(PeerInfo(peer, pieces()));
|
||||
|
||||
return peers;
|
||||
}
|
||||
@ -2408,6 +2445,171 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
||||
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)
|
||||
{
|
||||
if (!hasMetadata()) return;
|
||||
@ -2471,3 +2673,19 @@ QVector<qreal> TorrentImpl::availableFileFractions() const
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
class TorrentImpl final : public QObject, public Torrent
|
||||
class TorrentImpl final : public Torrent
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
||||
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentImpl)
|
||||
|
||||
public:
|
||||
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||
@ -236,6 +236,13 @@ namespace BitTorrent
|
||||
nonstd::expected<QByteArray, QString> exportToBuffer() 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;
|
||||
|
||||
// Session interface
|
||||
@ -290,6 +297,9 @@ namespace BitTorrent
|
||||
|
||||
nonstd::expected<lt::entry, QString> exportTorrent() const;
|
||||
|
||||
template <typename Func, typename Callback>
|
||||
void invokeAsync(Func func, Callback resultHandler) const;
|
||||
|
||||
SessionImpl *const m_session = nullptr;
|
||||
lt::session *m_nativeSession = nullptr;
|
||||
lt::torrent_handle m_nativeHandle;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <QHostAddress>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
#include <QShortcut>
|
||||
#include <QSortFilterProxyModel>
|
||||
@ -392,38 +393,47 @@ void PeerListWidget::saveSettings() const
|
||||
|
||||
void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
|
||||
{
|
||||
if (!torrent) return;
|
||||
if (!torrent)
|
||||
return;
|
||||
|
||||
const QVector<BitTorrent::PeerInfo> peers = torrent->peers();
|
||||
QSet<PeerEndpoint> existingPeers;
|
||||
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
|
||||
existingPeers << i.key();
|
||||
|
||||
for (const BitTorrent::PeerInfo &peer : peers)
|
||||
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &peers)
|
||||
{
|
||||
if (peer.address().ip.isNull()) continue;
|
||||
if (torrent != m_properties->getCurrentTorrent())
|
||||
return;
|
||||
|
||||
bool isNewPeer = false;
|
||||
updatePeer(torrent, peer, isNewPeer);
|
||||
if (!isNewPeer)
|
||||
QSet<PeerEndpoint> existingPeers;
|
||||
existingPeers.reserve(m_peerItems.size());
|
||||
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()};
|
||||
existingPeers.remove(peerEndpoint);
|
||||
if (peer.address().ip.isNull())
|
||||
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
|
||||
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers))
|
||||
{
|
||||
QStandardItem *item = m_peerItems.take(peerEndpoint);
|
||||
// Remove peers that are gone
|
||||
for (const PeerEndpoint &peerEndpoint : asConst(existingPeers))
|
||||
{
|
||||
QStandardItem *item = m_peerItems.take(peerEndpoint);
|
||||
|
||||
QSet<QStandardItem *> &items = m_itemsByIP[peerEndpoint.address.ip];
|
||||
items.remove(item);
|
||||
if (items.isEmpty())
|
||||
m_itemsByIP.remove(peerEndpoint.address.ip);
|
||||
QSet<QStandardItem *> &items = m_itemsByIP[peerEndpoint.address.ip];
|
||||
items.remove(item);
|
||||
if (items.isEmpty())
|
||||
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)
|
||||
@ -433,7 +443,7 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor
|
||||
const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter;
|
||||
|
||||
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 QString &toolTip = {})
|
||||
{
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <QHeaderView>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMenu>
|
||||
#include <QPointer>
|
||||
#include <QSplitter>
|
||||
#include <QShortcut>
|
||||
#include <QStackedWidget>
|
||||
@ -498,13 +499,20 @@ 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->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking())
|
||||
{
|
||||
// Pieces availability
|
||||
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));
|
||||
}
|
||||
else
|
||||
@ -515,7 +523,12 @@ void PropertiesWidget::loadDynamicData()
|
||||
// Progress
|
||||
qreal progress = m_torrent->progress() * 100.;
|
||||
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
|
||||
{
|
||||
@ -538,6 +551,19 @@ void PropertiesWidget::loadDynamicData()
|
||||
qDebug("Updating priorities in files tab");
|
||||
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
|
||||
const bool isContentInitialized = m_propListModel->model()->hasIndex(0, 0);
|
||||
if (!isContentInitialized)
|
||||
@ -546,9 +572,6 @@ void PropertiesWidget::loadDynamicData()
|
||||
m_propListModel->model()->setupModelData(*m_torrent);
|
||||
// Load file priorities
|
||||
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.
|
||||
// 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
|
||||
|
||||
m_propListModel->model()->updateFilesProgress(m_torrent->filesProgress());
|
||||
m_propListModel->model()->updateFilesAvailability(m_torrent->availableFileFractions());
|
||||
// XXX: We don't update file priorities regularly for performance
|
||||
// reasons. This means that priorities will not be updated if
|
||||
// set from the Web UI.
|
||||
@ -583,15 +604,21 @@ void PropertiesWidget::loadUrlSeeds()
|
||||
if (!m_torrent)
|
||||
return;
|
||||
|
||||
m_ui->listWebSeeds->clear();
|
||||
qDebug("Loading URL seeds");
|
||||
const QVector<QUrl> hcSeeds = m_torrent->urlSeeds();
|
||||
// Add url seeds
|
||||
for (const QUrl &hcSeed : hcSeeds)
|
||||
using TorrentPtr = QPointer<BitTorrent::Torrent>;
|
||||
m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QVector<QUrl> &urlSeeds)
|
||||
{
|
||||
qDebug("Loading URL seed: %s", qUtf8Printable(hcSeed.toString()));
|
||||
new QListWidgetItem(hcSeed.toString(), m_ui->listWebSeeds);
|
||||
}
|
||||
if (torrent != m_torrent)
|
||||
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
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
#include <QShortcut>
|
||||
#include <QStringList>
|
||||
#include <QTreeWidgetItem>
|
||||
@ -310,42 +311,50 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::Torrent *torrent)
|
||||
m_LSDItem->setText(COL_MSG, privateMsg);
|
||||
}
|
||||
|
||||
// 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 : asConst(torrent->peers()))
|
||||
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
|
||||
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &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())
|
||||
++seedsDHT;
|
||||
else
|
||||
++peersDHT;
|
||||
}
|
||||
if (peer.fromPeX())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsPeX;
|
||||
else
|
||||
++peersPeX;
|
||||
}
|
||||
if (peer.fromLSD())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsLSD;
|
||||
else
|
||||
++peersLSD;
|
||||
}
|
||||
}
|
||||
if (peer.isConnecting())
|
||||
continue;
|
||||
|
||||
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));
|
||||
if (peer.fromDHT())
|
||||
{
|
||||
if (peer.isSeed())
|
||||
++seedsDHT;
|
||||
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));
|
||||
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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user