Merge branch 'main' into docs/absorb-the-wiki

This commit is contained in:
Charles Kerr 2022-02-21 11:43:28 -06:00
commit aa657bb0ca
26 changed files with 448 additions and 38 deletions

View File

@ -94,7 +94,7 @@ static struct event_base* ev_base = nullptr;
**** Config File
***/
static auto constexpr Options = std::array<tr_option, 43>{
static auto constexpr Options = std::array<tr_option, 44>{
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
@ -103,6 +103,7 @@ static auto constexpr Options = std::array<tr_option, 43>{
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
{ 'd', "dump-settings", "Dump the settings and exit", "d", false, nullptr },
{ 943, "default-trackers", "Trackers for public torrents to use automatically", nullptr, true, "<list>" },
{ 'e', "logfile", "Dump the log messages to this filename", "e", true, "<filename>" },
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", false, nullptr },
{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
@ -416,6 +417,10 @@ static bool parse_args(
tr_variantDictAddBool(settings, TR_KEY_incomplete_dir_enabled, false);
break;
case 943:
tr_variantDictAddStr(settings, TR_KEY_default_trackers, optstr);
break;
case 'd':
*dump_settings = true;
break;

View File

@ -531,6 +531,7 @@ Response arguments: `path`, `name`, and `id`, holding the torrent ID integer
| `blocklist-url` | string | location of the blocklist to use for `blocklist-update`
| `cache-size-mb` | number | maximum size of the disk cache (MB)
| `config-dir` | string | location of transmission's configuration directory
| `default-trackers` | list of default trackers to use on public torrents
| `dht-enabled` | boolean | true means allow dht in public torrents
| `download-dir` | string | default path to download torrents
| `download-dir-free-space` | number | **DEPRECATED** Use the `free-space` method instead.
@ -940,6 +941,7 @@ Transmission 4.0.0 (`rpc-version-semver` 5.3.0, `rpc-version`: 17)
| `/upload` | :warning: undocumented `/upload` endpoint removed
| `session-get` | **DEPRECATED** `download-dir-free-space`. Use `free-space` instead.
| `free-space` | new return arg `total-capacity`
| `session-get` | new arg `default-trackers`
| `session-get` | new arg `rpc-version-semver`
| `session-get` | new arg `script-torrent-added-enabled`
| `session-get` | new arg `script-torrent-added-filename`

View File

@ -1022,6 +1022,10 @@ void Application::Impl::on_prefs_changed(tr_quark const key)
tr_sessionSetEncryption(tr, static_cast<tr_encryption_mode>(gtr_pref_int_get(key)));
break;
case TR_KEY_default_trackers:
tr_sessionSetDefaultTrackers(tr, gtr_pref_string_get(key).c_str());
break;
case TR_KEY_download_dir:
tr_sessionSetDownloadDir(tr, gtr_pref_string_get(key).c_str());
break;

View File

@ -2241,28 +2241,9 @@ namespace
std::string get_editable_tracker_list(tr_torrent const* tor)
{
std::ostringstream gstr;
int tier = 0;
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
auto const tracker = tr_torrentTracker(tor, i);
if (tier != tracker.tier)
{
tier = tracker.tier;
gstr << '\n';
}
gstr << tracker.announce << '\n';
}
auto str = gstr.str();
if (!str.empty())
{
str.resize(str.size() - 1);
}
char* cstr = tr_torrentGetTrackerList(tor);
auto str = std::string{ cstr != nullptr ? cstr : "" };
tr_free(cstr);
return str;
}
@ -2291,8 +2272,8 @@ void DetailsDialog::Impl::on_edit_trackers()
auto* l = Gtk::make_managed<Gtk::Label>();
l->set_markup(
_("To add a backup URL, add it on the line after the primary URL.\n"
"To add another primary URL, add it after a blank line."));
_("To add a backup URL, add it on the next line after a primary URL.\n"
"To add a new primary URL, add it after a blank line."));
l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER);
@ -2309,6 +2290,13 @@ void DetailsDialog::Impl::on_edit_trackers()
fr->set_size_request(500U, 166U);
t->add_wide_tall_control(row, *fr);
l = Gtk::make_managed<Gtk::Label>();
l->set_markup(_("Also see Default Public Trackers in Edit > Preferences > Network"));
l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER);
t->add_wide_control(row, *l);
gtr_dialog_set_content(*d, *t);
d->set_data(TORRENT_ID_KEY, GINT_TO_POINTER(torrent_id));

View File

@ -176,6 +176,32 @@ Gtk::Entry* new_entry(tr_quark const key, Glib::RefPtr<Session> const& core)
return w;
}
Gtk::Widget* new_text_view(tr_quark const key, Glib::RefPtr<Session> const& core)
{
auto* w = Gtk::make_managed<Gtk::TextView>();
auto buffer = w->get_buffer();
buffer->set_text(gtr_pref_string_get(key));
/* set up the scrolled window and put the text view in it */
auto* scroll = Gtk::make_managed<Gtk::ScrolledWindow>();
scroll->set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC);
scroll->set_shadow_type(Gtk::ShadowType::SHADOW_IN);
scroll->add(*w);
scroll->set_size_request(-1, 166);
/* signal */
w->add_events(Gdk::FOCUS_CHANGE_MASK);
w->signal_focus_out_event().connect(
[buffer, key, core](GdkEventFocus*)
{
core->set_pref(key, buffer->get_text());
return false;
});
return scroll;
}
void chosen_cb(Gtk::FileChooser* w, tr_quark const key, Glib::RefPtr<Session> const& core)
{
core->set_pref(key, w->get_filename());
@ -1051,6 +1077,16 @@ Gtk::Widget* PrefsDialog::Impl::networkPage()
w->set_tooltip_text(_("LPD is a tool for finding peers on your local network."));
t->add_wide_control(row, *w);
t->add_section_divider(row);
t->add_section_title(row, _("Default Public Trackers"));
auto tv = new_text_view(TR_KEY_default_trackers, core_);
tv->set_tooltip_text(
_("Trackers to use on all public torrents.\n\n"
"To add a backup URL, add it on the next line after a primary URL.\n"
"To add a new primary URL, add it after a blank line."));
t->add_wide_control(row, *tv);
return t;
}

