refactor: add tr_torrentTrackers() (#2282)

* refactor: add tr_torrentTrackers()
This commit is contained in:
Charles Kerr 2021-12-08 10:55:52 -06:00 committed by GitHub
parent 0a85c3aaa4
commit ab0c49859e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 315 additions and 421 deletions

View File

@ -1847,24 +1847,24 @@ Glib::ustring tr_strltime_rounded(time_t t)
return tr_strltime(t);
}
void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
{
if (st->hasAnnounced && st->announceState != TR_TRACKER_INACTIVE)
if (tracker.hasAnnounced && tracker.announceState != TR_TRACKER_INACTIVE)
{
gstr << '\n';
gstr << text_dir_mark[direction];
auto const timebuf = tr_strltime_rounded(now - st->lastAnnounceTime);
auto const timebuf = tr_strltime_rounded(now - tracker.lastAnnounceTime);
if (st->lastAnnounceSucceeded)
if (tracker.lastAnnounceSucceeded)
{
gstr << gtr_sprintf(
_("Got a list of %1$s%2$'d peers%3$s %4$s ago"),
success_markup_begin,
st->lastAnnouncePeerCount,
tracker.lastAnnouncePeerCount,
success_markup_end,
timebuf);
}
else if (st->lastAnnounceTimedOut)
else if (tracker.lastAnnounceTimedOut)
{
gstr << gtr_sprintf(
_("Peer list request %1$stimed out%2$s %3$s ago; will retry"),
@ -1877,13 +1877,13 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
gstr << gtr_sprintf(
_("Got an error %1$s\"%2$s\"%3$s %4$s ago"),
err_markup_begin,
st->lastAnnounceResult,
tracker.lastAnnounceResult,
err_markup_end,
timebuf);
}
}
switch (st->announceState)
switch (tracker.announceState)
{
case TR_TRACKER_INACTIVE:
gstr << '\n';
@ -1894,7 +1894,7 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
case TR_TRACKER_WAITING:
gstr << '\n';
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for more peers in %s"), tr_strltime_rounded(st->nextAnnounceTime - now));
gstr << gtr_sprintf(_("Asking for more peers in %s"), tr_strltime_rounded(tracker.nextAnnounceTime - now));
break;
case TR_TRACKER_QUEUED:
@ -1908,26 +1908,26 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(
_("Asking for more peers now… <small>%s</small>"),
tr_strltime_rounded(now - st->lastAnnounceStartTime));
tr_strltime_rounded(now - tracker.lastAnnounceStartTime));
break;
}
}
void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
{
if (st->hasScraped)
if (tracker.hasScraped)
{
gstr << '\n';
gstr << text_dir_mark[direction];
auto const timebuf = tr_strltime_rounded(now - st->lastScrapeTime);
auto const timebuf = tr_strltime_rounded(now - tracker.lastScrapeTime);
if (st->lastScrapeSucceeded)
if (tracker.lastScrapeSucceeded)
{
gstr << gtr_sprintf(
_("Tracker had %s%'d seeders and %'d leechers%s %s ago"),
success_markup_begin,
st->seederCount,
st->leecherCount,
tracker.seederCount,
tracker.leecherCount,
success_markup_end,
timebuf);
}
@ -1936,13 +1936,13 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
gstr << gtr_sprintf(
_("Got a scrape error \"%s%s%s\" %s ago"),
err_markup_begin,
st->lastScrapeResult,
tracker.lastScrapeResult,
err_markup_end,
timebuf);
}
}
switch (st->scrapeState)
switch (tracker.scrapeState)
{
case TR_TRACKER_INACTIVE:
break;
@ -1950,7 +1950,7 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
case TR_TRACKER_WAITING:
gstr << '\n';
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for peer counts in %s"), tr_strltime_rounded(st->nextScrapeTime - now));
gstr << gtr_sprintf(_("Asking for peer counts in %s"), tr_strltime_rounded(tracker.nextScrapeTime - now));
break;
case TR_TRACKER_QUEUED:
@ -1964,7 +1964,7 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(
_("Asking for peer counts now… <small>%s</small>"),
tr_strltime_rounded(now - st->lastScrapeStartTime));
tr_strltime_rounded(now - tracker.lastScrapeStartTime));
break;
}
}
@ -1972,25 +1972,25 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
void buildTrackerSummary(
std::ostream& gstr,
std::string const& key,
tr_tracker_stat const* st,
tr_tracker_view const& tracker,
bool showScrape,
Gtk::TextDirection direction)
{
// hostname
gstr << text_dir_mark[direction];
gstr << (st->isBackup ? "<i>" : "<b>");
gstr << Glib::Markup::escape_text(!key.empty() ? gtr_sprintf("%s - %s", st->host, key) : st->host);
gstr << (st->isBackup ? "</i>" : "</b>");
gstr << (tracker.isBackup ? "<i>" : "<b>");
gstr << Glib::Markup::escape_text(!key.empty() ? gtr_sprintf("%s - %s", tracker.host, key) : tracker.host);
gstr << (tracker.isBackup ? "</i>" : "</b>");
if (!st->isBackup)
if (!tracker.isBackup)
{
time_t const now = time(nullptr);
appendAnnounceInfo(st, now, direction, gstr);
appendAnnounceInfo(tracker, now, direction, gstr);
if (showScrape)
{
appendScrapeInfo(st, now, direction, gstr);
appendScrapeInfo(tracker, now, direction, gstr);
}
}
}
@ -2092,16 +2092,13 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
bool const showScrape = scrape_check_->get_active();
/* step 1: get all the trackers */
std::vector<int> statCount;
std::vector<tr_tracker_stat*> stats;
statCount.reserve(torrents.size());
stats.reserve(torrents.size());
for (auto const* torrent : torrents)
auto trackers = std::multimap<tr_torrent const*, tr_tracker_view>{};
for (auto const* tor : torrents)
{
int count = 0;
stats.push_back(tr_torrentTrackers(torrent, &count));
statCount.push_back(count);
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
trackers.emplace(tor, tr_torrentTracker(tor, i));
}
}
/* step 2: mark all the trackers in the list as not-updated */
@ -2110,61 +2107,49 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
row[tracker_cols.was_updated] = false;
}
/* step 3: add any new trackers */
for (size_t i = 0; i < statCount.size(); ++i)
/* step 3: add / update trackers */
for (auto const& [tor, tracker] : trackers)
{
int const jn = statCount.at(i);
auto const torrent_id = tr_torrentId(tor);
for (int j = 0; j < jn; ++j)
// build the key to find the row
gstr.str({});
gstr << torrent_id << '\t' << tracker.tier << '\t' << tracker.announce;
if (hash.find(gstr.str()) == hash.end())
{
tr_torrent const* tor = torrents.at(i);
tr_tracker_stat const* st = &stats.at(i)[j];
int const torrent_id = tr_torrentId(tor);
// if we didn't have that row, add it
auto const iter = store->append();
(*iter)[tracker_cols.torrent_id] = torrent_id;
(*iter)[tracker_cols.tracker_id] = tracker.id;
(*iter)[tracker_cols.key] = gstr.str();
/* build the key to find the row */
gstr.str({});
gstr << torrent_id << '\t' << st->tier << '\t' << st->announce;
if (hash.find(gstr.str()) == hash.end())
{
auto const iter = store->append();
(*iter)[tracker_cols.torrent_id] = torrent_id;
(*iter)[tracker_cols.tracker_id] = st->id;
(*iter)[tracker_cols.key] = gstr.str();
auto const p = store->get_path(iter);
hash.emplace(gstr.str(), Gtk::TreeRowReference(store, p));
gtr_get_favicon_from_url(
session,
st->announce,
[ref = Gtk::TreeRowReference(store, p)](auto const& pixbuf) mutable { favicon_ready_cb(pixbuf, ref); });
}
auto const p = store->get_path(iter);
hash.emplace(gstr.str(), Gtk::TreeRowReference(store, p));
gtr_get_favicon_from_url(
session,
tracker.announce,
[ref = Gtk::TreeRowReference(store, p)](auto const& pixbuf) mutable { favicon_ready_cb(pixbuf, ref); });
}
}
/* step 4: update the peers */
for (size_t i = 0; i < torrents.size(); ++i)
/* step 4: update the rows */
auto const summary_name = std::string(std::size(torrents) == 1 ? tr_torrentName(torrents.front()) : "");
for (auto const& [tor, tracker] : trackers)
{
tr_torrent const* tor = torrents.at(i);
auto const summary_name = std::string(torrents.size() > 1 ? tr_torrentName(tor) : "");
auto const torrent_id = tr_torrentId(tor);
for (int j = 0; j < statCount.at(i); ++j)
{
tr_tracker_stat const* st = &stats.at(i)[j];
// build the key to find the row
gstr.str({});
gstr << torrent_id << '\t' << tracker.tier << '\t' << tracker.announce;
auto const iter = store->get_iter(hash.at(gstr.str()).get_path());
/* build the key to find the row */
gstr.str({});
gstr << tr_torrentId(tor) << '\t' << st->tier << '\t' << st->announce;
auto const iter = store->get_iter(hash.at(gstr.str()).get_path());
/* update the row */
gstr.str({});
buildTrackerSummary(gstr, summary_name, st, showScrape, dialog_.get_direction());
(*iter)[tracker_cols.text] = gstr.str();
(*iter)[tracker_cols.is_backup] = st->isBackup;
(*iter)[tracker_cols.tracker_id] = st->id;
(*iter)[tracker_cols.was_updated] = true;
}
// update the row
gstr.str({});
buildTrackerSummary(gstr, summary_name, tracker, showScrape, dialog_.get_direction());
(*iter)[tracker_cols.text] = gstr.str();
(*iter)[tracker_cols.is_backup] = tracker.isBackup;
(*iter)[tracker_cols.tracker_id] = tracker.id;
(*iter)[tracker_cols.was_updated] = true;
}
/* step 5: remove trackers that have disappeared */
@ -2186,12 +2171,6 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
}
edit_trackers_button_->set_sensitive(tracker_list_get_current_torrent_id() >= 0);
/* cleanup */
for (size_t i = 0; i < stats.size(); ++i)
{
tr_torrentTrackersFree(stats[i], statCount[i]);
}
}
void DetailsDialog::Impl::onScrapeToggled()
@ -2274,19 +2253,18 @@ std::string get_editable_tracker_list(tr_torrent const* tor)
{
std::ostringstream gstr;
int tier = 0;
tr_info const* inf = tr_torrentInfo(tor);
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
tr_tracker_info const* t = &inf->trackers[i];
auto const tracker = tr_torrentTracker(tor, i);
if (tier != t->tier)
if (tier != tracker.tier)
{
tier = t->tier;
tier = tracker.tier;
gstr << '\n';
}
gstr << t->announce << '\n';
gstr << tracker.announce << '\n';
}
auto str = gstr.str();
@ -2649,8 +2627,7 @@ void DetailsDialog::Impl::set_torrents(std::vector<int> const& ids)
{
int const id = ids.front();
auto const* tor = core_->find_torrent(id);
auto const* inf = tr_torrentInfo(tor);
title = gtr_sprintf(_("%s Properties"), inf->name);
title = gtr_sprintf(_("%s Properties"), tr_torrentName(tor));
file_list_->set_torrent(id);
file_list_->show();

View File

@ -174,13 +174,12 @@ bool tracker_filter_model_update(Glib::RefPtr<Gtk::TreeStore> const& tracker_mod
for (auto const& row : tmodel->children())
{
auto const* tor = static_cast<tr_torrent const*>(row.get_value(torrent_cols.torrent));
auto const* const inf = tr_torrentInfo(tor);
std::set<std::string const*> keys;
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
auto const* const key = &*strings.insert(gtr_get_host_from_url(inf->trackers[i].announce)).first;
auto const* const key = &*strings.insert(gtr_get_host_from_url(tr_torrentTracker(tor, i).announce)).first;
if (auto const count = hosts_hash.find(key); count == hosts_hash.end())
{
@ -384,21 +383,20 @@ namespace
bool test_tracker(tr_torrent const* tor, int active_tracker_type, Glib::ustring const& host)
{
bool matches = true;
if (active_tracker_type == TRACKER_FILTER_TYPE_HOST)
if (active_tracker_type != TRACKER_FILTER_TYPE_HOST)
{
auto const* const inf = tr_torrentInfo(tor);
return true;
}
matches = false;
for (unsigned int i = 0; !matches && i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
if (gtr_get_host_from_url(tr_torrentTracker(tor, i).announce) == host)
{
matches = gtr_get_host_from_url(inf->trackers[i].announce) == host;
return true;
}
}
return matches;
return false;
}
/***

View File

@ -456,15 +456,9 @@ int compare_by_activity(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::itera
int compare_by_age(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto* const ta = static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent));
auto* const tb = static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent));
if (ret == 0)
{
ret = compare_time(tr_torrentStatCached(ta)->addedDate, tr_torrentStatCached(tb)->addedDate);
}
int ret = compare_time(tr_torrentStatCached(ta)->addedDate, tr_torrentStatCached(tb)->addedDate);
if (ret == 0)
{
@ -476,15 +470,9 @@ int compare_by_age(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator c
int compare_by_size(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const ia = tr_torrentInfo(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const ib = tr_torrentInfo(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_uint64(ia->totalSize, ib->totalSize);
}
auto const size_a = tr_torrentInfo(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)))->totalSize;
auto const size_b = tr_torrentInfo(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)))->totalSize;
int ret = compare_uint64(size_a, size_b);
if (ret == 0)
{
@ -496,15 +484,9 @@ int compare_by_size(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator
int compare_by_progress(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const sa = tr_torrentStatCached(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const sb = tr_torrentStatCached(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_double(sa->percentComplete, sb->percentComplete);
}
int ret = compare_double(sa->percentComplete, sb->percentComplete);
if (ret == 0)
{
@ -521,15 +503,9 @@ int compare_by_progress(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::itera
int compare_by_eta(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const sa = tr_torrentStatCached(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const sb = tr_torrentStatCached(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_eta(sa->eta, sb->eta);
}
int ret = compare_eta(sa->eta, sb->eta);
if (ret == 0)
{
@ -541,15 +517,9 @@ int compare_by_eta(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator c
int compare_by_state(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const sa = a->get_value(torrent_cols.activity);
auto const sb = b->get_value(torrent_cols.activity);
if (ret == 0)
{
ret = compare_int(sa, sb);
}
int ret = compare_int(sa, sb);
if (ret == 0)
{
@ -893,8 +863,7 @@ namespace
Glib::ustring get_collated_name(tr_torrent const* tor)
{
auto const* const inf = tr_torrentInfo(tor);
return gtr_sprintf("%s\t%s", Glib::ustring(tr_torrentName(tor)).lowercase(), inf->hashString);
return gtr_sprintf("%s\t%s", Glib::ustring(tr_torrentName(tor)).lowercase(), tr_torrentInfo(tor)->hashString);
}
struct metadata_callback_data
@ -951,14 +920,13 @@ namespace
unsigned int build_torrent_trackers_hash(tr_torrent* tor)
{
uint64_t hash = 0;
tr_info const* const inf = tr_torrentInfo(tor);
auto hash = uint64_t{};
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
for (char const* pch = inf->trackers[i].announce; *pch != '\0'; ++pch)
for (auto const ch : std::string_view{ tr_torrentTracker(tor, i).announce })
{
hash = (hash << 4) ^ (hash >> 28) ^ *pch;
hash = (hash << 4) ^ (hash >> 28) ^ ch;
}
}

View File

@ -33,13 +33,13 @@
namespace
{
Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_stat const* st)
Glib::ustring getProgressString(tr_torrent const* tor, uint64_t total_size, tr_stat const* st)
{
Glib::ustring gstr;
bool const isDone = st->leftUntilDone == 0;
uint64_t const haveTotal = st->haveUnchecked + st->haveValid;
bool const isSeed = st->haveValid >= info->totalSize;
bool const isSeed = st->haveValid >= total_size;
double seedRatio;
bool const hasSeedRatio = tr_torrentGetSeedRatio(tor, &seedRatio);
@ -67,7 +67,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%6$s is the ratio we want to reach before we stop uploading */
_("%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s Goal: %6$s)"),
tr_strlsize(haveTotal),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio),
@ -83,7 +83,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%5$s is our upload-to-download ratio */
_("%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s)"),
tr_strlsize(haveTotal),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio));
@ -99,7 +99,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%3$s is our upload-to-download ratio,
%4$s is the ratio we want to reach before we stop uploading */
_("%1$s, uploaded %2$s (Ratio: %3$s Goal: %4$s)"),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio),
tr_strlratio(seedRatio));
@ -111,7 +111,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%2$s is how much we've uploaded,
%3$s is our upload-to-download ratio */
_("%1$s, uploaded %2$s (Ratio: %3$s)"),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio));
}
@ -458,12 +458,12 @@ void TorrentCellRenderer::Impl::get_size_full(Gtk::Widget& widget, int& width, i
auto* const tor = static_cast<tr_torrent*>(torrent.get_value());
auto const* const st = tr_torrentStatCached(tor);
auto const* const inf = tr_torrentInfo(tor);
auto const total_size = tr_torrentInfo(tor)->totalSize;
auto const icon = get_icon(tor, FULL_ICON_SIZE, widget);
auto const name = Glib::ustring(tr_torrentName(tor));
auto const gstr_stat = getStatusString(tor, st, upload_speed_KBps.get_value(), download_speed_KBps.get_value());
auto const gstr_prog = getProgressString(tor, inf, st);
auto const gstr_prog = getProgressString(tor, total_size, st);
renderer_.get_padding(xpad, ypad);
/* get the idealized cell dimensions */
@ -677,7 +677,7 @@ void TorrentCellRenderer::Impl::render_full(
auto* const tor = static_cast<tr_torrent*>(torrent.get_value());
auto const* const st = tr_torrentStatCached(tor);
auto const* const inf = tr_torrentInfo(tor);
auto const total_size = tr_torrentInfo(tor)->totalSize;
bool const active = st->activity != TR_STATUS_STOPPED && st->activity != TR_STATUS_DOWNLOAD_WAIT &&
st->activity != TR_STATUS_SEED_WAIT;
auto const percentDone = get_percent_done(tor, st, &seed);
@ -685,7 +685,7 @@ void TorrentCellRenderer::Impl::render_full(
auto const icon = get_icon(tor, FULL_ICON_SIZE, widget);
auto const name = Glib::ustring(tr_torrentName(tor));
auto const gstr_prog = getProgressString(tor, inf, st);
auto const gstr_prog = getProgressString(tor, total_size, st);
auto const gstr_stat = getStatusString(tor, st, upload_speed_KBps.get_value(), download_speed_KBps.get_value());
renderer_.get_padding(xpad, ypad);
auto const text_color = get_text_color(widget, st);

View File

@ -1713,115 +1713,120 @@ static void onUpkeepTimer(evutil_socket_t /*fd*/, short /*what*/, void* vannounc
****
***/
tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerCount)
static tr_tracker_view trackerView(tr_torrent const& tor, int tier_index, tr_tier const& tier, tr_tracker const& tracker)
{
TR_ASSERT(tr_isTorrent(torrent));
auto const now = tr_time();
auto view = tr_tracker_view{};
time_t const now = tr_time();
view.host = tr_quark_get_string(tracker.key);
view.announce = tr_quark_get_string(tracker.announce_url);
view.scrape = tracker.scrape_info == nullptr ? "" : tr_quark_get_string(tracker.scrape_info->scrape_url);
int out = 0;
struct tr_torrent_tiers const* const tt = torrent->tiers;
view.id = tracker.id;
view.tier = tier_index;
view.isBackup = &tracker != tier.currentTracker;
view.lastScrapeStartTime = tier.lastScrapeStartTime;
view.seederCount = tracker.seederCount;
view.leecherCount = tracker.leecherCount;
view.downloadCount = tracker.downloadCount;
/* alloc the stats */
*setmeTrackerCount = tt->tracker_count;
tr_tracker_stat* const ret = tr_new0(tr_tracker_stat, tt->tracker_count);
/* populate the stats */
for (int i = 0; i < tt->tier_count; ++i)
if (view.isBackup)
{
tr_tier const* const tier = &tt->tiers[i];
for (int j = 0; j < tier->tracker_count; ++j)
view.scrapeState = TR_TRACKER_INACTIVE;
view.announceState = TR_TRACKER_INACTIVE;
view.nextScrapeTime = 0;
view.nextAnnounceTime = 0;
}
else
{
view.hasScraped = tier.lastScrapeTime;
if (view.hasScraped != 0)
{
tr_tracker const* const tracker = &tier->trackers[j];
tr_tracker_stat* st = &ret[out++];
view.lastScrapeTime = tier.lastScrapeTime;
view.lastScrapeSucceeded = tier.lastScrapeSucceeded;
view.lastScrapeTimedOut = tier.lastScrapeTimedOut;
tr_strlcpy(view.lastScrapeResult, tier.lastScrapeStr, sizeof(view.lastScrapeResult));
}
st->id = tracker->id;
st->host = tr_quark_get_string(tracker->key);
st->announce = tr_quark_get_string(tracker->announce_url);
st->tier = i;
st->isBackup = tracker != tier->currentTracker;
st->lastScrapeStartTime = tier->lastScrapeStartTime;
st->scrape = tracker->scrape_info == nullptr ? "" : tr_quark_get_string(tracker->scrape_info->scrape_url);
st->seederCount = tracker->seederCount;
st->leecherCount = tracker->leecherCount;
st->downloadCount = tracker->downloadCount;
if (tier.isScraping)
{
view.scrapeState = TR_TRACKER_ACTIVE;
}
else if (tier.scrapeAt == 0)
{
view.scrapeState = TR_TRACKER_INACTIVE;
}
else if (tier.scrapeAt > now)
{
view.scrapeState = TR_TRACKER_WAITING;
view.nextScrapeTime = tier.scrapeAt;
}
else
{
view.scrapeState = TR_TRACKER_QUEUED;
}
if (st->isBackup)
{
st->scrapeState = TR_TRACKER_INACTIVE;
st->announceState = TR_TRACKER_INACTIVE;
st->nextScrapeTime = 0;
st->nextAnnounceTime = 0;
}
else
{
st->hasScraped = tier->lastScrapeTime;
if (st->hasScraped != 0)
{
st->lastScrapeTime = tier->lastScrapeTime;
st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
tr_strlcpy(st->lastScrapeResult, tier->lastScrapeStr, sizeof(st->lastScrapeResult));
}
view.lastAnnounceStartTime = tier.lastAnnounceStartTime;
if (tier->isScraping)
{
st->scrapeState = TR_TRACKER_ACTIVE;
}
else if (tier->scrapeAt == 0)
{
st->scrapeState = TR_TRACKER_INACTIVE;
}
else if (tier->scrapeAt > now)
{
st->scrapeState = TR_TRACKER_WAITING;
st->nextScrapeTime = tier->scrapeAt;
}
else
{
st->scrapeState = TR_TRACKER_QUEUED;
}
view.hasAnnounced = tier.lastAnnounceTime;
if (view.hasAnnounced != 0)
{
view.lastAnnounceTime = tier.lastAnnounceTime;
view.lastAnnounceSucceeded = tier.lastAnnounceSucceeded;
view.lastAnnounceTimedOut = tier.lastAnnounceTimedOut;
view.lastAnnouncePeerCount = tier.lastAnnouncePeerCount;
tr_strlcpy(view.lastAnnounceResult, tier.lastAnnounceStr, sizeof(view.lastAnnounceResult));
}
st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
st->hasAnnounced = tier->lastAnnounceTime;
if (st->hasAnnounced != 0)
{
st->lastAnnounceTime = tier->lastAnnounceTime;
tr_strlcpy(st->lastAnnounceResult, tier->lastAnnounceStr, sizeof(st->lastAnnounceResult));
st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
}
if (tier->isAnnouncing)
{
st->announceState = TR_TRACKER_ACTIVE;
}
else if (!torrent->isRunning || tier->announceAt == 0)
{
st->announceState = TR_TRACKER_INACTIVE;
}
else if (tier->announceAt > now)
{
st->announceState = TR_TRACKER_WAITING;
st->nextAnnounceTime = tier->announceAt;
}
else
{
st->announceState = TR_TRACKER_QUEUED;
}
}
if (tier.isAnnouncing)
{
view.announceState = TR_TRACKER_ACTIVE;
}
else if (!tor.isRunning || tier.announceAt == 0)
{
view.announceState = TR_TRACKER_INACTIVE;
}
else if (tier.announceAt > now)
{
view.announceState = TR_TRACKER_WAITING;
view.nextAnnounceTime = tier.announceAt;
}
else
{
view.announceState = TR_TRACKER_QUEUED;
}
}
return ret;
TR_ASSERT(0 <= view.tier);
TR_ASSERT(view.tier < tor.tiers->tier_count);
return view;
}
void tr_announcerStatsFree(tr_tracker_stat* trackers, int /*trackerCount*/)
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
{
tr_free(trackers);
TR_ASSERT(tr_isTorrent(tor));
TR_ASSERT(tor->tiers != nullptr);
// find the nth tracker
struct tr_torrent_tiers const* const tt = tor->tiers;
if (nth >= size_t(tt->tracker_count))
{
return {};
}
auto const& tracker = tt->trackers[nth];
for (int i = 0; i < tt->tier_count; ++i)
{
tr_tier const& tier = tt->tiers[i];
for (int j = 0; j < tier.tracker_count; ++j)
{
if (&tier.trackers[j] == &tracker)
{
return trackerView(*tor, i, tier, tracker);
}
}
}
return {};
}
/***

View File

@ -93,9 +93,7 @@ void tr_announcerAddBytes(tr_torrent*, int up_down_or_corrupt, uint32_t byteCoun
time_t tr_announcerNextManualAnnounce(tr_torrent const*);
tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerCount);
void tr_announcerStatsFree(tr_tracker_stat* trackers, int trackerCount);
tr_tracker_view tr_announcerTracker(tr_torrent const* torrent, size_t i);
/***
****

View File

@ -128,6 +128,8 @@ void tr_file_priorities::set(tr_file_index_t const* files, size_t n, tr_priority
tr_priority_t tr_file_priorities::filePriority(tr_file_index_t file) const
{
TR_ASSERT(file < std::size(priorities_));
return priorities_[file];
}

View File

@ -426,39 +426,35 @@ static void addTrackers(tr_info const* info, tr_variant* trackers)
}
}
static void addTrackerStats(tr_tracker_stat const* st, int n, tr_variant* list)
static void addTrackerStats(tr_tracker_view const& tracker, tr_variant* list)
{
for (int i = 0; i < n; ++i)
{
tr_tracker_stat const* s = &st[i];
tr_variant* d = tr_variantListAddDict(list, 26);
tr_variantDictAddStr(d, TR_KEY_announce, s->announce);
tr_variantDictAddInt(d, TR_KEY_announceState, s->announceState);
tr_variantDictAddInt(d, TR_KEY_downloadCount, s->downloadCount);
tr_variantDictAddBool(d, TR_KEY_hasAnnounced, s->hasAnnounced);
tr_variantDictAddBool(d, TR_KEY_hasScraped, s->hasScraped);
tr_variantDictAddStr(d, TR_KEY_host, s->host);
tr_variantDictAddInt(d, TR_KEY_id, s->id);
tr_variantDictAddBool(d, TR_KEY_isBackup, s->isBackup);
tr_variantDictAddInt(d, TR_KEY_lastAnnouncePeerCount, s->lastAnnouncePeerCount);
tr_variantDictAddStr(d, TR_KEY_lastAnnounceResult, s->lastAnnounceResult);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceStartTime, s->lastAnnounceStartTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceSucceeded, s->lastAnnounceSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceTime, s->lastAnnounceTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceTimedOut, s->lastAnnounceTimedOut);
tr_variantDictAddStr(d, TR_KEY_lastScrapeResult, s->lastScrapeResult);
tr_variantDictAddInt(d, TR_KEY_lastScrapeStartTime, s->lastScrapeStartTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeSucceeded, s->lastScrapeSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastScrapeTime, s->lastScrapeTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeTimedOut, s->lastScrapeTimedOut);
tr_variantDictAddInt(d, TR_KEY_leecherCount, s->leecherCount);
tr_variantDictAddInt(d, TR_KEY_nextAnnounceTime, s->nextAnnounceTime);
tr_variantDictAddInt(d, TR_KEY_nextScrapeTime, s->nextScrapeTime);
tr_variantDictAddStr(d, TR_KEY_scrape, s->scrape);
tr_variantDictAddInt(d, TR_KEY_scrapeState, s->scrapeState);
tr_variantDictAddInt(d, TR_KEY_seederCount, s->seederCount);
tr_variantDictAddInt(d, TR_KEY_tier, s->tier);
}
auto* const d = tr_variantListAddDict(list, 26);
tr_variantDictAddStr(d, TR_KEY_announce, tracker.announce);
tr_variantDictAddInt(d, TR_KEY_announceState, tracker.announceState);
tr_variantDictAddInt(d, TR_KEY_downloadCount, tracker.downloadCount);
tr_variantDictAddBool(d, TR_KEY_hasAnnounced, tracker.hasAnnounced);
tr_variantDictAddBool(d, TR_KEY_hasScraped, tracker.hasScraped);
tr_variantDictAddStr(d, TR_KEY_host, tracker.host);
tr_variantDictAddInt(d, TR_KEY_id, tracker.id);
tr_variantDictAddBool(d, TR_KEY_isBackup, tracker.isBackup);
tr_variantDictAddInt(d, TR_KEY_lastAnnouncePeerCount, tracker.lastAnnouncePeerCount);
tr_variantDictAddStr(d, TR_KEY_lastAnnounceResult, tracker.lastAnnounceResult);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceStartTime, tracker.lastAnnounceStartTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceSucceeded, tracker.lastAnnounceSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceTime, tracker.lastAnnounceTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceTimedOut, tracker.lastAnnounceTimedOut);
tr_variantDictAddStr(d, TR_KEY_lastScrapeResult, tracker.lastScrapeResult);
tr_variantDictAddInt(d, TR_KEY_lastScrapeStartTime, tracker.lastScrapeStartTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeSucceeded, tracker.lastScrapeSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastScrapeTime, tracker.lastScrapeTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeTimedOut, tracker.lastScrapeTimedOut);
tr_variantDictAddInt(d, TR_KEY_leecherCount, tracker.leecherCount);
tr_variantDictAddInt(d, TR_KEY_nextAnnounceTime, tracker.nextAnnounceTime);
tr_variantDictAddInt(d, TR_KEY_nextScrapeTime, tracker.nextScrapeTime);
tr_variantDictAddStr(d, TR_KEY_scrape, tracker.scrape);
tr_variantDictAddInt(d, TR_KEY_scrapeState, tracker.scrapeState);
tr_variantDictAddInt(d, TR_KEY_seederCount, tracker.seederCount);
tr_variantDictAddInt(d, TR_KEY_tier, tracker.tier);
}
static void addPeers(tr_torrent* tor, tr_variant* list)
@ -791,11 +787,13 @@ static void initField(
case TR_KEY_trackerStats:
{
auto n = int{};
tr_tracker_stat* s = tr_torrentTrackers(tor, &n);
auto const n = tr_torrentTrackerCount(tor);
tr_variantInitList(initme, n);
addTrackerStats(s, n, initme);
tr_torrentTrackersFree(s, n);
for (size_t i = 0; i < n; ++i)
{
auto const& tracker = tr_torrentTracker(tor, i);
addTrackerStats(tracker, initme);
}
break;
}

View File

@ -1253,6 +1253,16 @@ size_t tr_torrentWebseedCount(tr_torrent const* tor)
return tor->webseedCount();
}
tr_tracker_view tr_torrentTracker(tr_torrent const* tor, size_t i)
{
return tr_announcerTracker(tor, i);
}
size_t tr_torrentTrackerCount(tr_torrent const* tor)
{
return tor->trackerCount();
}
/***
****
***/
@ -1269,18 +1279,6 @@ void tr_torrentPeersFree(tr_peer_stat* peers, int /*peerCount*/)
tr_free(peers);
}
tr_tracker_stat* tr_torrentTrackers(tr_torrent const* tor, int* setmeTrackerCount)
{
TR_ASSERT(tr_isTorrent(tor));
return tr_announcerStats(tor, setmeTrackerCount);
}
void tr_torrentTrackersFree(tr_tracker_stat* trackers, int trackerCount)
{
tr_announcerStatsFree(trackers, trackerCount);
}
void tr_torrentAvailability(tr_torrent const* tor, int8_t* tab, int size)
{
TR_ASSERT(tr_isTorrent(tor));
@ -1822,20 +1820,15 @@ static std::string buildTrackersString(tr_torrent const* tor)
{
auto buf = std::stringstream{};
int n = 0;
tr_tracker_stat* stats = tr_torrentTrackers(tor, &n);
for (int i = 0; i < n;)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
tr_tracker_stat const* s = &stats[i];
buf << s->host;
buf << tr_torrentTracker(tor, i).host;
if (++i < n)
{
buf << ',';
}
}
tr_torrentTrackersFree(stats, n);
return buf.str();
}

View File

@ -348,6 +348,13 @@ public:
return info.webseeds[i];
}
/// TRACKERS
auto trackerCount() const
{
return info.trackerCount;
}
/// CHECKSUMS
bool ensurePieceIsChecked(tr_piece_index_t piece)

View File

@ -1403,103 +1403,50 @@ enum tr_tracker_state
TR_TRACKER_ACTIVE = 3
};
struct tr_tracker_stat
/*
* Unlike other _view structs, it is safe to keep a tr_tracker_view copy.
* The announce, scrape, and host strings are interned & never go out-of-scope.
*/
struct tr_tracker_view
{
/* how many downloads this tracker knows of (-1 means it does not know) */
int downloadCount;
char const* announce; // full announce URL
char const* scrape; // full scrape URL
char const* host; // human-readable tracker name. (`${host}:${port}`)
/* whether or not we've ever sent this tracker an announcement */
bool hasAnnounced;
char lastAnnounceResult[128]; // if hasAnnounced, the human-readable result of latest announce
char lastScrapeResult[128]; // if hasScraped, the human-readable result of the latest scrape
/* whether or not we've ever scraped to this tracker */
bool hasScraped;
time_t lastAnnounceStartTime; // if hasAnnounced, when the latest announce request was sent
time_t lastAnnounceTime; // if hasAnnounced, when the latest announce reply was received
time_t nextAnnounceTime; // if announceState == TR_TRACKER_WAITING, time of next announce
/* human-readable string identifying the tracker.
* 'host' is a slight misnomer; the current format ist `$host:$port` */
char const* host;
time_t lastScrapeStartTime; // if hasScraped, when the latest scrape request was sent
time_t lastScrapeTime; // if hasScraped, when the latest scrape reply was received
time_t nextScrapeTime; // if scrapeState == TR_TRACKER_WAITING, time of next scrape
/* the full announce URL */
char const* announce;
int downloadCount; // number of times this torrent's been downloaded, or -1 if unknown
int lastAnnouncePeerCount; // if hasAnnounced, the number of peers the tracker gave us
int leecherCount; // number of leechers the tracker knows of, or -1 if unknown
int seederCount; // number of seeders the tracker knows of, or -1 if unknown
/* the full scrape URL */
char const* scrape;
int tier; // which tier this tracker is in
int id; // unique transmission-generated ID for use in libtransmission API
/* Transmission uses one tracker per tier,
* and the others are kept as backups */
bool isBackup;
tr_tracker_state announceState; // whether we're announcing, waiting to announce, etc.
tr_tracker_state scrapeState; // whether we're scraping, waiting to scrape, etc.
/* is the tracker announcing, waiting, queued, etc */
tr_tracker_state announceState;
/* is the tracker scraping, waiting, queued, etc */
tr_tracker_state scrapeState;
/* number of peers the tracker told us about last time.
* if "lastAnnounceSucceeded" is false, this field is undefined */
int lastAnnouncePeerCount;
/* human-readable string with the result of the last announce.
if "hasAnnounced" is false, this field is undefined */
char lastAnnounceResult[128];
/* when the last announce was sent to the tracker.
* if "hasAnnounced" is false, this field is undefined */
time_t lastAnnounceStartTime;
/* whether or not the last announce was a success.
if "hasAnnounced" is false, this field is undefined */
bool lastAnnounceSucceeded;
/* whether or not the last announce timed out. */
bool lastAnnounceTimedOut;
/* when the last announce was completed.
if "hasAnnounced" is false, this field is undefined */
time_t lastAnnounceTime;
/* human-readable string with the result of the last scrape.
* if "hasScraped" is false, this field is undefined */
char lastScrapeResult[128];
/* when the last scrape was sent to the tracker.
* if "hasScraped" is false, this field is undefined */
time_t lastScrapeStartTime;
/* whether or not the last scrape was a success.
if "hasAnnounced" is false, this field is undefined */
bool lastScrapeSucceeded;
/* whether or not the last scrape timed out. */
bool lastScrapeTimedOut;
/* when the last scrape was completed.
if "hasScraped" is false, this field is undefined */
time_t lastScrapeTime;
/* number of leechers this tracker knows of (-1 means it does not know) */
int leecherCount;
/* when the next periodic announce message will be sent out.
if announceState isn't TR_TRACKER_WAITING, this field is undefined */
time_t nextAnnounceTime;
/* when the next periodic scrape message will be sent out.
if scrapeState isn't TR_TRACKER_WAITING, this field is undefined */
time_t nextScrapeTime;
/* number of seeders this tracker knows of (-1 means it does not know) */
int seederCount;
/* which tier this tracker is in */
int tier;
/* used to match to a tr_tracker_info */
uint32_t id;
bool hasAnnounced; // true iff we've announced to this tracker during this session
bool hasScraped; // true iff we've scraped this tracker during this session
bool isBackup; // only one tracker per tier is used; the others are kept as backups
bool lastAnnounceSucceeded; // if hasAnnounced, whether or not the latest announce succeeded
bool lastAnnounceTimedOut; // true iff the latest announce request timed out
bool lastScrapeSucceeded; // if hasScraped, whether or not the latest scrape succeeded
bool lastScrapeTimedOut; // true iff the latest scrape request timed out
};
tr_tracker_stat* tr_torrentTrackers(tr_torrent const* torrent, int* setmeTrackerCount);
struct tr_tracker_view tr_torrentTracker(tr_torrent const* torrent, size_t i);
void tr_torrentTrackersFree(tr_tracker_stat* trackerStats, int trackerCount);
size_t tr_torrentTrackerCount(tr_torrent const* torrent);
/*
* This view structure is intended for short-term use. Its pointers are owned

View File

@ -20,7 +20,10 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <optional>
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/log.h>
#include <libtransmission/utils.h> // tr_new()
@ -736,25 +739,25 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSMutableArray*)allTrackerStats
{
int count;
tr_tracker_stat* stats = tr_torrentTrackers(fHandle, &count);
auto const count = tr_torrentTrackerCount(fHandle);
auto tier = std::optional<int>{};
NSMutableArray* trackers = [NSMutableArray arrayWithCapacity:(count > 0 ? count + (stats[count - 1].tier + 1) : 0)];
NSMutableArray* trackers = [NSMutableArray arrayWithCapacity:count * 2];
int prevTier = -1;
for (int i = 0; i < count; ++i)
for (size_t i = 0; i < count; ++i)
{
if (stats[i].tier != prevTier)
auto const tracker = tr_torrentTracker(fHandle, i);
if (!tier || tier != tracker.tier)
{
[trackers addObject:@{ @"Tier" : @(stats[i].tier + 1), @"Name" : self.name }];
prevTier = stats[i].tier;
tier = tracker.tier;
[trackers addObject:@{ @"Tier" : @(tracker.tier + 1), @"Name" : self.name }];
}
TrackerNode* tracker = [[TrackerNode alloc] initWithTrackerStat:&stats[i] torrent:self];
[trackers addObject:tracker];
auto* tracker_node = [[TrackerNode alloc] initWithTrackerView:&tracker torrent:self];
[trackers addObject:tracker_node];
}
tr_torrentTrackersFree(stats, count);
return trackers;
}
@ -1548,7 +1551,7 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSInteger)fileCount
{
return fInfo->fileCount;
return tr_torrentFileCount(fHandle);
}
- (CGFloat)fileProgress:(FileListNode*)node
@ -1792,21 +1795,19 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSString*)trackerSortKey
{
int count;
tr_tracker_stat* stats = tr_torrentTrackers(fHandle, &count);
NSString* best = nil;
for (int i = 0; i < count; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(fHandle); i < n; ++i)
{
NSString* tracker = @(stats[i].host);
if (!best || [tracker localizedCaseInsensitiveCompare:best] == NSOrderedAscending)
auto const tracker = tr_torrentTracker(fHandle, i);
NSString* host = @(tracker.host);
if (!best || [host localizedCaseInsensitiveCompare:best] == NSOrderedAscending)
{
best = tracker;
best = host;
}
}
tr_torrentTrackersFree(stats, count);
return best;
}

View File

@ -30,7 +30,7 @@
@property(nonatomic, weak, readonly) Torrent* torrent;
- (instancetype)initWithTrackerStat:(tr_tracker_stat*)stat torrent:(Torrent*)torrent;
- (instancetype)initWithTrackerView:(tr_tracker_view const*)stat torrent:(Torrent*)torrent;
- (BOOL)isEqual:(id)object;

View File

@ -26,10 +26,10 @@
@implementation TrackerNode
{
tr_tracker_stat fStat;
tr_tracker_view fStat;
}
- (instancetype)initWithTrackerStat:(tr_tracker_stat*)stat torrent:(Torrent*)torrent
- (instancetype)initWithTrackerView:(tr_tracker_view const*)stat torrent:(Torrent*)torrent
{
if ((self = [super init]))
{