Browse Source

Improve torrent loading code

adaptive-webui-19844
Vladimir Golovnev (Glassez) 4 years ago
parent
commit
dc3d23c045
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  1. 6
      src/base/bittorrent/addtorrentparams.h
  2. 459
      src/base/bittorrent/session.cpp
  3. 26
      src/base/bittorrent/session.h
  4. 99
      src/base/bittorrent/torrenthandleimpl.cpp
  5. 22
      src/base/bittorrent/torrenthandleimpl.h
  6. 2
      src/gui/categoryfiltermodel.cpp
  7. 4
      src/gui/mainwindow.cpp
  8. 2
      src/gui/tagfiltermodel.cpp
  9. 5
      src/gui/torrentcreatordialog.cpp
  10. 4
      src/gui/transferlistfilterswidget.cpp
  11. 2
      src/gui/transferlistmodel.cpp

6
src/base/bittorrent/addtorrentparams.h

@ -32,7 +32,8 @@
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "../tristatebool.h" #include "base/tristatebool.h"
#include "torrenthandle.h"
namespace BitTorrent namespace BitTorrent
{ {
@ -50,11 +51,12 @@ namespace BitTorrent
TriStateBool addForced; TriStateBool addForced;
TriStateBool addPaused; TriStateBool addPaused;
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
bool ignoreShareLimits = false;
bool skipChecking = false; bool skipChecking = false;
TriStateBool createSubfolder; TriStateBool createSubfolder;
TriStateBool useAutoTMM; TriStateBool useAutoTMM;
int uploadLimit = -1; int uploadLimit = -1;
int downloadLimit = -1; int downloadLimit = -1;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO;
}; };
} }

459
src/base/bittorrent/session.cpp

