diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index c1e8dcc98..402fdace5 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -4256,7 +4256,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath }); const bool torrentHasOutstandingJob = (iter != m_moveStorageQueue.end()); - torrent->handleMoveStorageJobFinished(torrentHasOutstandingJob); + torrent->handleMoveStorageJobFinished(currentLocation, torrentHasOutstandingJob); } } @@ -4307,7 +4307,7 @@ void Session::moveTorrentStorage(const MoveStorageJob &job) const ? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace)); } -void Session::handleMoveTorrentStorageJobFinished() +void Session::handleMoveTorrentStorageJobFinished(const Path &newPath) { const MoveStorageJob finishedJob = m_moveStorageQueue.takeFirst(); if (!m_moveStorageQueue.isEmpty()) @@ -4324,7 +4324,7 @@ void Session::handleMoveTorrentStorageJobFinished() TorrentImpl *torrent = m_torrents.value(finishedJob.torrentHandle.info_hash()); if (torrent) { - torrent->handleMoveStorageJobFinished(torrentHasOutstandingJob); + torrent->handleMoveStorageJobFinished(newPath, torrentHasOutstandingJob); } else if (!torrentHasOutstandingJob) { @@ -5295,7 +5295,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p) const QString torrentName = (torrent ? torrent->name() : id.toString()); LogMsg(tr("Moved torrent successfully. Torrent: \"%1\". Destination: \"%2\"").arg(torrentName, newPath.toString())); - handleMoveTorrentStorageJobFinished(); + handleMoveTorrentStorageJobFinished(newPath); } void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p) @@ -5313,12 +5313,13 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert TorrentImpl *torrent = m_torrents.value(id); const QString torrentName = (torrent ? torrent->name() : id.toString()); - const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path); + const Path currentLocation = (torrent ? torrent->actualStorageLocation() + : Path(p->handle.status(lt::torrent_handle::query_save_path).save_path)); const QString errorMessage = QString::fromStdString(p->message()); LogMsg(tr("Failed to move torrent. Torrent: \"%1\". Source: \"%2\". Destination: \"%3\". Reason: \"%4\"") - .arg(torrentName, currentLocation, currentJob.path.toString(), errorMessage), Log::WARNING); + .arg(torrentName, currentLocation.toString(), currentJob.path.toString(), errorMessage), Log::WARNING); - handleMoveTorrentStorageJobFinished(); + handleMoveTorrentStorageJobFinished(currentLocation); } void Session::handleStateUpdateAlert(const lt::state_update_alert *p) diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 7f8a99033..fe8f8b4ec 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -661,7 +661,7 @@ namespace BitTorrent std::vector getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const; void moveTorrentStorage(const MoveStorageJob &job) const; - void handleMoveTorrentStorageJobFinished(); + void handleMoveTorrentStorageJobFinished(const Path &newPath); void loadCategories(); void storeCategories() const; diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 7417e1aee..68e78703b 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015-2022 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -286,6 +286,7 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession m_filePaths.reserve(filesCount); m_indexMap.reserve(filesCount); m_filePriorities.reserve(filesCount); + m_completedFiles.resize(filesCount); const std::vector filePriorities = resized(m_ltAddTorrentParams.file_priorities, m_ltAddTorrentParams.ti->num_files() , LT::toNative(m_ltAddTorrentParams.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored)); @@ -839,7 +840,7 @@ Path TorrentImpl::filePath(const int index) const Path TorrentImpl::actualFilePath(const int index) const { const auto nativeIndex = m_torrentInfo.nativeIndexes().at(index); - return Path(m_nativeHandle.torrent_file()->files().file_path(nativeIndex)); + return Path(nativeTorrentInfo()->files().file_path(nativeIndex)); } qlonglong TorrentImpl::fileSize(const int index) const @@ -1125,6 +1126,9 @@ QVector TorrentImpl::filesProgress() const if (!hasMetadata()) return {}; + if (m_completedFiles.count(true) == filesCount()) + return QVector(filesCount(), 1); + std::vector fp; m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); @@ -1422,6 +1426,7 @@ void TorrentImpl::forceRecheck() m_nativeHandle.force_recheck(); m_hasMissingFiles = false; m_unchecked = false; + m_completedFiles.fill(false); if (isPaused()) { @@ -1509,15 +1514,23 @@ void TorrentImpl::updatePeerCount(const QString &trackerUrl, const lt::tcp::endp m_trackerPeerCounts[trackerUrl][endpoint] = count; } +std::shared_ptr TorrentImpl::nativeTorrentInfo() const +{ + if (m_nativeStatus.torrent_file.expired()) + m_nativeStatus.torrent_file = m_nativeHandle.torrent_file(); + return m_nativeStatus.torrent_file.lock(); +} + void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathList &fileNames) { Q_ASSERT(m_filePaths.isEmpty()); lt::add_torrent_params &p = m_ltAddTorrentParams; - const std::shared_ptr metadata = std::const_pointer_cast(m_nativeHandle.torrent_file()); + const std::shared_ptr metadata = std::const_pointer_cast(nativeTorrentInfo()); m_torrentInfo = TorrentInfo(*metadata); m_filePriorities.reserve(filesCount()); + m_completedFiles.resize(filesCount()); const auto nativeIndexes = m_torrentInfo.nativeIndexes(); const std::vector filePriorities = resized(p.file_priorities, metadata->files().num_files() @@ -1546,7 +1559,6 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi applyFirstLastPiecePriority(true); m_maintenanceJob = MaintenanceJob::None; - updateStatus(); prepareResumeData(p); m_session->handleTorrentMetadataReceived(this); @@ -1554,6 +1566,9 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi void TorrentImpl::reload() { + m_completedFiles.fill(false); + m_pieces.clear(); + const auto queuePos = m_nativeHandle.queue_position(); m_nativeSession->remove_torrent(m_nativeHandle, lt::session::delete_partfile); @@ -1580,6 +1595,9 @@ void TorrentImpl::reload() m_nativeHandle = m_nativeSession->add_torrent(p); if (queuePos >= lt::queue_position_t {}) m_nativeHandle.queue_position_set(queuePos); + + initializeStatus(m_nativeStatus, m_ltAddTorrentParams); + updateState(); } void TorrentImpl::pause() @@ -1614,9 +1632,8 @@ void TorrentImpl::resume(const TorrentOperatingMode mode) { m_hasMissingFiles = false; m_isStopped = false; - m_ltAddTorrentParams.ti = std::const_pointer_cast(m_nativeHandle.torrent_file()); + m_ltAddTorrentParams.ti = std::const_pointer_cast(nativeTorrentInfo()); reload(); - updateStatus(); return; } @@ -1644,7 +1661,7 @@ void TorrentImpl::moveStorage(const Path &newPath, const MoveStorageMode mode) if (m_session->addMoveTorrentStorageJob(this, newPath, mode)) { m_storageIsMoving = true; - updateStatus(); + updateState(); } } @@ -1660,13 +1677,16 @@ void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus) updateStatus(nativeStatus); } -void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob) +void TorrentImpl::handleMoveStorageJobFinished(const Path &path, const bool hasOutstandingJob) { m_session->handleTorrentNeedSaveResumeData(this); m_storageIsMoving = hasOutstandingJob; - updateStatus(); - m_session->handleTorrentSavePathChanged(this); + if (actualStorageLocation() != path) + { + m_nativeStatus.save_path = path.toString().toStdString(); + m_session->handleTorrentSavePathChanged(this); + } if (!m_storageIsMoving) { @@ -1675,21 +1695,18 @@ void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob) // it can be moved to the proper location m_hasMissingFiles = false; m_ltAddTorrentParams.save_path = m_nativeStatus.save_path; - m_ltAddTorrentParams.ti = std::const_pointer_cast(m_nativeHandle.torrent_file()); + m_ltAddTorrentParams.ti = std::const_pointer_cast(nativeTorrentInfo()); reload(); - updateStatus(); } while ((m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) - m_moveFinishedTriggers.takeFirst()(); + std::invoke(m_moveFinishedTriggers.dequeue()); } } void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p) { Q_UNUSED(p); - qDebug("\"%s\" have just finished checking.", qUtf8Printable(name())); - if (!hasMetadata()) { @@ -1698,26 +1715,29 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p) return; } - if (m_nativeHandle.need_save_resume_data()) - m_session->handleTorrentNeedSaveResumeData(this); + m_statusUpdatedTriggers.enqueue([this]() + { + qDebug("\"%s\" have just finished checking.", qUtf8Printable(name())); - if (m_fastresumeDataRejected && !m_hasMissingFiles) - m_fastresumeDataRejected = false; + if (m_nativeStatus.need_save_resume) + m_session->handleTorrentNeedSaveResumeData(this); - updateStatus(); + if (m_fastresumeDataRejected && !m_hasMissingFiles) + m_fastresumeDataRejected = false; - if (!m_hasMissingFiles) - { - if ((progress() < 1.0) && (wantedSize() > 0)) - m_hasSeedStatus = false; - else if (progress() == 1.0) - m_hasSeedStatus = true; + if (!m_hasMissingFiles) + { + if ((progress() < 1.0) && (wantedSize() > 0)) + m_hasSeedStatus = false; + else if (progress() == 1.0) + m_hasSeedStatus = true; - adjustStorageLocation(); - manageIncompleteFiles(); - } + adjustStorageLocation(); + manageIncompleteFiles(); + } - m_session->handleTorrentChecked(this); + m_session->handleTorrentChecked(this); + }); } void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p) @@ -1728,27 +1748,29 @@ void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p m_hasMissingFiles = false; if (m_hasSeedStatus) return; - updateStatus(); - m_hasSeedStatus = true; + m_statusUpdatedTriggers.enqueue([this]() + { + m_hasSeedStatus = true; - adjustStorageLocation(); - manageIncompleteFiles(); + adjustStorageLocation(); + manageIncompleteFiles(); - m_session->handleTorrentNeedSaveResumeData(this); + m_session->handleTorrentNeedSaveResumeData(this); - const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion(); - if (isMoveInProgress() || (m_renameCount > 0)) - { - if (recheckTorrentsOnCompletion) - m_moveFinishedTriggers.append([this]() { forceRecheck(); }); - m_moveFinishedTriggers.append([this]() { m_session->handleTorrentFinished(this); }); - } - else - { - if (recheckTorrentsOnCompletion && m_unchecked) - forceRecheck(); - m_session->handleTorrentFinished(this); - } + const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion(); + if (isMoveInProgress() || (m_renameCount > 0)) + { + if (recheckTorrentsOnCompletion) + m_moveFinishedTriggers.enqueue([this]() { forceRecheck(); }); + m_moveFinishedTriggers.enqueue([this]() { m_session->handleTorrentFinished(this); }); + } + else + { + if (recheckTorrentsOnCompletion && m_unchecked) + forceRecheck(); + m_session->handleTorrentFinished(this); + } + }); } void TorrentImpl::handleTorrentPausedAlert(const lt::torrent_paused_alert *p) @@ -1772,7 +1794,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) m_ltAddTorrentParams.have_pieces.clear(); m_ltAddTorrentParams.verified_pieces.clear(); - TorrentInfo metadata = TorrentInfo(*m_nativeHandle.torrent_file()); + TorrentInfo metadata = TorrentInfo(*nativeTorrentInfo()); const auto &renamedFiles = m_ltAddTorrentParams.renamed_files; PathList filePaths = metadata.filePaths(); @@ -1933,11 +1955,13 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p) if (m_maintenanceJob == MaintenanceJob::HandleMetadata) return; + const int fileIndex = m_indexMap.value(p->index, -1); + Q_ASSERT(fileIndex >= 0); + + m_completedFiles[fileIndex] = true; + if (m_session->isAppendExtensionEnabled()) { - const int fileIndex = m_indexMap.value(p->index, -1); - Q_ASSERT(fileIndex >= 0); - const Path path = filePath(fileIndex); const Path actualPath = actualFilePath(fileIndex); if (actualPath != path) @@ -2042,15 +2066,9 @@ void TorrentImpl::handleAlert(const lt::alert *a) void TorrentImpl::manageIncompleteFiles() { + const std::shared_ptr nativeInfo = nativeTorrentInfo(); + const lt::file_storage &nativeFiles = nativeInfo->files(); const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled(); - const QVector fp = filesProgress(); - if (fp.size() != filesCount()) - { - qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress"; - return; - } - - const lt::file_storage &nativeFiles = m_nativeHandle.torrent_file()->files(); for (int i = 0; i < filesCount(); ++i) { @@ -2059,7 +2077,7 @@ void TorrentImpl::manageIncompleteFiles() const auto nativeIndex = m_torrentInfo.nativeIndexes().at(i); const Path actualPath {nativeFiles.file_path(nativeIndex)}; - if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1)) + if (isAppendExtensionEnabled && (fileSize(i) > 0) && !m_completedFiles.at(i)) { const Path wantedPath = path + QB_EXT; if (actualPath != wantedPath) @@ -2099,11 +2117,6 @@ bool TorrentImpl::isMoveInProgress() const return m_storageIsMoving; } -void TorrentImpl::updateStatus() -{ - updateStatus(m_nativeHandle.status()); -} - void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus) { m_pieces.clear(); @@ -2122,6 +2135,9 @@ void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus) else if (isDownloading()) m_unchecked = true; } + + while (!m_statusUpdatedTriggers.isEmpty()) + std::invoke(m_statusUpdatedTriggers.dequeue()); } void TorrentImpl::setRatioLimit(qreal limit) @@ -2273,13 +2289,12 @@ void TorrentImpl::prioritizeFiles(const QVector &priorities) // Reset 'm_hasSeedStatus' if needed in order to react again to // 'torrent_finished_alert' and eg show tray notifications - const QVector progress = filesProgress(); const QVector oldPriorities = filePriorities(); for (int i = 0; i < oldPriorities.size(); ++i) { if ((oldPriorities[i] == DownloadPriority::Ignored) && (priorities[i] > DownloadPriority::Ignored) - && (progress[i] < 1.0)) + && !m_completedFiles.at(i)) { m_hasSeedStatus = false; break; diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index d7cb894de..af6a297cf 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015-2022 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -30,11 +30,13 @@ #pragma once #include +#include #include #include #include #include +#include #include #include @@ -239,14 +241,15 @@ namespace BitTorrent void handleCategoryOptionsChanged(); void handleAppendExtensionToggled(); void saveResumeData(); - void handleMoveStorageJobFinished(bool hasOutstandingJob); + void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob); void fileSearchFinished(const Path &savePath, const PathList &fileNames); void updatePeerCount(const QString &trackerUrl, const lt::tcp::endpoint &endpoint, int count); private: using EventTrigger = std::function; - void updateStatus(); + std::shared_ptr nativeTorrentInfo() const; + void updateStatus(const lt::torrent_status &nativeStatus); void updateState(); @@ -283,12 +286,13 @@ namespace BitTorrent Session *const m_session; lt::session *m_nativeSession; lt::torrent_handle m_nativeHandle; - lt::torrent_status m_nativeStatus; + mutable lt::torrent_status m_nativeStatus; TorrentState m_state = TorrentState::Unknown; TorrentInfo m_torrentInfo; PathList m_filePaths; QHash m_indexMap; QVector m_filePriorities; + QBitArray m_completedFiles; SpeedMonitor m_speedMonitor; InfoHash m_infoHash; @@ -299,6 +303,8 @@ namespace BitTorrent int m_renameCount = 0; bool m_storageIsMoving = false; + QQueue m_statusUpdatedTriggers; + MaintenanceJob m_maintenanceJob = MaintenanceJob::None; QHash> m_trackerPeerCounts;