Browse Source

Merge pull request #13234 from glassez/load-torrent

Improve torrent loading code
adaptive-webui-19844
Vladimir Golovnev 4 years ago committed by GitHub
parent
commit
e1d097a92d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/base/bittorrent/addtorrentparams.h
  2. 652
      src/base/bittorrent/session.cpp
  3. 26
      src/base/bittorrent/session.h
  4. 105
      src/base/bittorrent/torrenthandleimpl.cpp
  5. 24
      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 @@ @@ -32,7 +32,8 @@
#include <QString>
#include <QVector>
#include "../tristatebool.h"
#include "base/tristatebool.h"
#include "torrenthandle.h"
namespace BitTorrent
{
@ -50,11 +51,12 @@ namespace BitTorrent @@ -50,11 +51,12 @@ namespace BitTorrent
TriStateBool addForced;
TriStateBool addPaused;
QVector<DownloadPriority> filePriorities; // used if TorrentInfo is set
bool ignoreShareLimits = false;
bool skipChecking = false;
TriStateBool createSubfolder;
TriStateBool useAutoTMM;
int uploadLimit = -1;
int downloadLimit = -1;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO;
};
}

652
src/base/bittorrent/session.cpp

@ -68,12 +68,6 @@ @@ -68,12 +68,6 @@
#include <QTimer>
#include <QUuid>
#ifdef Q_OS_WIN
// TODO: Remove together with fixBrokenSavePath()
#define NEED_TO_FIX_BROKEN_PATH
#include <QSaveFile>
#endif
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
@ -83,6 +77,7 @@ @@ -83,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"
@ -117,129 +112,6 @@ namespace @@ -117,129 +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;
}
#ifdef NEED_TO_FIX_BROKEN_PATH
// TODO: Remove this after 4.2.5 && if at least one month has passed from v4.2.3
// Check the commit that introduced this function and identify all other pieces of code that
// need removal alongside this one.
void fixBrokenSavePath(QByteArray &data, lt::bdecode_node &root)
{
const QString path = fromLTString(root.dict_find_string_value("save_path"));
const int index = path.indexOf(QLatin1String("//"));
if (index < 1)
return;
const QString goodPath = path.mid(index).replace('/', '\\');
lt::entry entry {root};
entry["save_path"] = goodPath.toStdString();
const auto rawView = root.dict_find_string_value("info-hash");
const QByteArray rawHashView = QByteArray::fromRawData(rawView.data(), rawView.length());
const QString hexHash = QString::fromLatin1(rawHashView.toHex());
data.clear();
lt::bencode(std::back_inserter(data), entry);
const QString filename = QString("%1.fastresume").arg(hexHash);
const QDir resumeDataDir {Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER)};
const QString filepath = resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (file.open(QIODevice::WriteOnly)) {
file.write(data);
if (!file.commit()) {
Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::WARNING);
}
}
lt::error_code ec;
root = lt::bdecode(data, ec);
}
#endif
#ifdef NEED_TO_FIX_BROKEN_PATH
// TODO: Remove together with fixBrokenSavePath()
bool loadTorrentResumeData(QByteArray &data, CreateTorrentParams &torrentParams, int &queuePos, MagnetUri &magnetUri)
#else
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &queuePos, MagnetUri &magnetUri)
#endif
{
lt::error_code ec;
#if defined(NEED_TO_FIX_BROKEN_PATH)
// TODO: Remove together with fixBrokenSavePath()
lt::bdecode_node root = lt::bdecode(data, ec);
#else
const lt::bdecode_node root = lt::bdecode(data, ec);
#endif
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
#ifdef NEED_TO_FIX_BROKEN_PATH
fixBrokenSavePath(data, root);
#endif
torrentParams = CreateTorrentParams();
torrentParams.restored = true;
torrentParams.skipChecking = false;
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 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();
// **************************************************************************************
// Workaround to convert legacy label to category
// TODO: Should be removed in future
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-label"));
if (torrentParams.category.isEmpty())
// **************************************************************************************
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
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());
queuePos = root.dict_find_int_value("qBt-queuePosition");
magnetUri = MagnetUri(fromLTString(root.dict_find_string_value("qBt-magnetUri")));
return true;
}
void torrentQueuePositionUp(const lt::torrent_handle &handle)
{
try {
@ -1725,12 +1597,11 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result) @@ -1725,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);
@ -2001,11 +1872,10 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params) @@ -2001,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;
}
@ -2013,184 +1883,202 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params) @@ -2013,184 +1883,202 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
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;
if (!magnetUri.isValid()) return false;
return addTorrent_impl(CreateTorrentParams(params), MagnetUri(), torrentInfo);
}
const InfoHash hash = magnetUri.hash();
// 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, "");
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);
}
if (!params.category.isEmpty()) {
if (!m_categories.contains(params.category) && !addCategory(params.category))
params.category = "";
}
// Reuse existing torrent_handle
lt::torrent_handle handle = m_nativeSession->find_torrent(hash);
const bool fromMagnetUri = magnetUri.isValid();
lt::add_torrent_params p;
InfoHash 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 (fromMagnetUri) {
hash = magnetUri.hash();
LoadTorrentParams createTorrentParams = initLoadTorrentParams(params);
createTorrentParams.ltAddTorrentParams = it->ltAddTorrentParams;
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);
}
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();
}
// Reuse existing torrent_handle
lt::torrent_handle handle = m_nativeSession->find_torrent(hash);
m_loadedMetadata.remove(hash);
m_loadingTorrents.insert(hash, createTorrentParams);
// 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);
--m_extraLimit;
adjustLimits();
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();
}
// use common last step of torrent loading
createTorrentHandle(handle);
return true;
}
return addTorrent_impl(params, magnetUri);
}
m_loadedMetadata.remove(hash);
m_addingTorrents.insert(hash, params);
bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params)
{
if (!torrentInfo.isValid()) return false;
--m_extraLimit;
adjustLimits();
return addTorrent_impl(params, MagnetUri(), torrentInfo);
}
LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorrentParams)
{
LoadTorrentParams loadTorrentParams;
loadTorrentParams.name = addTorrentParams.name;
loadTorrentParams.tags = addTorrentParams.tags;
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);
// use common 2nd step of torrent addition
createTorrentHandle(handle);
return true;
}
const QString category = addTorrentParams.category;
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
loadTorrentParams.category = "";
else
loadTorrentParams.category = addTorrentParams.category;
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;
}
return loadTorrentParams;
}
hash = torrentInfo.hash();
}
// 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
// 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();
// 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 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 (!contentName.isEmpty() && (contentName != torrentInfo.name()))
params.name = contentName;
}
LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams);
lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
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 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 (!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
// 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);
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);
@ -2238,7 +2126,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri) @@ -2238,7 +2126,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...");
@ -2276,7 +2164,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri) @@ -2276,7 +2164,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();
@ -2313,10 +2201,7 @@ void Session::generateResumeData(const bool final) @@ -2313,10 +2201,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();
}
@ -3586,7 +3471,7 @@ void Session::setMaxRatioAction(const MaxRatioAction act) @@ -3586,7 +3471,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));
}
@ -3778,12 +3663,6 @@ void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, con @@ -3778,12 +3663,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);
@ -4002,36 +3881,105 @@ const CacheStatus &Session::cacheStatus() const @@ -4002,36 +3881,105 @@ 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.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;
};
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();
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
return false;
}
++resumedTorrentsCount;
buf = file.readAll();
return true;
};
qDebug("Starting up torrents...");
@ -4041,59 +3989,6 @@ void Session::startUpTorrents() @@ -4041,59 +3989,6 @@ void Session::startUpTorrents()
if (isQueueingSystemEnabled()) {
QFile queueFile {resumeDataDir.absoluteFilePath(QLatin1String {"queue"})};
// TODO: The following code is deprecated in 4.1.5. Remove after several releases in 4.2.x.
// === BEGIN DEPRECATED CODE === //
if (!queueFile.exists()) {
// Resume downloads in a legacy manner
QMap<int, TorrentResumeData> queuedResumeData;
int nextQueuePosition = 1;
int numOfRemappedFiles = 0;
for (const QString &fastresumeName : asConst(fastresumes)) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
QString hash = rxMatch.captured(1);
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
if (queuePosition <= nextQueuePosition) {
startupTorrent({ hash, magnetUri, torrentParams, data });
if (queuePosition == nextQueuePosition) {
++nextQueuePosition;
while (queuedResumeData.contains(nextQueuePosition)) {
startupTorrent(queuedResumeData.take(nextQueuePosition));
++nextQueuePosition;
}
}
}
else {
int q = queuePosition;
for (; queuedResumeData.contains(q); ++q) {}
if (q != queuePosition)
++numOfRemappedFiles;
queuedResumeData[q] = {hash, magnetUri, torrentParams, data};
}
}
}
if (numOfRemappedFiles > 0) {
LogMsg(tr("Queue positions were corrected in %1 resume files").arg(numOfRemappedFiles)
, Log::CRITICAL);
}
// starting up downloading torrents (queue position > 0)
for (const TorrentResumeData &torrentResumeData : asConst(queuedResumeData))
startupTorrent(torrentResumeData);
return;
}
// === END DEPRECATED CODE === //
QStringList queue;
if (queueFile.open(QFile::ReadOnly)) {
QByteArray line;
@ -4109,6 +4004,7 @@ void Session::startUpTorrents() @@ -4109,6 +4004,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;
@ -4116,12 +4012,23 @@ void Session::startUpTorrents() @@ -4116,12 +4012,23 @@ void Session::startUpTorrents()
const QString hash = rxMatch.captured(1);
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data)
&& loadTorrentResumeData(data, torrentParams, queuePosition, 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);
}
}
}
@ -4305,22 +4212,21 @@ void Session::dispatchTorrentAlert(const lt::alert *a) @@ -4305,22 +4212,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())};
@ -4352,10 +4258,10 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) @@ -4352,10 +4258,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())
@ -4363,8 +4269,8 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) @@ -4363,8 +4269,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
}
@ -4373,10 +4279,10 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p) @@ -4373,10 +4279,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);
}
}
@ -4387,7 +4293,7 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p) @@ -4387,7 +4293,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);
}
@ -4444,7 +4350,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p) @@ -4444,7 +4350,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);
}
}

