Improve torrent loading code

This commit is contained in:
Vladimir Golovnev (Glassez) 2020-08-05 08:31:41 +03:00
parent eb99bfe20f
commit dc3d23c045
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
11 changed files with 361 additions and 356 deletions

View file

@ -77,6 +77,7 @@
#include "base/profile.h"
#include "base/torrentfileguard.h"
#include "base/torrentfilter.h"
#include "base/tristatebool.h"
#include "base/unicodestrings.h"
#include "base/utils/bytearray.h"
#include "base/utils/fs.h"
@ -111,68 +112,6 @@ namespace
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
}
bool readFile(const QString &path, QByteArray &buf)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
qDebug("Cannot read file %s: %s", qUtf8Printable(path), qUtf8Printable(file.errorString()));
return false;
}
buf = file.readAll();
return true;
}
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, MagnetUri &magnetUri)
{
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(data, ec);
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
torrentParams = CreateTorrentParams();
torrentParams.restored = true;
torrentParams.skipChecking = false;
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
torrentParams.savePath = Profile::instance()->fromPortablePath(
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled");
torrentParams.sequential = root.dict_find_int_value("qBt-sequential");
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME);
const bool isAutoManaged = root.dict_find_int_value("auto_managed");
const bool isPaused = root.dict_find_int_value("paused");
torrentParams.paused = root.dict_find_int_value("qBt-paused", (isPaused && !isAutoManaged));
torrentParams.forced = root.dict_find_int_value("qBt-forced", (!isPaused && !isAutoManaged));
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
if (ratioLimitString.empty())
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0;
else
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
if (tagsNode.type() == lt::bdecode_node::list_t) {
for (int i = 0; i < tagsNode.list_size(); ++i) {
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
if (Session::isValidTag(tag))
torrentParams.tags << tag;
}
}
const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime");
if (addedTimeNode.type() == lt::bdecode_node::int_t)
torrentParams.addedTime = QDateTime::fromSecsSinceEpoch(addedTimeNode.int_value());
magnetUri = MagnetUri(fromLTString(root.dict_find_string_value("qBt-magnetUri")));
return true;
}
void torrentQueuePositionUp(const lt::torrent_handle &handle)
{
try {
@ -1658,12 +1597,11 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
switch (result.status) {
case Net::DownloadStatus::Success:
emit downloadFromUrlFinished(result.url);
addTorrent_impl(CreateTorrentParams(m_downloadedTorrents.take(result.url))
, MagnetUri(), TorrentInfo::load(result.data));
addTorrent(TorrentInfo::load(result.data), m_downloadedTorrents.take(result.url));
break;
case Net::DownloadStatus::RedirectedToMagnet:
emit downloadFromUrlFinished(result.url);
addTorrent_impl(CreateTorrentParams(m_downloadedTorrents.take(result.url)), MagnetUri(result.magnet));
addTorrent(MagnetUri {result.magnet}, m_downloadedTorrents.take(result.url));
break;
default:
emit downloadFromUrlFailed(result.url, result.errorString);
@ -1934,11 +1872,10 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
const MagnetUri magnetUri {source};
if (magnetUri.isValid())
return addTorrent_impl(CreateTorrentParams(params), magnetUri);
return addTorrent(magnetUri, params);
TorrentFileGuard guard(source);
if (addTorrent_impl(CreateTorrentParams(params)
, MagnetUri(), TorrentInfo::loadFromFile(source))) {
TorrentFileGuard guard {source};
if (addTorrent(TorrentInfo::loadFromFile(source), params)) {
guard.markAsAddedToSession();
return true;
}
@ -1946,184 +1883,203 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
return false;
}
bool Session::addTorrent(const MagnetUri &magnetUri, const AddTorrentParams &params)
{
if (!magnetUri.isValid()) return false;
const InfoHash hash = magnetUri.hash();
const auto it = m_loadedMetadata.constFind(hash);
if (it != m_loadedMetadata.constEnd()) {
// Adding preloaded torrent...
const TorrentInfo metadata = it->metadata;
if (metadata.isValid()) {
// Metadata is received and torrent_handle is being deleted
// so we can't reuse it. Just add torrent using its metadata.
return addTorrent(metadata, params);
}
// Reuse existing torrent_handle
lt::torrent_handle handle = m_nativeSession->find_torrent(hash);
// Preloaded torrent is in "Upload mode" so we need to disable it
// otherwise the torrent never be downloaded (until application restart)
handle.unset_flags(lt::torrent_flags::upload_mode);
LoadTorrentParams createTorrentParams = initLoadTorrentParams(params);
createTorrentParams.ltAddTorrentParams = it->ltAddTorrentParams;
if (createTorrentParams.paused) {
// Preloaded torrent isn't auto managed already
handle.pause();
}
else if (!createTorrentParams.forced) {
handle.set_flags(lt::torrent_flags::auto_managed);
handle.pause();
}
m_loadedMetadata.remove(hash);
m_loadingTorrents.insert(hash, createTorrentParams);
--m_extraLimit;
adjustLimits();
// use common last step of torrent loading
createTorrentHandle(handle);
return true;
}
return addTorrent_impl(params, magnetUri);
}
bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params)
{
if (!torrentInfo.isValid()) return false;
return addTorrent_impl(CreateTorrentParams(params), MagnetUri(), torrentInfo);
return addTorrent_impl(params, MagnetUri(), torrentInfo);
}
LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorrentParams)
{
LoadTorrentParams loadTorrentParams;
loadTorrentParams.name = addTorrentParams.name;
loadTorrentParams.tags = addTorrentParams.tags;
loadTorrentParams.disableTempPath = addTorrentParams.disableTempPath;
loadTorrentParams.sequential = addTorrentParams.sequential;
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
loadTorrentParams.hasRootFolder = ((addTorrentParams.createSubfolder == TriStateBool::Undefined)
? isKeepTorrentTopLevelFolder()
: (addTorrentParams.createSubfolder == TriStateBool::True));
loadTorrentParams.forced = (addTorrentParams.addForced == TriStateBool::True);
loadTorrentParams.paused = ((addTorrentParams.addPaused == TriStateBool::Undefined)
? isAddTorrentPaused()
: (addTorrentParams.addPaused == TriStateBool::True));
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
const bool useAutoTMM = ((addTorrentParams.useAutoTMM == TriStateBool::Undefined)
? !isAutoTMMDisabledByDefault()
: (addTorrentParams.useAutoTMM == TriStateBool::True));
if (useAutoTMM)
loadTorrentParams.savePath = "";
else if (addTorrentParams.savePath.trimmed().isEmpty())
loadTorrentParams.savePath = defaultSavePath();
else
loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath);
const QString category = addTorrentParams.category;
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
loadTorrentParams.category = "";
else
loadTorrentParams.category = addTorrentParams.category;
return loadTorrentParams;
}
// Add a torrent to the BitTorrent session
bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
TorrentInfo torrentInfo, const QByteArray &fastresumeData)
bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const MagnetUri &magnetUri, TorrentInfo metadata)
{
params.savePath = normalizeSavePath(params.savePath, "");
if (!params.category.isEmpty()) {
if (!m_categories.contains(params.category) && !addCategory(params.category))
params.category = "";
}
const bool fromMagnetUri = magnetUri.isValid();
lt::add_torrent_params p;
InfoHash hash;
if (fromMagnetUri) {
hash = magnetUri.hash();
const auto it = m_loadedMetadata.constFind(hash);
if (it != m_loadedMetadata.constEnd()) {
// Adding preloaded torrent...
const TorrentInfo metadata = it.value();
if (metadata.isValid()) {
// Metadata is received and torrent_handle is being deleted
// so we can't reuse it. Just add torrent using its metadata.
return addTorrent_impl(params
, MagnetUri {}, metadata, fastresumeData);
}
// Reuse existing torrent_handle
lt::torrent_handle handle = m_nativeSession->find_torrent(hash);
// Preloaded torrent is in "Upload mode" so we need to disable it
// otherwise the torrent never be downloaded (until application restart)
handle.unset_flags(lt::torrent_flags::upload_mode);
if (params.paused) {
// Preloaded torrent isn't auto managed already
handle.pause();
}
else if (!params.forced) {
handle.set_flags(lt::torrent_flags::auto_managed);
handle.pause();
}
m_loadedMetadata.remove(hash);
m_addingTorrents.insert(hash, params);
--m_extraLimit;
adjustLimits();
// use common 2nd step of torrent addition
createTorrentHandle(handle);
return true;
}
p = magnetUri.addTorrentParams();
if (isTempPathEnabled()) {
p.save_path = Utils::Fs::toNativePath(tempPath()).toStdString();
}
else {
// If empty then Automatic mode, otherwise Manual mode
const QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath;
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
}
}
else {
if (!torrentInfo.isValid()) {
// We can have an invalid torrentInfo when there isn't a matching
// .torrent file to the .fastresume we loaded. Possibly from a
// failed upgrade.
return false;
}
hash = torrentInfo.hash();
}
const bool hasMetadata = metadata.isValid();
const InfoHash hash = (hasMetadata ? metadata.hash() : magnetUri.hash());
// We should not add the torrent if it is already
// processed or is pending to add to session
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash))
if (m_loadingTorrents.contains(hash) || m_loadedMetadata.contains(hash))
return false;
TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (torrent) { // a duplicate torrent is added
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
if (torrent->isPrivate() || (hasMetadata && metadata.isPrivate()))
return false;
// merge trackers and web seeds
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers());
torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds());
torrent->addTrackers(hasMetadata ? metadata.trackers() : magnetUri.trackers());
torrent->addUrlSeeds(hasMetadata ? metadata.urlSeeds() : magnetUri.urlSeeds());
return true;
}
if (!fromMagnetUri) {
if (params.restored) { // load from existing fastresume
lt::error_code ec;
p = lt::read_resume_data(fastresumeData, ec);
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
}
else { // new torrent
if (!params.hasRootFolder)
torrentInfo.stripRootFolder();
LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams);
lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
// If empty then Automatic mode, otherwise Manual mode
QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath;
// Metadata
if (!params.hasSeedStatus)
findIncompleteFiles(torrentInfo, savePath); // if needed points savePath to incomplete folder too
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
// If empty then Automatic mode, otherwise Manual mode
QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
if (hasMetadata) {
if (!loadTorrentParams.hasRootFolder)
metadata.stripRootFolder();
// if torrent name wasn't explicitly set we handle the case of
// initial renaming of torrent content and rename torrent accordingly
if (params.name.isEmpty()) {
QString contentName = torrentInfo.rootFolder();
if (contentName.isEmpty() && (torrentInfo.filesCount() == 1))
contentName = torrentInfo.fileName(0);
if (!loadTorrentParams.hasSeedStatus)
findIncompleteFiles(metadata, actualSavePath); // if needed points savePath to incomplete folder too
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
params.name = contentName;
}
// if torrent name wasn't explicitly set we handle the case of
// initial renaming of torrent content and rename torrent accordingly
if (loadTorrentParams.name.isEmpty()) {
QString contentName = metadata.rootFolder();
if (contentName.isEmpty() && (metadata.filesCount() == 1))
contentName = metadata.fileName(0);
Q_ASSERT(p.file_priorities.empty());
std::transform(params.filePriorities.cbegin(), params.filePriorities.cend()
, std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority));
});
if (!contentName.isEmpty() && (contentName != metadata.name()))
loadTorrentParams.name = contentName;
}
p.ti = torrentInfo.nativeInfo();
Q_ASSERT(p.file_priorities.empty());
std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend()
, std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority));
});
p.ti = metadata.nativeInfo();
}
else {
p = magnetUri.addTorrentParams();
if (isTempPathEnabled())
actualSavePath = tempPath();
}
if (fromMagnetUri && params.restored && params.addedTime.isValid())
p.added_time = params.addedTime.toSecsSinceEpoch();
p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
if (fromMagnetUri || !params.restored) {
p.upload_limit = params.uploadLimit;
p.download_limit = params.downloadLimit;
p.upload_limit = addTorrentParams.uploadLimit;
p.download_limit = addTorrentParams.downloadLimit;
// Preallocation mode
p.storage_mode = isPreallocationEnabled()
? lt::storage_mode_allocate : lt::storage_mode_sparse;
// Preallocation mode
p.storage_mode = isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse;
// Seeding mode
// Skip checking and directly start seeding
if (params.skipChecking)
p.flags |= lt::torrent_flags::seed_mode;
else
p.flags &= ~lt::torrent_flags::seed_mode;
}
// Seeding mode
// Skip checking and directly start seeding
if (addTorrentParams.skipChecking)
p.flags |= lt::torrent_flags::seed_mode;
else
p.flags &= ~lt::torrent_flags::seed_mode;
// Common
p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked
if (params.paused || !params.forced)
if (loadTorrentParams.paused || !loadTorrentParams.forced)
p.flags |= lt::torrent_flags::paused;
else
p.flags &= ~lt::torrent_flags::paused;
if (params.paused || params.forced)
if (loadTorrentParams.paused || loadTorrentParams.forced)
p.flags &= ~lt::torrent_flags::auto_managed;
else
p.flags |= lt::torrent_flags::auto_managed;
return loadTorrent(loadTorrentParams);
}
// Add a torrent to the BitTorrent session
bool Session::loadTorrent(LoadTorrentParams params)
{
lt::add_torrent_params &p = params.ltAddTorrentParams;
p.storage = customStorageConstructor;
// Limits
p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent();
p.storage = customStorageConstructor;
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);
m_addingTorrents.insert(hash, params);
// Adding torrent to BitTorrent session
m_nativeSession->async_add_torrent(p);
@ -2171,7 +2127,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
// We should not add torrent if it's already
// processed or adding to session
if (m_torrents.contains(hash)) return false;
if (m_addingTorrents.contains(hash)) return false;
if (m_loadingTorrents.contains(hash)) return false;
if (m_loadedMetadata.contains(hash)) return false;
qDebug("Adding torrent to preload metadata...");
@ -2209,7 +2165,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
if (ec) return false;
// waiting for metadata...
m_loadedMetadata.insert(h.info_hash(), TorrentInfo());
m_loadedMetadata.insert(h.info_hash(), {p, TorrentInfo {}});
++m_extraLimit;
adjustLimits();
@ -2246,10 +2202,7 @@ void Session::generateResumeData(const bool final)
if (!torrent->isValid()) continue;
if (!final && !torrent->needSaveResumeData()) continue;
if (torrent->isPaused()
|| torrent->hasError()
|| torrent->hasMissingFiles())
continue;
if (torrent->isPaused()) continue;
torrent->saveResumeData();
}
@ -3519,7 +3472,7 @@ void Session::setMaxRatioAction(const MaxRatioAction act)
bool Session::isKnownTorrent(const InfoHash &hash) const
{
return (m_torrents.contains(hash)
|| m_addingTorrents.contains(hash)
|| m_loadingTorrents.contains(hash)
|| m_loadedMetadata.contains(hash));
}
@ -3711,12 +3664,6 @@ void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, con
#endif
}
void Session::handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent)
{
Q_UNUSED(torrent)
--m_numResumeData;
}
void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{
emit trackerSuccess(torrent, trackerUrl);
@ -3935,36 +3882,106 @@ const CacheStatus &Session::cacheStatus() const
return m_cacheStatus;
}
bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams)
{
torrentParams = {};
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(data, ec);
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
torrentParams.restored = true;
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
torrentParams.savePath = Profile::instance()->fromPortablePath(
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
torrentParams.disableTempPath = root.dict_find_int_value("qBt-tempPathDisabled");
torrentParams.sequential = root.dict_find_int_value("qBt-sequential");
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME);
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
if (ratioLimitString.empty())
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0;
else
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
if (tagsNode.type() == lt::bdecode_node::list_t) {
for (int i = 0; i < tagsNode.list_size(); ++i) {
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
if (Session::isValidTag(tag))
torrentParams.tags << tag;
}
}
// NOTE: Do we really need the following block in case of existing (restored) torrent?
torrentParams.savePath = normalizePath(torrentParams.savePath);
if (!torrentParams.category.isEmpty()) {
if (!m_categories.contains(torrentParams.category) && !addCategory(torrentParams.category))
torrentParams.category = "";
}
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
p = lt::read_resume_data(root, ec);
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
if (metadata.isValid())
p.ti = metadata.nativeInfo();
const bool hasMetadata = (p.ti && p.ti->is_valid());
if (!hasMetadata && !root.dict_find("info-hash")) {
// TODO: The following code is deprecated. Remove after several releases in 4.3.x.
// === BEGIN DEPRECATED CODE === //
// Try to load from legacy data used in older versions for torrents w/o metadata
const lt::bdecode_node magnetURINode = root.dict_find("qBt-magnetUri");
if (magnetURINode.type() == lt::bdecode_node::string_t) {
lt::parse_magnet_uri(magnetURINode.string_value(), p, ec);
if (isTempPathEnabled()) {
p.save_path = Utils::Fs::toNativePath(tempPath()).toStdString();
}
else {
// If empty then Automatic mode, otherwise Manual mode
const QString savePath = torrentParams.savePath.isEmpty() ? categorySavePath(torrentParams.category) : torrentParams.savePath;
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
}
// Preallocation mode
p.storage_mode = (isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse);
const lt::bdecode_node addedTimeNode = root.dict_find("qBt-addedTime");
if (addedTimeNode.type() == lt::bdecode_node::int_t)
p.added_time = addedTimeNode.int_value();
}
// === END DEPRECATED CODE === //
else {
return false;
}
}
return true;
}
// Will resume torrents in backup directory
void Session::startUpTorrents()
{
qDebug("Resuming torrents...");
const QDir resumeDataDir(m_resumeFolderPath);
const QDir resumeDataDir {m_resumeFolderPath};
QStringList fastresumes = resumeDataDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
struct TorrentResumeData
const auto readFile = [](const QString &path, QByteArray &buf) -> bool
{
QString hash;
MagnetUri magnetUri;
CreateTorrentParams addTorrentData;
QByteArray data;
};
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
return false;
}
int resumedTorrentsCount = 0;
const auto startupTorrent = [this, &resumeDataDir, &resumedTorrentsCount](const TorrentResumeData &params)
{
const QString filePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(params.hash));
qDebug() << "Starting up torrent" << params.hash << "...";
if (!addTorrent_impl(params.addTorrentData, params.magnetUri, TorrentInfo::loadFromFile(filePath), params.data))
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(params.hash), Log::CRITICAL);
// process add torrent messages before message queue overflow
if ((resumedTorrentsCount % 100) == 0) readAlerts();
++resumedTorrentsCount;
buf = file.readAll();
return true;
};
qDebug("Starting up torrents...");
@ -3989,6 +4006,7 @@ void Session::startUpTorrents()
fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values();
}
int resumedTorrentsCount = 0;
for (const QString &fastresumeName : asConst(fastresumes)) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
@ -3996,11 +4014,23 @@ void Session::startUpTorrents()
const QString hash = rxMatch.captured(1);
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
if (readFile(fastresumePath, data)
&& loadTorrentResumeData(data, torrentParams, magnetUri)) {
startupTorrent({hash, magnetUri, torrentParams, data});
LoadTorrentParams torrentParams;
const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash));
TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams)) {
qDebug() << "Starting up torrent" << hash << "...";
if (!loadTorrent(torrentParams))
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(hash), 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'.")
.arg(hash), Log::CRITICAL);
}
}
}
@ -4184,22 +4214,21 @@ void Session::dispatchTorrentAlert(const lt::alert *a)
void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
{
// Magnet added for preload its metadata
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash()));
const CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash());
const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());
TorrentHandleImpl *const torrent = new TorrentHandleImpl(this, nativeHandle, params);
TorrentHandleImpl *const torrent = new TorrentHandleImpl {this, nativeHandle, params};
m_torrents.insert(torrent->hash(), torrent);
const bool fromMagnetUri = !torrent->hasMetadata();
const bool hasMetadata = torrent->hasMetadata();
if (params.restored) {
LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name()));
}
else {
// The following is useless for newly added magnet
if (!fromMagnetUri) {
if (hasMetadata) {
// Backup torrent file
const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())};
@ -4231,10 +4260,10 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
m_seedingLimitTimer->start();
// Send torrent addition signal
emit torrentAdded(torrent);
emit torrentLoaded(torrent);
// Send new torrent signal
if (!params.restored)
emit torrentNew(torrent);
emit torrentAdded(torrent);
// Torrent could have error just after adding to libtorrent
if (torrent->hasError())
@ -4242,8 +4271,8 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
#if (LIBTORRENT_VERSION_NUM < 10208)
// Check if file(s) exist when using skip hash check
if (params.skipChecking && torrent->hasMetadata())
nativeHandle.read_piece(lt::piece_index_t(0));
if (nativeHandle.flags() & lt::torrent_flags::seed_mode)
nativeHandle.read_piece(lt::piece_index_t {0});
#endif
}
@ -4252,10 +4281,10 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
if (p->error) {
qDebug("/!\\ Error: Failed to add torrent!");
QString msg = QString::fromStdString(p->message());
LogMsg(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING);
emit addTorrentFailed(msg);
LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING);
emit loadTorrentFailed(msg);
}
else {
else if (m_loadingTorrents.contains(p->handle.info_hash())) {
createTorrentHandle(p->handle);
}
}
@ -4266,7 +4295,7 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
const auto loadedMetadataIter = m_loadedMetadata.find(infoHash);
if (loadedMetadataIter != m_loadedMetadata.end()) {
emit metadataLoaded(*loadedMetadataIter);
emit metadataLoaded(loadedMetadataIter->metadata);
m_loadedMetadata.erase(loadedMetadataIter);
}
@ -4323,7 +4352,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
if (loadedMetadataIter != m_loadedMetadata.end()) {
--m_extraLimit;
adjustLimits();
*loadedMetadataIter = TorrentInfo(p->handle.torrent_file());
loadedMetadataIter->metadata = TorrentInfo {p->handle.torrent_file()};
m_nativeSession->remove_torrent(p->handle, lt::session::delete_files);
}
}