View File

@ -98,6 +98,29 @@ bool tr_announce_list::add(std::string_view announce_url_sv, tr_tracker_tier_t t
return true;
}
void tr_announce_list::add(tr_announce_list const& src)
{
if (std::empty(src))
{
return;
}
auto src_tier = src.at(0).tier;
auto& tgt = *this;
auto tgt_tier = tgt.nextTier();
for (auto const& tracker : src)
{
if (src_tier != tracker.tier)
{
src_tier = tracker.tier;
++tgt_tier;
}
tgt.add(tracker.announce.full, tgt_tier);
}
}
std::optional<std::string> tr_announce_list::announceToScrape(std::string_view announce)
{
// To derive the scrape URL use the following steps:

View File

@ -92,12 +92,23 @@ public:
[[nodiscard]] tr_tracker_tier_t nextTier() const;
[[nodiscard]] bool operator==(tr_announce_list const& that) const
{
return trackers_ == that.trackers_;
}
[[nodiscard]] bool operator!=(tr_announce_list const& that) const
{
return trackers_ != that.trackers_;
}
bool add(std::string_view announce_url_sv)
{
return add(announce_url_sv, this->nextTier());
}
bool add(std::string_view announce_url_sv, tr_tracker_tier_t tier);
void add(tr_announce_list const& that);
bool remove(std::string_view announce_url);
bool remove(tr_tracker_id_t id);
bool replace(tr_tracker_id_t id, std::string_view announce_url_sv);

View File

@ -505,7 +505,7 @@ struct tr_torrent_announcer
{
// build the trackers
auto tier_to_infos = std::map<tr_tracker_tier_t, std::vector<tr_announce_list::tracker_info const*>>{};
auto const& announce_list = tor->announceList();
auto const announce_list = getAnnounceList(tor);
for (auto const& info : announce_list)
{
tier_to_infos[info.tier].emplace_back(&info);
@ -575,6 +575,20 @@ struct tr_torrent_announcer
tr_tracker_callback callback = nullptr;
void* callback_data = nullptr;
private:
[[nodiscard]] static tr_announce_list getAnnounceList(tr_torrent const* tor)
{
auto announce_list = tor->announceList();
// if it's a public torrent, add the default trackers
if (tor->isPublic())
{
announce_list.add(tor->session->defaultTrackers());
}
return announce_list;
}
};
static tr_tier* getTier(tr_announcer* announcer, tr_sha1_digest_t const& info_hash, int tier_id)

View File

@ -18,7 +18,7 @@ using namespace std::literals;
namespace
{
auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
auto constexpr my_static = std::array<std::string_view, 389>{ ""sv,
"activeTorrentCount"sv,
"activity-date"sv,
"activityDate"sv,
@ -74,6 +74,7 @@ auto constexpr my_static = std::array<std::string_view, 388>{ ""sv,
"current-stats"sv,
"date"sv,
"dateCreated"sv,
"default-trackers"sv,
"delete-local-data"sv,
"desiredAvailable"sv,
"destination"sv,

View File

@ -77,6 +77,7 @@ enum
TR_KEY_current_stats,
TR_KEY_date,
TR_KEY_dateCreated,
TR_KEY_default_trackers,
TR_KEY_delete_local_data,
TR_KEY_desiredAvailable,
TR_KEY_destination,

View File

@ -1789,6 +1789,11 @@ static char const* sessionSet(
tr_sessionSetQueueStalledEnabled(session, boolVal);
}
if (tr_variantDictFindStrView(args_in, TR_KEY_default_trackers, &sv))
{
session->setDefaultTrackers(sv);
}
if (tr_variantDictFindInt(args_in, TR_KEY_download_queue_size, &i))
{
tr_sessionSetQueueSize(session, TR_DOWN, (int)i);
@ -2071,6 +2076,10 @@ static void addSessionField(tr_session* s, tr_variant* d, tr_quark key)
tr_variantDictAddStr(d, key, tr_sessionGetConfigDir(s));
break;
case TR_KEY_default_trackers:
tr_variantDictAddStr(d, key, s->defaultTrackersStr());
break;
case TR_KEY_download_dir:
tr_variantDictAddStr(d, key, s->downloadDir());
break;

View File

@ -333,6 +333,7 @@ void tr_sessionGetDefaultSettings(tr_variant* d)
tr_variantDictAddBool(d, TR_KEY_utp_enabled, true);
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, false);
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_getDefaultDownloadDir());
tr_variantDictAddStr(d, TR_KEY_default_trackers, "");
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, 100);
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
tr_variantDictAddInt(d, TR_KEY_encryption, TR_DEFAULT_ENCRYPTION);
@ -411,6 +412,7 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
tr_variantDictAddBool(d, TR_KEY_utp_enabled, s->isUTPEnabled);
tr_variantDictAddBool(d, TR_KEY_lpd_enabled, s->isLPDEnabled);
tr_variantDictAddStr(d, TR_KEY_download_dir, tr_sessionGetDownloadDir(s));
tr_variantDictAddStr(d, TR_KEY_default_trackers, s->defaultTrackersStr());
tr_variantDictAddInt(d, TR_KEY_download_queue_size, tr_sessionGetQueueSize(s, TR_DOWN));
tr_variantDictAddBool(d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled(s, TR_DOWN));
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps(s, TR_DOWN));
@ -811,6 +813,11 @@ static void sessionSetImpl(void* vdata)
tr_sessionSetCacheLimit_MB(session, i);
}
if (tr_variantDictFindStrView(settings, TR_KEY_default_trackers, &sv))
{
session->setDefaultTrackers(sv);
}
if (tr_variantDictFindInt(settings, TR_KEY_peer_limit_per_torrent, &i))
{
tr_sessionSetPeerLimitPerTorrent(session, i);
@ -2244,6 +2251,37 @@ int tr_sessionGetCacheLimit_MB(tr_session const* session)
****
***/
void tr_session::setDefaultTrackers(std::string_view trackers)
{
auto const oldval = default_trackers_;
default_trackers_str_ = trackers;
default_trackers_.parse(trackers);
// if the list changed, update all the public torrents
if (default_trackers_ != oldval)
{
for (auto* tor : torrents)
{
if (tor->isPublic())
{
tr_announcerResetTorrent(announcer, tor);
}
}
}
}
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers)
{
TR_ASSERT(tr_isSession(session));
session->setDefaultTrackers(trackers != nullptr ? trackers : "");
}
/***
****
***/
struct port_forwarding_data
{
bool enabled;

View File

@ -27,6 +27,7 @@
#include "transmission.h"
#include "announce-list.h"
#include "net.h" // tr_socket_t
#include "quark.h"
#include "web.h"
@ -150,6 +151,21 @@ public:
download_dir_ = dir;
}
// default trackers
// (trackers to apply automatically to public torrents)
auto const& defaultTrackersStr() const
{
return default_trackers_str_;
}
auto const& defaultTrackers() const
{
return default_trackers_;
}
void setDefaultTrackers(std::string_view trackers);
// incomplete dir
std::string const& incompleteDir() const
@ -387,6 +403,8 @@ public:
std::unique_ptr<tr_rpc_server> rpc_server_;
tr_announce_list default_trackers_;
// One of <netinet/ip.h>'s IPTOS_ values.
// See tr_netTos*() in libtransmission/net.h for more info
// Only session.cc should use this.
@ -398,6 +416,7 @@ private:
std::array<std::string, TR_SCRIPT_N_TYPES> scripts_;
std::string blocklist_url_;
std::string download_dir_;
std::string default_trackers_str_;
std::string incomplete_dir_;
std::string peer_congestion_algorithm_;

View File

@ -392,6 +392,8 @@ bool tr_sessionIsRPCPasswordEnabled(tr_session const* session);
char const* tr_sessionGetRPCBindAddress(tr_session const* session);
void tr_sessionSetDefaultTrackers(tr_session* session, char const* trackers);
enum tr_rpc_callback_type
{
TR_RPC_TORRENT_ADDED,
@ -1187,7 +1189,13 @@ char* tr_torrentGetMagnetLink(tr_torrent const* tor);
/**
* Returns a newly-allocated string listing its tracker's announce URLs.
* One URL per line, with a blank line between tiers
* One URL per line, with a blank line between tiers.
*
* NOTE: this only includes the trackers included in the torrent and,
* along with tr_torrentSetTrackerList(), is intended for import/export
* and user editing. It does *not* include the "default trackers" that
* are applied to all public torrents. If you want a full display of all
* trackers, use tr_torrentTracker() and tr_torrentTrackerCount()
*/
char* tr_torrentGetTrackerList(tr_torrent const* tor);
@ -1413,6 +1421,13 @@ struct tr_tracker_view
struct tr_tracker_view tr_torrentTracker(tr_torrent const* torrent, size_t i);
/**
* Count all the trackers (both active and backup) this torrent is using.
*
* NOTE: this is for a status display only and may include trackers from
* the default tracker list if this is a public torrent. If you want a
* list of trackers the user can edit, see tr_torrentGetTrackerList().
*/
size_t tr_torrentTrackerCount(tr_torrent const* torrent);
/*

View File

@ -109,6 +109,7 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
{ ALT_SPEED_LIMIT_TIME_DAY, TR_KEY_alt_speed_time_day, QVariant::Int },
{ BLOCKLIST_ENABLED, TR_KEY_blocklist_enabled, QVariant::Bool },
{ BLOCKLIST_URL, TR_KEY_blocklist_url, QVariant::String },
{ DEFAULT_TRACKERS, TR_KEY_default_trackers, QVariant::String },
{ DSPEED, TR_KEY_speed_limit_down, QVariant::Int },
{ DSPEED_ENABLED, TR_KEY_speed_limit_down_enabled, QVariant::Bool },
{ DOWNLOAD_DIR, TR_KEY_download_dir, QVariant::String },

View File

@ -83,6 +83,7 @@ public:
ALT_SPEED_LIMIT_TIME_DAY,
BLOCKLIST_ENABLED,
BLOCKLIST_URL,
DEFAULT_TRACKERS,
DSPEED,
DSPEED_ENABLED,
DOWNLOAD_DIR,

View File

@ -25,6 +25,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QStyle>
@ -190,6 +191,10 @@ bool PrefsDialog::updateWidgetValue(QWidget* widget, int pref_key) const
{
pref_widget.as<FreeSpaceLabel>()->setPath(prefs_.getString(pref_key));
}
else if (pref_widget.is<QPlainTextEdit>())
{
pref_widget.as<QPlainTextEdit>()->setPlainText(prefs_.getString(pref_key));
}
else
{
return false;
@ -233,9 +238,51 @@ void PrefsDialog::linkWidgetToPref(QWidget* widget, int pref_key)
if (auto const* spin_box = qobject_cast<QAbstractSpinBox*>(widget); spin_box != nullptr)
{
connect(spin_box, &QAbstractSpinBox::editingFinished, this, &PrefsDialog::spinBoxEditingFinished);
return;
}
}
static bool isDescendantOf(QObject const* descendant, QObject const* ancestor)
{
if (ancestor == nullptr)
{
return false;
}
while (descendant != nullptr)
{
if (descendant == ancestor)
{
return true;
}
descendant = descendant->parent();
}
return false;
}
void PrefsDialog::focusChanged(QWidget* old, QWidget* cur)
{
// We don't want to change the preference every time there's a keystroke
// in a QPlainTextEdit, so instead of connecting to the textChanged signal,
// only update the pref when the text changed AND focus was lost.
char const constexpr* const StartValue = "StartValue";
if (auto* const edit = qobject_cast<QPlainTextEdit*>(cur); isDescendantOf(edit, this))
{
edit->setProperty(StartValue, edit->toPlainText());
}
if (auto const* const edit = qobject_cast<QPlainTextEdit*>(old); isDescendantOf(edit, this))
{
if (auto const val = edit->toPlainText(); val != edit->property(StartValue).toString())
{
setPref(PreferenceWidget{ old }.getPrefKey(), val);
}
}
// (TODO: we probably want to do this for single-line text entries too?)
}
void PrefsDialog::checkBoxToggled(bool checked)
{
PreferenceWidget const pref_widget(sender());
@ -423,6 +470,7 @@ void PrefsDialog::initNetworkTab()
linkWidgetToPref(ui_.enablePexCheck, Prefs::PEX_ENABLED);
linkWidgetToPref(ui_.enableDhtCheck, Prefs::DHT_ENABLED);
linkWidgetToPref(ui_.enableLpdCheck, Prefs::LPD_ENABLED);
linkWidgetToPref(ui_.defaultTrackersPlainTextEdit, Prefs::DEFAULT_TRACKERS);
auto* cr = new ColumnResizer(this);
cr->addLayout(ui_.incomingPeersSectionLayout);
@ -676,6 +724,8 @@ PrefsDialog::PrefsDialog(Session& session, Prefs& prefs, QWidget* parent)
}
adjustSize();
connect(qApp, &QApplication::focusChanged, this, &PrefsDialog::focusChanged);
}
void PrefsDialog::setPref(int key, QVariant const& v)
@ -775,11 +825,15 @@ void PrefsDialog::refreshPref(int key)
{
QWidget* w(it.value());
w->blockSignals(true);
if (!updateWidgetValue(w, key) && (key == Prefs::ENCRYPTION))
{
auto* combo_box = qobject_cast<QComboBox*>(w);
int const index = combo_box->findData(prefs_.getInt(key));
combo_box->setCurrentIndex(index);
}
w->blockSignals(false);
}
}

View File

@ -29,6 +29,7 @@ public:
PrefsDialog(Session&, Prefs&, QWidget* parent = nullptr);
private slots:
void focusChanged(QWidget* old, QWidget* now);
void checkBoxToggled(bool checked);
void spinBoxEditingFinished();
void timeEditingFinished();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>601</width>
<height>597</height>
<height>657</height>
</rect>
</property>
<property name="windowTitle">
@ -17,7 +17,7 @@
<item>
<widget class="QTabWidget" name="tabs">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<property name="elideMode">
<enum>Qt::ElideNone</enum>
@ -985,6 +985,58 @@
</item>
</layout>
</item>
<item>
<spacer name="defaultTrackersSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="defaultTrackersLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Default Public Trackers</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="defaultTrackersLayout">
<property name="leftMargin">
<number>18</number>
</property>
<item>
<widget class="QPlainTextEdit" name="defaultTrackersPlainTextEdit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Trackers to use on all public torrents.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;To add a backup URL, add it on the next line after a primary URL.&lt;/p&gt;&lt;p&gt;To add a new primary URL, add it after a blank line.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="plainText">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="networkTabBottomSpacer">
<property name="orientation">

View File

@ -155,6 +155,7 @@ void Session::updatePref(int key)
case Prefs::BLOCKLIST_DATE:
case Prefs::BLOCKLIST_ENABLED:
case Prefs::BLOCKLIST_URL:
case Prefs::DEFAULT_TRACKERS:
case Prefs::DHT_ENABLED:
case Prefs::DOWNLOAD_QUEUE_ENABLED:
case Prefs::DOWNLOAD_QUEUE_SIZE:

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<width>515</width>
<height>450</height>
</rect>
</property>
@ -33,19 +33,30 @@
<number>18</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="primaryLabel">
<property name="text">
<string>To add another primary URL, add it after a blank line.</string>
<string>To add a new primary URL, add it after a blank line.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPlainTextEdit" name="trackerList"/>
<widget class="QPlainTextEdit" name="trackerList">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="destinationLabel">
<widget class="QLabel" name="backupLabel">
<property name="text">
<string>To add a backup URL, add it on the line after the primary URL.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;To add a backup URL, add it on the next line after a primary URL.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="defaultsLabel">
<property name="text">
<string>Also see Default Public Trackers in Edit &gt; Preferences &gt; Network</string>
</property>
</widget>
</item>

View File

@ -611,3 +611,83 @@ TEST_F(AnnounceListTest, parseDuplicateUrl)
EXPECT_FALSE(announce_list.parse(Text));
}
TEST_F(AnnounceListTest, addAnnounceListWithSingleTracker)
{
auto constexpr Trackers =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"sv;
auto announce_list = tr_announce_list{};
announce_list.parse(Trackers);
auto constexpr AddStr = "https://www.baz.com/announce"sv;
auto tmp = tr_announce_list{};
tmp.parse(AddStr);
announce_list.add(tmp);
auto constexpr Expected =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"
"\n"
"https://www.baz.com/announce\n"sv;
EXPECT_EQ(Expected, announce_list.toString());
}
TEST_F(AnnounceListTest, addAnnounceWithSingleTier)
{
auto constexpr Trackers =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"sv;
auto announce_list = tr_announce_list{};
announce_list.parse(Trackers);
auto constexpr AddStr =
"https://www.baz.com/announce\n"
"https://www.qux.com/announce\n"sv;
auto tmp = tr_announce_list{};
tmp.parse(AddStr);
announce_list.add(tmp);
auto constexpr Expected =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"
"\n"
"https://www.baz.com/announce\n"
"https://www.qux.com/announce\n"sv;
EXPECT_EQ(Expected, announce_list.toString());
}
TEST_F(AnnounceListTest, addAnnounceListWithMultiTier)
{
auto constexpr Trackers =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"sv;
auto announce_list = tr_announce_list{};
announce_list.parse(Trackers);
auto constexpr AddStr =
"https://www.baz.com/announce\n"
"\n"
"https://www.qux.com/announce\n"sv;
auto tmp = tr_announce_list{};
tmp.parse(AddStr);
announce_list.add(tmp);
auto constexpr Expected =
"https://www.foo.com/announce\n"
"\n"
"https://www.bar.com/announce\n"
"\n"
"https://www.baz.com/announce\n"
"\n"
"https://www.qux.com/announce\n"sv;
EXPECT_EQ(Expected, announce_list.toString());
}

View File

@ -94,7 +94,7 @@ TEST_F(RpcTest, sessionGet)
EXPECT_TRUE(tr_variantDictFindDict(&response, TR_KEY_arguments, &args));
// what we expected
auto const expected_keys = std::array<tr_quark, 57>{
auto const expected_keys = std::array<tr_quark, 58>{
TR_KEY_alt_speed_down,
TR_KEY_alt_speed_enabled,
TR_KEY_alt_speed_time_begin,
@ -109,6 +109,7 @@ TEST_F(RpcTest, sessionGet)
TR_KEY_blocklist_url,
TR_KEY_cache_size_mb,
TR_KEY_config_dir,
TR_KEY_default_trackers,
TR_KEY_dht_enabled,
TR_KEY_download_dir,
TR_KEY_download_dir_free_space,

File diff suppressed because one or more lines are too long

View File

@ -63,6 +63,10 @@ export class PrefsDialog extends EventTarget {
}
static _getValue(e) {
if (e.tagName === 'TEXTAREA') {
return e.value;
}
switch (e.type) {
case 'checkbox':
case 'radio':
@ -128,6 +132,7 @@ export class PrefsDialog extends EventTarget {
}
break;
case 'text':
case 'textarea':
case 'url':
case 'email':
case 'number':
@ -150,6 +155,7 @@ export class PrefsDialog extends EventTarget {
}
break;
default:
console.log(element.type);
break;
}
}
@ -646,7 +652,32 @@ export class PrefsDialog extends EventTarget {
root.append(cal.root);
const utp_check = cal.check;
label = document.createElement('div');
label.textContent = 'Default Public Trackers';
label.classList.add('section-label');
root.append(label);
const tracker_labels = [
'Trackers to use on all public torrents.',
'To add a backup URL, add it on the next line after a primary URL.',
'To add a new primary URL, add it after a blank line.',
];
for (const text of tracker_labels) {
label = document.createElement('label');
label.classList.add('default-trackers-label');
label.textContent = text;
label.setAttribute('for', 'default-trackers');
root.append(label);
}
const textarea = document.createElement('textarea');
textarea.dataset.key = 'default-trackers';
textarea.id = 'default-trackers';
root.append(textarea);
const default_trackers_textarea = textarea;
return {
default_trackers_textarea,
port_forwarding_check,
port_input,
port_status_label,
@ -714,6 +745,8 @@ export class PrefsDialog extends EventTarget {
console.trace(`unhandled input: ${element.type}`);
break;
}
} else if (element.tagName === 'TEXTAREA') {
element.addEventListener('change', on_change);
}
}
};

View File

@ -762,6 +762,10 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
grid-column: span 2;
}
#default-trackers {
height: 300px;
}
.blocklist-size-label,
.blocklist-update-button,
.port-status {
@ -787,6 +791,11 @@ $popup-top: 61px; // TODO: ugly that this is hardcoded
}
}
#default-trackers,
.default-trackers-label {
grid-column: 1 / 3;
}
.alt-speed-label {
font-size: smaller;
font-style: lighter;