26
src/base/bittorrent/session.h

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

105
src/base/bittorrent/torrenthandleimpl.cpp

@ -57,7 +57,6 @@ @@ -57,7 +57,6 @@
#include "base/logger.h"
#include "base/preferences.h"
#include "base/profile.h"
#include "base/tristatebool.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
#include "common.h"
@ -111,44 +110,10 @@ namespace @@ -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(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params)
const LoadTorrentParams &params)
: QObject(session)
, m_session(session)
, m_nativeHandle(nativeHandle)
@ -159,9 +124,9 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle @@ -159,9 +124,9 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, const lt::torrent_handle
, m_ratioLimit(params.ratioLimit)
, m_seedingTimeLimit(params.seedingTimeLimit)
, m_hasSeedStatus(params.hasSeedStatus)
, m_tempPathDisabled(params.disableTempPath)
, m_hasRootFolder(params.hasRootFolder)
, m_useAutoTMM(params.savePath.isEmpty())
, m_ltAddTorrentParams(params.ltAddTorrentParams)
{
if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
@ -1505,18 +1470,36 @@ void TorrentHandleImpl::handleTorrentResumedAlert(const lt::torrent_resumed_aler @@ -1505,18 +1470,36 @@ void TorrentHandleImpl::handleTorrentResumedAlert(const lt::torrent_resumed_aler
void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
{
const bool useDummyResumeData = !p;
auto resumeDataPtr = std::make_shared<lt::entry>(useDummyResumeData
? lt::entry {}
: lt::write_resume_data(p->params));
lt::entry &resumeData = *resumeDataPtr;
if (p && !m_hasMissingFiles) {
// Update recent resume data
m_ltAddTorrentParams = p->params;
}
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) {
resumeData["qBt-magnetUri"] = createMagnetURI().toStdString();
resumeData["paused"] = isPaused();
resumeData["auto_managed"] = isAutoManaged();
// Both firstLastPiecePriority and sequential need to be stored in the
// resume data if there is no metadata, otherwise they won't be
// restored if qBittorrent quits before the metadata are retrieved:
@ -1525,10 +1508,8 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale @@ -1525,10 +1508,8 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
resumeData["qBt-addedTime"] = addedTime().toSecsSinceEpoch();
}
else {
const auto savePath = resumeData.find_key("save_path")->string();
resumeData["save_path"] = Profile::instance()->toPortablePath(QString::fromStdString(savePath)).toStdString();
}
// === END DEPRECATED CODE === //
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance()->toPortablePath(m_savePath).toStdString();
resumeData["qBt-ratioLimit"] = static_cast<int>(m_ratioLimit * 1000);
resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit;
@ -1536,33 +1517,19 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale @@ -1536,33 +1517,19 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale
resumeData["qBt-tags"] = setToEntryList(m_tags);
resumeData["qBt-name"] = m_name.toStdString();
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
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;
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);
}
void TorrentHandleImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p)
{
// if torrent has no metadata we should save dummy fastresume data
// containing Magnet URI and qBittorrent own resume data only
if (p->error.value() == lt::errors::no_metadata) {
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);
}
Q_UNUSED(p);
// 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);
}
void TorrentHandleImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)
@ -1846,7 +1813,7 @@ bool TorrentHandleImpl::isMoveInProgress() const @@ -1846,7 +1813,7 @@ bool TorrentHandleImpl::isMoveInProgress() const
bool TorrentHandleImpl::useTempPath() const
{
return !m_tempPathDisabled && m_session->isTempPathEnabled() && !(isSeed() || m_hasSeedStatus);
return m_session->isTempPathEnabled() && !(isSeed() || m_hasSeedStatus);
}
void TorrentHandleImpl::updateStatus()

