Revamp tracker entries handling

PR #17017.
This commit is contained in:
Vladimir Golovnev
2022-05-22 09:09:11 +03:00
committed by GitHub
parent 8c0cd09823
commit 7e0cd223fd
22 changed files with 366 additions and 334 deletions

View File

@@ -13,6 +13,7 @@ add_library(qbt_base STATIC
bittorrent/customstorage.h
bittorrent/dbresumedatastorage.h
bittorrent/downloadpriority.h
bittorrent/extensiondata.h
bittorrent/filesearcher.h
bittorrent/filterparserthread.h
bittorrent/infohash.h

View File

@@ -12,6 +12,7 @@ HEADERS += \
$$PWD/bittorrent/customstorage.h \
$$PWD/bittorrent/downloadpriority.h \
$$PWD/bittorrent/dbresumedatastorage.h \
$$PWD/bittorrent/extensiondata.h \
$$PWD/bittorrent/filesearcher.h \
$$PWD/bittorrent/filterparserthread.h \
$$PWD/bittorrent/infohash.h \

View File

@@ -0,0 +1,45 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2022 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <vector>
#include <libtorrent/announce_entry.hpp>
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/client_data.hpp>
using LTClientData = lt::client_data_t;
#else
using LTClientData = void *;
#endif
struct ExtensionData
{
std::vector<lt::announce_entry> trackers;
};

View File

@@ -100,8 +100,15 @@ MagnetUri::MagnetUri(const QString &source)
m_name = QString::fromStdString(m_addTorrentParams.name);
m_trackers.reserve(static_cast<decltype(m_trackers)::size_type>(m_addTorrentParams.trackers.size()));
for (const std::string &tracker : m_addTorrentParams.trackers)
m_trackers.append({QString::fromStdString(tracker)});
int tier = 0;
auto tierIter = m_addTorrentParams.tracker_tiers.cbegin();
for (const std::string &url : m_addTorrentParams.trackers)
{
if (tierIter != m_addTorrentParams.tracker_tiers.cend())
tier = *tierIter++;
m_trackers.append({QString::fromStdString(url), tier});
}
m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(m_addTorrentParams.url_seeds.size()));
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 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
@@ -30,6 +30,7 @@
#include <libtorrent/alert_types.hpp>
#include "extensiondata.h"
#include "nativetorrentextension.h"
namespace
@@ -49,9 +50,9 @@ lt::feature_flags_t NativeSessionExtension::implemented_features()
return alert_feature;
}
std::shared_ptr<lt::torrent_plugin> NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, ClientData)
std::shared_ptr<lt::torrent_plugin> NativeSessionExtension::new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData)
{
return std::make_shared<NativeTorrentExtension>(torrentHandle);
return std::make_shared<NativeTorrentExtension>(torrentHandle, static_cast<ExtensionData *>(clientData));
}
void NativeSessionExtension::on_alert(const lt::alert *alert)

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 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
@@ -30,15 +30,11 @@
#include <libtorrent/extensions.hpp>
#include "extensiondata.h"
class NativeSessionExtension final : public lt::plugin
{
#ifdef QBT_USES_LIBTORRENT2
using ClientData = lt::client_data_t;
#else
using ClientData = void *;
#endif
lt::feature_flags_t implemented_features() override;
std::shared_ptr<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, ClientData) override;
std::shared_ptr<lt::torrent_plugin> new_torrent(const lt::torrent_handle &torrentHandle, LTClientData clientData) override;
void on_alert(const lt::alert *alert) override;
};

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 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
@@ -38,10 +38,19 @@ namespace
}
}
NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle)
NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle, ExtensionData *data)
: m_torrentHandle {torrentHandle}
, m_data {data}
{
on_state(m_torrentHandle.status({}).state);
if (m_data)
m_data->trackers = m_torrentHandle.trackers();
}
NativeTorrentExtension::~NativeTorrentExtension()
{
delete m_data;
}
bool NativeTorrentExtension::on_pause()

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2020-2022 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,10 +31,13 @@
#include <libtorrent/extensions.hpp>
#include <libtorrent/torrent_handle.hpp>
#include "extensiondata.h"
class NativeTorrentExtension final : public lt::torrent_plugin
{
public:
explicit NativeTorrentExtension(const lt::torrent_handle &torrentHandle);
NativeTorrentExtension(const lt::torrent_handle &torrentHandle, ExtensionData *data);
~NativeTorrentExtension();
private:
bool on_pause() override;
@@ -42,4 +45,5 @@ private:
lt::torrent_handle m_torrentHandle;
lt::torrent_status::state_t m_state = lt::torrent_status::checking_resume_data;
ExtensionData *m_data = nullptr;
};

View File

@@ -95,6 +95,7 @@
#include "customstorage.h"
#include "dbresumedatastorage.h"
#include "downloadpriority.h"
#include "extensiondata.h"
#include "filesearcher.h"
#include "filterparserthread.h"
#include "loadtorrentparams.h"
@@ -292,67 +293,6 @@ namespace
return {};
}
#endif
#ifdef QBT_USES_LIBTORRENT2
TrackerEntryUpdateInfo getTrackerEntryUpdateInfo(const lt::announce_entry &nativeEntry, const lt::info_hash_t &hashes)
#else
TrackerEntryUpdateInfo getTrackerEntryUpdateInfo(const lt::announce_entry &nativeEntry)
#endif
{
TrackerEntryUpdateInfo result {};
int numUpdating = 0;
int numWorking = 0;
int numNotWorking = 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)
{
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
{
if (hashes.has(protocolVersion))
{
const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion];
if (!result.hasMessages)
result.hasMessages = !infoHash.message.empty();
if (infoHash.updating)
++numUpdating;
else if (infoHash.fails > 0)
++numNotWorking;
else if (nativeEntry.verified)
++numWorking;
}
}
}
#else
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
if (!result.hasMessages)
result.hasMessages = !endpoint.message.empty();
if (endpoint.updating)
++numUpdating;
else if (endpoint.fails > 0)
++numNotWorking;
else if (nativeEntry.verified)
++numWorking;
}
#endif
if (numEndpoints > 0)
{
if (numUpdating > 0)
result.status = TrackerEntry::Updating;
else if (numWorking > 0)
result.status = TrackerEntry::Working;
else if (numNotWorking == numEndpoints)
result.status = TrackerEntry::NotWorking;
}
return result;
}
}
const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
@@ -2327,17 +2267,6 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]);
if (isAddTrackersEnabled() && !torrentInfo.isPrivate())
{
p.trackers.reserve(static_cast<std::size_t>(m_additionalTrackerList.size()));
p.tracker_tiers.reserve(static_cast<std::size_t>(m_additionalTrackerList.size()));
for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList))
{
p.trackers.push_back(trackerEntry.url.toStdString());
p.tracker_tiers.push_back(trackerEntry.tier);
}
}
p.ti = torrentInfo.nativeInfo();
}
else
@@ -2351,6 +2280,18 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
p.save_path = actualSavePath.toString().toStdString();
if (isAddTrackersEnabled() && !(hasMetadata && p.ti->priv()))
{
p.trackers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerList.size()));
p.tracker_tiers.reserve(p.trackers.size() + static_cast<std::size_t>(m_additionalTrackerList.size()));
p.tracker_tiers.resize(p.trackers.size(), 0);
for (const TrackerEntry &trackerEntry : asConst(m_additionalTrackerList))
{
p.trackers.push_back(trackerEntry.url.toStdString());
p.tracker_tiers.push_back(trackerEntry.tier);
}
}
p.upload_limit = addTorrentParams.uploadLimit;
p.download_limit = addTorrentParams.downloadLimit;
@@ -2392,6 +2333,7 @@ bool Session::loadTorrent(LoadTorrentParams params)
{
lt::add_torrent_params &p = params.ltAddTorrentParams;
p.userdata = LTClientData(new ExtensionData);
#ifndef QBT_USES_LIBTORRENT2
p.storage = customStorageConstructor;
#endif
@@ -4147,12 +4089,12 @@ void Session::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVect
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers)
void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers)
{
for (const TrackerEntry &deletedTracker : deletedTrackers)
LogMsg(tr("Removed tracker from torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), deletedTracker.url));
for (const QString &deletedTracker : deletedTrackers)
LogMsg(tr("Removed tracker from torrent. Torrent: \"%1\". Tracker: \"%2\"").arg(torrent->name(), deletedTracker));
emit trackersRemoved(torrent, deletedTrackers);
if (torrent->trackers().empty())
if (torrent->trackers().isEmpty())
emit trackerlessStateChanged(torrent, true);
emit trackersChanged(torrent);
}
@@ -4831,6 +4773,7 @@ void Session::handleAlert(const lt::alert *a)
case lt::session_stats_alert::alert_type:
handleSessionStatsAlert(static_cast<const lt::session_stats_alert*>(a));
break;
case lt::tracker_announce_alert::alert_type:
case lt::tracker_error_alert::alert_type:
case lt::tracker_reply_alert::alert_type:
case lt::tracker_warning_alert::alert_type:
@@ -5374,43 +5317,30 @@ void Session::handleTrackerAlert(const lt::tracker_alert *a)
if (!torrent)
return;
const QByteArray trackerURL {a->tracker_url()};
const auto trackerURL = QString::fromUtf8(a->tracker_url());
m_updatedTrackerEntries[torrent].insert(trackerURL);
if (a->type() == lt::tracker_reply_alert::alert_type)
{
const int numPeers = static_cast<const lt::tracker_reply_alert *>(a)->num_peers;
torrent->updatePeerCount(QString::fromUtf8(trackerURL), a->local_endpoint, numPeers);
torrent->updatePeerCount(trackerURL, a->local_endpoint, numPeers);
}
}
void Session::processTrackerStatuses()
{
QHash<Torrent *, QHash<QString, TrackerEntryUpdateInfo>> updateInfos;
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
{
TorrentImpl *torrent = it.key();
const QSet<QByteArray> &updatedTrackers = it.value();
auto torrent = static_cast<TorrentImpl *>(it.key());
const QSet<QString> &updatedTrackers = it.value();
const std::vector<lt::announce_entry> trackerList = torrent->nativeHandle().trackers();
for (const lt::announce_entry &announceEntry : trackerList)
{
const auto trackerURL = QByteArray::fromRawData(announceEntry.url.c_str(), announceEntry.url.size());
if (!updatedTrackers.contains(trackerURL))
continue;
#ifdef QBT_USES_LIBTORRENT2
const TrackerEntryUpdateInfo updateInfo = getTrackerEntryUpdateInfo(announceEntry, torrent->nativeHandle().info_hashes());
#else
const TrackerEntryUpdateInfo updateInfo = getTrackerEntryUpdateInfo(announceEntry);
#endif
if ((updateInfo.status == TrackerEntry::Working) || (updateInfo.status == TrackerEntry::NotWorking))
updateInfos[torrent][QString::fromUtf8(trackerURL)] = updateInfo;
}
for (const QString &trackerURL : updatedTrackers)
torrent->invalidateTrackerEntry(trackerURL);
}
m_updatedTrackerEntries.clear();
if (!updateInfos.isEmpty())
emit trackerEntriesUpdated(updateInfos);
if (!m_updatedTrackerEntries.isEmpty())
{
emit trackerEntriesUpdated(m_updatedTrackerEntries);
m_updatedTrackerEntries.clear();
}
}

View File

@@ -211,12 +211,6 @@ namespace BitTorrent
} disk;
};
struct TrackerEntryUpdateInfo
{
TrackerEntry::Status status = TrackerEntry::NotContacted;
bool hasMessages = false;
};
class Session final : public QObject
{
Q_OBJECT
@@ -518,7 +512,7 @@ namespace BitTorrent
void handleTorrentChecked(TorrentImpl *const torrent);
void handleTorrentFinished(TorrentImpl *const torrent);
void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers);
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QStringList &deletedTrackers);
void handleTorrentTrackersChanged(TorrentImpl *const torrent);
void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds);
@@ -563,10 +557,10 @@ namespace BitTorrent
void trackerlessStateChanged(Torrent *torrent, bool trackerless);
void trackersAdded(Torrent *torrent, const QVector<TrackerEntry> &trackers);
void trackersChanged(Torrent *torrent);
void trackersRemoved(Torrent *torrent, const QVector<TrackerEntry> &trackers);
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
void trackerSuccess(Torrent *torrent, const QString &tracker);
void trackerWarning(Torrent *torrent, const QString &tracker);
void trackerEntriesUpdated(const QHash<Torrent *, QHash<QString, TrackerEntryUpdateInfo>> &updateInfos);
void trackerEntriesUpdated(const QHash<Torrent *, QSet<QString>> &updateInfos);
private slots:
void configureDeferred();
@@ -823,7 +817,7 @@ namespace BitTorrent
QMap<QString, CategoryOptions> m_categories;
QSet<QString> m_tags;
QHash<TorrentImpl *, QSet<QByteArray>> m_updatedTrackerEntries;
QHash<Torrent *, QSet<QString>> m_updatedTrackerEntries;
// I/O errored torrents
QSet<TorrentID> m_recentErroredTorrents;

