Improve "info hash" handling

Define "torrent ID" concept, which is either a SHA1 hash for torrents of version 1,
or a SHA256 hash (truncated to SHA1 hash length) for torrents of version 2.
Add support for native libtorrent2 info hashes.
This commit is contained in:
Vladimir Golovnev (Glassez)
2021-03-05 12:43:58 +03:00
committed by sledgehammer999
parent c2ccc9dfa4
commit 437b51b3a5
34 changed files with 463 additions and 320 deletions

View File

@@ -33,7 +33,7 @@
#include "base/bittorrent/common.h"
#include "base/bittorrent/infohash.h"
void FileSearcher::search(const BitTorrent::InfoHash &id, const QStringList &originalFileNames
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
, const QString &completeSavePath, const QString &incompleteSavePath)
{
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool

View File

@@ -32,7 +32,7 @@
namespace BitTorrent
{
class InfoHash;
class TorrentID;
}
class FileSearcher final : public QObject
@@ -44,9 +44,9 @@ public:
FileSearcher() = default;
public slots:
void search(const BitTorrent::InfoHash &id, const QStringList &originalFileNames
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
, const QString &completeSavePath, const QString &incompleteSavePath);
signals:
void searchFinished(const BitTorrent::InfoHash &id, const QString &savePath, const QStringList &fileNames);
void searchFinished(const BitTorrent::TorrentID &id, const QString &savePath, const QStringList &fileNames);
};

View File

@@ -28,14 +28,54 @@
#include "infohash.h"
const int InfoHashTypeId = qRegisterMetaType<BitTorrent::InfoHash>();
const int TorrentIDTypeId = qRegisterMetaType<BitTorrent::TorrentID>();
BitTorrent::InfoHash BitTorrent::InfoHash::fromString(const QString &hashString)
BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash)
: m_valid {true}
, m_nativeHash {nativeHash}
{
return {SHA1Hash::fromString(hashString)};
}
uint BitTorrent::qHash(const BitTorrent::InfoHash &key, const uint seed)
bool BitTorrent::InfoHash::isValid() const
{
return ::qHash(std::hash<InfoHash::UnderlyingType>()(key), seed);
return m_valid;
}
BitTorrent::TorrentID BitTorrent::InfoHash::toTorrentID() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
return m_nativeHash.get_best();
#else
return {m_nativeHash};
#endif
}
BitTorrent::InfoHash::operator WrappedType() const
{
return m_nativeHash;
}
BitTorrent::TorrentID BitTorrent::TorrentID::fromString(const QString &hashString)
{
return {BaseType::fromString(hashString)};
}
BitTorrent::TorrentID BitTorrent::TorrentID::fromInfoHash(const BitTorrent::InfoHash &infoHash)
{
return infoHash.toTorrentID();
}
uint BitTorrent::qHash(const BitTorrent::TorrentID &key, const uint seed)
{
return ::qHash(std::hash<TorrentID::UnderlyingType>()(key), seed);
}
bool BitTorrent::operator==(const BitTorrent::InfoHash &left, const BitTorrent::InfoHash &right)
{
return (static_cast<InfoHash::WrappedType>(left) == static_cast<InfoHash::WrappedType>(right));
}
bool BitTorrent::operator!=(const BitTorrent::InfoHash &left, const BitTorrent::InfoHash &right)
{
return !(left == right);
}

View File

@@ -28,6 +28,11 @@
#pragma once
#include <libtorrent/version.hpp>
#if (LIBTORRENT_VERSION_NUM >= 20000)
#include <libtorrent/info_hash.hpp>
#endif
#include <QHash>
#include <QMetaType>
@@ -38,15 +43,45 @@ using SHA256Hash = Digest32<256>;
namespace BitTorrent
{
class InfoHash : public SHA1Hash
class InfoHash;
class TorrentID : public Digest32<160>
{
public:
using SHA1Hash::SHA1Hash;
using BaseType = Digest32<160>;
using BaseType::BaseType;
static InfoHash fromString(const QString &hashString);
static TorrentID fromString(const QString &hashString);
static TorrentID fromInfoHash(const InfoHash &infoHash);
};
uint qHash(const InfoHash &key, const uint seed);
class InfoHash
{
public:
#if (LIBTORRENT_VERSION_NUM >= 20000)
using WrappedType = lt::info_hash_t;
#else
using WrappedType = lt::sha1_hash;
#endif
InfoHash() = default;
InfoHash(const InfoHash &other) = default;
InfoHash(const WrappedType &nativeHash);
bool isValid() const;
TorrentID toTorrentID() const;
operator WrappedType() const;
private:
bool m_valid = false;
WrappedType m_nativeHash;
};
uint qHash(const TorrentID &key, uint seed);
bool operator==(const InfoHash &left, const InfoHash &right);
bool operator!=(const InfoHash &left, const InfoHash &right);
}
Q_DECLARE_METATYPE(BitTorrent::InfoHash)
Q_DECLARE_METATYPE(BitTorrent::TorrentID)

