diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 38154e98a7a..43a57568cb9 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -36,6 +36,7 @@ add_library(qbt_base STATIC bittorrent/speedmonitor.h bittorrent/sslparameters.h bittorrent/torrent.h + bittorrent/torrentbackend.h bittorrent/torrentcontenthandler.h bittorrent/torrentcontentlayout.h bittorrent/torrentcontentremoveoption.h @@ -146,6 +147,7 @@ add_library(qbt_base STATIC bittorrent/speedmonitor.cpp bittorrent/sslparameters.cpp bittorrent/torrent.cpp + bittorrent/torrentbackend.cpp bittorrent/torrentcontenthandler.cpp bittorrent/torrentcontentremover.cpp bittorrent/torrentcreationmanager.cpp diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 5e52b3aa1f8..6a16a3a53dd 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -101,6 +101,7 @@ #include "nativesessionextension.h" #include "portforwarderimpl.h" #include "resumedatastorage.h" +#include "torrentbackend.h" #include "torrentcontentremover.h" #include "torrentdescriptor.h" #include "torrentimpl.h" @@ -530,6 +531,7 @@ SessionImpl::SessionImpl(QObject *parent) , m_startPaused {BITTORRENT_SESSION_KEY(u"StartPaused"_s)} , m_seedingLimitTimer {new QTimer(this)} , m_resumeDataTimer {new QTimer(this)} + , m_backendThread {new QThread} , m_ioThread {new QThread} , m_asyncWorker {new QThreadPool(this)} , m_recentErroredTorrentsTimer {new QTimer(this)} @@ -590,6 +592,8 @@ SessionImpl::SessionImpl(QObject *parent) , &Net::ProxyConfigurationManager::proxyConfigurationChanged , this, &SessionImpl::configureDeferred); + m_backendThread->start(); + m_fileSearcher = new FileSearcher; m_fileSearcher->moveToThread(m_ioThread.get()); connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater); @@ -5698,7 +5702,13 @@ void SessionImpl::dispatchTorrentAlert(const lt::torrent_alert *alert) TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) { - auto *const torrent = new TorrentImpl(this, m_nativeSession, nativeHandle, params); + auto *const torrentBackend = new TorrentBackend(m_nativeSession, nativeHandle); + torrentBackend->moveToThread(m_backendThread.get()); + connect(m_backendThread.get(), &QThread::finished, torrentBackend, &QObject::deleteLater); + + auto *const torrent = new TorrentImpl(this, torrentBackend, m_nativeSession, nativeHandle, params); + connect(torrent, &QObject::destroyed, torrentBackend, &QObject::deleteLater); + m_torrents.insert(torrent->id(), torrent); if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid()) m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent); diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index b1fe4efe0d9..17e6ce37459 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -772,6 +772,7 @@ namespace BitTorrent // Tracker QPointer m_tracker; + Utils::Thread::UniquePtr m_backendThread; Utils::Thread::UniquePtr m_ioThread; QThreadPool *m_asyncWorker = nullptr; ResumeDataStorage *m_resumeDataStorage = nullptr; diff --git a/src/base/bittorrent/torrentbackend.cpp b/src/base/bittorrent/torrentbackend.cpp new file mode 100644 index 00000000000..81586d74b0f --- /dev/null +++ b/src/base/bittorrent/torrentbackend.cpp @@ -0,0 +1,86 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "torrentbackend.h" + +#include +#include + +namespace +{ + lt::announce_entry makeLTAnnounceEntry(const QString &url, const int tier) + { + lt::announce_entry entry {url.toStdString()}; + entry.tier = tier; + return entry; + } +} + +BitTorrent::TorrentBackend::TorrentBackend(lt::session *ltSession, lt::torrent_handle ltTorrentHandle, QObject *parent) + : QObject(parent) + , m_ltSession {ltSession} + , m_ltTorrentHandle {std::move(ltTorrentHandle)} +{ +} + +void BitTorrent::TorrentBackend::addTrackers(const QList &trackers) +{ + for (const TrackerEntry &tracker : trackers) + m_ltTorrentHandle.add_tracker(makeLTAnnounceEntry(tracker.url, tracker.tier)); +} + +void BitTorrent::TorrentBackend::replaceTrackers(const QList &trackers) +{ + std::vector ltAnnounceEntries; + ltAnnounceEntries.reserve(trackers.size()); + for (const TrackerEntry &tracker : trackers) + ltAnnounceEntries.emplace_back(makeLTAnnounceEntry(tracker.url, tracker.tier)); + m_ltTorrentHandle.replace_trackers(ltAnnounceEntries); +} + +void BitTorrent::TorrentBackend::addUrlSeeds(const QList &urlSeeds) +{ + for (const QUrl &url : urlSeeds) + m_ltTorrentHandle.add_url_seed(url.toString().toStdString()); +} + +void BitTorrent::TorrentBackend::removeUrlSeeds(const QList &urlSeeds) +{ + for (const QUrl &url : urlSeeds) + m_ltTorrentHandle.remove_url_seed(url.toString().toStdString()); +} + +void BitTorrent::TorrentBackend::clearPeers() +{ + m_ltTorrentHandle.clear_peers(); +} + +void BitTorrent::TorrentBackend::requestResumeData(const lt::resume_data_flags_t flags) +{ + m_ltTorrentHandle.save_resume_data(flags); +} diff --git a/src/base/bittorrent/torrentbackend.h b/src/base/bittorrent/torrentbackend.h new file mode 100644 index 00000000000..9b2ae5c4dd4 --- /dev/null +++ b/src/base/bittorrent/torrentbackend.h @@ -0,0 +1,61 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +#include "trackerentry.h" + +namespace BitTorrent +{ + class TorrentBackend final : public QObject + { + Q_OBJECT + Q_DISABLE_COPY_MOVE(TorrentBackend) + + public: + TorrentBackend(lt::session *ltSession, lt::torrent_handle ltTorrentHandle, QObject *parent = nullptr); + + void addTrackers(const QList &trackers); + void replaceTrackers(const QList &trackers); + void addUrlSeeds(const QList &urlSeeds); + void removeUrlSeeds(const QList &urlSeeds); + void clearPeers(); + void requestResumeData(lt::resume_data_flags_t flags); + + private: + lt::session *m_ltSession; + lt::torrent_handle m_ltTorrentHandle; + }; +} diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 6d7ce0e3602..2bebf3ffa75 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -71,6 +71,7 @@ #include "peeraddress.h" #include "peerinfo.h" #include "sessionimpl.h" +#include "torrentbackend.h" #include "trackerentry.h" #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) @@ -85,13 +86,6 @@ using namespace BitTorrent; namespace { - lt::announce_entry makeNativeAnnounceEntry(const QString &url, const int tier) - { - lt::announce_entry entry {url.toStdString()}; - entry.tier = tier; - return entry; - } - QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint) { const auto ltNow = lt::clock_type::now(); @@ -284,36 +278,36 @@ namespace // TorrentImpl -TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession - , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) +TorrentImpl::TorrentImpl(SessionImpl *session, TorrentBackend *backend, lt::session *nativeSession + , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) : Torrent(session) - , m_session(session) - , m_nativeSession(nativeSession) - , m_nativeHandle(nativeHandle) + , m_session {session} + , m_backend {backend} + , m_nativeSession {nativeSession} + , m_nativeHandle {nativeHandle} #ifdef QBT_USES_LIBTORRENT2 - , m_infoHash(m_nativeHandle.info_hashes()) + , m_infoHash {m_nativeHandle.info_hashes()} #else - , m_infoHash(m_nativeHandle.info_hash()) + , m_infoHash {m_nativeHandle.info_hash()} #endif - , m_name(params.name) - , m_savePath(params.savePath) - , m_downloadPath(params.downloadPath) - , m_category(params.category) - , m_tags(params.tags) - , m_ratioLimit(params.ratioLimit) - , m_seedingTimeLimit(params.seedingTimeLimit) - , m_inactiveSeedingTimeLimit(params.inactiveSeedingTimeLimit) - , m_shareLimitAction(params.shareLimitAction) - , m_operatingMode(params.operatingMode) - , m_contentLayout(params.contentLayout) - , m_hasFinishedStatus(params.hasFinishedStatus) - , m_hasFirstLastPiecePriority(params.firstLastPiecePriority) - , m_useAutoTMM(params.useAutoTMM) - , m_isStopped(params.stopped) - , m_sslParams(params.sslParameters) - , m_ltAddTorrentParams(params.ltAddTorrentParams) - , m_downloadLimit(cleanLimitValue(m_ltAddTorrentParams.download_limit)) - , m_uploadLimit(cleanLimitValue(m_ltAddTorrentParams.upload_limit)) + , m_name {params.name} + , m_savePath {params.savePath} + , m_downloadPath {params.downloadPath} + , m_category {params.category} + , m_tags {params.tags} + , m_ratioLimit {params.ratioLimit} + , m_seedingTimeLimit {params.seedingTimeLimit} + , m_inactiveSeedingTimeLimit {params.inactiveSeedingTimeLimit} + , m_operatingMode {params.operatingMode} + , m_contentLayout {params.contentLayout} + , m_hasFinishedStatus {params.hasFinishedStatus} + , m_hasFirstLastPiecePriority {params.firstLastPiecePriority} + , m_useAutoTMM {params.useAutoTMM} + , m_isStopped {params.stopped} + , m_sslParams {params.sslParameters} + , m_ltAddTorrentParams {params.ltAddTorrentParams} + , m_downloadLimit {cleanLimitValue(m_ltAddTorrentParams.download_limit)} + , m_uploadLimit {cleanLimitValue(m_ltAddTorrentParams.upload_limit)} { if (m_ltAddTorrentParams.ti) { @@ -369,13 +363,6 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession applyFirstLastPiecePriority(m_hasFirstLastPiecePriority); } -TorrentImpl::~TorrentImpl() = default; - -bool TorrentImpl::isValid() const -{ - return m_nativeHandle.is_valid(); -} - Session *TorrentImpl::session() const { return m_session; @@ -576,14 +563,6 @@ Path TorrentImpl::actualStorageLocation() const return Path(m_nativeStatus.save_path); } -void TorrentImpl::setAutoManaged(const bool enable) -{ - if (enable) - m_nativeHandle.set_flags(lt::torrent_flags::auto_managed); - else - m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); -} - Path TorrentImpl::makeActualPath(int index, const Path &path) const { Path actualPath = path; @@ -640,15 +619,13 @@ void TorrentImpl::addTrackers(QList trackers) trackers = QList(newTrackerSet.cbegin(), newTrackerSet.cend()); for (const TrackerEntry &tracker : asConst(trackers)) - { - m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier)); m_trackerEntryStatuses.append({tracker.url, tracker.tier}); - } std::sort(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end() - , [](const TrackerEntryStatus &left, const TrackerEntryStatus &right) { return left.tier < right.tier; }); + , [](const TrackerEntryStatus &lhs, const TrackerEntryStatus &rhs) { return lhs.tier < rhs.tier; }); + m_session->handleTorrentTrackersAdded(this, trackers); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::addTrackers, trackers); deferredRequestResumeData(); - m_session->handleTorrentTrackersAdded(this, trackers); } void TorrentImpl::removeTrackers(const QStringList &trackers) @@ -660,18 +637,17 @@ void TorrentImpl::removeTrackers(const QStringList &trackers) removedTrackers.removeOne(tracker); } - std::vector nativeTrackers; - nativeTrackers.reserve(m_trackerEntryStatuses.size()); - for (const TrackerEntryStatus &tracker : asConst(m_trackerEntryStatuses)) - nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); + if (removedTrackers.isEmpty()) + return; - if (!removedTrackers.isEmpty()) - { - m_nativeHandle.replace_trackers(nativeTrackers); + m_session->handleTorrentTrackersRemoved(this, removedTrackers); - deferredRequestResumeData(); - m_session->handleTorrentTrackersRemoved(this, removedTrackers); - } + QList trackerEntries; + trackerEntries.reserve(m_trackerEntryStatuses.size()); + for (const TrackerEntryStatus &tracker : asConst(m_trackerEntryStatuses)) + trackerEntries.emplaceBack(tracker.url, tracker.tier); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::replaceTrackers, trackerEntries); + deferredRequestResumeData(); } void TorrentImpl::replaceTrackers(QList trackers) @@ -684,25 +660,17 @@ void TorrentImpl::replaceTrackers(QList trackers) std::sort(trackers.begin(), trackers.end() , [](const TrackerEntry &left, const TrackerEntry &right) { return left.tier < right.tier; }); - std::vector nativeTrackers; - nativeTrackers.reserve(trackers.size()); m_trackerEntryStatuses.clear(); - for (const TrackerEntry &tracker : trackers) - { - nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier)); m_trackerEntryStatuses.append({tracker.url, tracker.tier}); - } - - m_nativeHandle.replace_trackers(nativeTrackers); + m_session->handleTorrentTrackersChanged(this); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::replaceTrackers, trackers); // Clear the peer list if it's a private torrent since // we do not want to keep connecting with peers from old tracker. if (isPrivate()) - clearPeers(); - + QMetaObject::invokeMethod(m_backend, &TorrentBackend::clearPeers); deferredRequestResumeData(); - m_session->handleTorrentTrackersChanged(this); } QList TorrentImpl::urlSeeds() const @@ -712,107 +680,54 @@ QList TorrentImpl::urlSeeds() const void TorrentImpl::addUrlSeeds(const QList &urlSeeds) { - m_session->invokeAsync([urlSeeds, session = m_session - , nativeHandle = m_nativeHandle - , thisTorrent = QPointer(this)] - { - try - { - const std::set nativeSeeds = nativeHandle.url_seeds(); - QList currentSeeds; - currentSeeds.reserve(static_cast(nativeSeeds.size())); - for (const std::string &urlSeed : nativeSeeds) - currentSeeds.append(QString::fromStdString(urlSeed)); - - QList addedUrlSeeds; - addedUrlSeeds.reserve(urlSeeds.size()); - - for (const QUrl &url : urlSeeds) - { - if (!currentSeeds.contains(url)) - { - nativeHandle.add_url_seed(url.toString().toStdString()); - addedUrlSeeds.append(url); - } - } + QList addedUrlSeeds; + addedUrlSeeds.reserve(urlSeeds.size()); + std::copy_if(urlSeeds.cbegin(), urlSeeds.cend(), std::back_inserter(addedUrlSeeds) + , [this](const QUrl &url) { return !m_urlSeeds.contains(url); }); + if (addedUrlSeeds.isEmpty()) + return; - currentSeeds.append(addedUrlSeeds); - session->invoke([session, thisTorrent, currentSeeds, addedUrlSeeds] - { - if (!thisTorrent) - return; + m_urlSeeds.append(addedUrlSeeds); + m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds); - thisTorrent->m_urlSeeds = currentSeeds; - if (!addedUrlSeeds.isEmpty()) - { - thisTorrent->deferredRequestResumeData(); - session->handleTorrentUrlSeedsAdded(thisTorrent, addedUrlSeeds); - } - }); - } - catch (const std::exception &) {} - }); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::addUrlSeeds, addedUrlSeeds); + deferredRequestResumeData(); } void TorrentImpl::removeUrlSeeds(const QList &urlSeeds) { - m_session->invokeAsync([urlSeeds, session = m_session - , nativeHandle = m_nativeHandle - , thisTorrent = QPointer(this)] + QList removedUrlSeeds; + removedUrlSeeds.reserve(urlSeeds.size()); + for (const QUrl &url : urlSeeds) { - try - { - const std::set nativeSeeds = nativeHandle.url_seeds(); - QList currentSeeds; - currentSeeds.reserve(static_cast(nativeSeeds.size())); - for (const std::string &urlSeed : nativeSeeds) - currentSeeds.append(QString::fromStdString(urlSeed)); - - QList removedUrlSeeds; - removedUrlSeeds.reserve(urlSeeds.size()); - - for (const QUrl &url : urlSeeds) - { - if (currentSeeds.removeOne(url)) - { - nativeHandle.remove_url_seed(url.toString().toStdString()); - removedUrlSeeds.append(url); - } - } - - session->invoke([session, thisTorrent, currentSeeds, removedUrlSeeds] - { - if (!thisTorrent) - return; + if (m_urlSeeds.removeOne(url)) + removedUrlSeeds.append(url); + } + if (removedUrlSeeds.isEmpty()) + return; - thisTorrent->m_urlSeeds = currentSeeds; + m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds); - if (!removedUrlSeeds.isEmpty()) - { - thisTorrent->deferredRequestResumeData(); - session->handleTorrentUrlSeedsRemoved(thisTorrent, removedUrlSeeds); - } - }); - } - catch (const std::exception &) {} - }); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::removeUrlSeeds, removedUrlSeeds); + deferredRequestResumeData(); } void TorrentImpl::clearPeers() { - m_nativeHandle.clear_peers(); + if (m_nativeHandle.is_valid()) + m_nativeHandle.clear_peers(); } bool TorrentImpl::connectPeer(const PeerAddress &peerAddress) { - lt::error_code ec; - const lt::address addr = lt::make_address(peerAddress.ip.toString().toStdString(), ec); - if (ec) return false; - - const lt::tcp::endpoint endpoint(addr, peerAddress.port); try { - m_nativeHandle.connect_peer(endpoint); + lt::error_code ec; + const lt::address addr = lt::make_address(peerAddress.ip.toString().toStdString(), ec); + if (ec) + throw lt::system_error(ec); + + m_nativeHandle.connect_peer({addr, peerAddress.port}); } catch (const lt::system_error &err) { @@ -832,7 +747,7 @@ bool TorrentImpl::needSaveResumeData() const void TorrentImpl::requestResumeData(const lt::resume_data_flags_t flags) { - m_nativeHandle.save_resume_data(flags); + QMetaObject::invokeMethod(m_backend, &TorrentBackend::requestResumeData, flags); m_deferredRequestResumeDataInvoked = false; m_session->handleTorrentResumeDataRequested(this); @@ -1461,6 +1376,9 @@ bool TorrentImpl::isLSDDisabled() const QList TorrentImpl::peers() const { + if (!m_nativeHandle.is_valid()) + return {}; + std::vector nativePeers; m_nativeHandle.get_peer_info(nativePeers); @@ -1483,18 +1401,26 @@ QBitArray TorrentImpl::downloadingPieces() const if (!hasMetadata()) return {}; - std::vector queue; - m_nativeHandle.get_download_queue(queue); + if (m_nativeHandle.is_valid()) + { + std::vector queue; + m_nativeHandle.get_download_queue(queue); - QBitArray result {piecesCount()}; - for (const lt::partial_piece_info &info : queue) - result.setBit(LT::toUnderlyingType(info.piece_index)); + QBitArray result {piecesCount()}; + for (const lt::partial_piece_info &info : queue) + result.setBit(LT::toUnderlyingType(info.piece_index)); - return result; + return result; + } + + return {}; } QList TorrentImpl::pieceAvailability() const { + if (!m_nativeHandle.is_valid()) + return QVector(piecesCount(), 0); + std::vector avail; m_nativeHandle.piece_availability(avail); @@ -1628,12 +1554,14 @@ bool TorrentImpl::setCategory(const QString &category) void TorrentImpl::forceReannounce(const int index) { - m_nativeHandle.force_reannounce(0, index); + if (m_nativeHandle.is_valid()) + m_nativeHandle.force_reannounce(0, index); } void TorrentImpl::forceDHTAnnounce() { - m_nativeHandle.force_dht_announce(); + if (m_nativeHandle.is_valid()) + m_nativeHandle.force_dht_announce(); } void TorrentImpl::forceRecheck() @@ -1641,19 +1569,26 @@ void TorrentImpl::forceRecheck() if (!hasMetadata()) return; - m_nativeHandle.force_recheck(); // We have to force update the cached state, otherwise someone will be able to get // an incorrect one during the interval until the cached state is updated in a regular way. m_nativeStatus.state = lt::torrent_status::checking_resume_data; + m_nativeStatus.pieces.clear_all(); + m_nativeStatus.num_pieces = 0; if (m_hasMissingFiles) { m_hasMissingFiles = false; if (!isStopped()) { - setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged); if (m_operatingMode == TorrentOperatingMode::Forced) + { + m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); m_nativeHandle.resume(); + } + else + { + m_nativeHandle.set_flags(lt::torrent_flags::auto_managed); + } } } @@ -1662,8 +1597,9 @@ void TorrentImpl::forceRecheck() m_completedFiles.fill(false); m_filesProgress.fill(0); m_pieces.fill(false); - m_nativeStatus.pieces.clear_all(); - m_nativeStatus.num_pieces = 0; + + if (m_nativeHandle.is_valid()) + m_nativeHandle.force_recheck(); if (isStopped()) { @@ -1676,17 +1612,19 @@ void TorrentImpl::forceRecheck() void TorrentImpl::setSequentialDownload(const bool enable) { if (enable) - { - m_nativeHandle.set_flags(lt::torrent_flags::sequential_download); m_nativeStatus.flags |= lt::torrent_flags::sequential_download; // prevent return cached value - } else - { - m_nativeHandle.unset_flags(lt::torrent_flags::sequential_download); m_nativeStatus.flags &= ~lt::torrent_flags::sequential_download; // prevent return cached value - } - deferredRequestResumeData(); + if (m_nativeHandle.is_valid()) + { + if (enable) + m_nativeHandle.set_flags(lt::torrent_flags::sequential_download); + else + m_nativeHandle.unset_flags(lt::torrent_flags::sequential_download); + + deferredRequestResumeData(); + } } void TorrentImpl::setFirstLastPiecePriority(const bool enabled) @@ -1708,6 +1646,9 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled) { Q_ASSERT(hasMetadata()); + if (!m_nativeHandle.is_valid()) + return; + // Download first and last pieces first for every file in the torrent auto piecePriorities = std::vector(m_torrentInfo.piecesCount(), LT::toNative(DownloadPriority::Ignored)); @@ -1921,27 +1862,20 @@ void TorrentImpl::stop() { m_stopCondition = StopCondition::None; m_isStopped = true; + m_payloadRateMonitor.reset(); deferredRequestResumeData(); m_session->handleTorrentStopped(this); } - if (m_maintenanceJob == MaintenanceJob::None) + if ((m_maintenanceJob == MaintenanceJob::None) && m_nativeHandle.is_valid()) { - setAutoManaged(false); + m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); m_nativeHandle.pause(); - - m_payloadRateMonitor.reset(); } } void TorrentImpl::start(const TorrentOperatingMode mode) { - if (hasError()) - { - m_nativeHandle.clear_error(); - m_nativeHandle.unset_flags(lt::torrent_flags::upload_mode); - } - m_operatingMode = mode; if (m_hasMissingFiles) @@ -1960,11 +1894,20 @@ void TorrentImpl::start(const TorrentOperatingMode mode) m_session->handleTorrentStarted(this); } - if (m_maintenanceJob == MaintenanceJob::None) + if ((m_maintenanceJob == MaintenanceJob::None) && m_nativeHandle.is_valid()) { - setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged); + m_nativeHandle.clear_error(); + m_nativeHandle.unset_flags(lt::torrent_flags::upload_mode); + if (m_operatingMode == TorrentOperatingMode::Forced) + { + m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); m_nativeHandle.resume(); + } + else + { + m_nativeHandle.set_flags(lt::torrent_flags::auto_managed); + } } } @@ -2080,9 +2023,18 @@ void TorrentImpl::handleTorrentCheckedAlert([[maybe_unused]] const lt::torrent_c { // torrent is internally paused using NativeTorrentExtension after files checked // so we need to resume it if there is no corresponding "stop condition" set - setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged); - if (m_operatingMode == TorrentOperatingMode::Forced) - m_nativeHandle.resume(); + if (m_nativeHandle.is_valid()) + { + if (m_operatingMode == TorrentOperatingMode::Forced) + { + m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed); + m_nativeHandle.resume(); + } + else + { + m_nativeHandle.set_flags(lt::torrent_flags::auto_managed); + } + } } } @@ -2735,8 +2687,10 @@ void TorrentImpl::setUploadLimit(const int limit) return; m_uploadLimit = cleanValue; - m_nativeHandle.set_upload_limit(m_uploadLimit); deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + m_nativeHandle.set_upload_limit(m_uploadLimit); } void TorrentImpl::setDownloadLimit(const int limit) @@ -2746,8 +2700,10 @@ void TorrentImpl::setDownloadLimit(const int limit) return; m_downloadLimit = cleanValue; - m_nativeHandle.set_download_limit(m_downloadLimit); deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + m_nativeHandle.set_download_limit(m_downloadLimit); } void TorrentImpl::setSuperSeeding(const bool enable) @@ -2756,11 +2712,19 @@ void TorrentImpl::setSuperSeeding(const bool enable) return; if (enable) - m_nativeHandle.set_flags(lt::torrent_flags::super_seeding); + m_nativeStatus.flags |= lt::torrent_flags::super_seeding; else - m_nativeHandle.unset_flags(lt::torrent_flags::super_seeding); + m_nativeStatus.flags &= ~lt::torrent_flags::super_seeding; deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + { + if (enable) + m_nativeHandle.set_flags(lt::torrent_flags::super_seeding); + else + m_nativeHandle.unset_flags(lt::torrent_flags::super_seeding); + } } void TorrentImpl::setDHTDisabled(const bool disable) @@ -2769,11 +2733,19 @@ void TorrentImpl::setDHTDisabled(const bool disable) return; if (disable) - m_nativeHandle.set_flags(lt::torrent_flags::disable_dht); + m_nativeStatus.flags |= lt::torrent_flags::disable_dht; else - m_nativeHandle.unset_flags(lt::torrent_flags::disable_dht); + m_nativeStatus.flags &= ~lt::torrent_flags::disable_dht; deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + { + if (disable) + m_nativeHandle.set_flags(lt::torrent_flags::disable_dht); + else + m_nativeHandle.unset_flags(lt::torrent_flags::disable_dht); + } } void TorrentImpl::setPEXDisabled(const bool disable) @@ -2782,11 +2754,19 @@ void TorrentImpl::setPEXDisabled(const bool disable) return; if (disable) - m_nativeHandle.set_flags(lt::torrent_flags::disable_pex); + m_nativeStatus.flags |= lt::torrent_flags::disable_pex; else - m_nativeHandle.unset_flags(lt::torrent_flags::disable_pex); + m_nativeStatus.flags &= ~lt::torrent_flags::disable_pex; deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + { + if (disable) + m_nativeHandle.set_flags(lt::torrent_flags::disable_pex); + else + m_nativeHandle.unset_flags(lt::torrent_flags::disable_pex); + } } void TorrentImpl::setLSDDisabled(const bool disable) @@ -2795,16 +2775,25 @@ void TorrentImpl::setLSDDisabled(const bool disable) return; if (disable) - m_nativeHandle.set_flags(lt::torrent_flags::disable_lsd); + m_nativeStatus.flags |= lt::torrent_flags::disable_lsd; else - m_nativeHandle.unset_flags(lt::torrent_flags::disable_lsd); + m_nativeStatus.flags &= ~lt::torrent_flags::disable_lsd; deferredRequestResumeData(); + + if (m_nativeHandle.is_valid()) + { + if (disable) + m_nativeHandle.set_flags(lt::torrent_flags::disable_lsd); + else + m_nativeHandle.unset_flags(lt::torrent_flags::disable_lsd); + } } void TorrentImpl::flushCache() const { - m_nativeHandle.flush_cache(); + if (m_nativeHandle.is_valid()) + m_nativeHandle.flush_cache(); } QString TorrentImpl::createMagnetURI() const diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 9b0d4df2a7b..0518d734615 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-2023 Vladimir Golovnev + * Copyright (C) 2015-2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -60,6 +60,7 @@ namespace BitTorrent { class SessionImpl; + class TorrentBackend; struct LoadTorrentParams; enum class MoveStorageMode @@ -94,11 +95,8 @@ namespace BitTorrent Q_DISABLE_COPY_MOVE(TorrentImpl) public: - TorrentImpl(SessionImpl *session, lt::session *nativeSession - , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms); - ~TorrentImpl() override; - - bool isValid() const; + TorrentImpl(SessionImpl *session, TorrentBackend *backend, lt::session *nativeSession + , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms); Session *session() const override; @@ -309,8 +307,6 @@ namespace BitTorrent bool isMoveInProgress() const; - void setAutoManaged(bool enable); - Path makeActualPath(int index, const Path &path) const; Path makeUserPath(const Path &path) const; void adjustStorageLocation(); @@ -329,6 +325,7 @@ namespace BitTorrent void invokeAsync(Func func, Callback resultHandler) const; SessionImpl *const m_session = nullptr; + TorrentBackend *m_backend = nullptr; lt::session *m_nativeSession = nullptr; lt::torrent_handle m_nativeHandle; mutable lt::torrent_status m_nativeStatus;