View File

@@ -224,7 +224,6 @@ namespace BitTorrent
virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<QString> trackerURLs() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0;
@@ -294,8 +293,9 @@ namespace BitTorrent
virtual void setPEXDisabled(bool disable) = 0;
virtual void setLSDDisabled(bool disable) = 0;
virtual void flushCache() const = 0;
virtual void addTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void replaceTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void addTrackers(QVector<TrackerEntry> trackers) = 0;
virtual void removeTrackers(const QStringList &trackers) = 0;
virtual void replaceTrackers(QVector<TrackerEntry> trackers) = 0;
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;

View File

@@ -48,6 +48,7 @@
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QSet>
#include <QStringList>
#include <QUrl>
@@ -59,6 +60,7 @@
#include "base/utils/string.h"
#include "common.h"
#include "downloadpriority.h"
#include "extensiondata.h"
#include "loadtorrentparams.h"
#include "ltqbitarray.h"
#include "ltqhash.h"
@@ -66,7 +68,6 @@
#include "peeraddress.h"
#include "peerinfo.h"
#include "session.h"
#include "trackerentry.h"
using namespace BitTorrent;
@@ -80,14 +81,16 @@ namespace
}
#ifdef QBT_USES_LIBTORRENT2
TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
#else
TrackerEntry fromNativeAnnounceEntry(const lt::announce_entry &nativeEntry
, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
#endif
{
TrackerEntry trackerEntry {QString::fromStdString(nativeEntry.url), nativeEntry.tier};
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url));
trackerEntry.tier = nativeEntry.tier;
int numUpdating = 0;
int numWorking = 0;
@@ -96,7 +99,6 @@ namespace
QString firstErrorMessage;
#ifdef QBT_USES_LIBTORRENT2
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1));
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
@@ -106,8 +108,7 @@ namespace
const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion];
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.protocolVersion = (protocolVersion == lt::protocol_version::V1) ? 1 : 2;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numPeers = updateInfo.value(endpoint.local_endpoint, trackerEndpoint.numPeers);
trackerEndpoint.numSeeds = infoHash.scrape_complete;
trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
@@ -136,7 +137,7 @@ namespace
const QString errorMessage = QString::fromLocal8Bit(infoHash.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.stats[endpoint.local_endpoint][(protocolVersion == lt::protocol_version::V1) ? 1 : 2] = trackerEndpoint;
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
@@ -151,11 +152,10 @@ namespace
}
#else
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numPeers = updateInfo.value(endpoint.local_endpoint, trackerEndpoint.numPeers);
trackerEndpoint.numSeeds = endpoint.scrape_complete;
trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
@@ -184,7 +184,7 @@ namespace
const QString errorMessage = QString::fromLocal8Bit(endpoint.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.stats[endpoint.local_endpoint][1] = trackerEndpoint;
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
@@ -214,8 +214,6 @@ namespace
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
}
}
return trackerEntry;
}
void initializeStatus(lt::torrent_status &status, const lt::add_torrent_params &params)
@@ -308,6 +306,11 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
}
}
const auto extensionData = static_cast<ExtensionData *>(m_ltAddTorrentParams.userdata);
m_trackerEntries.reserve(static_cast<decltype(m_trackerEntries)::size_type>(extensionData->trackers.size()));
for (const lt::announce_entry &announceEntry : extensionData->trackers)
m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier});
initializeStatus(m_nativeStatus, m_ltAddTorrentParams);
updateState();
@@ -523,107 +526,78 @@ void TorrentImpl::setAutoManaged(const bool enable)
m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
}
QVector<QString> TorrentImpl::trackerURLs() const
{
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
QVector<QString> urls;
urls.reserve(static_cast<decltype(urls)::size_type>(nativeTrackers.size()));
for (const lt::announce_entry &tracker : nativeTrackers)
{
const QString trackerURL = QString::fromStdString(tracker.url);
urls.push_back(trackerURL);
}
return urls;
}
QVector<TrackerEntry> TorrentImpl::trackers() const
{
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
if (!m_updatedTrackerEntries.isEmpty())
refreshTrackerEntries();
QVector<TrackerEntry> entries;
entries.reserve(static_cast<decltype(entries)::size_type>(nativeTrackers.size()));
for (const lt::announce_entry &tracker : nativeTrackers)
{
const QString trackerURL = QString::fromStdString(tracker.url);
#ifdef QBT_USES_LIBTORRENT2
entries << fromNativeAnnounceEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]);
#else
entries << fromNativeAnnounceEntry(tracker, m_trackerPeerCounts[trackerURL]);
#endif
}
return entries;
return m_trackerEntries;
}
void TorrentImpl::addTrackers(const QVector<TrackerEntry> &trackers)
void TorrentImpl::addTrackers(QVector<TrackerEntry> trackers)
{
QSet<TrackerEntry> currentTrackers;
for (const lt::announce_entry &entry : m_nativeHandle.trackers())
currentTrackers.insert({QString::fromStdString(entry.url), entry.tier});
// TODO: use std::erase_if() in C++20
trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end());
QVector<TrackerEntry> newTrackers;
newTrackers.reserve(trackers.size());
const auto newTrackers = QSet<TrackerEntry>(trackers.cbegin(), trackers.cend())
- QSet<TrackerEntry>(m_trackerEntries.cbegin(), m_trackerEntries.cend());
if (newTrackers.isEmpty())
return;
trackers = QVector<TrackerEntry>(newTrackers.cbegin(), newTrackers.cend());
for (const TrackerEntry &tracker : trackers)
m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier));
m_trackerEntries.append(trackers);
std::sort(m_trackerEntries.begin(), m_trackerEntries.end()
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; });
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentTrackersAdded(this, trackers);
}
void TorrentImpl::removeTrackers(const QStringList &trackers)
{
QStringList removedTrackers = trackers;
for (const QString &tracker : trackers)
{
if (!currentTrackers.contains(tracker))
{
m_nativeHandle.add_tracker(makeNativeAnnounceEntry(tracker.url, tracker.tier));
newTrackers << tracker;
}
if (!m_trackerEntries.removeOne({tracker}))
removedTrackers.removeOne(tracker);
}
if (!newTrackers.isEmpty())
std::vector<lt::announce_entry> nativeTrackers;
nativeTrackers.reserve(m_trackerEntries.size());
for (const TrackerEntry &tracker : asConst(m_trackerEntries))
nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier));
if (!removedTrackers.isEmpty())
{
m_nativeHandle.replace_trackers(nativeTrackers);
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentTrackersAdded(this, newTrackers);
m_session->handleTorrentTrackersRemoved(this, removedTrackers);
}
}
void TorrentImpl::replaceTrackers(const QVector<TrackerEntry> &trackers)
void TorrentImpl::replaceTrackers(QVector<TrackerEntry> trackers)
{
QVector<TrackerEntry> currentTrackers = this->trackers();
QVector<TrackerEntry> newTrackers;
newTrackers.reserve(trackers.size());
// TODO: use std::erase_if() in C++20
trackers.erase(std::remove_if(trackers.begin(), trackers.end(), [](const TrackerEntry &entry) { return entry.url.isEmpty(); }), trackers.end());
std::sort(trackers.begin(), trackers.end()
, [](const TrackerEntry &lhs, const TrackerEntry &rhs) { return lhs.tier < rhs.tier; });
std::vector<lt::announce_entry> nativeTrackers;
nativeTrackers.reserve(trackers.size());
for (const TrackerEntry &tracker : trackers)
{
nativeTrackers.emplace_back(makeNativeAnnounceEntry(tracker.url, tracker.tier));
if (!currentTrackers.removeOne(tracker))
newTrackers << tracker;
}
m_nativeHandle.replace_trackers(nativeTrackers);
// Clear the peer list if it's a private torrent since
// we do not want to keep connecting with peers from old tracker.
if (isPrivate())
clearPeers();
m_trackerEntries = trackers;
m_session->handleTorrentNeedSaveResumeData(this);
if (newTrackers.isEmpty() && currentTrackers.isEmpty())
{
// when existing tracker reorders
m_session->handleTorrentTrackersChanged(this);
}
else
{
if (!currentTrackers.isEmpty())
m_session->handleTorrentTrackersRemoved(this, currentTrackers);
if (!newTrackers.isEmpty())
m_session->handleTorrentTrackersAdded(this, newTrackers);
// Clear the peer list if it's a private torrent since
// we do not want to keep connecting with peers from old tracker.
if (isPrivate())
clearPeers();
}
m_session->handleTorrentTrackersChanged(this);
}
QVector<QUrl> TorrentImpl::urlSeeds() const
@@ -1511,9 +1485,43 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN
endReceivedMetadataHandling(savePath, fileNames);
}
void TorrentImpl::updatePeerCount(const QString &trackerUrl, const lt::tcp::endpoint &endpoint, const int count)
void TorrentImpl::updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, const int count)
{
m_trackerPeerCounts[trackerUrl][endpoint] = count;
m_updatedTrackerEntries[trackerURL][endpoint] = count;
}
void TorrentImpl::invalidateTrackerEntry(const QString &trackerURL)
{
std::ignore = m_updatedTrackerEntries[trackerURL];
}
void TorrentImpl::refreshTrackerEntries() const
{
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
Q_ASSERT(nativeTrackers.size() == m_trackerEntries.size());
for (TrackerEntry &trackerEntry : m_trackerEntries)
{
const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerEntry.url);
if (updatedTrackerIter == m_updatedTrackerEntries.end())
continue;
const auto nativeTrackerIter = std::find_if(nativeTrackers.cbegin(), nativeTrackers.cend()
, [trackerURL = trackerEntry.url.toStdString()](const lt::announce_entry &announceEntry)
{
return (announceEntry.url == trackerURL);
});
Q_ASSERT(nativeTrackerIter != nativeTrackers.cend());
const lt::announce_entry &announceEntry = *nativeTrackerIter;
#ifdef QBT_USES_LIBTORRENT2
updateTrackerEntry(trackerEntry, announceEntry, m_nativeHandle.info_hashes(), updatedTrackerIter.value());
#else
updateTrackerEntry(trackerEntry, announceEntry, updatedTrackerIter.value());
#endif
}
m_updatedTrackerEntries.clear();
}
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const