@ -77,6 +77,7 @@
#include "base/profile.h" #include "base/profile.h"
#include "base/torrentfileguard.h" #include "base/torrentfileguard.h"
#include "base/torrentfilter.h" #include "base/torrentfilter.h"
#include "base/tristatebool.h"
#include "base/unicodestrings.h" #include "base/unicodestrings.h"
#include "base/utils/bytearray.h" #include "base/utils/bytearray.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
@ -111,68 +112,6 @@ namespace
return QString::fromUtf8(str.data(), static_cast<int>(str.size())); 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) void torrentQueuePositionUp(const lt::torrent_handle &handle)
{ {
try { try {
@ -1658,12 +1597,11 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
switch (result.status) { switch (result.status) {
case Net::DownloadStatus::Success: case Net::DownloadStatus::Success:
emit downloadFromUrlFinished(result.url); emit downloadFromUrlFinished(result.url);
addTorrent_impl(CreateTorrentParams(m_downloadedTorrents.take(result.url)) addTorrent(TorrentInfo::load(result.data), m_downloadedTorrents.take(result.url));
, MagnetUri(), TorrentInfo::load(result.data));
break; break;
case Net::DownloadStatus::RedirectedToMagnet: case Net::DownloadStatus::RedirectedToMagnet:
emit downloadFromUrlFinished(result.url); 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; break;
default: default:
emit downloadFromUrlFailed(result.url, result.errorString); emit downloadFromUrlFailed(result.url, result.errorString);
@ -1934,11 +1872,10 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
const MagnetUri magnetUri {source}; const MagnetUri magnetUri {source};
if (magnetUri.isValid()) if (magnetUri.isValid())
return addTorrent_impl(CreateTorrentParams(params), magnetUri); return addTorrent(magnetUri, params);
TorrentFileGuard guard(source); TorrentFileGuard guard {source};
if (addTorrent_impl(CreateTorrentParams(params) if (addTorrent(TorrentInfo::loadFromFile(source), params)) {
, MagnetUri(), TorrentInfo::loadFromFile(source))) {
guard.markAsAddedToSession(); guard.markAsAddedToSession();
return true; return true;
} }
@ -1946,40 +1883,20 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
return false; return false;
} }
bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params) bool Session::addTorrent(const MagnetUri &magnetUri, const AddTorrentParams &params)
{
if (!torrentInfo.isValid()) return false;
return addTorrent_impl(CreateTorrentParams(params), MagnetUri(), torrentInfo);
}
// Add a torrent to the BitTorrent session
bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
TorrentInfo torrentInfo, const QByteArray &fastresumeData)
{ {
params.savePath = normalizeSavePath(params.savePath, ""); if (!magnetUri.isValid()) return false;
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) { const InfoHash hash = magnetUri.hash();
hash = magnetUri.hash();
const auto it = m_loadedMetadata.constFind(hash); const auto it = m_loadedMetadata.constFind(hash);
if (it != m_loadedMetadata.constEnd()) { if (it != m_loadedMetadata.constEnd()) {
// Adding preloaded torrent... // Adding preloaded torrent...
const TorrentInfo metadata = it.value(); const TorrentInfo metadata = it->metadata;
if (metadata.isValid()) { if (metadata.isValid()) {
// Metadata is received and torrent_handle is being deleted // Metadata is received and torrent_handle is being deleted
// so we can't reuse it. Just add torrent using its metadata. // so we can't reuse it. Just add torrent using its metadata.
return addTorrent_impl(params return addTorrent(metadata, params);
, MagnetUri {}, metadata, fastresumeData);
} }
// Reuse existing torrent_handle // Reuse existing torrent_handle
@ -1989,141 +1906,180 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
// otherwise the torrent never be downloaded (until application restart) // otherwise the torrent never be downloaded (until application restart)
handle.unset_flags(lt::torrent_flags::upload_mode); handle.unset_flags(lt::torrent_flags::upload_mode);
if (params.paused) { LoadTorrentParams createTorrentParams = initLoadTorrentParams(params);
createTorrentParams.ltAddTorrentParams = it->ltAddTorrentParams;
if (createTorrentParams.paused) {
// Preloaded torrent isn't auto managed already // Preloaded torrent isn't auto managed already
handle.pause(); handle.pause();
} }
else if (!params.forced) { else if (!createTorrentParams.forced) {
handle.set_flags(lt::torrent_flags::auto_managed); handle.set_flags(lt::torrent_flags::auto_managed);
handle.pause(); handle.pause();
} }
m_loadedMetadata.remove(hash); m_loadedMetadata.remove(hash);
m_addingTorrents.insert(hash, params); m_loadingTorrents.insert(hash, createTorrentParams);
--m_extraLimit; --m_extraLimit;
adjustLimits(); adjustLimits();
// use common 2nd step of torrent addition // use common last step of torrent loading
createTorrentHandle(handle); createTorrentHandle(handle);
return true; return true;
} }
p = magnetUri.addTorrentParams(); return addTorrent_impl(params, magnetUri);
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(); bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params)
} {
if (!torrentInfo.isValid()) return false;
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(const AddTorrentParams &addTorrentParams, const MagnetUri &magnetUri, TorrentInfo metadata)
{
const bool hasMetadata = metadata.isValid();
const InfoHash hash = (hasMetadata ? metadata.hash() : magnetUri.hash());
// We should not add the torrent if it is already // We should not add the torrent if it is already
// processed or is pending to add to session // 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; return false;
TorrentHandleImpl *const torrent = m_torrents.value(hash); TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (torrent) { // a duplicate torrent is added if (torrent) { // a duplicate torrent is added
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate())) if (torrent->isPrivate() || (hasMetadata && metadata.isPrivate()))
return false; return false;
// merge trackers and web seeds // merge trackers and web seeds
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers()); torrent->addTrackers(hasMetadata ? metadata.trackers() : magnetUri.trackers());
torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds()); torrent->addUrlSeeds(hasMetadata ? metadata.urlSeeds() : magnetUri.urlSeeds());
return true; return true;
} }
if (!fromMagnetUri) { LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams);
if (params.restored) { // load from existing fastresume lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
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();
// If empty then Automatic mode, otherwise Manual mode // If empty then Automatic mode, otherwise Manual mode
QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath; QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
// Metadata if (hasMetadata) {
if (!params.hasSeedStatus) if (!loadTorrentParams.hasRootFolder)
findIncompleteFiles(torrentInfo, savePath); // if needed points savePath to incomplete folder too metadata.stripRootFolder();
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
if (!loadTorrentParams.hasSeedStatus)
findIncompleteFiles(metadata, actualSavePath); // if needed points savePath to incomplete folder too
// if torrent name wasn't explicitly set we handle the case of // if torrent name wasn't explicitly set we handle the case of
// initial renaming of torrent content and rename torrent accordingly // initial renaming of torrent content and rename torrent accordingly
if (params.name.isEmpty()) { if (loadTorrentParams.name.isEmpty()) {
QString contentName = torrentInfo.rootFolder(); QString contentName = metadata.rootFolder();
if (contentName.isEmpty() && (torrentInfo.filesCount() == 1)) if (contentName.isEmpty() && (metadata.filesCount() == 1))
contentName = torrentInfo.fileName(0); contentName = metadata.fileName(0);
if (!contentName.isEmpty() && (contentName != torrentInfo.name())) if (!contentName.isEmpty() && (contentName != metadata.name()))
params.name = contentName; loadTorrentParams.name = contentName;
} }
Q_ASSERT(p.file_priorities.empty()); Q_ASSERT(p.file_priorities.empty());
std::transform(params.filePriorities.cbegin(), params.filePriorities.cend() std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend()
, std::back_inserter(p.file_priorities), [](const DownloadPriority priority) , std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
{ {
return static_cast<lt::download_priority_t>( return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority)); static_cast<lt::download_priority_t::underlying_type>(priority));
}); });
}
p.ti = torrentInfo.nativeInfo(); p.ti = metadata.nativeInfo();
}
else {
p = magnetUri.addTorrentParams();
if (isTempPathEnabled())
actualSavePath = tempPath();
} }
if (fromMagnetUri && params.restored && params.addedTime.isValid()) p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
p.added_time = params.addedTime.toSecsSinceEpoch();
if (fromMagnetUri || !params.restored) { p.upload_limit = addTorrentParams.uploadLimit;
p.upload_limit = params.uploadLimit; p.download_limit = addTorrentParams.downloadLimit;
p.download_limit = params.downloadLimit;
// Preallocation mode // Preallocation mode
p.storage_mode = isPreallocationEnabled() p.storage_mode = isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse;
? lt::storage_mode_allocate : lt::storage_mode_sparse;
// Seeding mode // Seeding mode
// Skip checking and directly start seeding // Skip checking and directly start seeding
if (params.skipChecking) if (addTorrentParams.skipChecking)
p.flags |= lt::torrent_flags::seed_mode; p.flags |= lt::torrent_flags::seed_mode;
else else
p.flags &= ~lt::torrent_flags::seed_mode; p.flags &= ~lt::torrent_flags::seed_mode;
}
// Common if (loadTorrentParams.paused || !loadTorrentParams.forced)
p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked
if (params.paused || !params.forced)
p.flags |= lt::torrent_flags::paused; p.flags |= lt::torrent_flags::paused;
else else
p.flags &= ~lt::torrent_flags::paused; p.flags &= ~lt::torrent_flags::paused;
if (params.paused || params.forced) if (loadTorrentParams.paused || loadTorrentParams.forced)
p.flags &= ~lt::torrent_flags::auto_managed; p.flags &= ~lt::torrent_flags::auto_managed;
else else
p.flags |= lt::torrent_flags::auto_managed; 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 // Limits
p.max_connections = maxConnectionsPerTorrent(); p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent(); 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 // Adding torrent to BitTorrent session
m_nativeSession->async_add_torrent(p); 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 // We should not add torrent if it's already
// processed or adding to session // processed or adding to session
if (m_torrents.contains(hash)) return false; 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; if (m_loadedMetadata.contains(hash)) return false;
qDebug("Adding torrent to preload metadata..."); qDebug("Adding torrent to preload metadata...");
@ -2209,7 +2165,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
if (ec) return false; if (ec) return false;
// waiting for metadata... // waiting for metadata...
m_loadedMetadata.insert(h.info_hash(), TorrentInfo()); m_loadedMetadata.insert(h.info_hash(), {p, TorrentInfo {}});
++m_extraLimit; ++m_extraLimit;
adjustLimits(); adjustLimits();
@ -2246,10 +2202,7 @@ void Session::generateResumeData(const bool final)
if (!torrent->isValid()) continue; if (!torrent->isValid()) continue;
if (!final && !torrent->needSaveResumeData()) continue; if (!final && !torrent->needSaveResumeData()) continue;
if (torrent->isPaused() if (torrent->isPaused()) continue;
|| torrent->hasError()
|| torrent->hasMissingFiles())
continue;
torrent->saveResumeData(); torrent->saveResumeData();
} }
@ -3519,7 +3472,7 @@ void Session::setMaxRatioAction(const MaxRatioAction act)
bool Session::isKnownTorrent(const InfoHash &hash) const bool Session::isKnownTorrent(const InfoHash &hash) const
{ {
return (m_torrents.contains(hash) return (m_torrents.contains(hash)
|| m_addingTorrents.contains(hash) || m_loadingTorrents.contains(hash)
|| m_loadedMetadata.contains(hash)); || m_loadedMetadata.contains(hash));
} }
@ -3711,12 +3664,6 @@ void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, con
#endif #endif
} }
void Session::handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent)
{
Q_UNUSED(torrent)
--m_numResumeData;
}
void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl) void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{ {
emit trackerSuccess(torrent, trackerUrl); emit trackerSuccess(torrent, trackerUrl);
@ -3935,36 +3882,106 @@ const CacheStatus &Session::cacheStatus() const
return m_cacheStatus; 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 // Will resume torrents in backup directory
void Session::startUpTorrents() void Session::startUpTorrents()
{ {
qDebug("Resuming torrents..."); const QDir resumeDataDir {m_resumeFolderPath};
const QDir resumeDataDir(m_resumeFolderPath);
QStringList fastresumes = resumeDataDir.entryList( QStringList fastresumes = resumeDataDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted); QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
struct TorrentResumeData const auto readFile = [](const QString &path, QByteArray &buf) -> bool
{ {
QString hash; QFile file(path);
MagnetUri magnetUri; if (!file.open(QIODevice::ReadOnly)) {
CreateTorrentParams addTorrentData; LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
QByteArray data; 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..."); qDebug("Starting up torrents...");
@ -3989,6 +4006,7 @@ void Session::startUpTorrents()
fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values(); fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values();
} }
int resumedTorrentsCount = 0;
for (const QString &fastresumeName : asConst(fastresumes)) { for (const QString &fastresumeName : asConst(fastresumes)) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName); const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue; if (!rxMatch.hasMatch()) continue;
@ -3996,11 +4014,23 @@ void Session::startUpTorrents()
const QString hash = rxMatch.captured(1); const QString hash = rxMatch.captured(1);
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName); const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data; QByteArray data;
CreateTorrentParams torrentParams; LoadTorrentParams torrentParams;
MagnetUri magnetUri; const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash));
if (readFile(fastresumePath, data) TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
&& loadTorrentResumeData(data, torrentParams, magnetUri)) { if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams)) {
startupTorrent({hash, magnetUri, torrentParams, data}); 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) void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
{ {
// Magnet added for preload its metadata Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash()));
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
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); m_torrents.insert(torrent->hash(), torrent);
const bool fromMagnetUri = !torrent->hasMetadata(); const bool hasMetadata = torrent->hasMetadata();
if (params.restored) { if (params.restored) {
LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name())); LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name()));
} }
else { else {
// The following is useless for newly added magnet // The following is useless for newly added magnet
if (!fromMagnetUri) { if (hasMetadata) {
// Backup torrent file // Backup torrent file
const QDir resumeDataDir {m_resumeFolderPath}; const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())};
@ -4231,10 +4260,10 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
m_seedingLimitTimer->start(); m_seedingLimitTimer->start();
// Send torrent addition signal // Send torrent addition signal
emit torrentAdded(torrent); emit torrentLoaded(torrent);
// Send new torrent signal // Send new torrent signal
if (!params.restored) if (!params.restored)
emit torrentNew(torrent); emit torrentAdded(torrent);
// Torrent could have error just after adding to libtorrent // Torrent could have error just after adding to libtorrent
if (torrent->hasError()) if (torrent->hasError())
@ -4242,8 +4271,8 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
#if (LIBTORRENT_VERSION_NUM < 10208) #if (LIBTORRENT_VERSION_NUM < 10208)
// Check if file(s) exist when using skip hash check // Check if file(s) exist when using skip hash check
if (params.skipChecking && torrent->hasMetadata()) if (nativeHandle.flags() & lt::torrent_flags::seed_mode)
nativeHandle.read_piece(lt::piece_index_t(0)); nativeHandle.read_piece(lt::piece_index_t {0});
#endif #endif
} }
@ -4252,10 +4281,10 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
if (p->error) { if (p->error) {
qDebug("/!\\ Error: Failed to add torrent!"); qDebug("/!\\ Error: Failed to add torrent!");
QString msg = QString::fromStdString(p->message()); QString msg = QString::fromStdString(p->message());
LogMsg(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING); LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING);
emit addTorrentFailed(msg); emit loadTorrentFailed(msg);
} }
else { else if (m_loadingTorrents.contains(p->handle.info_hash())) {
createTorrentHandle(p->handle); createTorrentHandle(p->handle);
} }
} }
@ -4266,7 +4295,7 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
const auto loadedMetadataIter = m_loadedMetadata.find(infoHash); const auto loadedMetadataIter = m_loadedMetadata.find(infoHash);
if (loadedMetadataIter != m_loadedMetadata.end()) { if (loadedMetadataIter != m_loadedMetadata.end()) {
emit metadataLoaded(*loadedMetadataIter); emit metadataLoaded(loadedMetadataIter->metadata);
m_loadedMetadata.erase(loadedMetadataIter); m_loadedMetadata.erase(loadedMetadataIter);
} }
@ -4323,7 +4352,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
if (loadedMetadataIter != m_loadedMetadata.end()) { if (loadedMetadataIter != m_loadedMetadata.end()) {
--m_extraLimit; --m_extraLimit;
adjustLimits(); adjustLimits();
*loadedMetadataIter = TorrentInfo(p->handle.torrent_file()); loadedMetadataIter->metadata = TorrentInfo {p->handle.torrent_file()};
m_nativeSession->remove_torrent(p->handle, lt::session::delete_files); m_nativeSession->remove_torrent(p->handle, lt::session::delete_files);
} }
} }

