mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-26 16:15:10 -07:00
Store hybrid torrents using legacy filenames
* Make Digest32 implicitly shared class * Store hybrid torrents using legacy filenames PR #16237.
This commit is contained in:
parent
270e2023cd
commit
e93c360db6
4 changed files with 152 additions and 56 deletions
|
@ -36,6 +36,13 @@ BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
BitTorrent::InfoHash::InfoHash(const SHA1Hash &v1, const SHA256Hash &v2)
|
||||||
|
: InfoHash {WrappedType(v1, v2)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool BitTorrent::InfoHash::isValid() const
|
bool BitTorrent::InfoHash::isValid() const
|
||||||
{
|
{
|
||||||
return m_valid;
|
return m_valid;
|
||||||
|
|
|
@ -65,6 +65,9 @@ namespace BitTorrent
|
||||||
|
|
||||||
InfoHash() = default;
|
InfoHash() = default;
|
||||||
InfoHash(const WrappedType &nativeHash);
|
InfoHash(const WrappedType &nativeHash);
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
InfoHash(const SHA1Hash &v1, const SHA256Hash &v2);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
SHA1Hash v1() const;
|
SHA1Hash v1() const;
|
||||||
|
@ -85,3 +88,6 @@ namespace BitTorrent
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BitTorrent::TorrentID)
|
Q_DECLARE_METATYPE(BitTorrent::TorrentID)
|
||||||
|
// We can declare it as Q_MOVABLE_TYPE to improve performance
|
||||||
|
// since base type uses QSharedDataPointer as the only member
|
||||||
|
Q_DECLARE_TYPEINFO(BitTorrent::TorrentID, Q_MOVABLE_TYPE);
|
||||||
|
|
|
@ -291,6 +291,20 @@ namespace
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TorrentID resumeDataID(const BitTorrent::Torrent *torrent)
|
||||||
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
// use legacy ID to store hybrid torrent
|
||||||
|
const InfoHash infoHash = torrent->infoHash();
|
||||||
|
const TorrentID id = ((infoHash.v1().isValid() && infoHash.v2().isValid())
|
||||||
|
? InfoHash(infoHash.v1(), SHA256Hash()).toTorrentID()
|
||||||
|
: torrent->id());
|
||||||
|
#else
|
||||||
|
const TorrentID id = torrent->id();
|
||||||
|
#endif
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
|
const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
|
||||||
|
@ -1841,7 +1855,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove it from torrent resume directory
|
// Remove it from torrent resume directory
|
||||||
m_resumeDataStorage->remove(torrent->id());
|
m_resumeDataStorage->remove(resumeDataID(torrent));
|
||||||
|
|
||||||
delete torrent;
|
delete torrent;
|
||||||
return true;
|
return true;
|
||||||
|
@ -4011,7 +4025,7 @@ void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const Loa
|
||||||
{
|
{
|
||||||
--m_numResumeData;
|
--m_numResumeData;
|
||||||
|
|
||||||
m_resumeDataStorage->store(torrent->id(), data);
|
m_resumeDataStorage->store(resumeDataID(torrent), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
|
void Session::handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
|
||||||
|
@ -4346,53 +4360,115 @@ void Session::startUpTorrents()
|
||||||
const QVector<TorrentID> torrents = startupStorage->registeredTorrents();
|
const QVector<TorrentID> torrents = startupStorage->registeredTorrents();
|
||||||
int resumedTorrentsCount = 0;
|
int resumedTorrentsCount = 0;
|
||||||
QVector<TorrentID> queue;
|
QVector<TorrentID> queue;
|
||||||
for (const TorrentID &torrentID : torrents)
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const QSet<TorrentID> indexedTorrents {torrents.cbegin(), torrents.cend()};
|
||||||
|
QSet<TorrentID> skippedIDs;
|
||||||
|
#endif
|
||||||
|
for (TorrentID torrentID : torrents)
|
||||||
{
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
if (skippedIDs.contains(torrentID))
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
const std::optional<LoadTorrentParams> loadResumeDataResult = startupStorage->load(torrentID);
|
const std::optional<LoadTorrentParams> loadResumeDataResult = startupStorage->load(torrentID);
|
||||||
if (loadResumeDataResult)
|
if (!loadResumeDataResult)
|
||||||
{
|
|
||||||
LoadTorrentParams resumeData = *loadResumeDataResult;
|
|
||||||
bool needStore = false;
|
|
||||||
|
|
||||||
if (m_resumeDataStorage != startupStorage)
|
|
||||||
{
|
|
||||||
needStore = true;
|
|
||||||
if (isQueueingSystemEnabled() && !resumeData.hasSeedStatus)
|
|
||||||
queue.append(torrentID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove the following upgrade code in v4.5
|
|
||||||
// == BEGIN UPGRADE CODE ==
|
|
||||||
if (m_needUpgradeDownloadPath && isDownloadPathEnabled())
|
|
||||||
{
|
|
||||||
if (!resumeData.useAutoTMM)
|
|
||||||
{
|
|
||||||
resumeData.downloadPath = downloadPath();
|
|
||||||
needStore = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// == END UPGRADE CODE ==
|
|
||||||
|
|
||||||
if (needStore)
|
|
||||||
m_resumeDataStorage->store(torrentID, resumeData);
|
|
||||||
|
|
||||||
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
|
||||||
if (!loadTorrent(resumeData))
|
|
||||||
{
|
|
||||||
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
|
||||||
.arg(torrentID.toString()), Log::CRITICAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// process add torrent messages before message queue overflow
|
|
||||||
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
|
||||||
|
|
||||||
++resumedTorrentsCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
||||||
.arg(torrentID.toString()), Log::CRITICAL);
|
.arg(torrentID.toString()), Log::CRITICAL);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadTorrentParams resumeData = *loadResumeDataResult;
|
||||||
|
bool needStore = false;
|
||||||
|
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const lt::info_hash_t infoHash = (resumeData.ltAddTorrentParams.ti
|
||||||
|
? resumeData.ltAddTorrentParams.ti->info_hashes()
|
||||||
|
: resumeData.ltAddTorrentParams.info_hashes);
|
||||||
|
if (infoHash.has_v1() && infoHash.has_v2())
|
||||||
|
{
|
||||||
|
const auto torrentIDv1 = TorrentID::fromInfoHash(lt::info_hash_t(infoHash.v1));
|
||||||
|
const auto torrentIDv2 = TorrentID::fromInfoHash(infoHash);
|
||||||
|
if (torrentID == torrentIDv1)
|
||||||
|
{
|
||||||
|
if (indexedTorrents.contains(torrentIDv2))
|
||||||
|
{
|
||||||
|
// if we has no metadata trying to find it in alternative "resume data"
|
||||||
|
if (!resumeData.ltAddTorrentParams.ti)
|
||||||
|
{
|
||||||
|
const std::optional<LoadTorrentParams> loadAltResumeDataResult = startupStorage->load(torrentIDv2);
|
||||||
|
if (loadAltResumeDataResult)
|
||||||
|
{
|
||||||
|
LoadTorrentParams altResumeData = *loadAltResumeDataResult;
|
||||||
|
resumeData.ltAddTorrentParams.ti = altResumeData.ltAddTorrentParams.ti;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// remove alternative "resume data" and skip the attempt to load it
|
||||||
|
m_resumeDataStorage->remove(torrentIDv2);
|
||||||
|
skippedIDs.insert(torrentIDv2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
torrentID = torrentIDv1;
|
||||||
|
needStore = true;
|
||||||
|
m_resumeDataStorage->remove(torrentIDv2);
|
||||||
|
|
||||||
|
if (indexedTorrents.contains(torrentID))
|
||||||
|
{
|
||||||
|
skippedIDs.insert(torrentID);
|
||||||
|
|
||||||
|
const std::optional<LoadTorrentParams> loadPreferredResumeDataResult = startupStorage->load(torrentID);
|
||||||
|
if (loadPreferredResumeDataResult)
|
||||||
|
{
|
||||||
|
LoadTorrentParams preferredResumeData = *loadPreferredResumeDataResult;
|
||||||
|
std::shared_ptr<lt::torrent_info> ti = resumeData.ltAddTorrentParams.ti;
|
||||||
|
if (!preferredResumeData.ltAddTorrentParams.ti)
|
||||||
|
preferredResumeData.ltAddTorrentParams.ti = ti;
|
||||||
|
|
||||||
|
resumeData = preferredResumeData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_resumeDataStorage != startupStorage)
|
||||||
|
{
|
||||||
|
needStore = true;
|
||||||
|
if (isQueueingSystemEnabled() && !resumeData.hasSeedStatus)
|
||||||
|
queue.append(torrentID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove the following upgrade code in v4.5
|
||||||
|
// == BEGIN UPGRADE CODE ==
|
||||||
|
if (m_needUpgradeDownloadPath && isDownloadPathEnabled())
|
||||||
|
{
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.downloadPath = downloadPath();
|
||||||
|
needStore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// == END UPGRADE CODE ==
|
||||||
|
|
||||||
|
if (needStore)
|
||||||
|
m_resumeDataStorage->store(torrentID, resumeData);
|
||||||
|
|
||||||
|
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
||||||
|
if (!loadTorrent(resumeData))
|
||||||
|
{
|
||||||
|
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
||||||
|
.arg(torrentID.toString()), Log::CRITICAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// process add torrent messages before message queue overflow
|
||||||
|
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
||||||
|
|
||||||
|
++resumedTorrentsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_resumeDataStorage != startupStorage)
|
if (m_resumeDataStorage != startupStorage)
|
||||||
|
@ -4610,7 +4686,7 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_resumeDataStorage->store(torrent->id(), params);
|
m_resumeDataStorage->store(resumeDataID(torrent), params);
|
||||||
|
|
||||||
// The following is useless for newly added magnet
|
// The following is useless for newly added magnet
|
||||||
if (hasMetadata)
|
if (hasMetadata)
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QSharedData>
|
||||||
|
#include <QSharedDataPointer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
template <int N>
|
template <int N>
|
||||||
|
@ -43,11 +45,11 @@ public:
|
||||||
Digest32() = default;
|
Digest32() = default;
|
||||||
|
|
||||||
Digest32(const UnderlyingType &nativeDigest)
|
Digest32(const UnderlyingType &nativeDigest)
|
||||||
: m_valid {true}
|
|
||||||
, m_nativeDigest {nativeDigest}
|
|
||||||
{
|
{
|
||||||
|
m_dataPtr->valid = true;
|
||||||
|
m_dataPtr->nativeDigest = nativeDigest;
|
||||||
const QByteArray raw = QByteArray::fromRawData(nativeDigest.data(), length());
|
const QByteArray raw = QByteArray::fromRawData(nativeDigest.data(), length());
|
||||||
m_hashString = QString::fromLatin1(raw.toHex());
|
m_dataPtr->hashString = QString::fromLatin1(raw.toHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int length()
|
static constexpr int length()
|
||||||
|
@ -57,12 +59,12 @@ public:
|
||||||
|
|
||||||
bool isValid() const
|
bool isValid() const
|
||||||
{
|
{
|
||||||
return m_valid;
|
return m_dataPtr->valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator UnderlyingType() const
|
operator UnderlyingType() const
|
||||||
{
|
{
|
||||||
return m_nativeDigest;
|
return m_dataPtr->nativeDigest;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Digest32 fromString(const QString &digestString)
|
static Digest32 fromString(const QString &digestString)
|
||||||
|
@ -75,22 +77,27 @@ public:
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
Digest32 result;
|
Digest32 result;
|
||||||
result.m_valid = true;
|
result.m_dataPtr->valid = true;
|
||||||
result.m_hashString = digestString;
|
result.m_dataPtr->hashString = digestString;
|
||||||
result.m_nativeDigest.assign(raw.constData());
|
result.m_dataPtr->nativeDigest.assign(raw.constData());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString toString() const
|
QString toString() const
|
||||||
{
|
{
|
||||||
return m_hashString;
|
return m_dataPtr->hashString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_valid = false;
|
struct Data : public QSharedData
|
||||||
UnderlyingType m_nativeDigest;
|
{
|
||||||
QString m_hashString;
|
bool valid = false;
|
||||||
|
UnderlyingType nativeDigest;
|
||||||
|
QString hashString;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSharedDataPointer<Data> m_dataPtr {new Data};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int N>
|
template <int N>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue