diff --git a/src/base/bittorrent/extensiondata.h b/src/base/bittorrent/extensiondata.h index 811b7a0a3..9df0f61c2 100644 --- a/src/base/bittorrent/extensiondata.h +++ b/src/base/bittorrent/extensiondata.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022 Vladimir Golovnev + * Copyright (C) 2022-2023 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,6 +28,8 @@ #pragma once +#include +#include #include #include @@ -44,4 +46,5 @@ struct ExtensionData { lt::torrent_status status; std::vector trackers; + std::set urlSeeds; }; diff --git a/src/base/bittorrent/nativesessionextension.cpp b/src/base/bittorrent/nativesessionextension.cpp index 6449e9e78..ea6ad7827 100644 --- a/src/base/bittorrent/nativesessionextension.cpp +++ b/src/base/bittorrent/nativesessionextension.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020-2022 Vladimir Golovnev + * Copyright (C) 2020-2023 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -51,6 +51,7 @@ namespace { data->status = alert->handle.status({}); data->trackers = alert->handle.trackers(); + data->urlSeeds = alert->handle.url_seeds(); } #endif } diff --git a/src/base/bittorrent/nativetorrentextension.cpp b/src/base/bittorrent/nativetorrentextension.cpp index 1f8ab1a1e..2ebee716f 100644 --- a/src/base/bittorrent/nativetorrentextension.cpp +++ b/src/base/bittorrent/nativetorrentextension.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020-2022 Vladimir Golovnev + * Copyright (C) 2020-2023 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -46,6 +46,7 @@ NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrent { m_data->status = m_torrentHandle.status({}); m_data->trackers = m_torrentHandle.trackers(); + m_data->urlSeeds = m_torrentHandle.url_seeds(); } #endif diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 29a564751..fdd7078fe 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2022 Vladimir Golovnev + * Copyright (C) 2015-2023 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -302,6 +301,9 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession m_trackerEntries.reserve(static_cast(extensionData->trackers.size())); for (const lt::announce_entry &announceEntry : extensionData->trackers) m_trackerEntries.append({QString::fromStdString(announceEntry.url), announceEntry.tier}); + m_urlSeeds.reserve(static_cast(extensionData->urlSeeds.size())); + for (const std::string &urlSeed : extensionData->urlSeeds) + m_urlSeeds.append(QString::fromStdString(urlSeed)); m_nativeStatus = extensionData->status; updateState(); @@ -608,15 +610,7 @@ void TorrentImpl::replaceTrackers(QVector trackers) QVector TorrentImpl::urlSeeds() const { - const std::set currentSeeds = m_nativeHandle.url_seeds(); - - QVector urlSeeds; - urlSeeds.reserve(static_cast(currentSeeds.size())); - - for (const std::string &urlSeed : currentSeeds) - urlSeeds.append(QString::fromStdString(urlSeed)); - - return urlSeeds; + return m_urlSeeds; } void TorrentImpl::addUrlSeeds(const QVector &urlSeeds) @@ -645,11 +639,13 @@ void TorrentImpl::addUrlSeeds(const QVector &urlSeeds) } } - session->invoke([session, thisTorrent, addedUrlSeeds] + currentSeeds.append(addedUrlSeeds); + session->invoke([session, thisTorrent, currentSeeds, addedUrlSeeds] { if (!thisTorrent) return; + thisTorrent->m_urlSeeds = currentSeeds; if (!addedUrlSeeds.isEmpty()) { session->handleTorrentNeedSaveResumeData(thisTorrent); @@ -680,18 +676,20 @@ void TorrentImpl::removeUrlSeeds(const QVector &urlSeeds) for (const QUrl &url : urlSeeds) { - if (currentSeeds.contains(url)) + if (currentSeeds.removeOne(url)) { nativeHandle.remove_url_seed(url.toString().toStdString()); removedUrlSeeds.append(url); } } - session->invoke([session, thisTorrent, removedUrlSeeds] + session->invoke([session, thisTorrent, currentSeeds, removedUrlSeeds] { if (!thisTorrent) return; + thisTorrent->m_urlSeeds = currentSeeds; + if (!removedUrlSeeds.isEmpty()) { session->handleTorrentNeedSaveResumeData(thisTorrent); @@ -1887,6 +1885,14 @@ void TorrentImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p) void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) { + if (m_ltAddTorrentParams.url_seeds != p->params.url_seeds) + { + // URL seed list have been changed by libtorrent for some reason, so we need to update cached one. + // Unfortunately, URL seed list containing in "resume data" is generated according to different rules + // than the list we usually cache, so we have to request it from the appropriate source. + fetchURLSeeds([this](const QVector &urlSeeds) { m_urlSeeds = urlSeeds; }); + } + if (m_maintenanceJob == MaintenanceJob::HandleMetadata) { Q_ASSERT(m_indexMap.isEmpty()); @@ -2403,7 +2409,39 @@ void TorrentImpl::flushCache() const QString TorrentImpl::createMagnetURI() const { - return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle)); + QString ret = u"magnet:?"_qs; + + const SHA1Hash infoHash1 = infoHash().v1(); + if (infoHash1.isValid()) + { + ret += u"xt=urn:btih:" + infoHash1.toString(); + } + + const SHA256Hash infoHash2 = infoHash().v2(); + if (infoHash2.isValid()) + { + if (infoHash1.isValid()) + ret += u'&'; + ret += u"xt=urn:btmh:1220" + infoHash2.toString(); + } + + const QString displayName = name(); + if (displayName != id().toString()) + { + ret += u"&dn=" + QString::fromLatin1(QUrl::toPercentEncoding(displayName)); + } + + for (const TrackerEntry &tracker : asConst(trackers())) + { + ret += u"&tr=" + QString::fromLatin1(QUrl::toPercentEncoding(tracker.url)); + } + + for (const QUrl &urlSeed : asConst(urlSeeds())) + { + ret += u"&ws=" + QString::fromLatin1(urlSeed.toEncoded()); + } + + return ret; } nonstd::expected TorrentImpl::exportTorrent() const diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index c5f6f95c3..4f71a19ff 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -327,6 +327,7 @@ namespace BitTorrent MaintenanceJob m_maintenanceJob = MaintenanceJob::None; QVector m_trackerEntries; + QVector m_urlSeeds; FileErrorInfo m_lastFileError; // Persistent data