mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-06 13:11:25 -07:00
commit
d29f47c36e
5 changed files with 202 additions and 234 deletions
|
@ -493,7 +493,6 @@ namespace BitTorrent
|
|||
void categoryOptionsChanged(const QString &categoryName);
|
||||
void fullDiskError(Torrent *torrent, const QString &msg);
|
||||
void IPFilterParsed(bool error, int ruleCount);
|
||||
void loadTorrentFailed(const QString &error);
|
||||
void metadataDownloaded(const TorrentInfo &info);
|
||||
void restored();
|
||||
void paused();
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "sessionimpl.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <queue>
|
||||
|
@ -315,6 +316,44 @@ namespace
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
template <typename T>
|
||||
concept HasInfoHashMember = requires (T t) { { t.info_hashes } -> std::convertible_to<InfoHash>; };
|
||||
|
||||
template <typename T>
|
||||
concept HasInfoHashMemberFn = requires (T t) { { t.info_hashes() } -> std::convertible_to<InfoHash>; };
|
||||
|
||||
template <HasInfoHashMember T>
|
||||
InfoHash getInfoHash(const T &t) { return t.info_hashes; }
|
||||
|
||||
template <HasInfoHashMemberFn T>
|
||||
InfoHash getInfoHash(const T &t) { return t.info_hashes(); }
|
||||
|
||||
InfoHash getInfoHash(const lt::add_torrent_params &addTorrentParams)
|
||||
{
|
||||
const bool hasMetadata = (addTorrentParams.ti && addTorrentParams.ti->is_valid());
|
||||
return hasMetadata ? getInfoHash(*addTorrentParams.ti) : InfoHash(addTorrentParams.info_hashes);
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
concept HasInfoHashMember = requires (T t) { { t.info_hash } -> std::convertible_to<InfoHash>; };
|
||||
|
||||
template <typename T>
|
||||
concept HasInfoHashMemberFn = requires (T t) { { t.info_hash() } -> std::convertible_to<InfoHash>; };
|
||||
|
||||
template <HasInfoHashMember T>
|
||||
InfoHash getInfoHash(const T &t) { return t.info_hash; }
|
||||
|
||||
template <HasInfoHashMemberFn T>
|
||||
InfoHash getInfoHash(const T &t) { return t.info_hash(); }
|
||||
|
||||
InfoHash getInfoHash(const lt::add_torrent_params &addTorrentParams)
|
||||
{
|
||||
const bool hasMetadata = (addTorrentParams.ti && addTorrentParams.ti->is_valid());
|
||||
return hasMetadata ? getInfoHash(*addTorrentParams.ti) : InfoHash(addTorrentParams.info_hash);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct BitTorrent::SessionImpl::ResumeSessionContext final : public QObject
|
||||
|
@ -1443,10 +1482,8 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context)
|
|||
LoadTorrentParams resumeData = *loadResumeDataResult;
|
||||
bool needStore = false;
|
||||
|
||||
const InfoHash infoHash = getInfoHash(resumeData.ltAddTorrentParams);
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {(resumeData.ltAddTorrentParams.ti
|
||||
? resumeData.ltAddTorrentParams.ti->info_hashes()
|
||||
: resumeData.ltAddTorrentParams.info_hashes)};
|
||||
const bool isHybrid = infoHash.isHybrid();
|
||||
const auto torrentIDv2 = TorrentID::fromInfoHash(infoHash);
|
||||
const auto torrentIDv1 = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
|
@ -1494,9 +1531,6 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context)
|
|||
return;
|
||||
}
|
||||
#else
|
||||
const lt::sha1_hash infoHash = (resumeData.ltAddTorrentParams.ti
|
||||
? resumeData.ltAddTorrentParams.ti->info_hash()
|
||||
: resumeData.ltAddTorrentParams.info_hash);
|
||||
if (torrentID != TorrentID::fromInfoHash(infoHash))
|
||||
{
|
||||
LogMsg(tr("Failed to resume torrent: inconsistent torrent ID is detected. Torrent: \"%1\"")
|
||||
|
@ -1586,16 +1620,23 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context)
|
|||
#endif
|
||||
|
||||
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
||||
m_loadingTorrents.insert(torrentID, resumeData);
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
// this allows to know the being added hybrid torrent by its v1 info hash
|
||||
// without having yet another mapping table
|
||||
m_hybridTorrentsByAltID.insert(torrentIDv1, nullptr);
|
||||
}
|
||||
#endif
|
||||
m_nativeSession->async_add_torrent(resumeData.ltAddTorrentParams);
|
||||
m_addTorrentAlertHandlers.enqueue([this, resumeData = std::move(resumeData)](const lt::add_torrent_alert *alert)
|
||||
{
|
||||
if (alert->error)
|
||||
{
|
||||
const QString msg = QString::fromStdString(alert->message());
|
||||
LogMsg(tr("Failed to load torrent. Reason: \"%1\"").arg(msg), Log::WARNING);
|
||||
}
|
||||
else
|
||||
{
|
||||
Torrent *torrent = createTorrent(alert->handle, resumeData);
|
||||
m_loadedTorrents.append(torrent);
|
||||
|
||||
LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name()));
|
||||
}
|
||||
});
|
||||
|
||||
++context->processingResumeDataCount;
|
||||
}
|
||||
|
||||
|
@ -2398,7 +2439,7 @@ Torrent *SessionImpl::getTorrent(const TorrentID &id) const
|
|||
Torrent *SessionImpl::findTorrent(const InfoHash &infoHash) const
|
||||
{
|
||||
const auto id = TorrentID::fromInfoHash(infoHash);
|
||||
if (Torrent *torrent = m_torrents.value(id); torrent)
|
||||
if (Torrent *torrent = m_torrents.value(id))
|
||||
return torrent;
|
||||
|
||||
if (!infoHash.isHybrid())
|
||||
|
@ -2515,7 +2556,7 @@ bool SessionImpl::cancelDownloadMetadata(const TorrentID &id)
|
|||
return true;
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {nativeHandle.info_hashes()};
|
||||
const InfoHash infoHash = getInfoHash(nativeHandle);
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
// if magnet link was hybrid initially then it is indexed also by v1 info hash
|
||||
|
@ -2744,14 +2785,6 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
// alternative ID can be useful to find existing torrent in case if hybrid torrent was added by v1 info hash
|
||||
const auto altID = (infoHash.isHybrid() ? TorrentID::fromSHA1Hash(infoHash.v1()) : TorrentID());
|
||||
|
||||
// We should not add the torrent if it is already
|
||||
// processed or is pending to add to session
|
||||
if (m_loadingTorrents.contains(id) || (infoHash.isHybrid() && m_loadingTorrents.contains(altID)))
|
||||
{
|
||||
emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, tr("Duplicate torrent")});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Torrent *torrent = findTorrent(infoHash))
|
||||
{
|
||||
// a duplicate torrent is being added
|
||||
|
@ -2869,14 +2902,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
}
|
||||
|
||||
if (!loadTorrentParams.hasFinishedStatus)
|
||||
{
|
||||
needFindIncompleteFiles = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < filePaths.size(); ++index)
|
||||
p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString();
|
||||
}
|
||||
|
||||
const int internalFilesCount = torrentInfo.nativeInfo()->files().num_files(); // including .pad files
|
||||
// Use qBittorrent default priority rather than libtorrent's (4)
|
||||
|
@ -2896,8 +2922,6 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
loadTorrentParams.name = QString::fromStdString(p.name);
|
||||
}
|
||||
|
||||
p.save_path = actualSavePath.toString().toStdString();
|
||||
|
||||
if (isAddTrackersEnabled() && !(hasMetadata && p.ti->priv()))
|
||||
{
|
||||
const auto maxTierIter = std::max_element(p.tracker_tiers.cbegin(), p.tracker_tiers.cend());
|
||||
|
@ -2968,38 +2992,64 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
|
|||
p.storage = customStorageConstructor;
|
||||
#endif
|
||||
|
||||
m_loadingTorrents.insert(id, loadTorrentParams);
|
||||
if (infoHash.isHybrid())
|
||||
m_hybridTorrentsByAltID.insert(altID, nullptr);
|
||||
|
||||
if (needFindIncompleteFiles)
|
||||
const auto resolveFileNames = [&, this]
|
||||
{
|
||||
const Path actualDownloadPath = useAutoTMM
|
||||
if (!needFindIncompleteFiles)
|
||||
return QtFuture::makeReadyValueFuture(FileSearchResult {.savePath = actualSavePath, .fileNames = filePaths});
|
||||
|
||||
const Path actualDownloadPath = loadTorrentParams.useAutoTMM
|
||||
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||
findIncompleteFiles(actualSavePath, actualDownloadPath, filePaths).then(this
|
||||
, [this, id](const FileSearchResult &result)
|
||||
{
|
||||
const auto loadingTorrentsIter = m_loadingTorrents.find(id);
|
||||
Q_ASSERT(loadingTorrentsIter != m_loadingTorrents.end());
|
||||
if (loadingTorrentsIter == m_loadingTorrents.end()) [[unlikely]]
|
||||
return;
|
||||
return findIncompleteFiles(actualSavePath, actualDownloadPath, filePaths);
|
||||
};
|
||||
|
||||
LoadTorrentParams ¶ms = loadingTorrentsIter.value();
|
||||
lt::add_torrent_params &p = params.ltAddTorrentParams;
|
||||
resolveFileNames().then(this, [this, id, loadTorrentParams = std::move(loadTorrentParams)](const FileSearchResult &result) mutable
|
||||
{
|
||||
lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
|
||||
|
||||
p.save_path = result.savePath.toString().toStdString();
|
||||
if (p.ti)
|
||||
{
|
||||
const TorrentInfo torrentInfo {*p.ti};
|
||||
const auto nativeIndexes = torrentInfo.nativeIndexes();
|
||||
for (int i = 0; i < result.fileNames.size(); ++i)
|
||||
p.renamed_files[nativeIndexes[i]] = result.fileNames[i].toString().toStdString();
|
||||
}
|
||||
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
});
|
||||
m_addTorrentAlertHandlers.enqueue([this, loadTorrentParams = std::move(loadTorrentParams)](const lt::add_torrent_alert *alert)
|
||||
{
|
||||
if (alert->error)
|
||||
{
|
||||
const QString msg = QString::fromStdString(alert->message());
|
||||
LogMsg(tr("Failed to add torrent. Reason: \"%1\"").arg(msg), Log::WARNING);
|
||||
|
||||
const InfoHash infoHash = getInfoHash(alert->params);
|
||||
const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent)
|
||||
? AddTorrentError::DuplicateTorrent : AddTorrentError::Other;
|
||||
emit addTorrentFailed(infoHash, {errorKind, msg});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
if (loadTorrentParams.addToQueueTop)
|
||||
alert->handle.queue_position_top();
|
||||
|
||||
TorrentImpl *torrent = createTorrent(alert->handle, loadTorrentParams);
|
||||
m_loadedTorrents.append(torrent);
|
||||
|
||||
torrent->requestResumeData(lt::torrent_handle::save_info_dict);
|
||||
|
||||
LogMsg(tr("Added new torrent. Torrent: \"%1\"").arg(torrent->name()));
|
||||
emit torrentAdded(torrent);
|
||||
|
||||
// The following is useless for newly added magnet
|
||||
if (torrent->hasMetadata())
|
||||
{
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent, torrentExportDirectory());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3153,6 +3203,33 @@ bool SessionImpl::downloadMetadata(const TorrentDescriptor &torrentDescr)
|
|||
// Adding torrent to libtorrent session
|
||||
m_nativeSession->async_add_torrent(p);
|
||||
m_downloadedMetadata.insert(id, {});
|
||||
m_addTorrentAlertHandlers.enqueue([this](const lt::add_torrent_alert *alert)
|
||||
{
|
||||
if (alert->error)
|
||||
{
|
||||
const QString msg = QString::fromStdString(alert->message());
|
||||
LogMsg(tr("Failed to download torrent metadata. Reason: \"%1\"").arg(msg), Log::WARNING);
|
||||
|
||||
m_downloadedMetadata.remove(getInfoHash(alert->params).toTorrentID());
|
||||
}
|
||||
else
|
||||
{
|
||||
const InfoHash infoHash = getInfoHash(alert->handle);
|
||||
const auto torrentID = TorrentID::fromInfoHash(infoHash);
|
||||
|
||||
if (const auto downloadedMetadataIter = m_downloadedMetadata.find(torrentID)
|
||||
; downloadedMetadataIter != m_downloadedMetadata.end())
|
||||
{
|
||||
downloadedMetadataIter.value() = alert->handle;
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
// index hybrid magnet links by both v1 and v2 info hashes
|
||||
const auto altID = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
m_downloadedMetadata[altID] = alert->handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -5201,8 +5278,6 @@ bool SessionImpl::isKnownTorrent(const InfoHash &infoHash) const
|
|||
// in case if hybrid torrent was added by v1 info hash
|
||||
const auto altID = (isHybrid ? TorrentID::fromSHA1Hash(infoHash.v1()) : TorrentID());
|
||||
|
||||
if (m_loadingTorrents.contains(id) || (isHybrid && m_loadingTorrents.contains(altID)))
|
||||
return true;
|
||||
if (m_downloadedMetadata.contains(id) || (isHybrid && m_downloadedMetadata.contains(altID)))
|
||||
return true;
|
||||
return findTorrent(infoHash);
|
||||
|
@ -5415,15 +5490,25 @@ bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &new
|
|||
return true;
|
||||
}
|
||||
|
||||
lt::torrent_handle SessionImpl::reloadTorrent(const lt::torrent_handle ¤tHandle, lt::add_torrent_params params)
|
||||
{
|
||||
m_nativeSession->remove_torrent(currentHandle, lt::session::delete_partfile);
|
||||
|
||||
auto *const extensionData = new ExtensionData;
|
||||
params.userdata = LTClientData(extensionData);
|
||||
#ifndef QBT_USES_LIBTORRENT2
|
||||
params.storage = customStorageConstructor;
|
||||
#endif
|
||||
|
||||
// libtorrent will post an add_torrent_alert anyway, so we have to add an empty handler to ignore it.
|
||||
m_addTorrentAlertHandlers.enqueue({});
|
||||
return m_nativeSession->add_torrent(std::move(params));
|
||||
}
|
||||
|
||||
void SessionImpl::moveTorrentStorage(const MoveStorageJob &job) const
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
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());
|
||||
const TorrentImpl *torrent = getTorrent(job.torrentHandle);
|
||||
const QString torrentName = (torrent ? torrent->name() : getInfoHash(job.torrentHandle).toTorrentID().toString());
|
||||
LogMsg(tr("Start moving torrent. Torrent: \"%1\". Destination: \"%2\"").arg(torrentName, job.path.toString()));
|
||||
|
||||
job.torrentHandle.move_storage(job.path.toString().toStdString(), toNative(job.mode));
|
||||
|
@ -5443,7 +5528,7 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
|
|||
|
||||
const bool torrentHasOutstandingJob = (iter != m_moveStorageQueue.cend());
|
||||
|
||||
TorrentImpl *torrent = m_torrents.value(finishedJob.torrentHandle.info_hash());
|
||||
TorrentImpl *torrent = getTorrent(finishedJob.torrentHandle);
|
||||
if (torrent)
|
||||
{
|
||||
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
|
||||
|
@ -5451,8 +5536,9 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
|
|||
else if (!torrentHasOutstandingJob)
|
||||
{
|
||||
// Last job is completed for torrent that being removing, so actually remove it
|
||||
const lt::torrent_handle nativeHandle {finishedJob.torrentHandle};
|
||||
const RemovingTorrentData &removingTorrentData = m_removingTorrents[nativeHandle.info_hash()];
|
||||
const lt::torrent_handle nativeHandle = finishedJob.torrentHandle;
|
||||
const TorrentID torrentID = getInfoHash(nativeHandle).toTorrentID();
|
||||
const RemovingTorrentData &removingTorrentData = m_removingTorrents[torrentID];
|
||||
if (removingTorrentData.removeOption == TorrentRemoveOption::KeepContent)
|
||||
m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile);
|
||||
}
|
||||
|
@ -5743,72 +5829,12 @@ void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert)
|
|||
{
|
||||
++m_receivedAddTorrentAlertsCount;
|
||||
|
||||
if (alert->error)
|
||||
{
|
||||
const QString msg = QString::fromStdString(alert->message());
|
||||
LogMsg(tr("Failed to load torrent. Reason: \"%1\"").arg(msg), Log::WARNING);
|
||||
emit loadTorrentFailed(msg);
|
||||
|
||||
const lt::add_torrent_params ¶ms = alert->params;
|
||||
const bool hasMetadata = (params.ti && params.ti->is_valid());
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {(hasMetadata ? params.ti->info_hashes() : params.info_hashes)};
|
||||
if (infoHash.isHybrid())
|
||||
m_hybridTorrentsByAltID.remove(TorrentID::fromSHA1Hash(infoHash.v1()));
|
||||
#else
|
||||
const InfoHash infoHash {(hasMetadata ? params.ti->info_hash() : params.info_hash)};
|
||||
#endif
|
||||
if (const auto loadingTorrentsIter = m_loadingTorrents.constFind(TorrentID::fromInfoHash(infoHash))
|
||||
; loadingTorrentsIter != m_loadingTorrents.cend())
|
||||
{
|
||||
const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent)
|
||||
? AddTorrentError::DuplicateTorrent : AddTorrentError::Other;
|
||||
emit addTorrentFailed(infoHash, {errorKind, msg});
|
||||
m_loadingTorrents.erase(loadingTorrentsIter);
|
||||
}
|
||||
else if (const auto downloadedMetadataIter = m_downloadedMetadata.constFind(TorrentID::fromInfoHash(infoHash))
|
||||
; downloadedMetadataIter != m_downloadedMetadata.cend())
|
||||
{
|
||||
m_downloadedMetadata.erase(downloadedMetadataIter);
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
// index hybrid magnet links by both v1 and v2 info hashes
|
||||
const auto altID = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
m_downloadedMetadata.remove(altID);
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_addTorrentAlertHandlers.isEmpty());
|
||||
if (m_addTorrentAlertHandlers.isEmpty()) [[unlikely]]
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {alert->handle.info_hashes()};
|
||||
#else
|
||||
const InfoHash infoHash {alert->handle.info_hash()};
|
||||
#endif
|
||||
const auto torrentID = TorrentID::fromInfoHash(infoHash);
|
||||
|
||||
if (const auto loadingTorrentsIter = m_loadingTorrents.constFind(torrentID)
|
||||
; loadingTorrentsIter != m_loadingTorrents.cend())
|
||||
{
|
||||
const LoadTorrentParams params = loadingTorrentsIter.value();
|
||||
m_loadingTorrents.erase(loadingTorrentsIter);
|
||||
|
||||
Torrent *torrent = createTorrent(alert->handle, params);
|
||||
m_loadedTorrents.append(torrent);
|
||||
}
|
||||
else if (const auto downloadedMetadataIter = m_downloadedMetadata.find(torrentID)
|
||||
; downloadedMetadataIter != m_downloadedMetadata.end())
|
||||
{
|
||||
downloadedMetadataIter.value() = alert->handle;
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
// index hybrid magnet links by both v1 and v2 info hashes
|
||||
const auto altID = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
m_downloadedMetadata[altID] = alert->handle;
|
||||
}
|
||||
}
|
||||
if (const AddTorrentAlertHandler handleAlert = m_addTorrentAlertHandlers.dequeue())
|
||||
handleAlert(alert);
|
||||
}
|
||||
|
||||
void SessionImpl::handleAlert(const lt::alert *alert)
|
||||
|
@ -5930,12 +5956,11 @@ void SessionImpl::dispatchTorrentAlert(const lt::torrent_alert *alert)
|
|||
--m_numResumeData;
|
||||
}
|
||||
|
||||
const TorrentID torrentID {alert->handle.info_hash()};
|
||||
TorrentImpl *torrent = m_torrents.value(torrentID);
|
||||
TorrentImpl *torrent = getTorrent(alert->handle);
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
if (!torrent && (alert->type() == lt::metadata_received_alert::alert_type))
|
||||
{
|
||||
const InfoHash infoHash {alert->handle.info_hashes()};
|
||||
const InfoHash infoHash = getInfoHash(alert->handle);
|
||||
if (infoHash.isHybrid())
|
||||
torrent = m_torrents.value(TorrentID::fromSHA1Hash(infoHash.v1()));
|
||||
}
|
||||
|
@ -5957,42 +5982,17 @@ void SessionImpl::dispatchTorrentAlert(const lt::torrent_alert *alert)
|
|||
|
||||
TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||
{
|
||||
auto *const torrent = new TorrentImpl(this, m_nativeSession, nativeHandle, params);
|
||||
auto *const torrent = new TorrentImpl(this, nativeHandle, params);
|
||||
m_torrents.insert(torrent->id(), torrent);
|
||||
if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid())
|
||||
m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent);
|
||||
|
||||
if (isRestored())
|
||||
{
|
||||
if (params.addToQueueTop)
|
||||
nativeHandle.queue_position_top();
|
||||
|
||||
torrent->requestResumeData(lt::torrent_handle::save_info_dict);
|
||||
|
||||
// The following is useless for newly added magnet
|
||||
if (torrent->hasMetadata())
|
||||
{
|
||||
if (!torrentExportDirectory().isEmpty())
|
||||
exportTorrentFile(torrent, torrentExportDirectory());
|
||||
}
|
||||
}
|
||||
|
||||
if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0))
|
||||
&& !m_seedingLimitTimer->isActive())
|
||||
{
|
||||
m_seedingLimitTimer->start();
|
||||
}
|
||||
|
||||
if (!isRestored())
|
||||
{
|
||||
LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name()));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Added new torrent. Torrent: \"%1\"").arg(torrent->name()));
|
||||
emit torrentAdded(torrent);
|
||||
}
|
||||
|
||||
// Torrent could have error just after adding to libtorrent
|
||||
if (torrent->hasError())
|
||||
LogMsg(tr("Torrent errored. Torrent: \"%1\". Error: \"%2\"").arg(torrent->name(), torrent->error()), Log::WARNING);
|
||||
|
@ -6000,6 +6000,11 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle,
|
|||
return torrent;
|
||||
}
|
||||
|
||||
TorrentImpl *SessionImpl::getTorrent(const lt::torrent_handle &nativeHandle) const
|
||||
{
|
||||
return m_torrents.value(getInfoHash(nativeHandle).toTorrentID());
|
||||
}
|
||||
|
||||
void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert */*alert*/)
|
||||
{
|
||||
// We cannot consider `torrent_removed_alert` as a starting point for removing content,
|
||||
|
@ -6009,34 +6014,19 @@ void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert */*a
|
|||
|
||||
void SessionImpl::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *alert)
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes);
|
||||
#else
|
||||
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
||||
#endif
|
||||
handleRemovedTorrent(torrentID);
|
||||
handleRemovedTorrent(getInfoHash(*alert).toTorrentID());
|
||||
}
|
||||
|
||||
void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *alert)
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes);
|
||||
#else
|
||||
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
||||
#endif
|
||||
const TorrentID torrentID = getInfoHash(*alert).toTorrentID();
|
||||
const auto errorMessage = alert->error ? Utils::String::fromLocal8Bit(alert->error.message()) : QString();
|
||||
handleRemovedTorrent(torrentID, errorMessage);
|
||||
}
|
||||
|
||||
void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *alert)
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {alert->handle.info_hashes()};
|
||||
#else
|
||||
const InfoHash infoHash {alert->handle.info_hash()};
|
||||
#endif
|
||||
const auto torrentID = TorrentID::fromInfoHash(infoHash);
|
||||
|
||||
const TorrentID torrentID = getInfoHash(alert->handle).toTorrentID();
|
||||
TorrentImpl *const torrent = m_torrents.value(torrentID);
|
||||
if (!torrent) [[unlikely]]
|
||||
return;
|
||||
|
@ -6050,7 +6040,8 @@ void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *
|
|||
|
||||
void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *alert)
|
||||
{
|
||||
const TorrentID torrentID {alert->handle.info_hash()};
|
||||
const InfoHash infoHash = getInfoHash(alert->handle);
|
||||
const TorrentID torrentID = infoHash.toTorrentID();
|
||||
|
||||
bool found = false;
|
||||
if (const auto iter = m_downloadedMetadata.constFind(torrentID); iter != m_downloadedMetadata.cend())
|
||||
|
@ -6059,7 +6050,6 @@ void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert
|
|||
m_downloadedMetadata.erase(iter);
|
||||
}
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {alert->handle.info_hashes()};
|
||||
if (infoHash.isHybrid())
|
||||
{
|
||||
const auto altID = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
|
@ -6081,7 +6071,7 @@ void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert
|
|||
|
||||
void SessionImpl::handleFileErrorAlert(const lt::file_error_alert *alert)
|
||||
{
|
||||
TorrentImpl *const torrent = m_torrents.value(alert->handle.info_hash());
|
||||
TorrentImpl *const torrent = getTorrent(alert->handle);
|
||||
if (!torrent)
|
||||
return;
|
||||
|
||||
|
@ -6152,7 +6142,7 @@ void SessionImpl::handlePeerBanAlert(const lt::peer_ban_alert *alert)
|
|||
|
||||
void SessionImpl::handleUrlSeedAlert(const lt::url_seed_alert *alert)
|
||||
{
|
||||
const TorrentImpl *torrent = m_torrents.value(alert->handle.info_hash());
|
||||
const TorrentImpl *torrent = getTorrent(alert->handle);
|
||||
if (!torrent)
|
||||
return;
|
||||
|
||||
|
@ -6331,14 +6321,8 @@ void SessionImpl::handleStorageMovedAlert(const lt::storage_moved_alert *alert)
|
|||
const Path newPath {QString::fromUtf8(alert->storage_path())};
|
||||
Q_ASSERT(newPath == currentJob.path);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
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());
|
||||
TorrentImpl *torrent = getTorrent(currentJob.torrentHandle);
|
||||
const QString torrentName = (torrent ? torrent->name() : getInfoHash(currentJob.torrentHandle).toTorrentID().toString());
|
||||
LogMsg(tr("Moved torrent successfully. Torrent: \"%1\". Destination: \"%2\"").arg(torrentName, newPath.toString()));
|
||||
|
||||
handleMoveTorrentStorageJobFinished(newPath);
|
||||
|
@ -6351,14 +6335,8 @@ void SessionImpl::handleStorageMovedFailedAlert(const lt::storage_moved_failed_a
|
|||
const MoveStorageJob ¤tJob = m_moveStorageQueue.constFirst();
|
||||
Q_ASSERT(currentJob.torrentHandle == alert->handle);
|
||||
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
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());
|
||||
TorrentImpl *torrent = getTorrent(currentJob.torrentHandle);
|
||||
const QString torrentName = (torrent ? torrent->name() : getInfoHash(currentJob.torrentHandle).toTorrentID().toString());
|
||||
const Path currentLocation = (torrent ? torrent->actualStorageLocation()
|
||||
: Path(alert->handle.status(lt::torrent_handle::query_save_path).save_path));
|
||||
const QString errorMessage = QString::fromStdString(alert->message());
|
||||
|
@ -6375,12 +6353,7 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
|
|||
|
||||
for (const lt::torrent_status &status : alert->status)
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
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);
|
||||
TorrentImpl *const torrent = getTorrent(status.handle);
|
||||
if (!torrent)
|
||||
continue;
|
||||
|
||||
|
@ -6424,7 +6397,7 @@ void SessionImpl::handleI2PAlert(const lt::i2p_alert *alert) const
|
|||
|
||||
void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert)
|
||||
{
|
||||
TorrentImpl *torrent = m_torrents.value(alert->handle.info_hash());
|
||||
TorrentImpl *torrent = getTorrent(alert->handle);
|
||||
if (!torrent)
|
||||
return;
|
||||
|
||||
|
@ -6450,8 +6423,9 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert)
|
|||
#ifdef QBT_USES_LIBTORRENT2
|
||||
void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *alert)
|
||||
{
|
||||
const auto torrentIDv1 = TorrentID::fromSHA1Hash(alert->metadata->info_hashes().v1);
|
||||
const auto torrentIDv2 = TorrentID::fromSHA256Hash(alert->metadata->info_hashes().v2);
|
||||
const InfoHash infoHash = getInfoHash(*alert->metadata);
|
||||
const auto torrentIDv1 = TorrentID::fromSHA1Hash(infoHash.v1());
|
||||
const auto torrentIDv2 = TorrentID::fromSHA256Hash(infoHash.v2());
|
||||
TorrentImpl *torrent1 = m_torrents.value(torrentIDv1);
|
||||
TorrentImpl *torrent2 = m_torrents.value(torrentIDv2);
|
||||
if (torrent2)
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
#include <QSet>
|
||||
#include <QThreadPool>
|
||||
|
||||
|
@ -482,6 +484,8 @@ namespace BitTorrent
|
|||
|
||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context);
|
||||
|
||||
lt::torrent_handle reloadTorrent(const lt::torrent_handle ¤tHandle, lt::add_torrent_params params);
|
||||
|
||||
QFuture<FileSearchResult> findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths = {}) const;
|
||||
|
||||
void enablePortMapping();
|
||||
|
@ -606,6 +610,7 @@ namespace BitTorrent
|
|||
#endif
|
||||
|
||||
TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
||||
TorrentImpl *getTorrent(const lt::torrent_handle &nativeHandle) const;
|
||||
|
||||
void saveResumeData();
|
||||
void saveTorrentsQueue();
|
||||
|
@ -812,11 +817,13 @@ namespace BitTorrent
|
|||
FileSearcher *m_fileSearcher = nullptr;
|
||||
TorrentContentRemover *m_torrentContentRemover = nullptr;
|
||||
|
||||
using AddTorrentAlertHandler = std::function<void (const lt::add_torrent_alert *alert)>;
|
||||
QQueue<AddTorrentAlertHandler> m_addTorrentAlertHandlers;
|
||||
|
||||
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
||||
|
||||
QHash<TorrentID, TorrentImpl *> m_torrents;
|
||||
QHash<TorrentID, TorrentImpl *> m_hybridTorrentsByAltID;
|
||||
QHash<TorrentID, LoadTorrentParams> m_loadingTorrents;
|
||||
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
|
||||
QHash<TorrentID, TorrentID> m_changedTorrentIDs;
|
||||
QMap<QString, CategoryOptions> m_categories;
|
||||
|
|
|
@ -290,11 +290,9 @@ namespace
|
|||
|
||||
// TorrentImpl
|
||||
|
||||
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||
TorrentImpl::TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms)
|
||||
: Torrent(session)
|
||||
, m_session(session)
|
||||
, m_nativeSession(nativeSession)
|
||||
, m_nativeHandle(nativeHandle)
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
, m_infoHash(m_nativeHandle.info_hashes())
|
||||
|
@ -1837,16 +1835,6 @@ void TorrentImpl::reload()
|
|||
{
|
||||
try
|
||||
{
|
||||
m_completedFiles.fill(false);
|
||||
m_filesProgress.fill(0);
|
||||
m_pieces.fill(false);
|
||||
m_nativeStatus.pieces.clear_all();
|
||||
m_nativeStatus.num_pieces = 0;
|
||||
|
||||
const auto queuePos = m_nativeHandle.queue_position();
|
||||
|
||||
m_nativeSession->remove_torrent(m_nativeHandle, lt::session::delete_partfile);
|
||||
|
||||
lt::add_torrent_params p = m_ltAddTorrentParams;
|
||||
p.flags |= lt::torrent_flags::update_subscribe
|
||||
| lt::torrent_flags::override_trackers
|
||||
|
@ -1866,19 +1854,21 @@ void TorrentImpl::reload()
|
|||
p.flags &= ~(lt::torrent_flags::auto_managed | lt::torrent_flags::paused);
|
||||
}
|
||||
|
||||
auto *const extensionData = new ExtensionData;
|
||||
p.userdata = LTClientData(extensionData);
|
||||
#ifndef QBT_USES_LIBTORRENT2
|
||||
p.storage = customStorageConstructor;
|
||||
#endif
|
||||
m_nativeHandle = m_nativeSession->add_torrent(p);
|
||||
const auto queuePos = m_nativeHandle.queue_position();
|
||||
|
||||
m_nativeStatus = extensionData->status;
|
||||
m_nativeHandle = m_session->reloadTorrent(m_nativeHandle, std::move(p));
|
||||
m_nativeStatus = static_cast<ExtensionData *>(m_nativeHandle.userdata())->status;
|
||||
|
||||
if (queuePos >= lt::queue_position_t {})
|
||||
m_nativeHandle.queue_position_set(queuePos);
|
||||
m_nativeStatus.queue_position = queuePos;
|
||||
|
||||
m_completedFiles.fill(false);
|
||||
m_filesProgress.fill(0);
|
||||
m_pieces.fill(false);
|
||||
m_nativeStatus.pieces.clear_all();
|
||||
m_nativeStatus.num_pieces = 0;
|
||||
|
||||
updateState();
|
||||
}
|
||||
catch (const lt::system_error &err)
|
||||
|
|
|
@ -94,8 +94,7 @@ namespace BitTorrent
|
|||
Q_DISABLE_COPY_MOVE(TorrentImpl)
|
||||
|
||||
public:
|
||||
TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
||||
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
||||
TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms);
|
||||
~TorrentImpl() override;
|
||||
|
||||
bool isValid() const;
|
||||
|
@ -325,7 +324,6 @@ namespace BitTorrent
|
|||
QFuture<std::invoke_result_t<Func>> invokeAsync(Func &&func) const;
|
||||
|
||||
SessionImpl *const m_session = nullptr;
|
||||
lt::session *m_nativeSession = nullptr;
|
||||
lt::torrent_handle m_nativeHandle;
|
||||
mutable lt::torrent_status m_nativeStatus;
|
||||
TorrentState m_state = TorrentState::Unknown;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue