diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 4de4c860da2..e2d1663990a 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -419,6 +419,8 @@ namespace BitTorrent virtual void setBannedIPs(const QStringList &newList) = 0; virtual ResumeDataStorageType resumeDataStorageType() const = 0; virtual void setResumeDataStorageType(ResumeDataStorageType type) = 0; + virtual bool isMergeTrackersEnabled() const = 0; + virtual void setMergeTrackersEnabled(bool enabled) = 0; virtual bool isRestored() const = 0; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index d3c2304a47f..df81d241048 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -523,6 +523,7 @@ SessionImpl::SessionImpl(QObject *parent) , m_excludedFileNames(BITTORRENT_SESSION_KEY(u"ExcludedFileNames"_s)) , m_bannedIPs(u"State/BannedIPs"_s, QStringList(), Algorithm::sorted) , m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_s), ResumeDataStorageType::Legacy) + , m_isMergeTrackersEnabled(BITTORRENT_KEY(u"MergeTrackersEnabled"_s), false) , m_isI2PEnabled {BITTORRENT_SESSION_KEY(u"I2P/Enabled"_s), false} , m_I2PAddress {BITTORRENT_SESSION_KEY(u"I2P/Address"_s), u"127.0.0.1"_s} , m_I2PPort {BITTORRENT_SESSION_KEY(u"I2P/Port"_s), 7656} @@ -2677,10 +2678,31 @@ bool SessionImpl::addTorrent_impl(const std::variant &so if (Torrent *torrent = findTorrent(infoHash); torrent) { + // a duplicate torrent is being added + if (torrent->isPrivate()) + return false; + if (hasMetadata) { // Trying to set metadata to existing torrent in case if it has none torrent->setMetadata(std::get(source)); + + const TorrentInfo &torrentInfo = std::get(source); + + if (torrentInfo.isPrivate()) + return false; + + // merge trackers and web seeds + torrent->addTrackers(torrentInfo.trackers()); + torrent->addUrlSeeds(torrentInfo.urlSeeds()); + } + else + { + const MagnetUri &magnetUri = std::get(source); + + // merge trackers and web seeds + torrent->addTrackers(magnetUri.trackers()); + torrent->addUrlSeeds(magnetUri.urlSeeds()); } return false; @@ -3904,6 +3926,16 @@ void SessionImpl::setResumeDataStorageType(const ResumeDataStorageType type) m_resumeDataStorageType = type; } +bool SessionImpl::isMergeTrackersEnabled() const +{ + return m_isMergeTrackersEnabled; +} + +void SessionImpl::setMergeTrackersEnabled(const bool enabled) +{ + m_isMergeTrackersEnabled = enabled; +} + QStringList SessionImpl::bannedIPs() const { return m_bannedIPs; diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 2df338bcd21..227ae7d5c54 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -398,6 +398,8 @@ namespace BitTorrent void setBannedIPs(const QStringList &newList) override; ResumeDataStorageType resumeDataStorageType() const override; void setResumeDataStorageType(ResumeDataStorageType type) override; + bool isMergeTrackersEnabled() const override; + void setMergeTrackersEnabled(bool enabled) override; bool isRestored() const override; @@ -704,6 +706,7 @@ namespace BitTorrent CachedSettingValue m_excludedFileNames; CachedSettingValue m_bannedIPs; CachedSettingValue m_resumeDataStorageType; + CachedSettingValue m_isMergeTrackersEnabled; CachedSettingValue m_isI2PEnabled; CachedSettingValue m_I2PAddress; CachedSettingValue m_I2PPort; diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 133333a1017..48a128ed7a5 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -1508,6 +1508,19 @@ void Preferences::setConfirmPauseAndResumeAll(const bool enabled) setValue(u"GUI/ConfirmActions/PauseAndResumeAllTorrents"_s, enabled); } +bool Preferences::confirmMergeTrackers() const +{ + return value(u"GUI/ConfirmActions/MergeTrackers"_s, true); +} + +void Preferences::setConfirmMergeTrackers(const bool enabled) +{ + if (enabled == confirmMergeTrackers()) + return; + + setValue(u"GUI/ConfirmActions/MergeTrackers"_s, enabled); +} + #ifndef Q_OS_MACOS TrayIcon::Style Preferences::trayIconStyle() const { diff --git a/src/base/preferences.h b/src/base/preferences.h index b3539b781b5..5bb0950ab04 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -317,6 +317,8 @@ class Preferences final : public QObject void setConfirmRemoveAllTags(bool enabled); bool confirmPauseAndResumeAll() const; void setConfirmPauseAndResumeAll(bool enabled); + bool confirmMergeTrackers() const; + void setConfirmMergeTrackers(bool enabled); #ifndef Q_OS_MACOS bool systemTrayEnabled() const; void setSystemTrayEnabled(bool enabled); diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index e91c8417732..4b092be34e2 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -539,9 +539,10 @@ bool AddNewTorrentDialog::loadTorrentImpl() const BitTorrent::InfoHash infoHash = m_torrentInfo.infoHash(); // Prevent showing the dialog if download is already present - if (BitTorrent::Session::instance()->isKnownTorrent(infoHash)) + const auto *btSession = BitTorrent::Session::instance(); + if (btSession->isKnownTorrent(infoHash)) { - BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(infoHash); + BitTorrent::Torrent *const torrent = btSession->findTorrent(infoHash); if (torrent) { // Trying to set metadata to existing torrent in case if it has none @@ -553,10 +554,16 @@ bool AddNewTorrentDialog::loadTorrentImpl() } else { - const QMessageBox::StandardButton btn = RaisedMessageBox::question(this, tr("Torrent is already present") - , tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name()) - , (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes); - if (btn == QMessageBox::Yes) + bool mergeTrackers = btSession->isMergeTrackersEnabled(); + if (Preferences::instance()->confirmMergeTrackers()) + { + const QMessageBox::StandardButton btn = RaisedMessageBox::question(this, tr("Torrent is already present") + , tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name()) + , (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes); + mergeTrackers = (btn == QMessageBox::Yes); + } + + if (mergeTrackers) { torrent->addTrackers(m_torrentInfo.trackers()); torrent->addUrlSeeds(m_torrentInfo.urlSeeds()); @@ -592,9 +599,10 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) const BitTorrent::InfoHash infoHash = magnetUri.infoHash(); // Prevent showing the dialog if download is already present - if (BitTorrent::Session::instance()->isKnownTorrent(infoHash)) + auto *btSession = BitTorrent::Session::instance(); + if (btSession->isKnownTorrent(infoHash)) { - BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->findTorrent(infoHash); + BitTorrent::Torrent *const torrent = btSession->findTorrent(infoHash); if (torrent) { if (torrent->isPrivate()) @@ -603,10 +611,16 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) } else { - const QMessageBox::StandardButton btn = RaisedMessageBox::question(this, tr("Torrent is already present") - , tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name()) - , (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes); - if (btn == QMessageBox::Yes) + bool mergeTrackers = btSession->isMergeTrackersEnabled(); + if (Preferences::instance()->confirmMergeTrackers()) + { + const QMessageBox::StandardButton btn = RaisedMessageBox::question(this, tr("Torrent is already present") + , tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name()) + , (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes); + mergeTrackers = (btn == QMessageBox::Yes); + } + + if (mergeTrackers) { torrent->addTrackers(magnetUri.trackers()); torrent->addUrlSeeds(magnetUri.urlSeeds()); @@ -621,7 +635,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) return false; } - connect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataDownloaded, this, &AddNewTorrentDialog::updateMetadata); + connect(btSession, &BitTorrent::Session::metadataDownloaded, this, &AddNewTorrentDialog::updateMetadata); // Set dialog title const QString torrentName = magnetUri.name(); @@ -630,7 +644,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri) updateDiskSpaceLabel(); TMMChanged(m_ui->comboTTM->currentIndex()); - BitTorrent::Session::instance()->downloadMetadata(magnetUri); + btSession->downloadMetadata(magnetUri); setMetadataProgressIndicator(true, tr("Retrieving metadata...")); m_ui->labelInfohash1Data->setText(magnetUri.infoHash().v1().isValid() ? magnetUri.infoHash().v1().toString() : tr("N/A")); m_ui->labelInfohash2Data->setText(magnetUri.infoHash().v2().isValid() ? magnetUri.infoHash().v2().toString() : tr("N/A")); diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index e3898f652c4..c44e71bce05 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -504,6 +504,9 @@ void OptionsDialog::loadDownloadsTabOptions() m_ui->stopConditionLabel->setEnabled(!m_ui->checkStartPaused->isChecked()); m_ui->stopConditionComboBox->setEnabled(!m_ui->checkStartPaused->isChecked()); + m_ui->checkMergeTrackers->setChecked(session->isMergeTrackersEnabled()); + m_ui->checkConfirmMergeTrackers->setChecked(pref->confirmMergeTrackers()); + const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode(); m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never); m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always); @@ -619,6 +622,8 @@ void OptionsDialog::loadDownloadsTabOptions() m_ui->stopConditionComboBox->setEnabled(!checked); }); connect(m_ui->stopConditionComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + connect(m_ui->checkMergeTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkConfirmMergeTrackers, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -684,6 +689,8 @@ void OptionsDialog::saveDownloadsTabOptions() const TorrentFileGuard::setAutoDeleteMode(!m_ui->deleteTorrentBox->isChecked() ? TorrentFileGuard::Never : !m_ui->deleteCancelledTorrentBox->isChecked() ? TorrentFileGuard::IfAdded : TorrentFileGuard::Always); + session->setMergeTrackersEnabled(m_ui->checkMergeTrackers->isChecked()); + pref->setConfirmMergeTrackers(m_ui->checkConfirmMergeTrackers->isChecked()); session->setPreallocationEnabled(preAllocateAllFiles()); session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked()); diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 8be4a9fd199..2d032f93bcb 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -950,6 +950,41 @@ + + + + When duplicate torrent is being added + + + + + + Whether trackers should be merged to existing torrent + + + Merge trackers to existing torrent + + + false + + + + + + + Shows a confirmation dialog upon merging trackers to existing torrent + + + Confirm merging trackers + + + true + + + + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 1bb67cbe74c..860634b8f4b 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -126,6 +126,7 @@ void AppController::preferencesAction() data[u"add_to_top_of_queue"_s] = session->isAddTorrentToQueueTop(); data[u"start_paused_enabled"_s] = session->isAddTorrentPaused(); data[u"torrent_stop_condition"_s] = Utils::String::fromEnum(session->torrentStopCondition()); + data[u"merge_trackers"_s] = session->isMergeTrackersEnabled(); data[u"auto_delete_mode"_s] = static_cast(TorrentFileGuard::autoDeleteMode()); data[u"preallocate_all"_s] = session->isPreallocationEnabled(); data[u"incomplete_files_ext"_s] = session->isAppendExtensionEnabled(); @@ -477,6 +478,8 @@ void AppController::setPreferencesAction() session->setAddTorrentPaused(it.value().toBool()); if (hasKey(u"torrent_stop_condition"_s)) session->setTorrentStopCondition(Utils::String::toEnum(it.value().toString(), BitTorrent::Torrent::StopCondition::None)); + if (hasKey(u"merge_trackers"_s)) + session->setMergeTrackersEnabled(it.value().toBool()); if (hasKey(u"auto_delete_mode"_s)) TorrentFileGuard::setAutoDeleteMode(static_cast(it.value().toInt()));