From 4bd50672e88e8d1703d9c8ab9007947e135a8d7f Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Tue, 25 Mar 2025 09:13:15 +0300 Subject: [PATCH] Improve add torrent error handling PR #22468. --- src/app/application.cpp | 4 +-- src/base/CMakeLists.txt | 1 + src/base/addtorrentmanager.cpp | 6 ++-- src/base/addtorrentmanager.h | 5 +-- src/base/bittorrent/addtorrenterror.h | 49 +++++++++++++++++++++++++++ src/base/bittorrent/session.h | 3 +- src/base/bittorrent/sessionimpl.cpp | 19 ++++++++--- src/base/rss/rss_autodownloader.cpp | 2 +- src/base/rss/rss_autodownloader.h | 3 +- 9 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 src/base/bittorrent/addtorrenterror.h diff --git a/src/app/application.cpp b/src/app/application.cpp index 69d8af646..90e785e1a 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -929,10 +929,10 @@ int Application::exec() m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name())); }); connect(m_addTorrentManager, &AddTorrentManager::addTorrentFailed, this - , [this](const QString &source, const QString &reason) + , [this](const QString &source, const BitTorrent::AddTorrentError &reason) { m_desktopIntegration->showNotification(tr("Add torrent failed") - , tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason)); + , tr("Couldn't add torrent '%1', reason: %2.").arg(source, reason.message)); }); disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog); diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 7998d3501..fa4123251 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(qbt_base STATIC applicationcomponent.h asyncfilestorage.h bittorrent/abstractfilestorage.h + bittorrent/addtorrenterror.h bittorrent/addtorrentparams.h bittorrent/announcetimepoint.h bittorrent/bandwidthscheduler.h diff --git a/src/base/addtorrentmanager.cpp b/src/base/addtorrentmanager.cpp index ce446a72b..14332098a 100644 --- a/src/base/addtorrentmanager.cpp +++ b/src/base/addtorrentmanager.cpp @@ -140,7 +140,7 @@ void AddTorrentManager::onSessionTorrentAdded(BitTorrent::Torrent *torrent) } } -void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason) +void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason) { if (const QString source = m_sourcesByInfoHash.take(infoHash); !source.isEmpty()) { @@ -154,7 +154,7 @@ void AddTorrentManager::onSessionAddTorrentFailed(const BitTorrent::InfoHash &in void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QString &reason) { LogMsg(tr("Failed to add torrent. Source: \"%1\". Reason: \"%2\"").arg(source, reason), Log::WARNING); - emit addTorrentFailed(source, reason); + emit addTorrentFailed(source, {BitTorrent::AddTorrentError::Other, reason}); } void AddTorrentManager::handleDuplicateTorrent(const QString &source @@ -187,7 +187,7 @@ void AddTorrentManager::handleDuplicateTorrent(const QString &source LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: %2. Result: %3") .arg(source, existingTorrent->name(), message)); - emit addTorrentFailed(source, message); + emit addTorrentFailed(source, {BitTorrent::AddTorrentError::DuplicateTorrent, message}); } void AddTorrentManager::setTorrentFileGuard(const QString &source, std::shared_ptr torrentFileGuard) diff --git a/src/base/addtorrentmanager.h b/src/base/addtorrentmanager.h index 0e2fc6f49..0621be7a2 100644 --- a/src/base/addtorrentmanager.h +++ b/src/base/addtorrentmanager.h @@ -35,6 +35,7 @@ #include #include "base/applicationcomponent.h" +#include "base/bittorrent/addtorrenterror.h" #include "base/bittorrent/addtorrentparams.h" #include "base/torrentfileguard.h" @@ -66,7 +67,7 @@ public: signals: void torrentAdded(const QString &source, BitTorrent::Torrent *torrent); - void addTorrentFailed(const QString &source, const QString &reason); + void addTorrentFailed(const QString &source, const BitTorrent::AddTorrentError &reason); protected: bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr @@ -79,7 +80,7 @@ protected: private: void onDownloadFinished(const Net::DownloadResult &result); void onSessionTorrentAdded(BitTorrent::Torrent *torrent); - void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const QString &reason); + void onSessionAddTorrentFailed(const BitTorrent::InfoHash &infoHash, const BitTorrent::AddTorrentError &reason); bool processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr , const BitTorrent::AddTorrentParams &addTorrentParams); diff --git a/src/base/bittorrent/addtorrenterror.h b/src/base/bittorrent/addtorrenterror.h new file mode 100644 index 000000000..a0afb4705 --- /dev/null +++ b/src/base/bittorrent/addtorrenterror.h @@ -0,0 +1,49 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2025 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 + +namespace BitTorrent +{ + struct AddTorrentError + { + enum Kind + { + DuplicateTorrent, + Other + }; + + Kind kind = Other; + QString message; + }; +} + +Q_DECLARE_METATYPE(BitTorrent::AddTorrentError) diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 52d94c18e..95c7ac4be 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -34,6 +34,7 @@ #include "base/pathfwd.h" #include "base/tagset.h" +#include "addtorrenterror.h" #include "addtorrentparams.h" #include "categoryoptions.h" #include "sharelimitaction.h" @@ -485,7 +486,7 @@ namespace BitTorrent signals: void startupProgressUpdated(int progress); - void addTorrentFailed(const InfoHash &infoHash, const QString &reason); + void addTorrentFailed(const InfoHash &infoHash, const AddTorrentError &reason); void allTorrentsFinished(); void categoryAdded(const QString &categoryName); void categoryRemoved(const QString &categoryName); diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index f183ac7db..9c4922c4e 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -2772,7 +2772,10 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr // We should not add the torrent if it is already // processed or is pending to add to session if (m_loadingTorrents.contains(id) || (infoHash.isHybrid() && m_loadingTorrents.contains(altID))) + { + emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, tr("Duplicate torrent")}); return false; + } if (Torrent *torrent = findTorrent(infoHash)) { @@ -2786,16 +2789,20 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr if (!isMergeTrackersEnabled()) { + const QString message = tr("Merging of trackers is disabled"); LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2") - .arg(torrent->name(), tr("Merging of trackers is disabled"))); + .arg(torrent->name(), message)); + emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message}); return false; } const bool isPrivate = torrent->isPrivate() || (hasMetadata && source.info()->isPrivate()); if (isPrivate) { + const QString message = tr("Trackers cannot be merged because it is a private torrent"); LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2") - .arg(torrent->name(), tr("Trackers cannot be merged because it is a private torrent"))); + .arg(torrent->name(), message)); + emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message}); return false; } @@ -2803,8 +2810,10 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr torrent->addTrackers(source.trackers()); torrent->addUrlSeeds(source.urlSeeds()); + const QString message = tr("Trackers are merged from new source"); LogMsg(tr("Detected an attempt to add a duplicate torrent. Existing torrent: %1. Result: %2") - .arg(torrent->name(), tr("Trackers are merged from new source"))); + .arg(torrent->name(), message)); + emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, message}); return false; } @@ -5747,7 +5756,9 @@ void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert) if (const auto loadingTorrentsIter = m_loadingTorrents.constFind(TorrentID::fromInfoHash(infoHash)) ; loadingTorrentsIter != m_loadingTorrents.cend()) { - emit addTorrentFailed(infoHash, msg); + const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent) + ? AddTorrentError::DuplicateTorrent : AddTorrentError::Other; + emit addTorrentFailed(infoHash, {errorKind, msg}); m_loadingTorrents.erase(loadingTorrentsIter); } else if (const auto downloadedMetadataIter = m_downloadedMetadata.constFind(TorrentID::fromInfoHash(infoHash)) diff --git a/src/base/rss/rss_autodownloader.cpp b/src/base/rss/rss_autodownloader.cpp index a365a53ef..864c777e6 100644 --- a/src/base/rss/rss_autodownloader.cpp +++ b/src/base/rss/rss_autodownloader.cpp @@ -375,7 +375,7 @@ void AutoDownloader::handleTorrentAdded(const QString &source) } } -void AutoDownloader::handleAddTorrentFailed(const QString &source) +void AutoDownloader::handleAddTorrentFailed(const QString &source, [[maybe_unused]] const BitTorrent::AddTorrentError &error) { m_waitingJobs.remove(source); // TODO: Re-schedule job here. diff --git a/src/base/rss/rss_autodownloader.h b/src/base/rss/rss_autodownloader.h index e69877fb8..dcd752bcf 100644 --- a/src/base/rss/rss_autodownloader.h +++ b/src/base/rss/rss_autodownloader.h @@ -37,6 +37,7 @@ #include #include "base/applicationcomponent.h" +#include "base/bittorrent/addtorrenterror.h" #include "base/exceptions.h" #include "base/settingvalue.h" #include "base/utils/thread.h" @@ -111,7 +112,7 @@ namespace RSS private slots: void process(); void handleTorrentAdded(const QString &source); - void handleAddTorrentFailed(const QString &url); + void handleAddTorrentFailed(const QString &url, const BitTorrent::AddTorrentError &error); void handleNewArticle(const Article *article); void handleFeedURLChanged(Feed *feed, const QString &oldURL);