Browse Source

Perform own tracking of files progress

PR #18597.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
2f9b313287
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/base/bittorrent/torrentcontenthandler.h
  2. 124
      src/base/bittorrent/torrentimpl.cpp
  3. 7
      src/base/bittorrent/torrentimpl.h
  4. 7
      src/gui/addnewtorrentdialog.cpp
  5. 28
      src/gui/torrentcontentmodel.cpp

3
src/base/bittorrent/torrentcontenthandler.h

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2022-2023 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
@ -46,7 +46,6 @@ namespace BitTorrent
virtual Path actualFilePath(int fileIndex) const = 0; virtual Path actualFilePath(int fileIndex) const = 0;
virtual QVector<DownloadPriority> filePriorities() const = 0; virtual QVector<DownloadPriority> filePriorities() const = 0;
virtual QVector<qreal> filesProgress() const = 0; virtual QVector<qreal> filesProgress() const = 0;
virtual void fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const = 0;
/** /**
* @brief fraction of file pieces that are available at least from one peer * @brief fraction of file pieces that are available at least from one peer
* *

124
src/base/bittorrent/torrentimpl.cpp

@ -279,6 +279,7 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
, LT::toNative(m_ltAddTorrentParams.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored)); , LT::toNative(m_ltAddTorrentParams.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
m_completedFiles.fill(static_cast<bool>(m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode), filesCount); m_completedFiles.fill(static_cast<bool>(m_ltAddTorrentParams.flags & lt::torrent_flags::seed_mode), filesCount);
m_filesProgress.resize(filesCount);
for (int i = 0; i < filesCount; ++i) for (int i = 0; i < filesCount; ++i)
{ {
@ -306,6 +307,9 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
m_urlSeeds.append(QString::fromStdString(urlSeed)); m_urlSeeds.append(QString::fromStdString(urlSeed));
m_nativeStatus = extensionData->status; m_nativeStatus = extensionData->status;
if (hasMetadata())
updateProgress();
updateState(); updateState();
if (hasMetadata()) if (hasMetadata())
@ -1184,20 +1188,20 @@ QVector<qreal> TorrentImpl::filesProgress() const
if (!hasMetadata()) if (!hasMetadata())
return {}; return {};
if (m_completedFiles.count(true) == filesCount()) const int count = m_filesProgress.size();
return QVector<qreal>(filesCount(), 1); Q_ASSERT(count == filesCount());
if (Q_UNLIKELY(count != filesCount()))
return {};
std::vector<int64_t> fp; if (m_completedFiles.count(true) == count)
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); return QVector<qreal>(count, 1);
const int count = filesCount();
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
QVector<qreal> result; QVector<qreal> result;
result.reserve(count); result.reserve(count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])]; const int64_t progress = m_filesProgress.at(i);
const qlonglong size = fileSize(i); const int64_t size = fileSize(i);
if ((size <= 0) || (progress == size)) if ((size <= 0) || (progress == size))
result << 1; result << 1;
else else
@ -1323,8 +1327,6 @@ QVector<PeerInfo> TorrentImpl::peers() const
QBitArray TorrentImpl::pieces() const QBitArray TorrentImpl::pieces() const
{ {
if (m_pieces.isEmpty())
m_pieces = LT::toQBitArray(m_nativeStatus.pieces);
return m_pieces; return m_pieces;
} }
@ -1469,7 +1471,8 @@ void TorrentImpl::forceDHTAnnounce()
void TorrentImpl::forceRecheck() void TorrentImpl::forceRecheck()
{ {
if (!hasMetadata()) return; if (!hasMetadata())
return;
m_nativeHandle.force_recheck(); m_nativeHandle.force_recheck();
// We have to force update the cached state, otherwise someone will be able to get // We have to force update the cached state, otherwise someone will be able to get
@ -1478,7 +1481,12 @@ void TorrentImpl::forceRecheck()
m_hasMissingFiles = false; m_hasMissingFiles = false;
m_unchecked = false; m_unchecked = false;
m_completedFiles.fill(false); m_completedFiles.fill(false);
m_filesProgress.fill(0);
m_pieces.fill(false);
m_nativeStatus.pieces.clear_all();
m_nativeStatus.num_pieces = 0;
if (isPaused()) if (isPaused())
{ {
@ -1603,6 +1611,8 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
, LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored)); , LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
m_completedFiles.fill(static_cast<bool>(p.flags & lt::torrent_flags::seed_mode), filesCount()); m_completedFiles.fill(static_cast<bool>(p.flags & lt::torrent_flags::seed_mode), filesCount());
m_filesProgress.resize(filesCount());
updateProgress();
for (int i = 0; i < fileNames.size(); ++i) for (int i = 0; i < fileNames.size(); ++i)
{ {
@ -1650,7 +1660,10 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
void TorrentImpl::reload() void TorrentImpl::reload()
{ {
m_completedFiles.fill(false); m_completedFiles.fill(false);
m_pieces.clear(); m_filesProgress.fill(0);
m_pieces.fill(false);
m_nativeStatus.pieces.clear_all();
m_nativeStatus.num_pieces = 0;
const auto queuePos = m_nativeHandle.queue_position(); const auto queuePos = m_nativeHandle.queue_position();
@ -2279,8 +2292,11 @@ bool TorrentImpl::isMoveInProgress() const
void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus) void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
{ {
m_pieces.clear(); const lt::torrent_status oldStatus = std::exchange(m_nativeStatus, nativeStatus);
m_nativeStatus = nativeStatus;
if (m_nativeStatus.num_pieces != oldStatus.num_pieces)
updateProgress();
updateState(); updateState();
m_payloadRateMonitor.addSample({nativeStatus.download_payload_rate m_payloadRateMonitor.addSample({nativeStatus.download_payload_rate
@ -2300,6 +2316,44 @@ void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
std::invoke(m_statusUpdatedTriggers.dequeue()); std::invoke(m_statusUpdatedTriggers.dequeue());
} }
void TorrentImpl::updateProgress()
{
Q_ASSERT(hasMetadata());
if (Q_UNLIKELY(!hasMetadata()))
return;
Q_ASSERT(!m_filesProgress.isEmpty());
if (Q_UNLIKELY(m_filesProgress.isEmpty()))
m_filesProgress.resize(filesCount());
const QBitArray oldPieces = std::exchange(m_pieces, LT::toQBitArray(m_nativeStatus.pieces));
const QBitArray newPieces = m_pieces ^ oldPieces;
const int64_t pieceSize = m_torrentInfo.pieceLength();
for (qsizetype index = 0; index < newPieces.size(); ++index)
{
if (!newPieces.at(index))
continue;
int64_t size = m_torrentInfo.pieceLength(index);
int64_t pieceOffset = index * pieceSize;
for (const int fileIndex : asConst(m_torrentInfo.fileIndicesForPiece(index)))
{
const int64_t fileOffsetInPiece = pieceOffset - m_torrentInfo.fileOffset(fileIndex);
const int64_t add = std::min<int64_t>((m_torrentInfo.fileSize(fileIndex) - fileOffsetInPiece), size);
m_filesProgress[fileIndex] += add;
size -= add;
if (size <= 0)
break;
pieceOffset += add;
}
}
}
void TorrentImpl::setRatioLimit(qreal limit) void TorrentImpl::setRatioLimit(qreal limit)
{ {
if (limit < USE_GLOBAL_RATIO) if (limit < USE_GLOBAL_RATIO)
@ -2540,48 +2594,6 @@ void TorrentImpl::fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandle
, std::move(resultHandler)); , 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 void TorrentImpl::fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const
{ {
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int> invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int>

7
src/base/bittorrent/torrentimpl.h

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2015-2023 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
@ -238,7 +238,6 @@ namespace BitTorrent
void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override; void fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const override;
void fetchURLSeeds(std::function<void (QVector<QUrl>)> 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 fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const override;
void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override; void fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const override;
void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override; void fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const override;
@ -263,6 +262,7 @@ namespace BitTorrent
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const; std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
void updateStatus(const lt::torrent_status &nativeStatus); void updateStatus(const lt::torrent_status &nativeStatus);
void updateProgress();
void updateState(); void updateState();
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p); void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
@ -354,6 +354,7 @@ namespace BitTorrent
int m_downloadLimit = 0; int m_downloadLimit = 0;
int m_uploadLimit = 0; int m_uploadLimit = 0;
mutable QBitArray m_pieces; QBitArray m_pieces;
QVector<std::int64_t> m_filesProgress;
}; };
} }

7
src/gui/addnewtorrentdialog.cpp

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2012 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
@ -237,11 +237,6 @@ public:
return QVector<qreal>(filesCount(), 0); return QVector<qreal>(filesCount(), 0);
} }
void fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const override
{
resultHandler(filesProgress());
}
QVector<qreal> availableFileFractions() const override QVector<qreal> availableFileFractions() const override
{ {
return QVector<qreal>(filesCount(), 0); return QVector<qreal>(filesCount(), 0);

28
src/gui/torrentcontentmodel.cpp

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006-2012 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2006-2012 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
@ -216,23 +216,17 @@ void TorrentContentModel::updateFilesProgress()
{ {
Q_ASSERT(m_contentHandler && m_contentHandler->hasMetadata()); Q_ASSERT(m_contentHandler && m_contentHandler->hasMetadata());
using HandlerPtr = QPointer<BitTorrent::TorrentContentHandler>; const QVector<qreal> &filesProgress = m_contentHandler->filesProgress();
m_contentHandler->fetchFilesProgress([this, handler = HandlerPtr(m_contentHandler)](const QVector<qreal> &filesProgress) Q_ASSERT(m_filesIndex.size() == filesProgress.size());
{ // XXX: Why is this necessary?
if (handler != m_contentHandler) if (Q_UNLIKELY(m_filesIndex.size() != filesProgress.size()))
return; return;
Q_ASSERT(m_filesIndex.size() == filesProgress.size());
// XXX: Why is this necessary?
if (Q_UNLIKELY(m_filesIndex.size() != filesProgress.size()))
return;
for (int i = 0; i < filesProgress.size(); ++i) for (int i = 0; i < filesProgress.size(); ++i)
m_filesIndex[i]->setProgress(filesProgress[i]); m_filesIndex[i]->setProgress(filesProgress[i]);
// Update folders progress in the tree // Update folders progress in the tree
m_rootItem->recalculateProgress(); m_rootItem->recalculateProgress();
m_rootItem->recalculateAvailability(); m_rootItem->recalculateAvailability();
});
} }
void TorrentContentModel::updateFilesPriorities() void TorrentContentModel::updateFilesPriorities()

Loading…
Cancel
Save