Browse Source

Fix received metadata handling

adaptive-webui-19844
Vladimir Golovnev (Glassez) 4 years ago
parent
commit
4e04cd27c9
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  1. 8
      src/base/bittorrent/nativetorrentextension.cpp
  2. 2
      src/base/bittorrent/nativetorrentextension.h
  3. 9
      src/base/bittorrent/session.cpp
  4. 164
      src/base/bittorrent/torrenthandleimpl.cpp
  5. 16
      src/base/bittorrent/torrenthandleimpl.h

8
src/base/bittorrent/nativetorrentextension.cpp

@ -52,3 +52,11 @@ bool NativeTorrentExtension::on_pause()
// and other extensions to be also invoked. // and other extensions to be also invoked.
return false; 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
src/base/bittorrent/nativetorrentextension.h

@ -38,6 +38,8 @@ public:
private: private:
bool on_pause() override; bool on_pause() override;
void on_state(lt::torrent_status::state_t state) override;
lt::torrent_handle m_torrentHandle; lt::torrent_handle m_torrentHandle;
lt::torrent_status::state_t m_state = lt::torrent_status::checking_resume_data;
}; };

9
src/base/bittorrent/session.cpp

@ -1774,6 +1774,13 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
void Session::fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames) 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); const auto loadingTorrentsIter = m_loadingTorrents.find(id);
if (loadingTorrentsIter != m_loadingTorrents.end()) if (loadingTorrentsIter != m_loadingTorrents.end())
{ {
@ -4595,7 +4602,7 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash()); 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); m_torrents.insert(torrent->hash(), torrent);
const bool hasMetadata = torrent->hasMetadata(); const bool hasMetadata = torrent->hasMetadata();

164
src/base/bittorrent/torrenthandleimpl.cpp

