Browse Source

Merge pull request #18034 from glassez/fetch-async

Fetch data asynchronously
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
84fabf14c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/base/bittorrent/peerinfo.cpp
  2. 7
      src/base/bittorrent/peerinfo.h
  3. 2
      src/base/bittorrent/sessionimpl.cpp
  4. 2
      src/base/bittorrent/sessionimpl.h
  5. 24
      src/base/bittorrent/torrent.h
  6. 267
      src/base/bittorrent/torrentimpl.cpp
  7. 16
      src/base/bittorrent/torrentimpl.h
  8. 20
      src/gui/properties/peerlistwidget.cpp
  9. 49
      src/gui/properties/propertieswidget.cpp
  10. 13
      src/gui/properties/trackerlistwidget.cpp

10
src/base/bittorrent/peerinfo.cpp

@ -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;

7
src/base/bittorrent/peerinfo.h

@ -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 = {};

2
src/base/bittorrent/sessionimpl.cpp

@ -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();

2
src/base/bittorrent/sessionimpl.h

@ -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);

24
src/base/bittorrent/torrent.h

@ -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;

267
src/base/bittorrent/torrentimpl.cpp

@ -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 &params) , const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params)
: 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
, thisTorrent = QPointer<TorrentImpl>(this)]
{
try
{
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));
QVector<QUrl> addedUrlSeeds; QVector<QUrl> addedUrlSeeds;
addedUrlSeeds.reserve(urlSeeds.size()); addedUrlSeeds.reserve(urlSeeds.size());
for (const QUrl &url : urlSeeds) for (const QUrl &url : urlSeeds)
{ {
const std::string nativeUrl = url.toString().toStdString(); if (!currentSeeds.contains(url))
if (currentSeeds.find(nativeUrl) == currentSeeds.end())
{ {
m_nativeHandle.add_url_seed(nativeUrl); nativeHandle.add_url_seed(url.toString().toStdString());
addedUrlSeeds << url; addedUrlSeeds.append(url);
} }
} }
session->invoke([session, thisTorrent, addedUrlSeeds]
{
if (!thisTorrent)
return;
if (!addedUrlSeeds.isEmpty()) if (!addedUrlSeeds.isEmpty())
{ {
m_session->handleTorrentNeedSaveResumeData(this); session->handleTorrentNeedSaveResumeData(thisTorrent);
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds); 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
, thisTorrent = QPointer<TorrentImpl>(this)]
{
try
{
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));
QVector<QUrl> removedUrlSeeds; QVector<QUrl> removedUrlSeeds;
removedUrlSeeds.reserve(urlSeeds.size()); removedUrlSeeds.reserve(urlSeeds.size());
for (const QUrl &url : urlSeeds) for (const QUrl &url : urlSeeds)
{ {
const std::string nativeUrl = url.toString().toStdString(); if (currentSeeds.contains(url))
if (currentSeeds.find(nativeUrl) != currentSeeds.end())
{ {
m_nativeHandle.remove_url_seed(nativeUrl); nativeHandle.remove_url_seed(url.toString().toStdString());
removedUrlSeeds << url; removedUrlSeeds.append(url);
} }
} }
session->invoke([session, thisTorrent, removedUrlSeeds]
{
if (!thisTorrent)
return;
if (!removedUrlSeeds.isEmpty()) if (!removedUrlSeeds.isEmpty())
{ {
m_session->handleTorrentNeedSaveResumeData(this); session->handleTorrentNeedSaveResumeData(thisTorrent);
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds); 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);
});
});
}

16
src/base/bittorrent/torrentimpl.h

@ -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;

20
src/gui/properties/peerlistwidget.cpp

@ -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,16 +393,24 @@ void PeerListWidget::saveSettings() const
void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent) void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
{ {
if (!torrent) return; if (!torrent)
return;
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &peers)
{
if (torrent != m_properties->getCurrentTorrent())
return;
const QVector<BitTorrent::PeerInfo> peers = torrent->peers();
QSet<PeerEndpoint> existingPeers; QSet<PeerEndpoint> existingPeers;
existingPeers.reserve(m_peerItems.size());
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i) for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
existingPeers << i.key(); existingPeers.insert(i.key());
for (const BitTorrent::PeerInfo &peer : peers) for (const BitTorrent::PeerInfo &peer : peers)
{ {
if (peer.address().ip.isNull()) continue; if (peer.address().ip.isNull())
continue;
bool isNewPeer = false; bool isNewPeer = false;
updatePeer(torrent, peer, isNewPeer); updatePeer(torrent, peer, isNewPeer);
@ -424,6 +433,7 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
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 = {})
{ {

49
src/gui/properties/propertieswidget.cpp

@ -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;
using TorrentPtr = QPointer<BitTorrent::Torrent>;
m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QVector<QUrl> &urlSeeds)
{
if (torrent != m_torrent)
return;
m_ui->listWebSeeds->clear(); m_ui->listWebSeeds->clear();
qDebug("Loading URL seeds"); qDebug("Loading URL seeds");
const QVector<QUrl> hcSeeds = m_torrent->urlSeeds();
// Add url seeds // Add url seeds
for (const QUrl &hcSeed : hcSeeds) for (const QUrl &urlSeed : urlSeeds)
{ {
qDebug("Loading URL seed: %s", qUtf8Printable(hcSeed.toString())); qDebug("Loading URL seed: %s", qUtf8Printable(urlSeed.toString()));
new QListWidgetItem(hcSeed.toString(), m_ui->listWebSeeds); new QListWidgetItem(urlSeed.toString(), m_ui->listWebSeeds);
} }
});
} }
Path PropertiesWidget::getFullPath(const QModelIndex &index) const Path PropertiesWidget::getFullPath(const QModelIndex &index) const

13
src/gui/properties/trackerlistwidget.cpp

@ -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,12 +311,19 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::Torrent *torrent)
m_LSDItem->setText(COL_MSG, privateMsg); m_LSDItem->setText(COL_MSG, privateMsg);
} }
using TorrentPtr = QPointer<const BitTorrent::Torrent>;
torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QVector<BitTorrent::PeerInfo> &peers)
{
if (torrent != m_properties->getCurrentTorrent())
return;
// XXX: libtorrent should provide this info... // XXX: libtorrent should provide this info...
// Count peers from DHT, PeX, LSD // Count peers from DHT, PeX, LSD
uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0; uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers())) for (const BitTorrent::PeerInfo &peer : peers)
{ {
if (peer.isConnecting()) continue; if (peer.isConnecting())
continue;
if (peer.fromDHT()) if (peer.fromDHT())
{ {
@ -346,6 +354,7 @@ void TrackerListWidget::loadStickyItems(const BitTorrent::Torrent *torrent)
m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX)); m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX));
m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD)); m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD)); m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD));
});
} }
void TrackerListWidget::loadTrackers() void TrackerListWidget::loadTrackers()

Loading…
Cancel
Save