View File

@@ -34,7 +34,6 @@
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/torrent_status.hpp>
@@ -55,6 +54,7 @@
#include "torrent.h"
#include "torrentcontentlayout.h"
#include "torrentinfo.h"
#include "trackerentry.h"
namespace BitTorrent
{
@@ -156,7 +156,6 @@ namespace BitTorrent
bool hasMissingFiles() const override;
bool hasError() const override;
int queuePosition() const override;
QVector<QString> trackerURLs() const override;
QVector<TrackerEntry> trackers() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
@@ -221,8 +220,9 @@ namespace BitTorrent
void setPEXDisabled(bool disable) override;
void setLSDDisabled(bool disable) override;
void flushCache() const override;
void addTrackers(const QVector<TrackerEntry> &trackers) override;
void replaceTrackers(const QVector<TrackerEntry> &trackers) override;
void addTrackers(QVector<TrackerEntry> trackers) override;
void removeTrackers(const QStringList &trackers) override;
void replaceTrackers(QVector<TrackerEntry> trackers) override;
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
bool connectPeer(const PeerAddress &peerAddress) override;
@@ -244,13 +244,15 @@ namespace BitTorrent
void saveResumeData();
void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob);
void fileSearchFinished(const Path &savePath, const PathList &fileNames);
void updatePeerCount(const QString &trackerUrl, const lt::tcp::endpoint &endpoint, int count);
void updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, int count);
void invalidateTrackerEntry(const QString &trackerURL);
private:
using EventTrigger = std::function<void ()>;
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
void refreshTrackerEntries() const;
void updateStatus(const lt::torrent_status &nativeStatus);
void updateState();
@@ -310,7 +312,10 @@ namespace BitTorrent
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
// TODO: Use QHash<TrackerEntry::Endpoint, int> once Qt5 is dropped.
using TrackerEntryUpdateInfo = QMap<TrackerEntry::Endpoint, int>;
mutable QHash<QString, TrackerEntryUpdateInfo> m_updatedTrackerEntries;
mutable QVector<TrackerEntry> m_trackerEntries;
FileErrorInfo m_lastFileError;
// Persistent data