26
src/base/bittorrent/session.h

@ -33,6 +33,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp> #include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp> #include <libtorrent/torrent_handle.hpp>
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
@ -102,7 +103,7 @@ namespace BitTorrent
class TorrentHandleImpl; class TorrentHandleImpl;
class Tracker; class Tracker;
class TrackerEntry; class TrackerEntry;
struct CreateTorrentParams; struct LoadTorrentParams;
enum class MoveStorageMode; enum class MoveStorageMode;
@ -437,6 +438,7 @@ namespace BitTorrent
bool isKnownTorrent(const InfoHash &hash) const; bool isKnownTorrent(const InfoHash &hash) const;
bool addTorrent(const QString &source, const AddTorrentParams &params = AddTorrentParams()); 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 addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params = AddTorrentParams());
bool deleteTorrent(const InfoHash &hash, DeleteOption deleteOption = Torrent); bool deleteTorrent(const InfoHash &hash, DeleteOption deleteOption = Torrent);
bool loadMetadata(const MagnetUri &magnetUri); bool loadMetadata(const MagnetUri &magnetUri);
@ -468,7 +470,6 @@ namespace BitTorrent
void handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds); void handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds); void handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds);
void handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data); void handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data);
void handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent);
void handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl); void handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl); void handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl); void handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl);
@ -476,7 +477,6 @@ namespace BitTorrent
bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode); bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode);
signals: signals:
void addTorrentFailed(const QString &error);
void allTorrentsFinished(); void allTorrentsFinished();
void categoryAdded(const QString &categoryName); void categoryAdded(const QString &categoryName);
void categoryRemoved(const QString &categoryName); void categoryRemoved(const QString &categoryName);
@ -484,6 +484,7 @@ namespace BitTorrent
void downloadFromUrlFinished(const QString &url); void downloadFromUrlFinished(const QString &url);
void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg); void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg);
void IPFilterParsed(bool error, int ruleCount); void IPFilterParsed(bool error, int ruleCount);
void loadTorrentFailed(const QString &error);
void metadataLoaded(const BitTorrent::TorrentInfo &info); void metadataLoaded(const BitTorrent::TorrentInfo &info);
void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent); void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent);
void speedLimitModeChanged(bool alternative); void speedLimitModeChanged(bool alternative);
@ -496,8 +497,8 @@ namespace BitTorrent
void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory); void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory);
void torrentFinished(BitTorrent::TorrentHandle *const torrent); void torrentFinished(BitTorrent::TorrentHandle *const torrent);
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent); void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
void torrentLoaded(BitTorrent::TorrentHandle *const torrent);
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent); void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
void torrentNew(BitTorrent::TorrentHandle *const torrent);
void torrentPaused(BitTorrent::TorrentHandle *const torrent); void torrentPaused(BitTorrent::TorrentHandle *const torrent);
void torrentResumed(BitTorrent::TorrentHandle *const torrent); void torrentResumed(BitTorrent::TorrentHandle *const torrent);
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent); void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
@ -576,9 +577,10 @@ namespace BitTorrent
void applyOSMemoryPriority() const; void applyOSMemoryPriority() const;
#endif #endif
bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri, bool loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams);
TorrentInfo torrentInfo = TorrentInfo(), bool loadTorrent(LoadTorrentParams params);
const QByteArray &fastresumeData = {}); LoadTorrentParams initLoadTorrentParams(const AddTorrentParams &addTorrentParams);
bool addTorrent_impl(const AddTorrentParams &addTorrentParams, const MagnetUri &magnetUri, TorrentInfo torrentInfo = TorrentInfo());
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const; bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateSeedingLimitTimer(); void updateSeedingLimitTimer();
@ -744,9 +746,15 @@ namespace BitTorrent
QThread *m_ioThread = nullptr; QThread *m_ioThread = nullptr;
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr; ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
QHash<InfoHash, TorrentInfo> m_loadedMetadata; struct LoadedMetadataHandle
{
lt::add_torrent_params ltAddTorrentParams {};
TorrentInfo metadata;
};
QHash<InfoHash, LoadedMetadataHandle> m_loadedMetadata;
QHash<InfoHash, TorrentHandleImpl *> m_torrents; QHash<InfoHash, TorrentHandleImpl *> m_torrents;
QHash<InfoHash, CreateTorrentParams> m_addingTorrents; QHash<InfoHash, LoadTorrentParams> m_loadingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents; QHash<QString, AddTorrentParams> m_downloadedTorrents;
QHash<InfoHash, RemovingTorrentData> m_removingTorrents; QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
QStringMap m_categories; QStringMap m_categories;

99
src/base/bittorrent/torrenthandleimpl.cpp

@ -57,7 +57,6 @@
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/profile.h" #include "base/profile.h"
#include "base/tristatebool.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "common.h" #include "common.h"
@ -111,44 +110,10 @@ namespace
} }
} }
// CreateTorrentParams
CreateTorrentParams::CreateTorrentParams(const AddTorrentParams &params)
: name(params.name)
, category(params.category)
, tags(params.tags)
, savePath(params.savePath)
, uploadLimit(params.uploadLimit)
, downloadLimit(params.downloadLimit)
, disableTempPath(params.disableTempPath)
, sequential(params.sequential)
, firstLastPiecePriority(params.firstLastPiecePriority)
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
, skipChecking(params.skipChecking)
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
? Session::instance()->isKeepTorrentTopLevelFolder()
: params.createSubfolder == TriStateBool::True)
, forced(params.addForced == TriStateBool::True)
, paused(params.addPaused == TriStateBool::Undefined
? Session::instance()->isAddTorrentPaused()
: params.addPaused == TriStateBool::True)
, filePriorities(params.filePriorities)
, ratioLimit(params.ignoreShareLimits ? TorrentHandleImpl::NO_RATIO_LIMIT : TorrentHandleImpl::USE_GLOBAL_RATIO)
, seedingTimeLimit(params.ignoreShareLimits ? TorrentHandleImpl::NO_SEEDING_TIME_LIMIT : TorrentHandleImpl::USE_GLOBAL_SEEDING_TIME)
{
bool useAutoTMM = (params.useAutoTMM == TriStateBool::Undefined
? !Session::instance()->isAutoTMMDisabledByDefault()
: params.useAutoTMM == TriStateBool::True);
if (useAutoTMM)
savePath = "";
else if (savePath.trimmed().isEmpty())
savePath = Session::instance()->defaultSavePath();
}
// TorrentHandleImpl // TorrentHandleImpl
TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params) const LoadTorrentParams &params)
: QObject(session) : QObject(session)
, m_session(session) , m_session(session)
, m_nativeHandle(nativeHandle) , m_nativeHandle(nativeHandle)
@ -162,6 +127,7 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle
, m_tempPathDisabled(params.disableTempPath) , m_tempPathDisabled(params.disableTempPath)
, m_hasRootFolder(params.hasRootFolder) , m_hasRootFolder(params.hasRootFolder)
, m_useAutoTMM(params.savePath.isEmpty()) , m_useAutoTMM(params.savePath.isEmpty())
, m_ltAddTorrentParams(params.ltAddTorrentParams)
{ {
if (m_useAutoTMM) if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
@ -1505,18 +1471,36 @@ void TorrentHandleImpl::handleTorrentResumedAlert(const lt::torrent_resumed_aler
void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
{ {
const bool useDummyResumeData = !p; if (p && !m_hasMissingFiles) {
auto resumeDataPtr = std::make_shared<lt::entry>(useDummyResumeData // Update recent resume data
? lt::entry {} m_ltAddTorrentParams = p->params;
: lt::write_resume_data(p->params)); }
lt::entry &resumeData = *resumeDataPtr;
updateStatus(); updateStatus();
m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch();
m_ltAddTorrentParams.save_path = Profile::instance()->toPortablePath(
QString::fromStdString(m_ltAddTorrentParams.save_path)).toStdString();
if (!m_hasMissingFiles) {
m_ltAddTorrentParams.flags = m_nativeStatus.flags;
if (m_nativeStatus.flags & lt::torrent_flags::stop_when_ready) {
// We need to redefine these values when torrent starting/rechecking
// in "paused" state since native values can be logically wrong
// (torrent can be not paused and auto_managed when it is checking).
m_ltAddTorrentParams.flags |= lt::torrent_flags::paused;
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::auto_managed;
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::stop_when_ready;
}
}
auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(m_ltAddTorrentParams));
lt::entry &resumeData = *resumeDataPtr;
// TODO: The following code is deprecated. Remove after several releases in 4.3.x.
// === BEGIN DEPRECATED CODE === //
const bool useDummyResumeData = !p;
if (useDummyResumeData) { if (useDummyResumeData) {
resumeData["qBt-magnetUri"] = createMagnetURI().toStdString(); resumeData["qBt-magnetUri"] = createMagnetURI().toStdString();
resumeData["paused"] = isPaused();
resumeData["auto_managed"] = isAutoManaged();
// Both firstLastPiecePriority and sequential need to be stored in the // Both firstLastPiecePriority and sequential need to be stored in the
// resume data if there is no metadata, otherwise they won't be // resume data if there is no metadata, otherwise they won't be
// restored if qBittorrent quits before the metadata are retrieved: // restored if qBittorrent quits before the metadata are retrieved:
@ -1525,10 +1509,8 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
resumeData["qBt-addedTime"] = addedTime().toSecsSinceEpoch(); resumeData["qBt-addedTime"] = addedTime().toSecsSinceEpoch();
} }
else { // === END DEPRECATED CODE === //
const auto savePath = resumeData.find_key("save_path")->string();
resumeData["save_path"] = Profile::instance()->toPortablePath(QString::fromStdString(savePath)).toStdString();
}
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance()->toPortablePath(m_savePath).toStdString(); resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance()->toPortablePath(m_savePath).toStdString();
resumeData["qBt-ratioLimit"] = static_cast<int>(m_ratioLimit * 1000); resumeData["qBt-ratioLimit"] = static_cast<int>(m_ratioLimit * 1000);
resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit; resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit;
@ -1537,32 +1519,19 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
resumeData["qBt-name"] = m_name.toStdString(); resumeData["qBt-name"] = m_name.toStdString();
resumeData["qBt-seedStatus"] = m_hasSeedStatus; resumeData["qBt-seedStatus"] = m_hasSeedStatus;
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled; resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
resumeData["qBt-queuePosition"] = (static_cast<int>(nativeHandle().queue_position()) + 1); // qBt starts queue at 1
resumeData["qBt-hasRootFolder"] = m_hasRootFolder; resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
if (m_nativeStatus.flags & lt::torrent_flags::stop_when_ready) {
// We need to redefine these values when torrent starting/rechecking
// in "paused" state since native values can be logically wrong
// (torrent can be not paused and auto_managed when it is checking).
resumeData["paused"] = true;
resumeData["auto_managed"] = false;
}
m_session->handleTorrentResumeDataReady(this, resumeDataPtr); m_session->handleTorrentResumeDataReady(this, resumeDataPtr);
} }
void TorrentHandleImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p) void TorrentHandleImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p)
{ {
// if torrent has no metadata we should save dummy fastresume data Q_UNUSED(p);
// containing Magnet URI and qBittorrent own resume data only
if (p->error.value() == lt::errors::no_metadata) { // if torrent has no metadata libtorrent doesn't generate "fastresume" data
// so we should save dummy "fastresume" data containing the values used to
// load torrent and qBittorrent own resume data
handleSaveResumeDataAlert(nullptr); handleSaveResumeDataAlert(nullptr);
}
else {
LogMsg(tr("Save resume data failed. Torrent: \"%1\", error: \"%2\"")
.arg(name(), QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
m_session->handleTorrentResumeDataFailed(this);
}
} }
void TorrentHandleImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p) void TorrentHandleImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)

22
src/base/bittorrent/torrenthandleimpl.h

@ -31,6 +31,7 @@
#include <functional> #include <functional>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp> #include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp> #include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp> #include <libtorrent/torrent_status.hpp>
@ -53,35 +54,26 @@ namespace BitTorrent
class Session; class Session;
struct AddTorrentParams; struct AddTorrentParams;
struct CreateTorrentParams struct LoadTorrentParams
{ {
CreateTorrentParams() = default; lt::add_torrent_params ltAddTorrentParams {};
explicit CreateTorrentParams(const AddTorrentParams &params);
// for both new and restored torrents
QString name; QString name;
QString category; QString category;
QSet<QString> tags; QSet<QString> tags;
QString savePath; QString savePath;
int uploadLimit = -1;
int downloadLimit = -1;
bool disableTempPath = false; bool disableTempPath = false;
bool sequential = false; bool sequential = false;
bool firstLastPiecePriority = false; bool firstLastPiecePriority = false;
bool hasSeedStatus = false; bool hasSeedStatus = false;
bool skipChecking = false;
bool hasRootFolder = true; bool hasRootFolder = true;
bool forced = false; bool forced = false;
bool paused = false; bool paused = false;
bool restored = false; // is existing torrent job?
// for new torrents
QVector<DownloadPriority> filePriorities;
QDateTime addedTime;
// for restored torrents
qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO; qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME; int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
bool restored = false; // is existing torrent job?
}; };
enum class MoveStorageMode enum class MoveStorageMode
@ -97,7 +89,7 @@ namespace BitTorrent
public: public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle, TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params); const LoadTorrentParams &params);
~TorrentHandleImpl() override; ~TorrentHandleImpl() override;
bool isValid() const; bool isValid() const;
@ -329,5 +321,7 @@ namespace BitTorrent
bool m_useAutoTMM; bool m_useAutoTMM;
bool m_unchecked = false; bool m_unchecked = false;
lt::add_torrent_params m_ltAddTorrentParams;
}; };
} }

2
src/gui/categoryfiltermodel.cpp

@ -182,7 +182,7 @@ CategoryFilterModel::CategoryFilterModel(QObject *parent)
connect(session, &Session::categoryRemoved, this, &CategoryFilterModel::categoryRemoved); connect(session, &Session::categoryRemoved, this, &CategoryFilterModel::categoryRemoved);
connect(session, &Session::torrentCategoryChanged, this, &CategoryFilterModel::torrentCategoryChanged); connect(session, &Session::torrentCategoryChanged, this, &CategoryFilterModel::torrentCategoryChanged);
connect(session, &Session::subcategoriesSupportChanged, this, &CategoryFilterModel::subcategoriesSupportChanged); connect(session, &Session::subcategoriesSupportChanged, this, &CategoryFilterModel::subcategoriesSupportChanged);
connect(session, &Session::torrentAdded, this, &CategoryFilterModel::torrentAdded); connect(session, &Session::torrentLoaded, this, &CategoryFilterModel::torrentAdded);
connect(session, &Session::torrentAboutToBeRemoved, this, &CategoryFilterModel::torrentAboutToBeRemoved); connect(session, &Session::torrentAboutToBeRemoved, this, &CategoryFilterModel::torrentAboutToBeRemoved);
populate(); populate();

4
src/gui/mainwindow.cpp

@ -195,8 +195,8 @@ MainWindow::MainWindow(QWidget *parent)
// Creating Bittorrent session // Creating Bittorrent session
connect(BitTorrent::Session::instance(), &BitTorrent::Session::fullDiskError, this, &MainWindow::fullDiskError); connect(BitTorrent::Session::instance(), &BitTorrent::Session::fullDiskError, this, &MainWindow::fullDiskError);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::addTorrentFailed, this, &MainWindow::addTorrentFailed); connect(BitTorrent::Session::instance(), &BitTorrent::Session::loadTorrentFailed, this, &MainWindow::addTorrentFailed);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentNew,this, &MainWindow::torrentNew); connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded,this, &MainWindow::torrentNew);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &MainWindow::finishedTorrent); connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &MainWindow::finishedTorrent);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed, this, &MainWindow::handleDownloadFromUrlFailure); connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed, this, &MainWindow::handleDownloadFromUrlFailure);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn); connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn);

2
src/gui/tagfiltermodel.cpp

@ -98,7 +98,7 @@ TagFilterModel::TagFilterModel(QObject *parent)
connect(session, &Session::tagRemoved, this, &TagFilterModel::tagRemoved); connect(session, &Session::tagRemoved, this, &TagFilterModel::tagRemoved);
connect(session, &Session::torrentTagAdded, this, &TagFilterModel::torrentTagAdded); connect(session, &Session::torrentTagAdded, this, &TagFilterModel::torrentTagAdded);
connect(session, &Session::torrentTagRemoved, this, &TagFilterModel::torrentTagRemoved); connect(session, &Session::torrentTagRemoved, this, &TagFilterModel::torrentTagRemoved);
connect(session, &Session::torrentAdded, this, &TagFilterModel::torrentAdded); connect(session, &Session::torrentLoaded, this, &TagFilterModel::torrentAdded);
connect(session, &Session::torrentAboutToBeRemoved, this, &TagFilterModel::torrentAboutToBeRemoved); connect(session, &Session::torrentAboutToBeRemoved, this, &TagFilterModel::torrentAboutToBeRemoved);
populate(); populate();
} }