View File

@@ -47,8 +47,8 @@ namespace
// == 20 (SHA-1 length in bytes) * 2 (each byte maps to 2 hex characters)
// 2. 32 chars Base32 encoded string
// == 20 (SHA-1 length in bytes) * 1.6 (the efficiency of Base32 encoding)
const int SHA1_HEX_SIZE = BitTorrent::InfoHash::length() * 2;
const int SHA1_BASE32_SIZE = BitTorrent::InfoHash::length() * 1.6;
const int SHA1_HEX_SIZE = SHA1Hash::length() * 2;
const int SHA1_BASE32_SIZE = SHA1Hash::length() * 1.6;
return ((((string.size() == SHA1_HEX_SIZE))
&& !string.contains(QRegularExpression(QLatin1String("[^0-9A-Fa-f]"))))
@@ -73,7 +73,13 @@ MagnetUri::MagnetUri(const QString &source)
if (ec) return;
m_valid = true;
m_hash = m_addTorrentParams.info_hash;
#if (LIBTORRENT_VERSION_NUM >= 20000)
m_infoHash = m_addTorrentParams.info_hashes;
#else
m_infoHash = m_addTorrentParams.info_hash;
#endif
m_name = QString::fromStdString(m_addTorrentParams.name);
m_trackers.reserve(m_addTorrentParams.trackers.size());
@@ -90,9 +96,9 @@ bool MagnetUri::isValid() const
return m_valid;
}
InfoHash MagnetUri::hash() const
InfoHash MagnetUri::infoHash() const
{
return m_hash;
return m_infoHash;
}
QString MagnetUri::name() const

View File

@@ -46,7 +46,7 @@ namespace BitTorrent
explicit MagnetUri(const QString &source = {});
bool isValid() const;
InfoHash hash() const;
InfoHash infoHash() const;
QString name() const;
QVector<TrackerEntry> trackers() const;
QVector<QUrl> urlSeeds() const;
@@ -57,7 +57,7 @@ namespace BitTorrent
private:
bool m_valid;
QString m_url;
InfoHash m_hash;
InfoHash m_infoHash;
QString m_name;
QVector<TrackerEntry> m_trackers;
QVector<QUrl> m_urlSeeds;

View File

@@ -1616,7 +1616,7 @@ void Session::processShareLimits()
// We shouldn't iterate over `m_torrents` in the loop below
// since `deleteTorrent()` modifies it indirectly
const QHash<InfoHash, TorrentImpl *> torrents {m_torrents};
const QHash<TorrentID, TorrentImpl *> torrents {m_torrents};
for (TorrentImpl *const torrent : torrents)
{
if (torrent->isSeed() && !torrent->isForced())
@@ -1638,12 +1638,12 @@ void Session::processShareLimits()
if (m_maxRatioAction == Remove)
{
LogMsg(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent->name()));
deleteTorrent(torrent->hash());
deleteTorrent(torrent->id());
}
else if (m_maxRatioAction == DeleteFiles)
{
LogMsg(tr("'%1' reached the maximum ratio you set. Removed torrent and its files.").arg(torrent->name()));
deleteTorrent(torrent->hash(), DeleteTorrentAndFiles);
deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
}
else if ((m_maxRatioAction == Pause) && !torrent->isPaused())
{
@@ -1677,12 +1677,12 @@ void Session::processShareLimits()
if (m_maxRatioAction == Remove)
{
LogMsg(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name()));
deleteTorrent(torrent->hash());
deleteTorrent(torrent->id());
}
else if (m_maxRatioAction == DeleteFiles)
{
LogMsg(tr("'%1' reached the maximum seeding time you set. Removed torrent and its files.").arg(torrent->name()));
deleteTorrent(torrent->hash(), DeleteTorrentAndFiles);
deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
}
else if ((m_maxRatioAction == Pause) && !torrent->isPaused())
{
@@ -1719,7 +1719,7 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
}
}
void Session::fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames)
void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames)
{
TorrentImpl *torrent = m_torrents.value(id);
if (torrent)
@@ -1745,9 +1745,9 @@ void Session::fileSearchFinished(const InfoHash &id, const QString &savePath, co
}
// Return the torrent handle, given its hash
Torrent *Session::findTorrent(const InfoHash &hash) const
Torrent *Session::findTorrent(const TorrentID &id) const
{
return m_torrents.value(hash);
return m_torrents.value(id);
}
bool Session::hasActiveTorrents() const
@@ -1795,18 +1795,18 @@ void Session::banIP(const QString &ip)
// Delete a torrent from the session, given its hash
// and from the disk, if the corresponding deleteOption is chosen
bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOption)
bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption)
{
TorrentImpl *const torrent = m_torrents.take(hash);
TorrentImpl *const torrent = m_torrents.take(id);
if (!torrent) return false;
qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash().toString()));
qDebug("Deleting torrent with ID: %s", qUtf8Printable(torrent->id().toString()));
emit torrentAboutToBeRemoved(torrent);
// Remove it from session
if (deleteOption == DeleteTorrent)
{
m_removingTorrents[torrent->hash()] = {torrent->name(), "", deleteOption};
m_removingTorrents[torrent->id()] = {torrent->name(), "", deleteOption};
const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
@@ -1835,7 +1835,7 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio
rootPath = torrent->actualStorageLocation();
}
m_removingTorrents[torrent->hash()] = {torrent->name(), rootPath, deleteOption};
m_removingTorrents[torrent->id()] = {torrent->name(), rootPath, deleteOption};
if (m_moveStorageQueue.size() > 1)
{
@@ -1854,8 +1854,8 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio
}
// Remove it from torrent resume directory
const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->hash().toString());
const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->hash().toString());
const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(m_resumeDataSavingManager, [this, resumedataFile, metadataFile]()
{
@@ -1871,19 +1871,19 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio
return true;
}
bool Session::cancelDownloadMetadata(const InfoHash &hash)
bool Session::cancelDownloadMetadata(const TorrentID &id)
{
const auto downloadedMetadataIter = m_downloadedMetadata.find(hash);
const auto downloadedMetadataIter = m_downloadedMetadata.find(id);
if (downloadedMetadataIter == m_downloadedMetadata.end()) return false;
m_downloadedMetadata.erase(downloadedMetadataIter);
--m_extraLimit;
adjustLimits();
m_nativeSession->remove_torrent(m_nativeSession->find_torrent(hash), lt::session::delete_files);
m_nativeSession->remove_torrent(m_nativeSession->find_torrent(id), lt::session::delete_files);
return true;
}
void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::increaseTorrentsQueuePos(const QVector<TorrentID> &ids)
{
using ElementType = std::pair<int, const TorrentImpl *>;
std::priority_queue<ElementType
@@ -1891,9 +1891,9 @@ void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
, std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes)
for (const TorrentID &id : ids)
{
const TorrentImpl *torrent = m_torrents.value(infoHash);
const TorrentImpl *torrent = m_torrents.value(id);
if (!torrent) continue;
if (const int position = torrent->queuePosition(); position >= 0)
torrentQueue.emplace(position, torrent);
@@ -1910,15 +1910,15 @@ void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
saveTorrentsQueue();
}
void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::decreaseTorrentsQueuePos(const QVector<TorrentID> &ids)
{
using ElementType = std::pair<int, const TorrentImpl *>;
std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes)
for (const TorrentID &id : ids)
{
const TorrentImpl *torrent = m_torrents.value(infoHash);
const TorrentImpl *torrent = m_torrents.value(id);
if (!torrent) continue;
if (const int position = torrent->queuePosition(); position >= 0)
torrentQueue.emplace(position, torrent);
@@ -1938,15 +1938,15 @@ void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
saveTorrentsQueue();
}
void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::topTorrentsQueuePos(const QVector<TorrentID> &ids)
{
using ElementType = std::pair<int, const TorrentImpl *>;
std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes)
for (const TorrentID &id : ids)
{
const TorrentImpl *torrent = m_torrents.value(infoHash);
const TorrentImpl *torrent = m_torrents.value(id);
if (!torrent) continue;
if (const int position = torrent->queuePosition(); position >= 0)
torrentQueue.emplace(position, torrent);
@@ -1963,7 +1963,7 @@ void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
saveTorrentsQueue();
}
void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::bottomTorrentsQueuePos(const QVector<TorrentID> &ids)
{
using ElementType = std::pair<int, const TorrentImpl *>;
std::priority_queue<ElementType
@@ -1971,9 +1971,9 @@ void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
, std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position
for (const InfoHash &infoHash : hashes)
for (const TorrentID &id : ids)
{
const TorrentImpl *torrent = m_torrents.value(infoHash);
const TorrentImpl *torrent = m_torrents.value(id);
if (!torrent) continue;
if (const int position = torrent->queuePosition(); position >= 0)
torrentQueue.emplace(position, torrent);
@@ -2088,20 +2088,20 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
const bool hasMetadata = std::holds_alternative<TorrentInfo>(source);
TorrentInfo metadata = (hasMetadata ? std::get<TorrentInfo>(source) : TorrentInfo {});
const MagnetUri &magnetUri = (hasMetadata ? MagnetUri {} : std::get<MagnetUri>(source));
const InfoHash hash = (hasMetadata ? metadata.hash() : magnetUri.hash());
const auto id = TorrentID::fromInfoHash(hasMetadata ? metadata.infoHash() : magnetUri.infoHash());
// It looks illogical that we don't just use an existing handle,
// but as previous experience has shown, it actually creates unnecessary
// problems and unwanted behavior due to the fact that it was originally
// added with parameters other than those provided by the user.
cancelDownloadMetadata(hash);
cancelDownloadMetadata(id);
// We should not add the torrent if it is already
// processed or is pending to add to session
if (m_loadingTorrents.contains(hash))
if (m_loadingTorrents.contains(id))
return false;
TorrentImpl *const torrent = m_torrents.value(hash);
TorrentImpl *const torrent = m_torrents.value(id);
if (torrent)
{ // a duplicate torrent is added
if (torrent->isPrivate() || (hasMetadata && metadata.isPrivate()))
@@ -2192,7 +2192,7 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
if (!isFindingIncompleteFiles)
return loadTorrent(loadTorrentParams);
m_loadingTorrents.insert(hash, loadTorrentParams);
m_loadingTorrents.insert(id, loadTorrentParams);
return true;
}
@@ -2209,8 +2209,12 @@ bool Session::loadTorrent(LoadTorrentParams params)
p.max_uploads = maxUploadsPerTorrent();
const bool hasMetadata = (p.ti && p.ti->is_valid());
const InfoHash hash = (hasMetadata ? p.ti->info_hash() : p.info_hash);
m_loadingTorrents.insert(hash, params);
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hashes() : p.info_hashes);
#else
const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hash() : p.info_hash);
#endif
m_loadingTorrents.insert(id, params);
// Adding torrent to BitTorrent session
m_nativeSession->async_add_torrent(p);
@@ -2220,7 +2224,7 @@ bool Session::loadTorrent(LoadTorrentParams params)
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath) const
{
const InfoHash searchId = torrentInfo.hash();
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
const QStringList originalFileNames = torrentInfo.filePaths();
const QString completeSavePath = savePath;
const QString incompleteSavePath = (isTempPathEnabled() ? torrentTempPath(torrentInfo) : QString {});
@@ -2231,7 +2235,7 @@ void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString
});
#else
QMetaObject::invokeMethod(m_fileSearcher, "search"
, Q_ARG(BitTorrent::InfoHash, searchId), Q_ARG(QStringList, originalFileNames)
, Q_ARG(BitTorrent::TorrentID, searchId), Q_ARG(QStringList, originalFileNames)
, Q_ARG(QString, completeSavePath), Q_ARG(QString, incompleteSavePath));
#endif
}
@@ -2242,17 +2246,17 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
{
if (!magnetUri.isValid()) return false;
const InfoHash hash = magnetUri.hash();
const auto id = TorrentID::fromInfoHash(magnetUri.infoHash());
const QString name = magnetUri.name();
// We should not add torrent if it's already
// processed or adding to session
if (m_torrents.contains(hash)) return false;
if (m_loadingTorrents.contains(hash)) return false;
if (m_downloadedMetadata.contains(hash)) return false;
if (m_torrents.contains(id)) return false;
if (m_loadingTorrents.contains(id)) return false;
if (m_downloadedMetadata.contains(id)) return false;
qDebug("Adding torrent to preload metadata...");
qDebug(" -> Hash: %s", qUtf8Printable(hash.toString()));
qDebug(" -> Torrent ID: %s", qUtf8Printable(id.toString()));
qDebug(" -> Name: %s", qUtf8Printable(name));
lt::add_torrent_params p = magnetUri.addTorrentParams();
@@ -2268,7 +2272,7 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent();
const QString savePath = Utils::Fs::tempPath() + hash.toString();
const QString savePath = Utils::Fs::tempPath() + id.toString();
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
// Forced start
@@ -2301,7 +2305,7 @@ void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder fold
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
const QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->hash().toString());
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
const QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
@@ -2374,13 +2378,13 @@ void Session::saveTorrentsQueue() const
// We require actual (non-cached) queue position here!
const int queuePos = static_cast<LTUnderlyingType<lt::queue_position_t>>(torrent->nativeHandle().queue_position());
if (queuePos >= 0)
queue[queuePos] = torrent->hash().toString();
queue[queuePos] = torrent->id().toString();
}
QByteArray data;
data.reserve(((InfoHash::length() * 2) + 1) * queue.size());
for (const QString &hash : asConst(queue))
data += (hash.toLatin1() + '\n');
data.reserve(((TorrentID::length() * 2) + 1) * queue.size());
for (const QString &torrentID : asConst(queue))
data += (torrentID.toLatin1() + '\n');
const QString filename = QLatin1String {"queue"};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
@@ -3758,11 +3762,11 @@ void Session::setMaxRatioAction(const MaxRatioAction act)
// If this functions returns true, we cannot add torrent to session,
// but it is still possible to merge trackers in some cases
bool Session::isKnownTorrent(const InfoHash &hash) const
bool Session::isKnownTorrent(const TorrentID &id) const
{
return (m_torrents.contains(hash)
|| m_loadingTorrents.contains(hash)
|| m_downloadedMetadata.contains(hash));
return (m_torrents.contains(id)
|| m_loadingTorrents.contains(id)
|| m_downloadedMetadata.contains(id));
}
void Session::updateSeedingLimitTimer()
@@ -3868,7 +3872,7 @@ void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent)
{
// Save metadata
const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash().toString())};
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->id().toString())};
try
{
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
@@ -3948,7 +3952,7 @@ void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const std
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Copying lt::entry objects around isn't cheap.
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash().toString());
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, filename, data]() { m_resumeDataSavingManager->save(filename, data); });
@@ -4024,9 +4028,13 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
void Session::moveTorrentStorage(const MoveStorageJob &job) const
{
const InfoHash infoHash = job.torrentHandle.info_hash();
const TorrentImpl *torrent = m_torrents.value(infoHash);
const QString torrentName = (torrent ? torrent->name() : infoHash.toString());
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hash());
#endif
const TorrentImpl *torrent = m_torrents.value(id);
const QString torrentName = (torrent ? torrent->name() : id.toString());
LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path));
job.torrentHandle.move_storage(job.path.toUtf8().constData()
@@ -4157,9 +4165,9 @@ void Session::disableIPFilter()
m_nativeSession->set_ip_filter(filter);
}
void Session::recursiveTorrentDownload(const InfoHash &hash)
void Session::recursiveTorrentDownload(const TorrentID &id)
{
TorrentImpl *const torrent = m_torrents.value(hash);
TorrentImpl *const torrent = m_torrents.value(id);
if (!torrent) return;
for (int i = 0; i < torrent->filesCount(); ++i)
@@ -4584,7 +4592,7 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());
auto *const torrent = new TorrentImpl {this, m_nativeSession, nativeHandle, params};
m_torrents.insert(torrent->hash(), torrent);
m_torrents.insert(torrent->id(), torrent);
const bool hasMetadata = torrent->hasMetadata();
@@ -4599,7 +4607,7 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
{
// Backup torrent file
const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->hash().toString())};
const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->id().toString())};
try
{
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
@@ -4657,9 +4665,13 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
{
const InfoHash infoHash {p->info_hash};
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
#endif
const auto removingTorrentDataIter = m_removingTorrents.find(infoHash);
const auto removingTorrentDataIter = m_removingTorrents.find(id);
if (removingTorrentDataIter != m_removingTorrents.end())
{
if (removingTorrentDataIter->deleteOption == DeleteTorrent)
@@ -4672,8 +4684,13 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
{
const InfoHash infoHash {p->info_hash};
const auto removingTorrentDataIter = m_removingTorrents.find(infoHash);
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
#endif
const auto removingTorrentDataIter = m_removingTorrents.find(id);
if (removingTorrentDataIter == m_removingTorrents.end())
return;
@@ -4685,8 +4702,13 @@ void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *p)
{
const InfoHash infoHash {p->info_hash};
const auto removingTorrentDataIter = m_removingTorrents.find(infoHash);
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
#endif
const auto removingTorrentDataIter = m_removingTorrents.find(id);
if (removingTorrentDataIter == m_removingTorrents.end())
return;
@@ -4710,8 +4732,13 @@ void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_ale
void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
{
const InfoHash hash {p->handle.info_hash()};
const auto downloadedMetadataIter = m_downloadedMetadata.find(hash);
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(p->handle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(p->handle.info_hash());
#endif
const auto downloadedMetadataIter = m_downloadedMetadata.find(id);
if (downloadedMetadataIter != m_downloadedMetadata.end())
{
@@ -4732,11 +4759,11 @@ void Session::handleFileErrorAlert(const lt::file_error_alert *p)
if (!torrent)
return;
const InfoHash hash = torrent->hash();
const TorrentID id = torrent->id();
if (!m_recentErroredTorrents.contains(hash))
if (!m_recentErroredTorrents.contains(id))
{
m_recentErroredTorrents.insert(hash);
m_recentErroredTorrents.insert(id);
const QString msg = QString::fromStdString(p->message());
LogMsg(tr("File error alert. Torrent: \"%1\". File: \"%2\". Reason: %3")
@@ -4935,9 +4962,14 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
const QString newPath {p->storage_path()};
Q_ASSERT(newPath == currentJob.path);
const InfoHash infoHash = currentJob.torrentHandle.info_hash();
TorrentImpl *torrent = m_torrents.value(infoHash);
const QString torrentName = (torrent ? torrent->name() : infoHash.toString());
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
#endif
TorrentImpl *torrent = m_torrents.value(id);
const QString torrentName = (torrent ? torrent->name() : id.toString());
LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath));
handleMoveTorrentStorageJobFinished();
@@ -4950,9 +4982,14 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
Q_ASSERT(currentJob.torrentHandle == p->handle);
const InfoHash infoHash = currentJob.torrentHandle.info_hash();
TorrentImpl *torrent = m_torrents.value(infoHash);
const QString torrentName = (torrent ? torrent->name() : infoHash.toString());
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
#endif
TorrentImpl *torrent = m_torrents.value(id);
const QString torrentName = (torrent ? torrent->name() : id.toString());
const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path);
const QString errorMessage = QString::fromStdString(p->message());
LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
@@ -4968,8 +5005,12 @@ void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
for (const lt::torrent_status &status : p->status)
{
TorrentImpl *const torrent = m_torrents.value(status.info_hash);
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto id = TorrentID::fromInfoHash(status.info_hashes);
#else
const auto id = TorrentID::fromInfoHash(status.info_hash);
#endif
TorrentImpl *const torrent = m_torrents.value(id);
if (!torrent)
continue;

View File

@@ -434,7 +434,7 @@ namespace BitTorrent
#endif
void startUpTorrents();
Torrent *findTorrent(const InfoHash &hash) const;
Torrent *findTorrent(const TorrentID &id) const;
QVector<Torrent *> torrents() const;
bool hasActiveTorrents() const;
bool hasUnfinishedTorrents() const;
@@ -450,19 +450,19 @@ namespace BitTorrent
void banIP(const QString &ip);
bool isKnownTorrent(const InfoHash &hash) const;
bool isKnownTorrent(const TorrentID &id) const;
bool addTorrent(const QString &source, const AddTorrentParams &params = AddTorrentParams());
bool addTorrent(const MagnetUri &magnetUri, const AddTorrentParams &params = AddTorrentParams());
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params = AddTorrentParams());
bool deleteTorrent(const InfoHash &hash, DeleteOption deleteOption = DeleteTorrent);
bool deleteTorrent(const TorrentID &id, DeleteOption deleteOption = DeleteTorrent);
bool downloadMetadata(const MagnetUri &magnetUri);
bool cancelDownloadMetadata(const InfoHash &hash);
bool cancelDownloadMetadata(const TorrentID &id);
void recursiveTorrentDownload(const InfoHash &hash);
void increaseTorrentsQueuePos(const QVector<InfoHash> &hashes);
void decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes);
void topTorrentsQueuePos(const QVector<InfoHash> &hashes);
void bottomTorrentsQueuePos(const QVector<InfoHash> &hashes);
void recursiveTorrentDownload(const TorrentID &id);
void increaseTorrentsQueuePos(const QVector<TorrentID> &ids);
void decreaseTorrentsQueuePos(const QVector<TorrentID> &ids);
void topTorrentsQueuePos(const QVector<TorrentID> &ids);
void bottomTorrentsQueuePos(const QVector<TorrentID> &ids);
// Torrent interface
void handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent);
@@ -539,7 +539,7 @@ namespace BitTorrent
void handleIPFilterParsed(int ruleCount);
void handleIPFilterError();
void handleDownloadFinished(const Net::DownloadResult &result);
void fileSearchFinished(const InfoHash &id, const QString &savePath, const QStringList &fileNames);
void fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames);
// Session reconfiguration triggers
void networkOnlineStateChanged(bool online);
@@ -764,17 +764,17 @@ namespace BitTorrent
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
FileSearcher *m_fileSearcher = nullptr;
QSet<InfoHash> m_downloadedMetadata;
QSet<TorrentID> m_downloadedMetadata;
QHash<InfoHash, TorrentImpl *> m_torrents;
QHash<InfoHash, LoadTorrentParams> m_loadingTorrents;
QHash<TorrentID, TorrentImpl *> m_torrents;
QHash<TorrentID, LoadTorrentParams> m_loadingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents;
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
QStringMap m_categories;
QSet<QString> m_tags;
// I/O errored torrents
QSet<InfoHash> m_recentErroredTorrents;
QSet<TorrentID> m_recentErroredTorrents;
QTimer *m_recentErroredTorrentsTimer = nullptr;
SessionMetricIndices m_metricIndices;

View File

@@ -31,6 +31,8 @@
#include <QHash>
#include "infohash.h"
namespace BitTorrent
{
uint qHash(const TorrentState key, const uint seed)
@@ -49,6 +51,11 @@ namespace BitTorrent
const qreal Torrent::MAX_RATIO = 9999;
const int Torrent::MAX_SEEDING_TIME = 525600;
TorrentID Torrent::id() const
{
return infoHash().toTorrentID();
}
bool Torrent::isResumed() const
{
return !isPaused();

View File

@@ -44,6 +44,7 @@ namespace BitTorrent
enum class DownloadPriority;
class InfoHash;
class PeerInfo;
class TorrentID;
class TorrentInfo;
struct PeerAddress;
struct TrackerEntry;
@@ -105,7 +106,7 @@ namespace BitTorrent
virtual ~Torrent() = default;
virtual InfoHash hash() const = 0;
virtual InfoHash infoHash() const = 0;
virtual QString name() const = 0;
virtual QDateTime creationDate() const = 0;
virtual QString creator() const = 0;
@@ -290,6 +291,7 @@ namespace BitTorrent
virtual QString createMagnetURI() const = 0;
TorrentID id() const;
bool isResumed() const;
qlonglong remainingSize() const;

View File

@@ -220,6 +220,11 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
, m_session(session)
, m_nativeSession(nativeSession)
, m_nativeHandle(nativeHandle)
#if (LIBTORRENT_VERSION_NUM >= 20000)
, m_infoHash(m_nativeHandle.info_hashes())
#else
, m_infoHash(m_nativeHandle.info_hash())
#endif
, m_name(params.name)
, m_savePath(Utils::Fs::toNativePath(params.savePath))
, m_category(params.category)
@@ -237,7 +242,6 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
m_hash = InfoHash {m_nativeHandle.info_hash()};
if (m_ltAddTorrentParams.ti)
{
// Initialize it only if torrent is added with metadata.
@@ -282,9 +286,9 @@ bool TorrentImpl::isValid() const
return m_nativeHandle.is_valid();
}
InfoHash TorrentImpl::hash() const
InfoHash TorrentImpl::infoHash() const
{
return m_hash;
return m_infoHash;
}
QString TorrentImpl::name() const
@@ -299,7 +303,7 @@ QString TorrentImpl::name() const
if (!name.isEmpty())
return name;
return m_hash.toString();
return id().toString();
}
QDateTime TorrentImpl::creationDate() const

View File

@@ -99,7 +99,7 @@ namespace BitTorrent
bool isValid() const;
InfoHash hash() const override;
InfoHash infoHash() const override;
QString name() const override;
QDateTime creationDate() const override;
QString creator() const override;
@@ -301,7 +301,7 @@ namespace BitTorrent
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
InfoHash m_hash;
InfoHash m_infoHash;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete

View File

@@ -184,10 +184,15 @@ bool TorrentInfo::isValid() const
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
}
InfoHash TorrentInfo::hash() const
InfoHash TorrentInfo::infoHash() const
{
if (!isValid()) return {};
#if (LIBTORRENT_VERSION_NUM >= 20000)
return m_nativeInfo->info_hashes();
#else
return m_nativeInfo->info_hash();
#endif
}
QString TorrentInfo::name() const
@@ -374,7 +379,7 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
hashes.reserve(count);
for (int i = 0; i < count; ++i)
hashes += {m_nativeInfo->hash_for_piece_ptr(lt::piece_index_t {i}), InfoHash::length()};
hashes += {m_nativeInfo->hash_for_piece_ptr(lt::piece_index_t {i}), SHA1Hash::length()};
return hashes;
}

View File

@@ -62,7 +62,7 @@ namespace BitTorrent
TorrentInfo &operator=(const TorrentInfo &other);
bool isValid() const;
InfoHash hash() const;
InfoHash infoHash() const;
QString name() const;
QDateTime creationDate() const;
QString creator() const;

View File

@@ -153,7 +153,7 @@ struct Tracker::TrackerAnnounceRequest
{
QHostAddress socketAddress;
QByteArray claimedAddress; // self claimed by peer
InfoHash infoHash;
TorrentID torrentID;
QString event;
Peer peer;
int numwant = 50;
@@ -295,11 +295,11 @@ void Tracker::processAnnounceRequest()
if (infoHashIter == queryParams.end())
throw TrackerError("Missing \"info_hash\" parameter");
const auto infoHash = InfoHash::fromString(infoHashIter->toHex());
if (!infoHash.isValid())
const auto torrentID = TorrentID::fromString(infoHashIter->toHex());
if (!torrentID.isValid())
throw TrackerError("Invalid \"info_hash\" parameter");
announceReq.infoHash = infoHash;
announceReq.torrentID = torrentID;
// 2. peer_id
const auto peerIdIter = queryParams.find(ANNOUNCE_REQUEST_PEER_ID);
@@ -381,19 +381,19 @@ void Tracker::processAnnounceRequest()
void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq)
{
if (!m_torrents.contains(announceReq.infoHash))
if (!m_torrents.contains(announceReq.torrentID))
{
// Reached max size, remove a random torrent
if (m_torrents.size() >= MAX_TORRENTS)
m_torrents.erase(m_torrents.begin());
}
m_torrents[announceReq.infoHash].setPeer(announceReq.peer);
m_torrents[announceReq.torrentID].setPeer(announceReq.peer);
}
void Tracker::unregisterPeer(const TrackerAnnounceRequest &announceReq)
{
const auto torrentStatsIter = m_torrents.find(announceReq.infoHash);
const auto torrentStatsIter = m_torrents.find(announceReq.torrentID);
if (torrentStatsIter == m_torrents.end())
return;
@@ -405,7 +405,7 @@ void Tracker::unregisterPeer(const TrackerAnnounceRequest &announceReq)
void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq)
{
const TorrentStats &torrentStats = m_torrents[announceReq.infoHash];
const TorrentStats &torrentStats = m_torrents[announceReq.torrentID];
lt::entry::dictionary_type replyDict
{

View File

@@ -102,6 +102,6 @@ namespace BitTorrent
Http::Request m_request;
Http::Environment m_env;
QHash<InfoHash, TorrentStats> m_torrents;
QHash<TorrentID, TorrentStats> m_torrents;
};
}

View File

@@ -32,7 +32,7 @@
#include "bittorrent/torrent.h"
const QString TorrentFilter::AnyCategory;
const InfoHashSet TorrentFilter::AnyHash {{}};
const TorrentIDSet TorrentFilter::AnyID {{}};
const QString TorrentFilter::AnyTag;
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
@@ -49,19 +49,19 @@ const TorrentFilter TorrentFilter::ErroredTorrent(TorrentFilter::Errored);
using BitTorrent::Torrent;
TorrentFilter::TorrentFilter(const Type type, const InfoHashSet &hashSet, const QString &category, const QString &tag)
TorrentFilter::TorrentFilter(const Type type, const TorrentIDSet &idSet, const QString &category, const QString &tag)
: m_type(type)
, m_category(category)
, m_tag(tag)
, m_hashSet(hashSet)
, m_idSet(idSet)
{
}
TorrentFilter::TorrentFilter(const QString &filter, const InfoHashSet &hashSet, const QString &category, const QString &tag)
TorrentFilter::TorrentFilter(const QString &filter, const TorrentIDSet &idSet, const QString &category, const QString &tag)
: m_type(All)
, m_category(category)
, m_tag(tag)
, m_hashSet(hashSet)
, m_idSet(idSet)
{
setTypeByName(filter);
}
@@ -107,11 +107,11 @@ bool TorrentFilter::setTypeByName(const QString &filter)
return setType(type);
}
bool TorrentFilter::setHashSet(const InfoHashSet &hashSet)
bool TorrentFilter::setTorrentIDSet(const TorrentIDSet &idSet)
{
if (m_hashSet != hashSet)
if (m_idSet != idSet)
{
m_hashSet = hashSet;
m_idSet = idSet;
return true;
}
@@ -189,9 +189,9 @@ bool TorrentFilter::matchState(const BitTorrent::Torrent *const torrent) const
bool TorrentFilter::matchHash(const BitTorrent::Torrent *const torrent) const
{
if (m_hashSet == AnyHash) return true;
if (m_idSet == AnyID) return true;
return m_hashSet.contains(torrent->hash());
return m_idSet.contains(torrent->id());
}
bool TorrentFilter::matchCategory(const BitTorrent::Torrent *const torrent) const

View File

@@ -38,7 +38,7 @@ namespace BitTorrent
class Torrent;
}
using InfoHashSet = QSet<BitTorrent::InfoHash>;
using TorrentIDSet = QSet<BitTorrent::TorrentID>;
class TorrentFilter
{
@@ -61,7 +61,7 @@ public:
// These mean any permutation, including no category / tag.
static const QString AnyCategory;
static const InfoHashSet AnyHash;
static const TorrentIDSet AnyID;
static const QString AnyTag;
static const TorrentFilter DownloadingTorrent;
@@ -79,12 +79,12 @@ public:
TorrentFilter() = default;
// category & tags: pass empty string for uncategorized / untagged torrents.
// Pass null string (QString()) to disable filtering (i.e. all torrents).
TorrentFilter(Type type, const InfoHashSet &hashSet = AnyHash, const QString &category = AnyCategory, const QString &tag = AnyTag);
TorrentFilter(const QString &filter, const InfoHashSet &hashSet = AnyHash, const QString &category = AnyCategory, const QString &tags = AnyTag);
TorrentFilter(Type type, const TorrentIDSet &idSet = AnyID, const QString &category = AnyCategory, const QString &tag = AnyTag);
TorrentFilter(const QString &filter, const TorrentIDSet &idSet = AnyID, const QString &category = AnyCategory, const QString &tags = AnyTag);
bool setType(Type type);
bool setTypeByName(const QString &filter);
bool setHashSet(const InfoHashSet &hashSet);
bool setTorrentIDSet(const TorrentIDSet &idSet);
bool setCategory(const QString &category);
bool setTag(const QString &tag);
@@ -99,5 +99,5 @@ private:
Type m_type {All};
QString m_category;
QString m_tag;
InfoHashSet m_hashSet;
TorrentIDSet m_idSet;
};