View File

@@ -275,9 +275,8 @@ QVector<TrackerEntry> TorrentInfo::trackers() const
QVector<TrackerEntry> ret;
ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size()));
for (const lt::announce_entry &tracker : trackers)
ret.append({QString::fromStdString(tracker.url)});
ret.append({QString::fromStdString(tracker.url), tracker.tier});
return ret;
}

View File

@@ -30,8 +30,8 @@
#include <libtorrent/torrent_info.hpp>
#include <QCoreApplication>
#include <QtContainerFwd>
#include <QCoreApplication>
#include <QVector>
#include "base/3rdparty/expected.hpp"

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2022 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
@@ -28,22 +28,16 @@
#include "trackerentry.h"
#include <QUrl>
bool BitTorrent::operator==(const TrackerEntry &left, const TrackerEntry &right)
{
return ((left.tier == right.tier)
&& QUrl(left.url) == QUrl(right.url));
return (left.url == right.url);
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
std::size_t BitTorrent::qHash(const TrackerEntry &key, const std::size_t seed)
{
return qHashMulti(seed, key.url, key.tier);
}
#else
uint BitTorrent::qHash(const TrackerEntry &key, const uint seed)
{
return (::qHash(key.url, seed) ^ ::qHash(key.tier));
}
#endif
{
return ::qHash(key.url, seed);
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2022 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
@@ -28,14 +28,19 @@
#pragma once
#include <libtorrent/socket.hpp>
#include <QtGlobal>
#include <QHash>
#include <QMap>
#include <QString>
#include <QVector>
namespace BitTorrent
{
struct TrackerEntry
{
using Endpoint = lt::tcp::endpoint;
enum Status
{
NotContacted = 1,
@@ -46,8 +51,6 @@ namespace BitTorrent
struct EndpointStats
{
int protocolVersion = 1;
Status status = NotContacted;
int numPeers = -1;
int numSeeds = -1;
@@ -59,7 +62,8 @@ namespace BitTorrent
QString url {};
int tier = 0;
QVector<EndpointStats> endpoints {};
// TODO: Use QHash<TrackerEntry::Endpoint, QHash<int, EndpointStats>> once Qt5 is dropped.
QMap<Endpoint, QHash<int, EndpointStats>> stats {};
// Deprecated fields
Status status = NotContacted;