5
src/gui/torrentcreatordialog.cpp

@ -209,7 +209,10 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr
BitTorrent::AddTorrentParams params; BitTorrent::AddTorrentParams params;
params.savePath = branchPath; params.savePath = branchPath;
params.skipChecking = true; params.skipChecking = true;
params.ignoreShareLimits = m_ui->checkIgnoreShareLimits->isChecked(); if (m_ui->checkIgnoreShareLimits->isChecked()) {
params.ratioLimit = BitTorrent::TorrentHandle::NO_RATIO_LIMIT;
params.seedingTimeLimit = BitTorrent::TorrentHandle::NO_SEEDING_TIME_LIMIT;
}
params.useAutoTMM = TriStateBool::False; // otherwise if it is on by default, it will overwrite `savePath` to the default save path params.useAutoTMM = TriStateBool::False; // otherwise if it is on by default, it will overwrite `savePath` to the default save path
BitTorrent::Session::instance()->addTorrent(info, params); BitTorrent::Session::instance()->addTorrent(info, params);

4
src/gui/transferlistfilterswidget.cpp

@ -120,7 +120,7 @@ BaseFilterWidget::BaseFilterWidget(QWidget *parent, TransferListWidget *transfer
connect(this, &BaseFilterWidget::customContextMenuRequested, this, &BaseFilterWidget::showMenu); connect(this, &BaseFilterWidget::customContextMenuRequested, this, &BaseFilterWidget::showMenu);
connect(this, &BaseFilterWidget::currentRowChanged, this, &BaseFilterWidget::applyFilter); connect(this, &BaseFilterWidget::currentRowChanged, this, &BaseFilterWidget::applyFilter);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentLoaded
, this, &BaseFilterWidget::handleNewTorrent); , this, &BaseFilterWidget::handleNewTorrent);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAboutToBeRemoved connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAboutToBeRemoved
, this, &BaseFilterWidget::torrentAboutToBeDeleted); , this, &BaseFilterWidget::torrentAboutToBeDeleted);
@ -155,7 +155,7 @@ void BaseFilterWidget::toggleFilter(bool checked)
StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *transferList) StatusFilterWidget::StatusFilterWidget(QWidget *parent, TransferListWidget *transferList)
: BaseFilterWidget(parent, transferList) : BaseFilterWidget(parent, transferList)
{ {
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAdded connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentLoaded
, this, &StatusFilterWidget::updateTorrentNumbers); , this, &StatusFilterWidget::updateTorrentNumbers);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsUpdated connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsUpdated
, this, &StatusFilterWidget::updateTorrentNumbers); , this, &StatusFilterWidget::updateTorrentNumbers);

2
src/gui/transferlistmodel.cpp

@ -137,7 +137,7 @@ TransferListModel::TransferListModel(QObject *parent)
addTorrent(torrent); addTorrent(torrent);
// Listen for torrent changes // Listen for torrent changes
connect(Session::instance(), &Session::torrentAdded, this, &TransferListModel::addTorrent); connect(Session::instance(), &Session::torrentLoaded, this, &TransferListModel::addTorrent);
connect(Session::instance(), &Session::torrentAboutToBeRemoved, this, &TransferListModel::handleTorrentAboutToBeRemoved); connect(Session::instance(), &Session::torrentAboutToBeRemoved, this, &TransferListModel::handleTorrentAboutToBeRemoved);
connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated); connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);

Loading…
Cancel
Save