@ -41,6 +41,7 @@
#include <libtorrent/alert_types.hpp> #include <libtorrent/alert_types.hpp>
#include <libtorrent/entry.hpp> #include <libtorrent/entry.hpp>
#include <libtorrent/magnet_uri.hpp> #include <libtorrent/magnet_uri.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/storage_defs.hpp> #include <libtorrent/storage_defs.hpp>
#include <libtorrent/time.hpp> #include <libtorrent/time.hpp>
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
@ -101,10 +102,11 @@ namespace
// TorrentHandleImpl // TorrentHandleImpl
TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, TorrentHandleImpl::TorrentHandleImpl(Session *session, lt::session *nativeSession
const LoadTorrentParams &params) , const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params)
: QObject(session) : QObject(session)
, m_session(session) , m_session(session)
, m_nativeSession(nativeSession)
, m_nativeHandle(nativeHandle) , m_nativeHandle(nativeHandle)
, m_name(params.name) , m_name(params.name)
, m_savePath(Utils::Fs::toNativePath(params.savePath)) , m_savePath(Utils::Fs::toNativePath(params.savePath))
@ -124,7 +126,12 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
m_hash = InfoHash {m_nativeHandle.info_hash()}; 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(); updateStatus();
@ -648,7 +655,9 @@ bool TorrentHandleImpl::isPaused() const
bool TorrentHandleImpl::isQueued() const bool TorrentHandleImpl::isQueued() const
{ {
// Torrent is Queued if it isn't in Paused state but paused internally // 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 bool TorrentHandleImpl::isChecking() const
@ -755,10 +764,19 @@ void TorrentHandleImpl::updateState()
{ {
m_state = TorrentState::Error; 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) else if ((m_nativeStatus.state == lt::torrent_status::checking_files)
&& (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed) && (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
|| !(m_nativeStatus.flags & lt::torrent_flags::paused))) || !(m_nativeStatus.flags & lt::torrent_flags::paused)))
{ {
// If the torrent is not just in the "checking" state, but is being actually checked // If the torrent is not just in the "checking" state, but is being actually checked
m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading; m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
} }
@ -781,8 +799,6 @@ void TorrentHandleImpl::updateState()
m_state = TorrentState::PausedDownloading; m_state = TorrentState::PausedDownloading;
else if (m_session->isQueueingSystemEnabled() && isQueued()) else if (m_session->isQueueingSystemEnabled() && isQueued())
m_state = TorrentState::QueuedDownloading; 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()) else if (isForced())
m_state = TorrentState::ForcedDownloading; m_state = TorrentState::ForcedDownloading;
else if (m_nativeStatus.download_payload_rate > 0) else if (m_nativeStatus.download_payload_rate > 0)
@ -896,6 +912,9 @@ qlonglong TorrentHandleImpl::eta() const
QVector<qreal> TorrentHandleImpl::filesProgress() const QVector<qreal> TorrentHandleImpl::filesProgress() const
{ {
if (!hasMetadata())
return {};
std::vector<int64_t> fp; std::vector<int64_t> fp;
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
@ -1283,18 +1302,66 @@ void TorrentHandleImpl::applyFirstLastPiecePriority(const bool enabled, const QV
m_nativeHandle.prioritize_pieces(piecePriorities); m_nativeHandle.prioritize_pieces(piecePriorities);
} }
void TorrentHandleImpl::pause() void TorrentHandleImpl::fileSearchFinished(const QString &savePath, const QStringList &fileNames)
{ {
setAutoManaged(false); endReceivedMetadataHandling(savePath, fileNames);
m_nativeHandle.pause(); }
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) if (!m_isStopped)
{ {
m_isStopped = true; m_isStopped = true;
m_session->handleTorrentPaused(this); m_session->handleTorrentPaused(this);
} }
if (m_maintenanceJob == MaintenanceJob::None)
{
setAutoManaged(false);
m_nativeHandle.pause();
m_speedMonitor.reset();
}
} }
void TorrentHandleImpl::resume(const TorrentOperatingMode mode) void TorrentHandleImpl::resume(const TorrentOperatingMode mode)
@ -1308,24 +1375,24 @@ void TorrentHandleImpl::resume(const TorrentOperatingMode mode)
m_nativeHandle.force_recheck(); m_nativeHandle.force_recheck();
} }
m_operatingMode = mode;
if (m_isStopped) if (m_isStopped)
{ {
// Torrent may have been temporarily resumed to perform checking files // Torrent may have been temporarily resumed to perform checking files
// so we have to ensure it will not pause after checking is done. // so we have to ensure it will not pause after checking is done.
m_nativeHandle.unset_flags(lt::torrent_flags::stop_when_ready); 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_isStopped = false;
m_session->handleTorrentResumed(this); 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) void TorrentHandleImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
@ -1410,7 +1477,15 @@ void TorrentHandleImpl::handleTrackerErrorAlert(const lt::tracker_error_alert *p
void TorrentHandleImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p) void TorrentHandleImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
{ {
Q_UNUSED(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) if (m_fastresumeDataRejected && !m_hasMissingFiles)
{ {
@ -1500,6 +1575,18 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath( m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath(
QString::fromStdString(m_ltAddTorrentParams.save_path)).toStdString(); 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)); auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(m_ltAddTorrentParams));
lt::entry &resumeData = *resumeDataPtr; lt::entry &resumeData = *resumeDataPtr;
@ -1643,20 +1730,9 @@ void TorrentHandleImpl::handleMetadataReceivedAlert(const lt::metadata_received_
Q_UNUSED(p); Q_UNUSED(p);
qDebug("Metadata received for torrent %s.", qUtf8Printable(name())); qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
if (m_session->isAppendExtensionEnabled()) m_maintenanceJob = MaintenanceJob::HandleMetadata;
manageIncompleteFiles(); saveResumeData();
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);
} }
void TorrentHandleImpl::handlePerformanceAlert(const lt::performance_alert *p) const void TorrentHandleImpl::handlePerformanceAlert(const lt::performance_alert *p) const
@ -1828,18 +1904,20 @@ void TorrentHandleImpl::updateStatus()
void TorrentHandleImpl::updateStatus(const lt::torrent_status &nativeStatus) void TorrentHandleImpl::updateStatus(const lt::torrent_status &nativeStatus)
{ {
m_nativeStatus = nativeStatus; m_nativeStatus = nativeStatus;
updateState(); 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 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) void TorrentHandleImpl::setRatioLimit(qreal limit)

16
src/base/bittorrent/torrenthandleimpl.h

@ -80,14 +80,20 @@ namespace BitTorrent
Overwrite Overwrite
}; };
enum class MaintenanceJob
{
None,
HandleMetadata
};
class TorrentHandleImpl final : public QObject, public TorrentHandle class TorrentHandleImpl final : public QObject, public TorrentHandle
{ {
Q_DISABLE_COPY(TorrentHandleImpl) Q_DISABLE_COPY(TorrentHandleImpl)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandleImpl) Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandleImpl)
public: public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, TorrentHandleImpl(Session *session, lt::session *nativeSession
const LoadTorrentParams &params); , const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params);
~TorrentHandleImpl() override; ~TorrentHandleImpl() override;
bool isValid() const; bool isValid() const;
@ -241,6 +247,7 @@ namespace BitTorrent
void handleAppendExtensionToggled(); void handleAppendExtensionToggled();
void saveResumeData(); void saveResumeData();
void handleMoveStorageJobFinished(bool hasOutstandingJob); void handleMoveStorageJobFinished(bool hasOutstandingJob);
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
QString actualStorageLocation() const; QString actualStorageLocation() const;
@ -278,7 +285,10 @@ namespace BitTorrent
void manageIncompleteFiles(); void manageIncompleteFiles();
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {}); void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);
Session *const m_session; Session *const m_session;
lt::session *m_nativeSession;
lt::torrent_handle m_nativeHandle; lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus; lt::torrent_status m_nativeStatus;
TorrentState m_state = TorrentState::Unknown; TorrentState m_state = TorrentState::Unknown;
@ -293,6 +303,8 @@ namespace BitTorrent
int m_renameCount = 0; int m_renameCount = 0;
bool m_storageIsMoving = false; bool m_storageIsMoving = false;
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
// Until libtorrent provide an "old_name" field in `file_renamed_alert` // Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders // we will rely on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath; QHash<lt::file_index_t, QVector<QString>> m_oldPath;

Loading…
Cancel
Save