24
src/base/bittorrent/torrenthandleimpl.h

@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
#include <functional>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
@ -53,35 +54,25 @@ namespace BitTorrent @@ -53,35 +54,25 @@ namespace BitTorrent
class Session;
struct AddTorrentParams;
struct CreateTorrentParams
struct LoadTorrentParams
{
CreateTorrentParams() = default;
explicit CreateTorrentParams(const AddTorrentParams &params);
lt::add_torrent_params ltAddTorrentParams {};
// for both new and restored torrents
QString name;
QString category;
QSet<QString> tags;
QString savePath;
int uploadLimit = -1;
int downloadLimit = -1;
bool disableTempPath = false;
bool sequential = false;
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool skipChecking = false;
bool hasRootFolder = true;
bool forced = 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;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
bool restored = false; // is existing torrent job?
};
enum class MoveStorageMode
@ -97,7 +88,7 @@ namespace BitTorrent @@ -97,7 +88,7 @@ namespace BitTorrent
public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params);
const LoadTorrentParams &params);
~TorrentHandleImpl() override;
bool isValid() const;
@ -321,7 +312,6 @@ namespace BitTorrent @@ -321,7 +312,6 @@ namespace BitTorrent
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_hasSeedStatus;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected = false;
bool m_hasMissingFiles = false;
bool m_hasRootFolder;
@ -329,5 +319,7 @@ namespace BitTorrent @@ -329,5 +319,7 @@ namespace BitTorrent
bool m_useAutoTMM;
bool m_unchecked = false;
lt::add_torrent_params m_ltAddTorrentParams;
};
}

