From 9e7f50517e6e954bf880ce1e202426daea8e4b82 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 24 Jun 2019 23:37:31 +0800 Subject: [PATCH] Add "Tracker entries" dialog --- src/base/bittorrent/trackerentry.cpp | 8 +- src/base/bittorrent/trackerentry.h | 3 + src/gui/CMakeLists.txt | 3 + src/gui/gui.pri | 3 + src/gui/trackerentriesdialog.cpp | 112 +++++++++++++++++++++++++++ src/gui/trackerentriesdialog.h | 64 +++++++++++++++ src/gui/trackerentriesdialog.ui | 43 ++++++++++ src/gui/transferlistwidget.cpp | 41 ++++++++++ src/gui/transferlistwidget.h | 1 + 9 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/gui/trackerentriesdialog.cpp create mode 100644 src/gui/trackerentriesdialog.h create mode 100644 src/gui/trackerentriesdialog.ui diff --git a/src/base/bittorrent/trackerentry.cpp b/src/base/bittorrent/trackerentry.cpp index 9946f3ae7..3fc809197 100644 --- a/src/base/bittorrent/trackerentry.cpp +++ b/src/base/bittorrent/trackerentry.cpp @@ -154,5 +154,11 @@ const lt::announce_entry &TrackerEntry::nativeEntry() const bool BitTorrent::operator==(const TrackerEntry &left, const TrackerEntry &right) { - return (QUrl(left.url()) == QUrl(right.url())); + return ((left.tier() == right.tier()) + && QUrl(left.url()) == QUrl(right.url())); +} + +uint BitTorrent::qHash(const TrackerEntry &key, const uint seed) +{ + return (::qHash(key.url(), seed) ^ key.tier()); } diff --git a/src/base/bittorrent/trackerentry.h b/src/base/bittorrent/trackerentry.h index b427aa455..22f7406d1 100644 --- a/src/base/bittorrent/trackerentry.h +++ b/src/base/bittorrent/trackerentry.h @@ -31,6 +31,8 @@ #include +#include + class QString; namespace BitTorrent @@ -70,6 +72,7 @@ namespace BitTorrent }; bool operator==(const TrackerEntry &left, const TrackerEntry &right); + uint qHash(const TrackerEntry &key, uint seed); } #endif // BITTORRENT_TRACKERENTRY_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 8c4b73ce7..22f12945e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -52,6 +52,7 @@ torrentcontentmodelfolder.h torrentcontentmodelitem.h torrentcontenttreeview.h torrentcreatordialog.h +trackerentriesdialog.h transferlistdelegate.h transferlistfilterswidget.h transferlistmodel.h @@ -97,6 +98,7 @@ torrentcontentmodelfolder.cpp torrentcontentmodelitem.cpp torrentcontenttreeview.cpp torrentcreatordialog.cpp +trackerentriesdialog.cpp transferlistdelegate.cpp transferlistfilterswidget.cpp transferlistmodel.cpp @@ -123,6 +125,7 @@ speedlimitdialog.ui statsdialog.ui torrentcategorydialog.ui torrentcreatordialog.ui +trackerentriesdialog.ui updownratiodialog.ui ) diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 587bf1236..c1439f151 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -57,6 +57,7 @@ HEADERS += \ $$PWD/torrentcontentmodelitem.h \ $$PWD/torrentcontenttreeview.h \ $$PWD/torrentcreatordialog.h \ + $$PWD/trackerentriesdialog.h \ $$PWD/transferlistdelegate.h \ $$PWD/transferlistfilterswidget.h \ $$PWD/transferlistmodel.h \ @@ -113,6 +114,7 @@ SOURCES += \ $$PWD/torrentcontentmodelitem.cpp \ $$PWD/torrentcontenttreeview.cpp \ $$PWD/torrentcreatordialog.cpp \ + $$PWD/trackerentriesdialog.cpp \ $$PWD/transferlistdelegate.cpp \ $$PWD/transferlistfilterswidget.cpp \ $$PWD/transferlistmodel.cpp \ @@ -155,6 +157,7 @@ FORMS += \ $$PWD/statsdialog.ui \ $$PWD/torrentcategorydialog.ui \ $$PWD/torrentcreatordialog.ui \ + $$PWD/trackerentriesdialog.ui \ $$PWD/updownratiodialog.ui RESOURCES += $$PWD/about.qrc diff --git a/src/gui/trackerentriesdialog.cpp b/src/gui/trackerentriesdialog.cpp new file mode 100644 index 000000000..f1dd8b33d --- /dev/null +++ b/src/gui/trackerentriesdialog.cpp @@ -0,0 +1,112 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2019 Mike Tzou (Chocobo1) + * + * 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 "trackerentriesdialog.h" + +#include + +#include + +#include "base/bittorrent/trackerentry.h" +#include "ui_trackerentriesdialog.h" +#include "utils.h" + +#define SETTINGS_KEY(name) "TrackerEntriesDialog/" name + +TrackerEntriesDialog::TrackerEntriesDialog(QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::TrackerEntriesDialog) + , m_storeDialogSize(SETTINGS_KEY("Dimension")) +{ + m_ui->setupUi(this); + + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + loadSettings(); +} + +TrackerEntriesDialog::~TrackerEntriesDialog() +{ + saveSettings(); + + delete m_ui; +} + +void TrackerEntriesDialog::setTrackers(const QVector &trackers) +{ + int maxTier = -1; + QHash tiers; // + + for (const BitTorrent::TrackerEntry &entry : trackers) { + tiers[entry.tier()] += (entry.url() + '\n'); + maxTier = std::max(maxTier, entry.tier()); + } + + QString text = tiers.value(0); + + for (int i = 1; i <= maxTier; ++i) + text += ('\n' + tiers.value(i)); + + m_ui->plainTextEdit->setPlainText(text); +} + +QVector TrackerEntriesDialog::trackers() const +{ + const QString plainText = m_ui->plainTextEdit->toPlainText(); + const QVector lines = plainText.splitRef('\n'); + + QVector entries; + entries.reserve(lines.size()); + + int tier = 0; + for (QStringRef line : lines) { + line = line.trimmed(); + + if (line.isEmpty()) { + ++tier; + continue; + } + + BitTorrent::TrackerEntry entry {line.toString()}; + entry.setTier(tier); + entries.append(entry); + } + + return entries; +} + +void TrackerEntriesDialog::saveSettings() +{ + m_storeDialogSize = size(); +} + +void TrackerEntriesDialog::loadSettings() +{ + Utils::Gui::resize(this, m_storeDialogSize); +} diff --git a/src/gui/trackerentriesdialog.h b/src/gui/trackerentriesdialog.h new file mode 100644 index 000000000..d85d0273c --- /dev/null +++ b/src/gui/trackerentriesdialog.h @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2019 Mike Tzou (Chocobo1) + * + * 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 "base/settingvalue.h" + +namespace BitTorrent +{ + class TrackerEntry; +} + +namespace Ui +{ + class TrackerEntriesDialog; +} + +class TrackerEntriesDialog : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY(TrackerEntriesDialog) + +public: + explicit TrackerEntriesDialog(QWidget *parent); + ~TrackerEntriesDialog() override; + + void setTrackers(const QVector &trackers); + QVector trackers() const; + +private: + void saveSettings(); + void loadSettings(); + + Ui::TrackerEntriesDialog *m_ui; + CachedSettingValue m_storeDialogSize; +}; diff --git a/src/gui/trackerentriesdialog.ui b/src/gui/trackerentriesdialog.ui new file mode 100644 index 000000000..74bc23dee --- /dev/null +++ b/src/gui/trackerentriesdialog.ui @@ -0,0 +1,43 @@ + + + TrackerEntriesDialog + + + + 0 + 0 + 506 + 500 + + + + Edit trackers + + + + + + One tracker URL per line. + +- You can split the trackers into groups by inserting blank lines. +- All trackers within the same group will belong to the same tier. +- The group on top will be tier 0, the next group tier 1 and so on. +- Below will show the common subset of trackers of the selected torrents. + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 853c97f46..290f70ce5 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -28,6 +28,8 @@ #include "transferlistwidget.h" +#include + #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +47,7 @@ #include "base/bittorrent/session.h" #include "base/bittorrent/torrenthandle.h" +#include "base/bittorrent/trackerentry.h" #include "base/global.h" #include "base/logger.h" #include "base/preferences.h" @@ -59,6 +63,7 @@ #include "previewselectdialog.h" #include "speedlimitdialog.h" #include "torrentcategorydialog.h" +#include "trackerentriesdialog.h" #include "transferlistdelegate.h" #include "transferlistmodel.h" #include "transferlistsortmodel.h" @@ -807,6 +812,39 @@ void TransferListWidget::askAddTagsForSelection() addSelectionTag(tag); } +void TransferListWidget::editTorrentTrackers() +{ + const QList torrents = getSelectedTorrents(); + QList commonTrackers; + + if (!torrents.empty()) { + commonTrackers = torrents[0]->trackers(); + + for (const BitTorrent::TorrentHandle *torrent : torrents) { + QSet trackerSet; + + for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers())) + trackerSet.insert(entry); + + commonTrackers.erase(std::remove_if(commonTrackers.begin(), commonTrackers.end() + , [&trackerSet](const BitTorrent::TrackerEntry &entry) { return !trackerSet.contains(entry); }) + , commonTrackers.end()); + } + } + + auto trackerDialog = new TrackerEntriesDialog(this); + trackerDialog->setAttribute(Qt::WA_DeleteOnClose); + trackerDialog->setTrackers(commonTrackers); + + connect(trackerDialog, &QDialog::accepted, this, [torrents, trackerDialog]() + { + for (BitTorrent::TorrentHandle *torrent : torrents) + torrent->replaceTrackers(trackerDialog->trackers()); + }); + + trackerDialog->open(); +} + void TransferListWidget::confirmRemoveAllTagsForSelection() { QMessageBox::StandardButton response = QMessageBox::question( @@ -951,6 +989,8 @@ void TransferListWidget::displayListMenu(const QPoint &) actionAutoTMM->setCheckable(true); actionAutoTMM->setToolTip(tr("Automatic mode means that various torrent properties(eg save path) will be decided by the associated category")); connect(actionAutoTMM, &QAction::triggered, this, &TransferListWidget::setSelectedAutoTMMEnabled); + QAction *actionEditTracker = new QAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Edit trackers..."), listMenu); + connect(actionEditTracker, &QAction::triggered, this, &TransferListWidget::editTorrentTrackers); // End of actions // Enable/disable pause/start action given the DL state @@ -1046,6 +1086,7 @@ void TransferListWidget::displayListMenu(const QPoint &) listMenu->addAction(actionSetTorrentPath); if (selectedIndexes.size() == 1) listMenu->addAction(actionRename); + listMenu->addAction(actionEditTracker); // Category Menu QStringList categories = BitTorrent::Session::instance()->categories().keys(); diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index c738166ff..862c6ca19 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -118,6 +118,7 @@ signals: private: void wheelEvent(QWheelEvent *event) override; void askAddTagsForSelection(); + void editTorrentTrackers(); void confirmRemoveAllTagsForSelection(); QStringList askTagsForSelection(const QString &dialogTitle); void applyToSelectedTorrents(const std::function &fn);