mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-23 13:04:23 +00:00
Improve torrent loading code
This commit is contained in:
parent
eb99bfe20f
commit
dc3d23c045
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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 ¶ms)
|
|||||||
|
|
||||||
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 ¶ms)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms)
|
bool Session::addTorrent(const MagnetUri &magnetUri, const AddTorrentParams ¶ms)
|
||||||
{
|
{
|
||||||
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, "");
|
|
||||||
|
|
||||||
if (!params.category.isEmpty()) {
|
|
||||||
if (!m_categories.contains(params.category) && !addCategory(params.category))
|
|
||||||
params.category = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool fromMagnetUri = magnetUri.isValid();
|
|
||||||
lt::add_torrent_params p;
|
|
||||||
InfoHash hash;
|
|
||||||
|
|
||||||
if (fromMagnetUri) {
|
|
||||||
hash = magnetUri.hash();
|
|
||||||
|
|
||||||
const auto it = m_loadedMetadata.constFind(hash);
|
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 ¶ms)
|
||||||
|
{
|
||||||
|
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 = metadata.nativeInfo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p = magnetUri.addTorrentParams();
|
||||||
|
if (isTempPathEnabled())
|
||||||
|
actualSavePath = tempPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
p.ti = torrentInfo.nativeInfo();
|
p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
|
||||||
}
|
|
||||||
|
|
||||||
if (fromMagnetUri && params.restored && params.addedTime.isValid())
|
p.upload_limit = addTorrentParams.uploadLimit;
|
||||||
p.added_time = params.addedTime.toSecsSinceEpoch();
|
p.download_limit = addTorrentParams.downloadLimit;
|
||||||
|
|
||||||
if (fromMagnetUri || !params.restored) {
|
|
||||||
p.upload_limit = params.uploadLimit;
|
|
||||||
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;
|
buf = file.readAll();
|
||||||
const auto startupTorrent = [this, &resumeDataDir, &resumedTorrentsCount](const TorrentResumeData ¶ms)
|
return true;
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 ¶ms = AddTorrentParams());
|
bool addTorrent(const QString &source, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||||
|
bool addTorrent(const MagnetUri &magnetUri, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||||
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams());
|
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = 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;
|
||||||
|
@ -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 ¶ms)
|
|
||||||
: 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 ¶ms)
|
const LoadTorrentParams ¶ms)
|
||||||
: 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,33 +1519,20 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -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 ¶ms);
|
|
||||||
|
|
||||||
// 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 ¶ms);
|
const LoadTorrentParams ¶ms);
|
||||||
~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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user