2
src/gui/categoryfiltermodel.cpp

@ -182,7 +182,7 @@ CategoryFilterModel::CategoryFilterModel(QObject *parent) @@ -182,7 +182,7 @@ CategoryFilterModel::CategoryFilterModel(QObject *parent)
connect(session, &Session::categoryRemoved, this, &CategoryFilterModel::categoryRemoved);
connect(session, &Session::torrentCategoryChanged, this, &CategoryFilterModel::torrentCategoryChanged);
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);
populate();

4
src/gui/mainwindow.cpp

@ -195,8 +195,8 @@ MainWindow::MainWindow(QWidget *parent) @@ -195,8 +195,8 @@ MainWindow::MainWindow(QWidget *parent)
// Creating Bittorrent session
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::torrentNew,this, &MainWindow::torrentNew);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::loadTorrentFailed, this, &MainWindow::addTorrentFailed);
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::downloadFromUrlFailed, this, &MainWindow::handleDownloadFromUrlFailure);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::speedLimitModeChanged, this, &MainWindow::updateAltSpeedsBtn);

2
src/gui/tagfiltermodel.cpp

@ -98,7 +98,7 @@ TagFilterModel::TagFilterModel(QObject *parent) @@ -98,7 +98,7 @@ TagFilterModel::TagFilterModel(QObject *parent)
connect(session, &Session::tagRemoved, this, &TagFilterModel::tagRemoved);
connect(session, &Session::torrentTagAdded, this, &TagFilterModel::torrentTagAdded);
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);
populate();
}

5
src/gui/torrentcreatordialog.cpp

@ -209,7 +209,10 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr @@ -209,7 +209,10 @@ void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QStr
BitTorrent::AddTorrentParams params;
params.savePath = branchPath;
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
BitTorrent::Session::instance()->addTorrent(info, params);

4
src/gui/transferlistfilterswidget.cpp

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

2
src/gui/transferlistmodel.cpp

@ -137,7 +137,7 @@ TransferListModel::TransferListModel(QObject *parent) @@ -137,7 +137,7 @@ TransferListModel::TransferListModel(QObject *parent)
addTorrent(torrent);
// 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::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);

Loading…
Cancel
Save