Merge pull request #18034 from glassez/fetch-async

Fetch data asynchronously
This commit is contained in:
Vladimir Golovnev
2022-12-15 06:59:29 +03:00
committed by GitHub
10 changed files with 427 additions and 133 deletions

View File

@@ -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 &params)
: QObject(session)
: Torrent(session)
, m_session(session)
, m_nativeSession(nativeSession)
, m_nativeHandle(nativeHandle)
@@ -620,50 +621,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()
@@ -1282,7 +1319,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;
}
@@ -2025,7 +2062,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
const int fileIndex = m_indexMap.value(p->index, -1);
Q_ASSERT(fileIndex >= 0);
m_completedFiles[fileIndex] = true;
m_completedFiles.setBit(fileIndex);
if (m_session->isAppendExtensionEnabled())
{
@@ -2185,17 +2222,24 @@ lt::torrent_handle TorrentImpl::nativeHandle() const
return m_nativeHandle;
}
bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
void TorrentImpl::setMetadata(const TorrentInfo &torrentInfo)
{
if (hasMetadata())
return false;
return;
m_session->invokeAsync([nativeHandle = m_nativeHandle, torrentInfo]
{
try
{
#ifdef QBT_USES_LIBTORRENT2
return m_nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section());
#else
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()));
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo();
nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size()));
#endif
}
catch (const std::exception &) {}
});
}
Torrent::StopCondition TorrentImpl::stopCondition() const
@@ -2415,6 +2459,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;
@@ -2478,3 +2687,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);
});
});
}