mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-21 16:55:46 +03:00
Improve tracker entries handling
PR #19496. * Add torrent entry status to represent tracker error * Add torrent entry status to represent unreachable endpoint * Display tracker entry next/min announce time * Reset tracker entries when torrent is stopped
This commit is contained in:
parent
2f94c92df9
commit
7cd2445a49
11 changed files with 239 additions and 69 deletions
|
@ -4841,6 +4841,15 @@ void SessionImpl::handleTorrentMetadataReceived(TorrentImpl *const torrent)
|
|||
|
||||
void SessionImpl::handleTorrentPaused(TorrentImpl *const torrent)
|
||||
{
|
||||
torrent->resetTrackerEntries();
|
||||
|
||||
const auto &trackerEntries = torrent->trackers();
|
||||
QHash<QString, TrackerEntry> updatedTrackerEntries;
|
||||
updatedTrackerEntries.reserve(trackerEntries.size());
|
||||
for (const auto &trackerEntry : trackerEntries)
|
||||
updatedTrackerEntries.emplace(trackerEntry.url, trackerEntry);
|
||||
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
|
||||
|
||||
LogMsg(tr("Torrent paused. Torrent: \"%1\"").arg(torrent->name()));
|
||||
emit torrentPaused(torrent);
|
||||
}
|
||||
|
@ -5985,39 +5994,7 @@ void SessionImpl::processTrackerStatuses()
|
|||
|
||||
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
|
||||
{
|
||||
invokeAsync([this, torrentHandle = it.key(), updatedTrackers = it.value()]() mutable
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
|
||||
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
|
||||
, updatedTrackers = std::move(updatedTrackers)]
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
|
||||
if (!torrent)
|
||||
return;
|
||||
|
||||
QHash<QString, TrackerEntry> updatedTrackerEntries;
|
||||
updatedTrackerEntries.reserve(updatedTrackers.size());
|
||||
for (const lt::announce_entry &announceEntry : nativeTrackers)
|
||||
{
|
||||
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
|
||||
if (updatedTrackersIter == updatedTrackers.end())
|
||||
continue;
|
||||
|
||||
const auto &updateInfo = updatedTrackersIter.value();
|
||||
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
|
||||
const QString url = trackerEntry.url;
|
||||
updatedTrackerEntries.emplace(url, std::move(trackerEntry));
|
||||
}
|
||||
|
||||
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
|
||||
});
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
});
|
||||
updateTrackerEntries(it.key(), it.value());
|
||||
}
|
||||
|
||||
m_updatedTrackerEntries.clear();
|
||||
|
@ -6046,3 +6023,40 @@ void SessionImpl::loadStatistics()
|
|||
m_previouslyDownloaded = value[u"AlltimeDL"_s].toLongLong();
|
||||
m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong();
|
||||
}
|
||||
|
||||
void SessionImpl::updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<TrackerEntry::Endpoint, QMap<int, int>>> updatedTrackers)
|
||||
{
|
||||
invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
|
||||
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
|
||||
, updatedTrackers = std::move(updatedTrackers)]
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
|
||||
if (!torrent || torrent->isPaused())
|
||||
return;
|
||||
|
||||
QHash<QString, TrackerEntry> updatedTrackerEntries;
|
||||
updatedTrackerEntries.reserve(updatedTrackers.size());
|
||||
for (const lt::announce_entry &announceEntry : nativeTrackers)
|
||||
{
|
||||
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
|
||||
if (updatedTrackersIter == updatedTrackers.end())
|
||||
continue;
|
||||
|
||||
const auto &updateInfo = updatedTrackersIter.value();
|
||||
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
|
||||
const QString url = trackerEntry.url;
|
||||
updatedTrackerEntries.emplace(url, std::move(trackerEntry));
|
||||
}
|
||||
|
||||
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
|
||||
});
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -572,6 +572,8 @@ namespace BitTorrent
|
|||
void saveStatistics() const;
|
||||
void loadStatistics();
|
||||
|
||||
void updateTrackerEntries(lt::torrent_handle torrentHandle, QHash<std::string, QHash<TrackerEntry::Endpoint, QMap<int, int>>> updatedTrackers);
|
||||
|
||||
// BitTorrent
|
||||
lt::session *m_nativeSession = nullptr;
|
||||
NativeSessionExtension *m_nativeSessionExtension = nullptr;
|
||||
|
|
|
@ -78,6 +78,15 @@ namespace
|
|||
return entry;
|
||||
}
|
||||
|
||||
QDateTime fromLTTimePoint32(const lt::time_point32 &timePoint)
|
||||
{
|
||||
const auto ltNow = lt::clock_type::now();
|
||||
const auto qNow = QDateTime::currentDateTime();
|
||||
const auto secsSinceNow = lt::duration_cast<lt::seconds>(timePoint - ltNow + lt::milliseconds(500)).count();
|
||||
|
||||
return qNow.addSecs(secsSinceNow);
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
|
||||
, const lt::info_hash_t &hashes, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo)
|
||||
|
@ -103,8 +112,8 @@ namespace
|
|||
int numUpdating = 0;
|
||||
int numWorking = 0;
|
||||
int numNotWorking = 0;
|
||||
QString firstTrackerMessage;
|
||||
QString firstErrorMessage;
|
||||
int numTrackerError = 0;
|
||||
int numUnreachable = 0;
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size()) * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
|
||||
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
|
||||
|
@ -127,6 +136,8 @@ namespace
|
|||
trackerEndpoint.numSeeds = infoHash.scrape_complete;
|
||||
trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
|
||||
trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
|
||||
trackerEndpoint.nextAnnounceTime = fromLTTimePoint32(infoHash.next_announce);
|
||||
trackerEndpoint.minAnnounceTime = fromLTTimePoint32(infoHash.min_announce);
|
||||
|
||||
if (infoHash.updating)
|
||||
{
|
||||
|
@ -135,8 +146,21 @@ namespace
|
|||
}
|
||||
else if (infoHash.fails > 0)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::NotWorking;
|
||||
++numNotWorking;
|
||||
if (infoHash.last_error == lt::errors::tracker_failure)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::TrackerError;
|
||||
++numTrackerError;
|
||||
}
|
||||
else if (infoHash.last_error == lt::errors::announce_skipped)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::Unreachable;
|
||||
++numUnreachable;
|
||||
}
|
||||
else
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::NotWorking;
|
||||
++numNotWorking;
|
||||
}
|
||||
}
|
||||
else if (nativeEntry.verified)
|
||||
{
|
||||
|
@ -151,14 +175,10 @@ namespace
|
|||
if (!infoHash.message.empty())
|
||||
{
|
||||
trackerEndpoint.message = QString::fromStdString(infoHash.message);
|
||||
if (firstTrackerMessage.isEmpty())
|
||||
firstTrackerMessage = trackerEndpoint.message;
|
||||
}
|
||||
else if (infoHash.last_error)
|
||||
{
|
||||
trackerEndpoint.message = QString::fromLocal8Bit(infoHash.last_error.message());
|
||||
if (firstErrorMessage.isEmpty())
|
||||
firstErrorMessage = trackerEndpoint.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -175,6 +195,8 @@ namespace
|
|||
trackerEndpoint.numSeeds = endpoint.scrape_complete;
|
||||
trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
|
||||
trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
|
||||
trackerEndpoint.nextAnnounceTime = fromLTTimePoint32(endpoint.next_announce);
|
||||
trackerEndpoint.minAnnounceTime = fromLTTimePoint32(endpoint.min_announce);
|
||||
|
||||
if (endpoint.updating)
|
||||
{
|
||||
|
@ -183,8 +205,21 @@ namespace
|
|||
}
|
||||
else if (endpoint.fails > 0)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::NotWorking;
|
||||
++numNotWorking;
|
||||
if (endpoint.last_error == lt::errors::tracker_failure)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::TrackerError;
|
||||
++numTrackerError;
|
||||
}
|
||||
else if (endpoint.last_error == lt::errors::announce_skipped)
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::Unreachable;
|
||||
++numUnreachable;
|
||||
}
|
||||
else
|
||||
{
|
||||
trackerEndpoint.status = TrackerEntry::NotWorking;
|
||||
++numNotWorking;
|
||||
}
|
||||
}
|
||||
else if (nativeEntry.verified)
|
||||
{
|
||||
|
@ -199,14 +234,10 @@ namespace
|
|||
if (!endpoint.message.empty())
|
||||
{
|
||||
trackerEndpoint.message = QString::fromStdString(endpoint.message);
|
||||
if (firstTrackerMessage.isEmpty())
|
||||
firstTrackerMessage = trackerEndpoint.message;
|
||||
}
|
||||
else if (endpoint.last_error)
|
||||
{
|
||||
trackerEndpoint.message = QString::fromLocal8Bit(endpoint.last_error.message());
|
||||
if (firstErrorMessage.isEmpty())
|
||||
firstErrorMessage = trackerEndpoint.message;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -220,12 +251,18 @@ namespace
|
|||
else if (numWorking > 0)
|
||||
{
|
||||
trackerEntry.status = TrackerEntry::Working;
|
||||
trackerEntry.message = firstTrackerMessage;
|
||||
}
|
||||
else if (numNotWorking == numEndpoints)
|
||||
else if (numTrackerError > 0)
|
||||
{
|
||||
trackerEntry.status = TrackerEntry::TrackerError;
|
||||
}
|
||||
else if (numUnreachable == numEndpoints)
|
||||
{
|
||||
trackerEntry.status = TrackerEntry::Unreachable;
|
||||
}
|
||||
else if ((numUnreachable + numNotWorking) == numEndpoints)
|
||||
{
|
||||
trackerEntry.status = TrackerEntry::NotWorking;
|
||||
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1639,6 +1676,12 @@ TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceE
|
|||
return *it;
|
||||
}
|
||||
|
||||
void TorrentImpl::resetTrackerEntries()
|
||||
{
|
||||
for (auto &trackerEntry : m_trackerEntries)
|
||||
trackerEntry = {trackerEntry.url, trackerEntry.tier};
|
||||
}
|
||||
|
||||
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const
|
||||
{
|
||||
if (m_nativeStatus.torrent_file.expired())
|
||||
|
|
|
@ -264,7 +264,8 @@ namespace BitTorrent
|
|||
void saveResumeData(lt::resume_data_flags_t flags = {});
|
||||
void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob);
|
||||
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
|
||||
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<TrackerEntry::Endpoint, QMap<int, int> > &updateInfo);
|
||||
TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QHash<TrackerEntry::Endpoint, QMap<int, int>> &updateInfo);
|
||||
void resetTrackerEntries();
|
||||
|
||||
private:
|
||||
using EventTrigger = std::function<void ()>;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2023 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -31,6 +31,7 @@
|
|||
#include <libtorrent/socket.hpp>
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QStringView>
|
||||
|
@ -46,24 +47,30 @@ namespace BitTorrent
|
|||
NotContacted = 1,
|
||||
Working = 2,
|
||||
Updating = 3,
|
||||
NotWorking = 4
|
||||
NotWorking = 4,
|
||||
TrackerError = 5,
|
||||
Unreachable = 6
|
||||
};
|
||||
|
||||
struct EndpointStats
|
||||
{
|
||||
QString name {};
|
||||
|
||||
Status status = NotContacted;
|
||||
QString message {};
|
||||
|
||||
int numPeers = -1;
|
||||
int numSeeds = -1;
|
||||
int numLeeches = -1;
|
||||
int numDownloaded = -1;
|
||||
QString message {};
|
||||
QString name {};
|
||||
|
||||
QDateTime nextAnnounceTime;
|
||||
QDateTime minAnnounceTime;
|
||||
};
|
||||
|
||||
QString url {};
|
||||
int tier = 0;
|
||||
Status status = NotContacted;
|
||||
QString message {};
|
||||
|
||||
QHash<Endpoint, QHash<int, EndpointStats>> stats {};
|
||||
};
|
||||
|
|
|
@ -352,7 +352,7 @@ bool Utils::Misc::isPreviewable(const Path &filePath)
|
|||
return multimediaExtensions.contains(filePath.extension().toUpper());
|
||||
}
|
||||
|
||||
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap)
|
||||
QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglong maxCap, const TimeResolution resolution)
|
||||
{
|
||||
if (seconds < 0)
|
||||
return C_INFINITY;
|
||||
|
@ -363,29 +363,34 @@ QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglo
|
|||
return u"0"_s;
|
||||
|
||||
if (seconds < 60)
|
||||
return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
|
||||
{
|
||||
if (resolution == TimeResolution::Minutes)
|
||||
return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
|
||||
|
||||
return QCoreApplication::translate("misc", "%1s", "e.g: 10 seconds").arg(QString::number(seconds));
|
||||
}
|
||||
|
||||
qlonglong minutes = (seconds / 60);
|
||||
if (minutes < 60)
|
||||
return QCoreApplication::translate("misc", "%1m", "e.g: 10minutes").arg(QString::number(minutes));
|
||||
return QCoreApplication::translate("misc", "%1m", "e.g: 10 minutes").arg(QString::number(minutes));
|
||||
|
||||
qlonglong hours = (minutes / 60);
|
||||
if (hours < 24)
|
||||
{
|
||||
minutes -= (hours * 60);
|
||||
return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3hours 5minutes").arg(QString::number(hours), QString::number(minutes));
|
||||
return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3 hours 5 minutes").arg(QString::number(hours), QString::number(minutes));
|
||||
}
|
||||
|
||||
qlonglong days = (hours / 24);
|
||||
if (days < 365)
|
||||
{
|
||||
hours -= (days * 24);
|
||||
return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2days 10hours").arg(QString::number(days), QString::number(hours));
|
||||
return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2 days 10 hours").arg(QString::number(days), QString::number(hours));
|
||||
}
|
||||
|
||||
qlonglong years = (days / 365);
|
||||
days -= (years * 365);
|
||||
return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2years 10days").arg(QString::number(years), QString::number(days));
|
||||
return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2 years 10 days").arg(QString::number(years), QString::number(days));
|
||||
}
|
||||
|
||||
QString Utils::Misc::getUserIDString()
|
||||
|
|
|
@ -60,6 +60,12 @@ namespace Utils::Misc
|
|||
// YobiByte, // 1024^8
|
||||
};
|
||||
|
||||
enum class TimeResolution
|
||||
{
|
||||
Seconds,
|
||||
Minutes
|
||||
};
|
||||
|
||||
QString parseHtmlLinks(const QString &rawText);
|
||||
|
||||
void shutdownComputer(const ShutdownDialogAction &action);
|
||||
|
@ -82,7 +88,7 @@ namespace Utils::Misc
|
|||
|
||||
// Take a number of seconds and return a user-friendly
|
||||
// time duration like "1d 2h 10m".
|
||||
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1);
|
||||
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1, TimeResolution resolution = TimeResolution::Minutes);
|
||||
QString getUserIDString();
|
||||
|
||||
QString languageToLocalizedString(const QString &localeStr);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QHeaderView>
|
||||
#include <QLocale>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPointer>
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include "base/bittorrent/trackerentry.h"
|
||||
#include "base/global.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "gui/autoexpandabledialog.h"
|
||||
#include "gui/uithememanager.h"
|
||||
#include "propertieswidget.h"
|
||||
|
@ -128,6 +130,8 @@ TrackerListWidget::TrackerListWidget(PropertiesWidget *properties)
|
|||
headerItem()->setTextAlignment(COL_SEEDS, alignment);
|
||||
headerItem()->setTextAlignment(COL_LEECHES, alignment);
|
||||
headerItem()->setTextAlignment(COL_TIMES_DOWNLOADED, alignment);
|
||||
headerItem()->setTextAlignment(COL_NEXT_ANNOUNCE, alignment);
|
||||
headerItem()->setTextAlignment(COL_MIN_ANNOUNCE, alignment);
|
||||
|
||||
// Set hotkeys
|
||||
const auto *editHotkey = new QShortcut(Qt::Key_F2, this, nullptr, nullptr, Qt::WidgetShortcut);
|
||||
|
@ -145,6 +149,8 @@ TrackerListWidget::TrackerListWidget(PropertiesWidget *properties)
|
|||
item->setText(COL_LEECHES, QString());
|
||||
item->setText(COL_TIMES_DOWNLOADED, QString());
|
||||
item->setText(COL_MSG, QString());
|
||||
item->setText(COL_NEXT_ANNOUNCE, QString());
|
||||
item->setText(COL_MIN_ANNOUNCE, QString());
|
||||
});
|
||||
connect(this, &QTreeWidget::itemCollapsed, this, [](QTreeWidgetItem *item)
|
||||
{
|
||||
|
@ -153,6 +159,12 @@ TrackerListWidget::TrackerListWidget(PropertiesWidget *properties)
|
|||
item->setText(COL_LEECHES, item->data(COL_LEECHES, Qt::UserRole).toString());
|
||||
item->setText(COL_TIMES_DOWNLOADED, item->data(COL_TIMES_DOWNLOADED, Qt::UserRole).toString());
|
||||
item->setText(COL_MSG, item->data(COL_MSG, Qt::UserRole).toString());
|
||||
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
const auto secsToNextAnnounce = now.secsTo(item->data(COL_NEXT_ANNOUNCE, Qt::UserRole).toDateTime());
|
||||
item->setText(COL_NEXT_ANNOUNCE, Utils::Misc::userFriendlyDuration(secsToNextAnnounce, -1, Utils::Misc::TimeResolution::Seconds));
|
||||
const auto secsToMinAnnounce = now.secsTo(item->data(COL_MIN_ANNOUNCE, Qt::UserRole).toDateTime());
|
||||
item->setText(COL_MIN_ANNOUNCE, Utils::Misc::userFriendlyDuration(secsToMinAnnounce, -1, Utils::Misc::TimeResolution::Seconds));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -386,8 +398,11 @@ void TrackerListWidget::loadTrackers()
|
|||
|
||||
const auto setAlignment = [](QTreeWidgetItem *item)
|
||||
{
|
||||
for (const TrackerListColumn col : {COL_TIER, COL_PROTOCOL, COL_PEERS, COL_SEEDS, COL_LEECHES, COL_TIMES_DOWNLOADED})
|
||||
for (const TrackerListColumn col : {COL_TIER, COL_PROTOCOL, COL_PEERS, COL_SEEDS
|
||||
, COL_LEECHES, COL_TIMES_DOWNLOADED, COL_NEXT_ANNOUNCE, COL_MIN_ANNOUNCE})
|
||||
{
|
||||
item->setTextAlignment(col, (Qt::AlignRight | Qt::AlignVCenter));
|
||||
}
|
||||
};
|
||||
|
||||
const auto prettyCount = [](const int val)
|
||||
|
@ -405,6 +420,10 @@ void TrackerListWidget::loadTrackers()
|
|||
return tr("Updating...");
|
||||
case BitTorrent::TrackerEntry::Status::NotWorking:
|
||||
return tr("Not working");
|
||||
case BitTorrent::TrackerEntry::Status::TrackerError:
|
||||
return tr("Tracker error");
|
||||
case BitTorrent::TrackerEntry::Status::Unreachable:
|
||||
return tr("Unreachable");
|
||||
case BitTorrent::TrackerEntry::Status::NotContacted:
|
||||
return tr("Not contacted yet");
|
||||
}
|
||||
|
@ -432,10 +451,15 @@ void TrackerListWidget::loadTrackers()
|
|||
oldTrackerURLs.removeOne(trackerURL);
|
||||
}
|
||||
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
|
||||
int peersMax = -1;
|
||||
int seedsMax = -1;
|
||||
int leechesMax = -1;
|
||||
int downloadedMax = -1;
|
||||
QDateTime nextAnnounceTime;
|
||||
QDateTime minAnnounceTime;
|
||||
QString message;
|
||||
|
||||
int index = 0;
|
||||
for (const auto &endpoint : entry.stats)
|
||||
|
@ -450,6 +474,26 @@ void TrackerListWidget::loadTrackers()
|
|||
leechesMax = std::max(leechesMax, protocolStats.numLeeches);
|
||||
downloadedMax = std::max(downloadedMax, protocolStats.numDownloaded);
|
||||
|
||||
if (protocolStats.status == entry.status)
|
||||
{
|
||||
if (!nextAnnounceTime.isValid() || (nextAnnounceTime > protocolStats.nextAnnounceTime))
|
||||
{
|
||||
nextAnnounceTime = protocolStats.nextAnnounceTime;
|
||||
minAnnounceTime = protocolStats.minAnnounceTime;
|
||||
if ((protocolStats.status != BitTorrent::TrackerEntry::Status::Working)
|
||||
|| !protocolStats.message.isEmpty())
|
||||
{
|
||||
message = protocolStats.message;
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolStats.status == BitTorrent::TrackerEntry::Status::Working)
|
||||
{
|
||||
if (message.isEmpty())
|
||||
message = protocolStats.message;
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem *child = (index < item->childCount()) ? item->child(index) : new QTreeWidgetItem(item);
|
||||
child->setText(COL_URL, protocolStats.name);
|
||||
child->setText(COL_PROTOCOL, tr("v%1").arg(protocolVersion));
|
||||
|
@ -460,6 +504,8 @@ void TrackerListWidget::loadTrackers()
|
|||
child->setText(COL_TIMES_DOWNLOADED, prettyCount(protocolStats.numDownloaded));
|
||||
child->setText(COL_MSG, protocolStats.message);
|
||||
child->setToolTip(COL_MSG, protocolStats.message);
|
||||
child->setText(COL_NEXT_ANNOUNCE, Utils::Misc::userFriendlyDuration(now.secsTo(protocolStats.nextAnnounceTime), -1, Utils::Misc::TimeResolution::Seconds));
|
||||
child->setText(COL_MIN_ANNOUNCE, Utils::Misc::userFriendlyDuration(now.secsTo(protocolStats.minAnnounceTime), -1, Utils::Misc::TimeResolution::Seconds));
|
||||
setAlignment(child);
|
||||
++index;
|
||||
}
|
||||
|
@ -475,7 +521,9 @@ void TrackerListWidget::loadTrackers()
|
|||
item->setData(COL_SEEDS, Qt::UserRole, prettyCount(seedsMax));
|
||||
item->setData(COL_LEECHES, Qt::UserRole, prettyCount(leechesMax));
|
||||
item->setData(COL_TIMES_DOWNLOADED, Qt::UserRole, prettyCount(downloadedMax));
|
||||
item->setData(COL_MSG, Qt::UserRole, entry.message);
|
||||
item->setData(COL_MSG, Qt::UserRole, message);
|
||||
item->setData(COL_NEXT_ANNOUNCE, Qt::UserRole, nextAnnounceTime);
|
||||
item->setData(COL_MIN_ANNOUNCE, Qt::UserRole, minAnnounceTime);
|
||||
if (!item->isExpanded())
|
||||
{
|
||||
item->setText(COL_PEERS, item->data(COL_PEERS, Qt::UserRole).toString());
|
||||
|
@ -483,6 +531,10 @@ void TrackerListWidget::loadTrackers()
|
|||
item->setText(COL_LEECHES, item->data(COL_LEECHES, Qt::UserRole).toString());
|
||||
item->setText(COL_TIMES_DOWNLOADED, item->data(COL_TIMES_DOWNLOADED, Qt::UserRole).toString());
|
||||
item->setText(COL_MSG, item->data(COL_MSG, Qt::UserRole).toString());
|
||||
const auto secsToNextAnnounce = now.secsTo(item->data(COL_NEXT_ANNOUNCE, Qt::UserRole).toDateTime());
|
||||
item->setText(COL_NEXT_ANNOUNCE, Utils::Misc::userFriendlyDuration(secsToNextAnnounce, -1, Utils::Misc::TimeResolution::Seconds));
|
||||
const auto secsToMinAnnounce = now.secsTo(item->data(COL_MIN_ANNOUNCE, Qt::UserRole).toDateTime());
|
||||
item->setText(COL_MIN_ANNOUNCE, Utils::Misc::userFriendlyDuration(secsToMinAnnounce, -1, Utils::Misc::TimeResolution::Seconds));
|
||||
}
|
||||
setAlignment(item);
|
||||
}
|
||||
|
@ -687,6 +739,8 @@ QStringList TrackerListWidget::headerLabels()
|
|||
, tr("Leeches")
|
||||
, tr("Times Downloaded")
|
||||
, tr("Message")
|
||||
, tr("Next announce")
|
||||
, tr("Min announce")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
COL_LEECHES,
|
||||
COL_TIMES_DOWNLOADED,
|
||||
COL_MSG,
|
||||
COL_NEXT_ANNOUNCE,
|
||||
COL_MIN_ANNOUNCE,
|
||||
|
||||
COL_COUNT
|
||||
};
|
||||
|
|
|
@ -335,7 +335,15 @@ void TrackersFilterWidget::handleTrackerEntriesUpdated(const BitTorrent::Torrent
|
|||
errored.remove(trackerEntry.url);
|
||||
}
|
||||
|
||||
if (trackerEntry.message.isEmpty())
|
||||
const bool hasNoWarningMessages = std::all_of(trackerEntry.stats.cbegin(), trackerEntry.stats.cend(), [](const auto &endpoint)
|
||||
{
|
||||
return std::all_of(endpoint.cbegin(), endpoint.cend()
|
||||
, [](const BitTorrent::TrackerEntry::EndpointStats &protocolStats)
|
||||
{
|
||||
return protocolStats.message.isEmpty() || (protocolStats.status != BitTorrent::TrackerEntry::Working);
|
||||
});
|
||||
});
|
||||
if (hasNoWarningMessages)
|
||||
{
|
||||
if (warningHashesIt != m_warnings.end())
|
||||
{
|
||||
|
@ -350,7 +358,9 @@ void TrackersFilterWidget::handleTrackerEntriesUpdated(const BitTorrent::Torrent
|
|||
warningHashesIt.value().insert(trackerEntry.url);
|
||||
}
|
||||
}
|
||||
else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking)
|
||||
else if ((trackerEntry.status == BitTorrent::TrackerEntry::NotWorking)
|
||||
|| (trackerEntry.status == BitTorrent::TrackerEntry::TrackerError)
|
||||
|| (trackerEntry.status == BitTorrent::TrackerEntry::Unreachable))
|
||||
{
|
||||
if (errorHashesIt == m_errors.end())
|
||||
errorHashesIt = m_errors.insert(id, {});
|
||||
|
|
|
@ -487,6 +487,9 @@ void TorrentsController::trackersAction()
|
|||
int numSeeds = -1;
|
||||
int numLeeches = -1;
|
||||
int numDownloaded = -1;
|
||||
QDateTime nextAnnounceTime;
|
||||
QDateTime minAnnounceTime;
|
||||
QString message;
|
||||
for (const auto &endpoint : tracker.stats)
|
||||
{
|
||||
for (const auto &protocolStat : endpoint)
|
||||
|
@ -495,15 +498,38 @@ void TorrentsController::trackersAction()
|
|||
numSeeds = std::max(numSeeds, protocolStat.numSeeds);
|
||||
numLeeches = std::max(numLeeches, protocolStat.numLeeches);
|
||||
numDownloaded = std::max(numDownloaded, protocolStat.numDownloaded);
|
||||
|
||||
if (protocolStat.status == tracker.status)
|
||||
{
|
||||
if (!nextAnnounceTime.isValid() || (nextAnnounceTime > protocolStat.nextAnnounceTime))
|
||||
{
|
||||
nextAnnounceTime = protocolStat.nextAnnounceTime;
|
||||
minAnnounceTime = protocolStat.minAnnounceTime;
|
||||
if ((protocolStat.status != BitTorrent::TrackerEntry::Status::Working)
|
||||
|| !protocolStat.message.isEmpty())
|
||||
{
|
||||
message = protocolStat.message;
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolStat.status == BitTorrent::TrackerEntry::Status::Working)
|
||||
{
|
||||
if (message.isEmpty())
|
||||
message = protocolStat.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool isNotWorking = (tracker.status == BitTorrent::TrackerEntry::Status::NotWorking)
|
||||
|| (tracker.status == BitTorrent::TrackerEntry::Status::TrackerError)
|
||||
|| (tracker.status == BitTorrent::TrackerEntry::Status::Unreachable);
|
||||
trackerList << QJsonObject
|
||||
{
|
||||
{KEY_TRACKER_URL, tracker.url},
|
||||
{KEY_TRACKER_TIER, tracker.tier},
|
||||
{KEY_TRACKER_STATUS, static_cast<int>(tracker.status)},
|
||||
{KEY_TRACKER_MSG, tracker.message},
|
||||
{KEY_TRACKER_STATUS, static_cast<int>((isNotWorking ? BitTorrent::TrackerEntry::Status::NotWorking : tracker.status))},
|
||||
{KEY_TRACKER_MSG, message},
|
||||
{KEY_TRACKER_PEERS_COUNT, numPeers},
|
||||
{KEY_TRACKER_SEEDS_COUNT, numSeeds},
|
||||
{KEY_TRACKER_LEECHES_COUNT, numLeeches},
|
||||
|
|
Loading…
Reference in a new issue