Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix received metadata handling #13905

Merged
merged 1 commit into from
Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/base/bittorrent/nativetorrentextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ bool NativeTorrentExtension::on_pause()
// and other extensions to be also invoked.
return false;
}

void NativeTorrentExtension::on_state(const lt::torrent_status::state_t state)
{
if (m_state == lt::torrent_status::downloading_metadata)
m_torrentHandle.set_flags(lt::torrent_flags::stop_when_ready);

m_state = state;
}
2 changes: 2 additions & 0 deletions src/base/bittorrent/nativetorrentextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class NativeTorrentExtension final : public lt::torrent_plugin

private:
bool on_pause() override;
void on_state(lt::torrent_status::state_t state) override;

lt::torrent_handle m_torrentHandle;
lt::torrent_status::state_t m_state = lt::torrent_status::checking_resume_data;
};
9 changes: 8 additions & 1 deletion src/base/bittorrent/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,13 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)

void Session::fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames)
{
TorrentHandleImpl *torrent = m_torrents.value(id);
if (torrent)
{
torrent->fileSearchFinished(savePath, fileNames);
return;
}

const auto loadingTorrentsIter = m_loadingTorrents.find(id);
if (loadingTorrentsIter != m_loadingTorrents.end())
{
Expand Down Expand Up @@ -4595,7 +4602,7 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)

const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());

auto *const torrent = new TorrentHandleImpl {this, nativeHandle, params};
auto *const torrent = new TorrentHandleImpl {this, m_nativeSession, nativeHandle, params};
m_torrents.insert(torrent->hash(), torrent);

const bool hasMetadata = torrent->hasMetadata();
Expand Down
164 changes: 121 additions & 43 deletions src/base/bittorrent/torrenthandleimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <libtorrent/alert_types.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/storage_defs.hpp>
#include <libtorrent/time.hpp>
#include <libtorrent/version.hpp>
Expand Down Expand Up @@ -101,10 +102,11 @@ namespace

// TorrentHandleImpl

TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const LoadTorrentParams &params)
TorrentHandleImpl::TorrentHandleImpl(Session *session, lt::session *nativeSession
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params)
: QObject(session)
, m_session(session)
, m_nativeSession(nativeSession)
, m_nativeHandle(nativeHandle)
, m_name(params.name)
, m_savePath(Utils::Fs::toNativePath(params.savePath))
Expand All @@ -124,7 +126,12 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));

m_hash = InfoHash {m_nativeHandle.info_hash()};
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
if (m_ltAddTorrentParams.ti)
{
// Initialize it only if torrent is added with metadata.
// Otherwise it should be initialized in "Metadata received" handler.
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
}

updateStatus();

Expand Down Expand Up @@ -648,7 +655,9 @@ bool TorrentHandleImpl::isPaused() const
bool TorrentHandleImpl::isQueued() const
{
// Torrent is Queued if it isn't in Paused state but paused internally
return ((m_nativeStatus.flags & lt::torrent_flags::paused) && !isPaused());
return (!isPaused()
&& (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
&& (m_nativeStatus.flags & lt::torrent_flags::paused));
}

bool TorrentHandleImpl::isChecking() const
Expand Down Expand Up @@ -755,10 +764,19 @@ void TorrentHandleImpl::updateState()
{
m_state = TorrentState::Error;
}
else if (!hasMetadata())
{
if (isPaused())
m_state = TorrentState::PausedDownloading;
else if (m_session->isQueueingSystemEnabled() && isQueued())
m_state = TorrentState::QueuedDownloading;
else
m_state = TorrentState::DownloadingMetadata;
}
else if ((m_nativeStatus.state == lt::torrent_status::checking_files)
&& (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
|| !(m_nativeStatus.flags & lt::torrent_flags::paused)))
{
{
// If the torrent is not just in the "checking" state, but is being actually checked
m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
}
Expand All @@ -781,8 +799,6 @@ void TorrentHandleImpl::updateState()
m_state = TorrentState::PausedDownloading;
else if (m_session->isQueueingSystemEnabled() && isQueued())
m_state = TorrentState::QueuedDownloading;
else if (m_nativeStatus.state == lt::torrent_status::downloading_metadata) // must come after queue check
m_state = TorrentState::DownloadingMetadata;
else if (isForced())
m_state = TorrentState::ForcedDownloading;
else if (m_nativeStatus.download_payload_rate > 0)
Expand Down Expand Up @@ -896,6 +912,9 @@ qlonglong TorrentHandleImpl::eta() const

QVector<qreal> TorrentHandleImpl::filesProgress() const
{
if (!hasMetadata())
return {};

std::vector<int64_t> fp;
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);

Expand Down Expand Up @@ -1283,18 +1302,66 @@ void TorrentHandleImpl::applyFirstLastPiecePriority(const bool enabled, const QV
m_nativeHandle.prioritize_pieces(piecePriorities);
}

void TorrentHandleImpl::pause()
void TorrentHandleImpl::fileSearchFinished(const QString &savePath, const QStringList &fileNames)
{
setAutoManaged(false);
m_nativeHandle.pause();
endReceivedMetadataHandling(savePath, fileNames);
}

void TorrentHandleImpl::endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
{
const auto queuePos = m_nativeHandle.queue_position();

lt::add_torrent_params p = m_ltAddTorrentParams;
p.ti = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());

m_nativeSession->remove_torrent(m_nativeHandle, lt::session::delete_partfile);

m_speedMonitor.reset();
for (int i = 0; i < fileNames.size(); ++i)
p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString();

p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
p.flags |= lt::torrent_flags::update_subscribe
| lt::torrent_flags::override_trackers
| lt::torrent_flags::override_web_seeds;

m_nativeHandle = m_nativeSession->add_torrent(p);
m_nativeHandle.queue_position_set(queuePos);

// If first/last piece priority was specified when adding this torrent,
// we should apply it now that we have metadata:
if (m_hasFirstLastPiecePriority)
applyFirstLastPiecePriority(true);

if (!m_isStopped)
{
setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged);
if (m_operatingMode == TorrentOperatingMode::Forced)
m_nativeHandle.resume();
}

m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
m_maintenanceJob = MaintenanceJob::None;

updateStatus();

m_session->handleTorrentMetadataReceived(this);
}

void TorrentHandleImpl::pause()
{
if (!m_isStopped)
{
m_isStopped = true;
m_session->handleTorrentPaused(this);
}

if (m_maintenanceJob == MaintenanceJob::None)
{
setAutoManaged(false);
m_nativeHandle.pause();

m_speedMonitor.reset();
}
}

void TorrentHandleImpl::resume(const TorrentOperatingMode mode)
Expand All @@ -1308,24 +1375,24 @@ void TorrentHandleImpl::resume(const TorrentOperatingMode mode)
m_nativeHandle.force_recheck();
}

m_operatingMode = mode;

if (m_isStopped)
{
// Torrent may have been temporarily resumed to perform checking files
// so we have to ensure it will not pause after checking is done.
m_nativeHandle.unset_flags(lt::torrent_flags::stop_when_ready);
}

setAutoManaged(mode == TorrentOperatingMode::AutoManaged);
if (mode == TorrentOperatingMode::Forced)
m_nativeHandle.resume();

m_operatingMode = mode;

if (m_isStopped)
{
m_isStopped = false;
m_session->handleTorrentResumed(this);
}

if (m_maintenanceJob == MaintenanceJob::None)
{
setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged);
if (m_operatingMode == TorrentOperatingMode::Forced)
m_nativeHandle.resume();
}
}

void TorrentHandleImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
Expand Down Expand Up @@ -1410,7 +1477,15 @@ void TorrentHandleImpl::handleTrackerErrorAlert(const lt::tracker_error_alert *p
void TorrentHandleImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
{
Q_UNUSED(p);
qDebug("\"%s\" have just finished checking", qUtf8Printable(name()));
qDebug("\"%s\" have just finished checking.", qUtf8Printable(name()));


if (!hasMetadata())
{
// The torrent is checked due to metadata received, but we should not process
// this event until the torrent is reloaded using the received metadata.
return;
}

if (m_fastresumeDataRejected && !m_hasMissingFiles)
{
Expand Down Expand Up @@ -1500,6 +1575,18 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath(
QString::fromStdString(m_ltAddTorrentParams.save_path)).toStdString();

if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
{
m_ltAddTorrentParams.have_pieces.clear();
m_ltAddTorrentParams.verified_pieces.clear();

TorrentInfo metadata = TorrentInfo {m_nativeHandle.torrent_file()};
if (!m_hasRootFolder)
metadata.stripRootFolder();

m_session->findIncompleteFiles(metadata, m_savePath);
}

auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(m_ltAddTorrentParams));
lt::entry &resumeData = *resumeDataPtr;

Expand Down Expand Up @@ -1643,20 +1730,9 @@ void TorrentHandleImpl::handleMetadataReceivedAlert(const lt::metadata_received_
Q_UNUSED(p);

qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};

if (m_session->isAppendExtensionEnabled())
manageIncompleteFiles();
if (!m_hasRootFolder)
m_torrentInfo.stripRootFolder();
if (filesCount() == 1)
m_hasRootFolder = false;
m_session->handleTorrentMetadataReceived(this);

// If first/last piece priority was specified when adding this torrent,
// we should apply it now that we have metadata:
if (m_hasFirstLastPiecePriority)
applyFirstLastPiecePriority(true);
m_maintenanceJob = MaintenanceJob::HandleMetadata;
saveResumeData();
}

void TorrentHandleImpl::handlePerformanceAlert(const lt::performance_alert *p) const
Expand Down Expand Up @@ -1828,18 +1904,20 @@ void TorrentHandleImpl::updateStatus()
void TorrentHandleImpl::updateStatus(const lt::torrent_status &nativeStatus)
{
m_nativeStatus = nativeStatus;

updateState();

// NOTE: Don't change the order of these conditionals!
// Otherwise it will not work properly since torrent can be CheckingDownloading.
if (isChecking())
m_unchecked = false;
else if (isDownloading())
m_unchecked = true;

m_speedMonitor.addSample({nativeStatus.download_payload_rate
, nativeStatus.upload_payload_rate});
, nativeStatus.upload_payload_rate});

if (hasMetadata())
{
// NOTE: Don't change the order of these conditionals!
// Otherwise it will not work properly since torrent can be CheckingDownloading.
if (isChecking())
m_unchecked = false;
else if (isDownloading())
m_unchecked = true;
}
}

void TorrentHandleImpl::setRatioLimit(qreal limit)
Expand Down
16 changes: 14 additions & 2 deletions src/base/bittorrent/torrenthandleimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,20 @@ namespace BitTorrent
Overwrite
};

enum class MaintenanceJob
{
None,
HandleMetadata
};

class TorrentHandleImpl final : public QObject, public TorrentHandle
{
Q_DISABLE_COPY(TorrentHandleImpl)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandleImpl)

public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const LoadTorrentParams &params);
TorrentHandleImpl(Session *session, lt::session *nativeSession
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params);
~TorrentHandleImpl() override;

bool isValid() const;
Expand Down Expand Up @@ -241,6 +247,7 @@ namespace BitTorrent
void handleAppendExtensionToggled();
void saveResumeData();
void handleMoveStorageJobFinished(bool hasOutstandingJob);
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);

QString actualStorageLocation() const;

Expand Down Expand Up @@ -278,7 +285,10 @@ namespace BitTorrent
void manageIncompleteFiles();
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});

void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);

Session *const m_session;
lt::session *m_nativeSession;
lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus;
TorrentState m_state = TorrentState::Unknown;
Expand All @@ -293,6 +303,8 @@ namespace BitTorrent
int m_renameCount = 0;
bool m_storageIsMoving = false;

MaintenanceJob m_maintenanceJob = MaintenanceJob::None;

// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath;
Expand Down