mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-10-22 10:46:04 +03:00
Merge pull request #15793 from glassez/save-path
Redesign "Incomplete folder" feature
This commit is contained in:
commit
61504ae3b1
52 changed files with 1498 additions and 599 deletions
|
@ -8,6 +8,7 @@ add_library(qbt_base STATIC
|
||||||
bittorrent/bandwidthscheduler.h
|
bittorrent/bandwidthscheduler.h
|
||||||
bittorrent/bencoderesumedatastorage.h
|
bittorrent/bencoderesumedatastorage.h
|
||||||
bittorrent/cachestatus.h
|
bittorrent/cachestatus.h
|
||||||
|
bittorrent/categoryoptions.h
|
||||||
bittorrent/common.h
|
bittorrent/common.h
|
||||||
bittorrent/customstorage.h
|
bittorrent/customstorage.h
|
||||||
bittorrent/dbresumedatastorage.h
|
bittorrent/dbresumedatastorage.h
|
||||||
|
@ -100,6 +101,7 @@ add_library(qbt_base STATIC
|
||||||
bittorrent/abstractfilestorage.cpp
|
bittorrent/abstractfilestorage.cpp
|
||||||
bittorrent/bandwidthscheduler.cpp
|
bittorrent/bandwidthscheduler.cpp
|
||||||
bittorrent/bencoderesumedatastorage.cpp
|
bittorrent/bencoderesumedatastorage.cpp
|
||||||
|
bittorrent/categoryoptions.cpp
|
||||||
bittorrent/customstorage.cpp
|
bittorrent/customstorage.cpp
|
||||||
bittorrent/dbresumedatastorage.cpp
|
bittorrent/dbresumedatastorage.cpp
|
||||||
bittorrent/downloadpriority.cpp
|
bittorrent/downloadpriority.cpp
|
||||||
|
|
|
@ -7,6 +7,7 @@ HEADERS += \
|
||||||
$$PWD/bittorrent/bandwidthscheduler.h \
|
$$PWD/bittorrent/bandwidthscheduler.h \
|
||||||
$$PWD/bittorrent/bencoderesumedatastorage.h \
|
$$PWD/bittorrent/bencoderesumedatastorage.h \
|
||||||
$$PWD/bittorrent/cachestatus.h \
|
$$PWD/bittorrent/cachestatus.h \
|
||||||
|
$$PWD/bittorrent/categoryoptions.h \
|
||||||
$$PWD/bittorrent/common.h \
|
$$PWD/bittorrent/common.h \
|
||||||
$$PWD/bittorrent/customstorage.h \
|
$$PWD/bittorrent/customstorage.h \
|
||||||
$$PWD/bittorrent/downloadpriority.h \
|
$$PWD/bittorrent/downloadpriority.h \
|
||||||
|
@ -100,6 +101,7 @@ SOURCES += \
|
||||||
$$PWD/bittorrent/abstractfilestorage.cpp \
|
$$PWD/bittorrent/abstractfilestorage.cpp \
|
||||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||||
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
|
||||||
|
$$PWD/bittorrent/categoryoptions.cpp \
|
||||||
$$PWD/bittorrent/customstorage.cpp \
|
$$PWD/bittorrent/customstorage.cpp \
|
||||||
$$PWD/bittorrent/dbresumedatastorage.cpp \
|
$$PWD/bittorrent/dbresumedatastorage.cpp \
|
||||||
$$PWD/bittorrent/downloadpriority.cpp \
|
$$PWD/bittorrent/downloadpriority.cpp \
|
||||||
|
|
|
@ -48,7 +48,8 @@ namespace BitTorrent
|
||||||
QString category;
|
QString category;
|
||||||
TagSet tags;
|
TagSet tags;
|
||||||
QString savePath;
|
QString savePath;
|
||||||
bool disableTempPath = false; // e.g. for imported torrents
|
std::optional<bool> useDownloadPath;
|
||||||
|
QString downloadPath;
|
||||||
bool sequential = false;
|
bool sequential = false;
|
||||||
bool firstLastPiecePriority = false;
|
bool firstLastPiecePriority = false;
|
||||||
bool addForced = false;
|
bool addForced = false;
|
||||||
|
|
|
@ -173,12 +173,19 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
|
||||||
torrentParams.restored = true;
|
torrentParams.restored = true;
|
||||||
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
|
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
|
||||||
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
|
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.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
|
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
|
||||||
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
|
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||||
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
|
||||||
|
|
||||||
|
torrentParams.savePath = Profile::instance()->fromPortablePath(
|
||||||
|
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
|
||||||
|
torrentParams.useAutoTMM = torrentParams.savePath.isEmpty();
|
||||||
|
if (!torrentParams.useAutoTMM)
|
||||||
|
{
|
||||||
|
torrentParams.downloadPath = Profile::instance()->fromPortablePath(
|
||||||
|
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
|
||||||
// === BEGIN DEPRECATED CODE === //
|
// === BEGIN DEPRECATED CODE === //
|
||||||
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
|
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
|
||||||
|
@ -352,7 +359,6 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
|
||||||
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
|
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
|
||||||
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
|
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
|
||||||
data["qBt-category"] = resumeData.category.toStdString();
|
data["qBt-category"] = resumeData.category.toStdString();
|
||||||
|
@ -362,6 +368,12 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
||||||
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
|
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
|
||||||
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
||||||
|
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
|
||||||
|
data["qBt-downloadPath"] = Profile::instance()->toPortablePath(resumeData.downloadPath).toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
|
||||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|
78
src/base/bittorrent/categoryoptions.cpp
Normal file
78
src/base/bittorrent/categoryoptions.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "categoryoptions.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
const QString OPTION_SAVEPATH {QStringLiteral("save_path")};
|
||||||
|
const QString OPTION_DOWNLOADPATH {QStringLiteral("download_path")};
|
||||||
|
|
||||||
|
BitTorrent::CategoryOptions BitTorrent::CategoryOptions::fromJSON(const QJsonObject &jsonObj)
|
||||||
|
{
|
||||||
|
CategoryOptions options;
|
||||||
|
options.savePath = jsonObj.value(OPTION_SAVEPATH).toString();
|
||||||
|
|
||||||
|
const QJsonValue downloadPathValue = jsonObj.value(OPTION_DOWNLOADPATH);
|
||||||
|
if (downloadPathValue.isBool())
|
||||||
|
options.downloadPath = {downloadPathValue.toBool()};
|
||||||
|
else if (downloadPathValue.isString())
|
||||||
|
options.downloadPath = {true, downloadPathValue.toString()};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject BitTorrent::CategoryOptions::toJSON() const
|
||||||
|
{
|
||||||
|
QJsonValue downloadPathValue = QJsonValue::Undefined;
|
||||||
|
if (downloadPath)
|
||||||
|
{
|
||||||
|
if (downloadPath->enabled)
|
||||||
|
downloadPathValue = downloadPath->path;
|
||||||
|
else
|
||||||
|
downloadPathValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
{OPTION_SAVEPATH, savePath},
|
||||||
|
{OPTION_DOWNLOADPATH, downloadPathValue}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitTorrent::operator==(const BitTorrent::CategoryOptions::DownloadPathOption &left, const BitTorrent::CategoryOptions::DownloadPathOption &right)
|
||||||
|
{
|
||||||
|
return ((left.enabled == right.enabled)
|
||||||
|
&& (left.path == right.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitTorrent::operator==(const BitTorrent::CategoryOptions &left, const BitTorrent::CategoryOptions &right)
|
||||||
|
{
|
||||||
|
return ((left.savePath == right.savePath)
|
||||||
|
&& (left.downloadPath == right.downloadPath));
|
||||||
|
}
|
56
src/base/bittorrent/categoryoptions.h
Normal file
56
src/base/bittorrent/categoryoptions.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class QJsonObject;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct CategoryOptions
|
||||||
|
{
|
||||||
|
struct DownloadPathOption
|
||||||
|
{
|
||||||
|
bool enabled;
|
||||||
|
QString path;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString savePath;
|
||||||
|
std::optional<DownloadPathOption> downloadPath;
|
||||||
|
|
||||||
|
static CategoryOptions fromJSON(const QJsonObject &jsonObj);
|
||||||
|
QJsonObject toJSON() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const CategoryOptions::DownloadPathOption &left, const CategoryOptions::DownloadPathOption &right);
|
||||||
|
bool operator==(const CategoryOptions &left, const CategoryOptions &right);
|
||||||
|
}
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "dbresumedatastorage.h"
|
#include "dbresumedatastorage.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <libtorrent/bdecode.hpp>
|
#include <libtorrent/bdecode.hpp>
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
|
@ -57,11 +59,13 @@ namespace
|
||||||
{
|
{
|
||||||
const char DB_CONNECTION_NAME[] = "ResumeDataStorage";
|
const char DB_CONNECTION_NAME[] = "ResumeDataStorage";
|
||||||
|
|
||||||
const int DB_VERSION = 1;
|
const int DB_VERSION = 2;
|
||||||
|
|
||||||
const char DB_TABLE_META[] = "meta";
|
const char DB_TABLE_META[] = "meta";
|
||||||
const char DB_TABLE_TORRENTS[] = "torrents";
|
const char DB_TABLE_TORRENTS[] = "torrents";
|
||||||
|
|
||||||
|
const char META_VERSION[] = "version";
|
||||||
|
|
||||||
struct Column
|
struct Column
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
|
@ -80,6 +84,7 @@ namespace
|
||||||
const Column DB_COLUMN_CATEGORY = makeColumn("category");
|
const Column DB_COLUMN_CATEGORY = makeColumn("category");
|
||||||
const Column DB_COLUMN_TAGS = makeColumn("tags");
|
const Column DB_COLUMN_TAGS = makeColumn("tags");
|
||||||
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
|
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
|
||||||
|
const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn("download_path");
|
||||||
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
|
||||||
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
|
||||||
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
|
||||||
|
@ -109,42 +114,50 @@ namespace
|
||||||
return QString::fromLatin1("CREATE TABLE %1 (%2)").arg(quoted(tableName), items.join(QLatin1Char(',')));
|
return QString::fromLatin1("CREATE TABLE %1 (%2)").arg(quoted(tableName), items.join(QLatin1Char(',')));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
std::pair<QString, QString> joinColumns(const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
QStringList names;
|
int namesSize = columns.size();
|
||||||
names.reserve(columns.size());
|
int valuesSize = columns.size();
|
||||||
QStringList values;
|
|
||||||
values.reserve(columns.size());
|
|
||||||
for (const Column &column : columns)
|
for (const Column &column : columns)
|
||||||
{
|
{
|
||||||
names.append(quoted(column.name));
|
namesSize += column.name.size() + 2;
|
||||||
values.append(column.placeholder);
|
valuesSize += column.placeholder.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString jointNames = names.join(QLatin1Char(','));
|
QString names;
|
||||||
const QString jointValues = values.join(QLatin1Char(','));
|
names.reserve(namesSize);
|
||||||
|
QString values;
|
||||||
|
values.reserve(valuesSize);
|
||||||
|
for (const Column &column : columns)
|
||||||
|
{
|
||||||
|
names.append(quoted(column.name) + QLatin1Char(','));
|
||||||
|
values.append(column.placeholder + QLatin1Char(','));
|
||||||
|
}
|
||||||
|
names.chop(1);
|
||||||
|
values.chop(1);
|
||||||
|
|
||||||
|
return std::make_pair(names, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
|
{
|
||||||
|
const auto [names, values] = joinColumns(columns);
|
||||||
return QString::fromLatin1("INSERT INTO %1 (%2) VALUES (%3)")
|
return QString::fromLatin1("INSERT INTO %1 (%2) VALUES (%3)")
|
||||||
.arg(quoted(tableName), jointNames, jointValues);
|
.arg(quoted(tableName), names, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString makeUpdateStatement(const QString &tableName, const QVector<Column> &columns)
|
||||||
|
{
|
||||||
|
const auto [names, values] = joinColumns(columns);
|
||||||
|
return QString::fromLatin1("UPDATE %1 SET (%2) = (%3)")
|
||||||
|
.arg(quoted(tableName), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
|
||||||
{
|
{
|
||||||
QStringList names;
|
const auto [names, values] = joinColumns(columns);
|
||||||
names.reserve(columns.size());
|
|
||||||
QStringList values;
|
|
||||||
values.reserve(columns.size());
|
|
||||||
for (const Column &column : columns)
|
|
||||||
{
|
|
||||||
names.append(quoted(column.name));
|
|
||||||
values.append(column.placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString jointNames = names.join(QLatin1Char(','));
|
|
||||||
const QString jointValues = values.join(QLatin1Char(','));
|
|
||||||
|
|
||||||
return QString::fromLatin1(" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)")
|
return QString::fromLatin1(" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)")
|
||||||
.arg(quoted(constraint.name), jointNames, jointValues);
|
.arg(quoted(constraint.name), names, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeColumnDefinition(const Column &column, const char *definition)
|
QString makeColumnDefinition(const Column &column, const char *definition)
|
||||||
|
@ -187,7 +200,15 @@ BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObj
|
||||||
throw RuntimeError(db.lastError().text());
|
throw RuntimeError(db.lastError().text());
|
||||||
|
|
||||||
if (needCreateDB)
|
if (needCreateDB)
|
||||||
|
{
|
||||||
createDB();
|
createDB();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int dbVersion = currentDBVersion();
|
||||||
|
if (dbVersion == 1)
|
||||||
|
updateDBFromVersion1();
|
||||||
|
}
|
||||||
|
|
||||||
m_asyncWorker = new Worker(dbPath, QLatin1String("ResumeDataStorageWorker"));
|
m_asyncWorker = new Worker(dbPath, QLatin1String("ResumeDataStorageWorker"));
|
||||||
m_asyncWorker->moveToThread(m_ioThread);
|
m_asyncWorker->moveToThread(m_ioThread);
|
||||||
|
@ -276,8 +297,6 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
||||||
const QStringList tagList = tagsData.split(QLatin1Char(','));
|
const QStringList tagList = tagsData.split(QLatin1Char(','));
|
||||||
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
|
||||||
}
|
}
|
||||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
|
||||||
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
|
||||||
resumeData.hasSeedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
resumeData.hasSeedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
|
||||||
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
|
||||||
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
|
||||||
|
@ -288,6 +307,15 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
||||||
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
|
||||||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||||
|
|
||||||
|
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||||
|
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||||
|
resumeData.useAutoTMM = resumeData.savePath.isEmpty();
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||||
|
Utils::Fs::toUniformPath(query.value(DB_COLUMN_DOWNLOAD_PATH.name).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
|
||||||
const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
|
const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
|
||||||
const QByteArray allData = ((bencodedMetadata.isEmpty() || bencodedResumeData.isEmpty())
|
const QByteArray allData = ((bencodedMetadata.isEmpty() || bencodedResumeData.isEmpty())
|
||||||
|
@ -297,6 +325,9 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
|
||||||
lt::error_code ec;
|
lt::error_code ec;
|
||||||
const lt::bdecode_node root = lt::bdecode(allData, ec);
|
const lt::bdecode_node root = lt::bdecode(allData, ec);
|
||||||
|
|
||||||
|
resumeData.downloadPath = Profile::instance()->fromPortablePath(
|
||||||
|
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-downloadPath"))));
|
||||||
|
|
||||||
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
|
||||||
|
|
||||||
p = lt::read_resume_data(root, ec);
|
p = lt::read_resume_data(root, ec);
|
||||||
|
@ -329,6 +360,33 @@ void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int BitTorrent::DBResumeDataStorage::currentDBVersion() const
|
||||||
|
{
|
||||||
|
const auto selectDBVersionStatement = QString::fromLatin1("SELECT %1 FROM %2 WHERE %3 = %4;")
|
||||||
|
.arg(quoted(DB_COLUMN_VALUE.name), quoted(DB_TABLE_META), quoted(DB_COLUMN_NAME.name), DB_COLUMN_NAME.placeholder);
|
||||||
|
|
||||||
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
|
QSqlQuery query {db};
|
||||||
|
|
||||||
|
if (!query.prepare(selectDBVersionStatement))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
|
||||||
|
|
||||||
|
if (!query.exec())
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
if (!query.next())
|
||||||
|
throw RuntimeError(tr("Database is corrupted."));
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
const int dbVersion = query.value(0).toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
throw RuntimeError(tr("Database is corrupted."));
|
||||||
|
|
||||||
|
return dbVersion;
|
||||||
|
}
|
||||||
|
|
||||||
void BitTorrent::DBResumeDataStorage::createDB() const
|
void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
{
|
{
|
||||||
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
|
@ -353,7 +411,7 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
if (!query.prepare(insertMetaVersionQuery))
|
if (!query.prepare(insertMetaVersionQuery))
|
||||||
throw RuntimeError(query.lastError().text());
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1("version"));
|
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
|
||||||
query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
|
query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
|
||||||
|
|
||||||
if (!query.exec())
|
if (!query.exec())
|
||||||
|
@ -391,6 +449,42 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitTorrent::DBResumeDataStorage::updateDBFromVersion1() const
|
||||||
|
{
|
||||||
|
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
|
||||||
|
|
||||||
|
if (!db.transaction())
|
||||||
|
throw RuntimeError(db.lastError().text());
|
||||||
|
|
||||||
|
QSqlQuery query {db};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto alterTableTorrentsQuery = QString::fromLatin1("ALTER TABLE %1 ADD %2")
|
||||||
|
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
||||||
|
if (!query.exec(alterTableTorrentsQuery))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||||
|
if (!query.prepare(updateMetaVersionQuery))
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1(META_VERSION));
|
||||||
|
query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
|
||||||
|
|
||||||
|
if (!query.exec())
|
||||||
|
throw RuntimeError(query.lastError().text());
|
||||||
|
|
||||||
|
if (!db.commit())
|
||||||
|
throw RuntimeError(db.lastError().text());
|
||||||
|
}
|
||||||
|
catch (const RuntimeError &)
|
||||||
|
{
|
||||||
|
db.rollback();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
|
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
|
||||||
: m_path {dbPath}
|
: m_path {dbPath}
|
||||||
, m_connectionName {dbConnectionName}
|
, m_connectionName {dbConnectionName}
|
||||||
|
@ -499,7 +593,6 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||||
query.bindValue(DB_COLUMN_CATEGORY.placeholder, resumeData.category);
|
query.bindValue(DB_COLUMN_CATEGORY.placeholder, resumeData.category);
|
||||||
query.bindValue(DB_COLUMN_TAGS.placeholder, (resumeData.tags.isEmpty()
|
query.bindValue(DB_COLUMN_TAGS.placeholder, (resumeData.tags.isEmpty()
|
||||||
? QVariant(QVariant::String) : resumeData.tags.join(QLatin1String(","))));
|
? QVariant(QVariant::String) : resumeData.tags.join(QLatin1String(","))));
|
||||||
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
|
||||||
query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(resumeData.contentLayout));
|
query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(resumeData.contentLayout));
|
||||||
query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast<int>(resumeData.ratioLimit * 1000));
|
query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast<int>(resumeData.ratioLimit * 1000));
|
||||||
query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, resumeData.seedingTimeLimit);
|
query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, resumeData.seedingTimeLimit);
|
||||||
|
@ -507,6 +600,13 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
|
||||||
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, resumeData.hasSeedStatus);
|
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, resumeData.hasSeedStatus);
|
||||||
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(resumeData.operatingMode));
|
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(resumeData.operatingMode));
|
||||||
query.bindValue(DB_COLUMN_STOPPED.placeholder, resumeData.stopped);
|
query.bindValue(DB_COLUMN_STOPPED.placeholder, resumeData.stopped);
|
||||||
|
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
|
||||||
|
query.bindValue(DB_COLUMN_DOWNLOAD_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.downloadPath));
|
||||||
|
}
|
||||||
|
|
||||||
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
|
||||||
if (!bencodedMetadata.isEmpty())
|
if (!bencodedMetadata.isEmpty())
|
||||||
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
|
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
|
||||||
|
|
|
@ -50,7 +50,9 @@ namespace BitTorrent
|
||||||
void storeQueue(const QVector<TorrentID> &queue) const override;
|
void storeQueue(const QVector<TorrentID> &queue) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int currentDBVersion() const;
|
||||||
void createDB() const;
|
void createDB() const;
|
||||||
|
void updateDBFromVersion1() const;
|
||||||
|
|
||||||
QThread *m_ioThread = nullptr;
|
QThread *m_ioThread = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
|
|
||||||
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||||
, const QString &completeSavePath, const QString &incompleteSavePath)
|
, const QString &savePath, const QString &downloadPath)
|
||||||
{
|
{
|
||||||
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool
|
const auto findInDir = [](const QString &dirPath, QStringList &fileNames) -> bool
|
||||||
{
|
{
|
||||||
|
@ -56,14 +56,14 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const QStringList &or
|
||||||
return found;
|
return found;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString savePath = completeSavePath;
|
QString usedPath = savePath;
|
||||||
QStringList adjustedFileNames = originalFileNames;
|
QStringList adjustedFileNames = originalFileNames;
|
||||||
const bool found = findInDir(savePath, adjustedFileNames);
|
const bool found = findInDir(usedPath, adjustedFileNames);
|
||||||
if (!found && !incompleteSavePath.isEmpty())
|
if (!found && !downloadPath.isEmpty())
|
||||||
{
|
{
|
||||||
savePath = incompleteSavePath;
|
usedPath = downloadPath;
|
||||||
findInDir(savePath, adjustedFileNames);
|
findInDir(usedPath, adjustedFileNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit searchFinished(id, savePath, adjustedFileNames);
|
emit searchFinished(id, usedPath, adjustedFileNames);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
void search(const BitTorrent::TorrentID &id, const QStringList &originalFileNames
|
||||||
, const QString &completeSavePath, const QString &incompleteSavePath);
|
, const QString &savePath, const QString &downloadPath);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void searchFinished(const BitTorrent::TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
void searchFinished(const BitTorrent::TorrentID &id, const QString &savePath, const QStringList &fileNames);
|
||||||
|
|
|
@ -46,8 +46,10 @@ namespace BitTorrent
|
||||||
QString category;
|
QString category;
|
||||||
TagSet tags;
|
TagSet tags;
|
||||||
QString savePath;
|
QString savePath;
|
||||||
|
QString downloadPath;
|
||||||
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
|
||||||
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
|
||||||
|
bool useAutoTMM = false;
|
||||||
bool firstLastPiecePriority = false;
|
bool firstLastPiecePriority = false;
|
||||||
bool hasSeedStatus = false;
|
bool hasSeedStatus = false;
|
||||||
bool stopped = false;
|
bool stopped = false;
|
||||||
|
|
|
@ -58,6 +58,10 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
#include <QNetworkAddressEntry>
|
#include <QNetworkAddressEntry>
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
#include <QNetworkConfigurationManager>
|
#include <QNetworkConfigurationManager>
|
||||||
|
@ -80,6 +84,7 @@
|
||||||
#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"
|
||||||
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "base/utils/random.h"
|
#include "base/utils/random.h"
|
||||||
|
@ -103,6 +108,8 @@
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
const QString CATEGORIES_FILE_NAME {QStringLiteral("categories.json")};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const char PEER_ID[] = "qB";
|
const char PEER_ID[] = "qB";
|
||||||
|
@ -156,42 +163,9 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringMap map_cast(const QVariantMap &map)
|
QMap<QString, CategoryOptions> expandCategories(const QMap<QString, CategoryOptions> &categories)
|
||||||
{
|
{
|
||||||
QStringMap result;
|
QMap<QString, CategoryOptions> expanded = categories;
|
||||||
for (auto i = map.cbegin(); i != map.cend(); ++i)
|
|
||||||
result[i.key()] = i.value().toString();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap map_cast(const QStringMap &map)
|
|
||||||
{
|
|
||||||
QVariantMap result;
|
|
||||||
for (auto i = map.cbegin(); i != map.cend(); ++i)
|
|
||||||
result[i.key()] = i.value();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString normalizePath(const QString &path)
|
|
||||||
{
|
|
||||||
QString tmp = Utils::Fs::toUniformPath(path.trimmed());
|
|
||||||
if (!tmp.isEmpty() && !tmp.endsWith('/'))
|
|
||||||
return tmp + '/';
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString normalizeSavePath(QString path, const QString &defaultPath = specialFolderLocation(SpecialFolder::Downloads))
|
|
||||||
{
|
|
||||||
path = path.trimmed();
|
|
||||||
if (path.isEmpty())
|
|
||||||
path = Utils::Fs::toUniformPath(defaultPath.trimmed());
|
|
||||||
|
|
||||||
return normalizePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringMap expandCategories(const QStringMap &categories)
|
|
||||||
{
|
|
||||||
QStringMap expanded = categories;
|
|
||||||
|
|
||||||
for (auto i = categories.cbegin(); i != categories.cend(); ++i)
|
for (auto i = categories.cbegin(); i != categories.cend(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -199,7 +173,7 @@ namespace
|
||||||
for (const QString &subcat : asConst(Session::expandCategory(category)))
|
for (const QString &subcat : asConst(Session::expandCategory(category)))
|
||||||
{
|
{
|
||||||
if (!expanded.contains(subcat))
|
if (!expanded.contains(subcat))
|
||||||
expanded[subcat] = "";
|
expanded[subcat] = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,13 +392,12 @@ Session::Session(QObject *parent)
|
||||||
, clampValue(ChokingAlgorithm::FixedSlots, ChokingAlgorithm::RateBased))
|
, clampValue(ChokingAlgorithm::FixedSlots, ChokingAlgorithm::RateBased))
|
||||||
, m_seedChokingAlgorithm(BITTORRENT_SESSION_KEY("SeedChokingAlgorithm"), SeedChokingAlgorithm::FastestUpload
|
, m_seedChokingAlgorithm(BITTORRENT_SESSION_KEY("SeedChokingAlgorithm"), SeedChokingAlgorithm::FastestUpload
|
||||||
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
, clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
|
||||||
, m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
|
|
||||||
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
, m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
|
||||||
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
|
||||||
, m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), normalizePath)
|
, m_savePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), Utils::Fs::toUniformPath)
|
||||||
, m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath)
|
, m_downloadPath(BITTORRENT_SESSION_KEY("TempPath"), specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp"), Utils::Fs::toUniformPath)
|
||||||
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
|
||||||
, m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
, m_isDownloadPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
|
||||||
, m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
|
, m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
|
||||||
, m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
|
, m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
|
||||||
, m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
|
, m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
|
||||||
|
@ -472,12 +445,11 @@ Session::Session(QObject *parent)
|
||||||
if (isBandwidthSchedulerEnabled())
|
if (isBandwidthSchedulerEnabled())
|
||||||
enableBandwidthScheduler();
|
enableBandwidthScheduler();
|
||||||
|
|
||||||
m_categories = map_cast(m_storedCategories);
|
loadCategories();
|
||||||
if (isSubcategoriesEnabled())
|
if (isSubcategoriesEnabled())
|
||||||
{
|
{
|
||||||
// if subcategories support changed manually
|
// if subcategories support changed manually
|
||||||
m_categories = expandCategories(m_categories);
|
m_categories = expandCategories(m_categories);
|
||||||
m_storedCategories = map_cast(m_categories);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QStringList storedTags = m_storedTags.get();
|
const QStringList storedTags = m_storedTags.get();
|
||||||
|
@ -566,18 +538,18 @@ void Session::setPeXEnabled(const bool enabled)
|
||||||
LogMsg(tr("Restart is required to toggle PeX support"), Log::WARNING);
|
LogMsg(tr("Restart is required to toggle PeX support"), Log::WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::isTempPathEnabled() const
|
bool Session::isDownloadPathEnabled() const
|
||||||
{
|
{
|
||||||
return m_isTempPathEnabled;
|
return m_isDownloadPathEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setTempPathEnabled(const bool enabled)
|
void Session::setDownloadPathEnabled(const bool enabled)
|
||||||
{
|
{
|
||||||
if (enabled != isTempPathEnabled())
|
if (enabled != isDownloadPathEnabled())
|
||||||
{
|
{
|
||||||
m_isTempPathEnabled = enabled;
|
m_isDownloadPathEnabled = enabled;
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->handleTempPathChanged();
|
torrent->handleDownloadPathChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,22 +617,14 @@ void Session::setFinishedTorrentExportDirectory(QString path)
|
||||||
m_finishedTorrentExportDirectory = path;
|
m_finishedTorrentExportDirectory = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::defaultSavePath() const
|
QString Session::savePath() const
|
||||||
{
|
{
|
||||||
return Utils::Fs::toUniformPath(m_defaultSavePath);
|
return m_savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::tempPath() const
|
QString Session::downloadPath() const
|
||||||
{
|
{
|
||||||
return Utils::Fs::toUniformPath(m_tempPath);
|
return m_downloadPath;
|
||||||
}
|
|
||||||
|
|
||||||
QString Session::torrentTempPath(const TorrentInfo &torrentInfo) const
|
|
||||||
{
|
|
||||||
if ((torrentInfo.filesCount() > 1) && !torrentInfo.hasRootFolder())
|
|
||||||
return tempPath() + torrentInfo.name() + '/';
|
|
||||||
|
|
||||||
return tempPath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::isValidCategoryName(const QString &name)
|
bool Session::isValidCategoryName(const QString &name)
|
||||||
|
@ -692,29 +656,53 @@ QStringList Session::expandCategory(const QString &category)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringMap Session::categories() const
|
QStringList Session::categories() const
|
||||||
{
|
{
|
||||||
return m_categories;
|
return m_categories.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
CategoryOptions Session::categoryOptions(const QString &categoryName) const
|
||||||
|
{
|
||||||
|
return m_categories.value(categoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Session::categorySavePath(const QString &categoryName) const
|
QString Session::categorySavePath(const QString &categoryName) const
|
||||||
{
|
{
|
||||||
const QString basePath = m_defaultSavePath;
|
const QString basePath = savePath();
|
||||||
if (categoryName.isEmpty()) return basePath;
|
if (categoryName.isEmpty())
|
||||||
|
return basePath;
|
||||||
|
|
||||||
QString path = m_categories.value(categoryName);
|
QString path = m_categories.value(categoryName).savePath;
|
||||||
if (path.isEmpty()) // use implicit save path
|
if (path.isEmpty()) // use implicit save path
|
||||||
path = Utils::Fs::toValidFileSystemName(categoryName, true);
|
path = Utils::Fs::toValidFileSystemName(categoryName, true);
|
||||||
|
|
||||||
if (!QDir::isAbsolutePath(path))
|
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||||
path.prepend(basePath);
|
|
||||||
|
|
||||||
return normalizeSavePath(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::addCategory(const QString &name, const QString &savePath)
|
QString Session::categoryDownloadPath(const QString &categoryName) const
|
||||||
{
|
{
|
||||||
if (name.isEmpty()) return false;
|
const CategoryOptions categoryOptions = m_categories.value(categoryName);
|
||||||
|
const CategoryOptions::DownloadPathOption downloadPathOption =
|
||||||
|
categoryOptions.downloadPath.value_or(CategoryOptions::DownloadPathOption {isDownloadPathEnabled(), downloadPath()});
|
||||||
|
if (!downloadPathOption.enabled)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const QString basePath = downloadPath();
|
||||||
|
if (categoryName.isEmpty())
|
||||||
|
return basePath;
|
||||||
|
|
||||||
|
const QString path = (!downloadPathOption.path.isEmpty()
|
||||||
|
? downloadPathOption.path
|
||||||
|
: Utils::Fs::toValidFileSystemName(categoryName, true)); // use implicit download path
|
||||||
|
|
||||||
|
return (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, basePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Session::addCategory(const QString &name, const CategoryOptions &options)
|
||||||
|
{
|
||||||
|
if (name.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!isValidCategoryName(name) || m_categories.contains(name))
|
if (!isValidCategoryName(name) || m_categories.contains(name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -724,37 +712,46 @@ bool Session::addCategory(const QString &name, const QString &savePath)
|
||||||
{
|
{
|
||||||
if ((parent != name) && !m_categories.contains(parent))
|
if ((parent != name) && !m_categories.contains(parent))
|
||||||
{
|
{
|
||||||
m_categories[parent] = "";
|
m_categories[parent] = {};
|
||||||
emit categoryAdded(parent);
|
emit categoryAdded(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_categories[name] = savePath;
|
m_categories[name] = options;
|
||||||
m_storedCategories = map_cast(m_categories);
|
storeCategories();
|
||||||
emit categoryAdded(name);
|
emit categoryAdded(name);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::editCategory(const QString &name, const QString &savePath)
|
bool Session::editCategory(const QString &name, const CategoryOptions &options)
|
||||||
{
|
{
|
||||||
if (!m_categories.contains(name)) return false;
|
const auto it = m_categories.find(name);
|
||||||
if (categorySavePath(name) == savePath) return false;
|
if (it == m_categories.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
m_categories[name] = savePath;
|
CategoryOptions ¤tOptions = it.value();
|
||||||
m_storedCategories = map_cast(m_categories);
|
if (options == currentOptions)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
currentOptions = options;
|
||||||
|
storeCategories();
|
||||||
if (isDisableAutoTMMWhenCategorySavePathChanged())
|
if (isDisableAutoTMMWhenCategorySavePathChanged())
|
||||||
{
|
{
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
|
{
|
||||||
if (torrent->category() == name)
|
if (torrent->category() == name)
|
||||||
torrent->setAutoTMMEnabled(false);
|
torrent->setAutoTMMEnabled(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
|
{
|
||||||
if (torrent->category() == name)
|
if (torrent->category() == name)
|
||||||
torrent->handleCategorySavePathChanged();
|
torrent->handleCategoryOptionsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -763,8 +760,10 @@ bool Session::editCategory(const QString &name, const QString &savePath)
|
||||||
bool Session::removeCategory(const QString &name)
|
bool Session::removeCategory(const QString &name)
|
||||||
{
|
{
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
|
{
|
||||||
if (torrent->belongsToCategory(name))
|
if (torrent->belongsToCategory(name))
|
||||||
torrent->setCategory("");
|
torrent->setCategory("");
|
||||||
|
}
|
||||||
|
|
||||||
// remove stored category and its subcategories if exist
|
// remove stored category and its subcategories if exist
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
@ -772,7 +771,7 @@ bool Session::removeCategory(const QString &name)
|
||||||
{
|
{
|
||||||
// remove subcategories
|
// remove subcategories
|
||||||
const QString test = name + '/';
|
const QString test = name + '/';
|
||||||
Algorithm::removeIf(m_categories, [this, &test, &result](const QString &category, const QString &)
|
Algorithm::removeIf(m_categories, [this, &test, &result](const QString &category, const CategoryOptions &)
|
||||||
{
|
{
|
||||||
if (category.startsWith(test))
|
if (category.startsWith(test))
|
||||||
{
|
{
|
||||||
|
@ -789,7 +788,7 @@ bool Session::removeCategory(const QString &name)
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
// update stored categories
|
// update stored categories
|
||||||
m_storedCategories = map_cast(m_categories);
|
storeCategories();
|
||||||
emit categoryRemoved(name);
|
emit categoryRemoved(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,12 +809,12 @@ void Session::setSubcategoriesEnabled(const bool value)
|
||||||
// expand categories to include all parent categories
|
// expand categories to include all parent categories
|
||||||
m_categories = expandCategories(m_categories);
|
m_categories = expandCategories(m_categories);
|
||||||
// update stored categories
|
// update stored categories
|
||||||
m_storedCategories = map_cast(m_categories);
|
storeCategories();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// reload categories
|
// reload categories
|
||||||
m_categories = map_cast(m_storedCategories);
|
loadCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_isSubcategoriesEnabled = value;
|
m_isSubcategoriesEnabled = value;
|
||||||
|
@ -1823,14 +1822,7 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QString rootPath = torrent->rootPath(true);
|
m_removingTorrents[torrent->id()] = {torrent->name(), torrent->rootPath(), deleteOption};
|
||||||
if (!rootPath.isEmpty() && torrent->useTempPath())
|
|
||||||
{
|
|
||||||
// torrent without root folder still has it in its temporary save path
|
|
||||||
rootPath = torrent->actualStorageLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_removingTorrents[torrent->id()] = {torrent->name(), rootPath, deleteOption};
|
|
||||||
|
|
||||||
if (m_moveStorageQueue.size() > 1)
|
if (m_moveStorageQueue.size() > 1)
|
||||||
{
|
{
|
||||||
|
@ -2059,6 +2051,7 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
|
||||||
LoadTorrentParams loadTorrentParams;
|
LoadTorrentParams loadTorrentParams;
|
||||||
|
|
||||||
loadTorrentParams.name = addTorrentParams.name;
|
loadTorrentParams.name = addTorrentParams.name;
|
||||||
|
loadTorrentParams.useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
|
||||||
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
|
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
|
||||||
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
|
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
|
||||||
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
|
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
|
||||||
|
@ -2067,21 +2060,26 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
|
||||||
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
||||||
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
||||||
|
|
||||||
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
|
if (!loadTorrentParams.useAutoTMM)
|
||||||
if (useAutoTMM)
|
{
|
||||||
loadTorrentParams.savePath = "";
|
loadTorrentParams.savePath = (QDir::isAbsolutePath(addTorrentParams.savePath)
|
||||||
else if (addTorrentParams.savePath.isEmpty())
|
? addTorrentParams.savePath
|
||||||
loadTorrentParams.savePath = defaultSavePath();
|
: Utils::Fs::resolvePath(addTorrentParams.savePath, savePath()));
|
||||||
else if (QDir(addTorrentParams.savePath).isRelative())
|
|
||||||
loadTorrentParams.savePath = QDir(defaultSavePath()).absoluteFilePath(addTorrentParams.savePath);
|
const bool useDownloadPath = addTorrentParams.useDownloadPath.value_or(isDownloadPathEnabled());
|
||||||
else
|
if (useDownloadPath)
|
||||||
loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath);
|
{
|
||||||
|
loadTorrentParams.downloadPath = (QDir::isAbsolutePath(addTorrentParams.downloadPath)
|
||||||
|
? addTorrentParams.downloadPath
|
||||||
|
: Utils::Fs::resolvePath(addTorrentParams.downloadPath, downloadPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QString category = addTorrentParams.category;
|
const QString category = addTorrentParams.category;
|
||||||
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
|
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
|
||||||
loadTorrentParams.category = "";
|
loadTorrentParams.category = "";
|
||||||
else
|
else
|
||||||
loadTorrentParams.category = addTorrentParams.category;
|
loadTorrentParams.category = category;
|
||||||
|
|
||||||
for (const QString &tag : addTorrentParams.tags)
|
for (const QString &tag : addTorrentParams.tags)
|
||||||
{
|
{
|
||||||
|
@ -2143,8 +2141,9 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
|
|
||||||
bool isFindingIncompleteFiles = false;
|
bool isFindingIncompleteFiles = false;
|
||||||
|
|
||||||
// If empty then Automatic mode, otherwise Manual mode
|
const bool useAutoTMM = loadTorrentParams.useAutoTMM;
|
||||||
const QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
const QString actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
|
||||||
|
|
||||||
if (hasMetadata)
|
if (hasMetadata)
|
||||||
{
|
{
|
||||||
const TorrentInfo &torrentInfo = std::get<TorrentInfo>(source);
|
const TorrentInfo &torrentInfo = std::get<TorrentInfo>(source);
|
||||||
|
@ -2170,7 +2169,9 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
|
||||||
|
|
||||||
if (!loadTorrentParams.hasSeedStatus)
|
if (!loadTorrentParams.hasSeedStatus)
|
||||||
{
|
{
|
||||||
findIncompleteFiles(torrentInfo, actualSavePath, filePaths);
|
const QString actualDownloadPath = useAutoTMM
|
||||||
|
? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath;
|
||||||
|
findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths);
|
||||||
isFindingIncompleteFiles = true;
|
isFindingIncompleteFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2264,17 +2265,16 @@ bool Session::loadTorrent(LoadTorrentParams params)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath, const QStringList &filePaths) const
|
void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||||
|
, const QString &downloadPath, const QStringList &filePaths) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount()));
|
||||||
|
|
||||||
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
|
||||||
const QStringList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
const QStringList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths);
|
||||||
const QString completeSavePath = savePath;
|
|
||||||
const QString incompleteSavePath = (isTempPathEnabled() ? torrentTempPath(torrentInfo) : QString {});
|
|
||||||
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
QMetaObject::invokeMethod(m_fileSearcher, [=]()
|
||||||
{
|
{
|
||||||
m_fileSearcher->search(searchId, originalFileNames, completeSavePath, incompleteSavePath);
|
m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2432,30 +2432,37 @@ void Session::removeTorrentsQueue() const
|
||||||
m_resumeDataStorage->storeQueue({});
|
m_resumeDataStorage->storeQueue({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setDefaultSavePath(QString path)
|
void Session::setSavePath(const QString &path)
|
||||||
{
|
{
|
||||||
path = normalizeSavePath(path);
|
const QString baseSavePath = specialFolderLocation(SpecialFolder::Downloads);
|
||||||
if (path == m_defaultSavePath) return;
|
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseSavePath));
|
||||||
|
if (resolvedPath == m_savePath) return;
|
||||||
|
|
||||||
m_defaultSavePath = path;
|
m_savePath = resolvedPath;
|
||||||
|
|
||||||
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
if (isDisableAutoTMMWhenDefaultSavePathChanged())
|
||||||
|
{
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->setAutoTMMEnabled(false);
|
torrent->setAutoTMMEnabled(false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->handleCategorySavePathChanged();
|
torrent->handleCategoryOptionsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setTempPath(QString path)
|
void Session::setDownloadPath(const QString &path)
|
||||||
{
|
{
|
||||||
path = normalizeSavePath(path, defaultSavePath() + "temp/");
|
const QString baseDownloadPath = specialFolderLocation(SpecialFolder::Downloads) + QLatin1String("/temp");
|
||||||
if (path == m_tempPath) return;
|
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, baseDownloadPath));
|
||||||
|
if (resolvedPath != m_downloadPath)
|
||||||
m_tempPath = path;
|
{
|
||||||
|
m_downloadPath = resolvedPath;
|
||||||
|
|
||||||
for (TorrentImpl *const torrent : asConst(m_torrents))
|
for (TorrentImpl *const torrent : asConst(m_torrents))
|
||||||
torrent->handleTempPathChanged();
|
torrent->handleDownloadPathChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
@ -3969,7 +3976,7 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
|
||||||
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
|
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
qDebug("Found possible recursive torrent download.");
|
qDebug("Found possible recursive torrent download.");
|
||||||
const QString torrentFullpath = torrent->savePath(true) + '/' + torrentRelpath;
|
const QString torrentFullpath = torrent->actualStorageLocation() + '/' + torrentRelpath;
|
||||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
||||||
if (TorrentInfo::loadFromFile(torrentFullpath))
|
if (TorrentInfo::loadFromFile(torrentFullpath))
|
||||||
{
|
{
|
||||||
|
@ -4022,7 +4029,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
|
||||||
Q_ASSERT(torrent);
|
Q_ASSERT(torrent);
|
||||||
|
|
||||||
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
const lt::torrent_handle torrentHandle = torrent->nativeHandle();
|
||||||
const QString currentLocation = torrent->actualStorageLocation();
|
const QString currentLocation = Utils::Fs::toNativePath(torrent->actualStorageLocation());
|
||||||
|
|
||||||
if (m_moveStorageQueue.size() > 1)
|
if (m_moveStorageQueue.size() > 1)
|
||||||
{
|
{
|
||||||
|
@ -4116,6 +4123,89 @@ void Session::handleMoveTorrentStorageJobFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::storeCategories() const
|
||||||
|
{
|
||||||
|
QJsonObject jsonObj;
|
||||||
|
for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it)
|
||||||
|
{
|
||||||
|
const QString &categoryName = it.key();
|
||||||
|
const CategoryOptions &categoryOptions = it.value();
|
||||||
|
jsonObj[categoryName] = categoryOptions.toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path = QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME);
|
||||||
|
const QByteArray data = QJsonDocument(jsonObj).toJson();
|
||||||
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, data);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't store Categories configuration to %1. Error: %2")
|
||||||
|
.arg(path, result.error()), Log::WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::upgradeCategories()
|
||||||
|
{
|
||||||
|
const auto legacyCategories = SettingValue<QVariantMap>("BitTorrent/Session/Categories").get();
|
||||||
|
for (auto it = legacyCategories.cbegin(); it != legacyCategories.cend(); ++it)
|
||||||
|
{
|
||||||
|
const QString categoryName = it.key();
|
||||||
|
CategoryOptions categoryOptions;
|
||||||
|
categoryOptions.savePath = it.value().toString();
|
||||||
|
m_categories[categoryName] = categoryOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::loadCategories()
|
||||||
|
{
|
||||||
|
m_categories.clear();
|
||||||
|
|
||||||
|
QFile confFile {QDir(specialFolderLocation(SpecialFolder::Config)).absoluteFilePath(CATEGORIES_FILE_NAME)};
|
||||||
|
if (!confFile.exists())
|
||||||
|
{
|
||||||
|
// TODO: Remove the following upgrade code in v4.5
|
||||||
|
// == BEGIN UPGRADE CODE ==
|
||||||
|
upgradeCategories();
|
||||||
|
m_needUpgradeDownloadPath = true;
|
||||||
|
// == END UPGRADE CODE ==
|
||||||
|
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confFile.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't load Categories from %1. Error: %2")
|
||||||
|
.arg(confFile.fileName(), confFile.errorString()), Log::CRITICAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
|
||||||
|
if (jsonError.error != QJsonParseError::NoError)
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't parse Categories configuration from %1. Error: %2")
|
||||||
|
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonDoc.isObject())
|
||||||
|
{
|
||||||
|
LogMsg(tr("Couldn't load Categories configuration from %1. Invalid data format.")
|
||||||
|
.arg(confFile.fileName()), Log::WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const QJsonObject jsonObj = jsonDoc.object();
|
||||||
|
for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it)
|
||||||
|
{
|
||||||
|
const QString &categoryName = it.key();
|
||||||
|
const auto categoryOptions = CategoryOptions::fromJSON(it.value().toObject());
|
||||||
|
m_categories[categoryName] = categoryOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Session::handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl)
|
void Session::handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl)
|
||||||
{
|
{
|
||||||
emit trackerWarning(torrent, trackerUrl);
|
emit trackerWarning(torrent, trackerUrl);
|
||||||
|
@ -4258,20 +4348,40 @@ void Session::startUpTorrents()
|
||||||
QVector<TorrentID> queue;
|
QVector<TorrentID> queue;
|
||||||
for (const TorrentID &torrentID : torrents)
|
for (const TorrentID &torrentID : torrents)
|
||||||
{
|
{
|
||||||
const std::optional<LoadTorrentParams> resumeData = startupStorage->load(torrentID);
|
const std::optional<LoadTorrentParams> loadResumeDataResult = startupStorage->load(torrentID);
|
||||||
if (resumeData)
|
if (loadResumeDataResult)
|
||||||
{
|
{
|
||||||
|
LoadTorrentParams resumeData = *loadResumeDataResult;
|
||||||
|
bool needStore = false;
|
||||||
|
|
||||||
if (m_resumeDataStorage != startupStorage)
|
if (m_resumeDataStorage != startupStorage)
|
||||||
{
|
{
|
||||||
m_resumeDataStorage->store(torrentID, *resumeData);
|
needStore = true;
|
||||||
if (isQueueingSystemEnabled() && !resumeData->hasSeedStatus)
|
if (isQueueingSystemEnabled() && !resumeData.hasSeedStatus)
|
||||||
queue.append(torrentID);
|
queue.append(torrentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove the following upgrade code in v4.5
|
||||||
|
// == BEGIN UPGRADE CODE ==
|
||||||
|
if (m_needUpgradeDownloadPath && isDownloadPathEnabled())
|
||||||
|
{
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.downloadPath = downloadPath();
|
||||||
|
needStore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// == END UPGRADE CODE ==
|
||||||
|
|
||||||
|
if (needStore)
|
||||||
|
m_resumeDataStorage->store(torrentID, resumeData);
|
||||||
|
|
||||||
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
|
||||||
if (!loadTorrent(*resumeData))
|
if (!loadTorrent(resumeData))
|
||||||
|
{
|
||||||
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
|
||||||
.arg(torrentID.toString()), Log::CRITICAL);
|
.arg(torrentID.toString()), Log::CRITICAL);
|
||||||
|
}
|
||||||
|
|
||||||
// process add torrent messages before message queue overflow
|
// process add torrent messages before message queue overflow
|
||||||
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "addtorrentparams.h"
|
#include "addtorrentparams.h"
|
||||||
#include "cachestatus.h"
|
#include "cachestatus.h"
|
||||||
|
#include "categoryoptions.h"
|
||||||
#include "sessionstatus.h"
|
#include "sessionstatus.h"
|
||||||
#include "torrentinfo.h"
|
#include "torrentinfo.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
@ -211,22 +212,23 @@ namespace BitTorrent
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static Session *instance();
|
static Session *instance();
|
||||||
|
|
||||||
QString defaultSavePath() const;
|
QString savePath() const;
|
||||||
void setDefaultSavePath(QString path);
|
void setSavePath(const QString &path);
|
||||||
QString tempPath() const;
|
QString downloadPath() const;
|
||||||
void setTempPath(QString path);
|
void setDownloadPath(const QString &path);
|
||||||
bool isTempPathEnabled() const;
|
bool isDownloadPathEnabled() const;
|
||||||
void setTempPathEnabled(bool enabled);
|
void setDownloadPathEnabled(bool enabled);
|
||||||
QString torrentTempPath(const TorrentInfo &torrentInfo) const;
|
|
||||||
|
|
||||||
static bool isValidCategoryName(const QString &name);
|
static bool isValidCategoryName(const QString &name);
|
||||||
// returns category itself and all top level categories
|
// returns category itself and all top level categories
|
||||||
static QStringList expandCategory(const QString &category);
|
static QStringList expandCategory(const QString &category);
|
||||||
|
|
||||||
QStringMap categories() const;
|
QStringList categories() const;
|
||||||
|
CategoryOptions categoryOptions(const QString &categoryName) const;
|
||||||
QString categorySavePath(const QString &categoryName) const;
|
QString categorySavePath(const QString &categoryName) const;
|
||||||
bool addCategory(const QString &name, const QString &savePath = "");
|
QString categoryDownloadPath(const QString &categoryName) const;
|
||||||
bool editCategory(const QString &name, const QString &savePath);
|
bool addCategory(const QString &name, const CategoryOptions &options = {});
|
||||||
|
bool editCategory(const QString &name, const CategoryOptions &options);
|
||||||
bool removeCategory(const QString &name);
|
bool removeCategory(const QString &name);
|
||||||
bool isSubcategoriesEnabled() const;
|
bool isSubcategoriesEnabled() const;
|
||||||
void setSubcategoriesEnabled(bool value);
|
void setSubcategoriesEnabled(bool value);
|
||||||
|
@ -499,7 +501,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, MoveStorageMode mode);
|
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, MoveStorageMode mode);
|
||||||
|
|
||||||
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath, const QStringList &filePaths = {}) const;
|
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath
|
||||||
|
, const QString &downloadPath, const QStringList &filePaths = {}) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void allTorrentsFinished();
|
void allTorrentsFinished();
|
||||||
|
@ -642,6 +645,10 @@ namespace BitTorrent
|
||||||
void moveTorrentStorage(const MoveStorageJob &job) const;
|
void moveTorrentStorage(const MoveStorageJob &job) const;
|
||||||
void handleMoveTorrentStorageJobFinished();
|
void handleMoveTorrentStorageJobFinished();
|
||||||
|
|
||||||
|
void loadCategories();
|
||||||
|
void storeCategories() const;
|
||||||
|
void upgradeCategories();
|
||||||
|
|
||||||
// BitTorrent
|
// BitTorrent
|
||||||
lt::session *m_nativeSession = nullptr;
|
lt::session *m_nativeSession = nullptr;
|
||||||
|
|
||||||
|
@ -729,13 +736,12 @@ namespace BitTorrent
|
||||||
CachedSettingValue<bool> m_isProxyPeerConnectionsEnabled;
|
CachedSettingValue<bool> m_isProxyPeerConnectionsEnabled;
|
||||||
CachedSettingValue<ChokingAlgorithm> m_chokingAlgorithm;
|
CachedSettingValue<ChokingAlgorithm> m_chokingAlgorithm;
|
||||||
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
CachedSettingValue<SeedChokingAlgorithm> m_seedChokingAlgorithm;
|
||||||
CachedSettingValue<QVariantMap> m_storedCategories;
|
|
||||||
CachedSettingValue<QStringList> m_storedTags;
|
CachedSettingValue<QStringList> m_storedTags;
|
||||||
CachedSettingValue<int> m_maxRatioAction;
|
CachedSettingValue<int> m_maxRatioAction;
|
||||||
CachedSettingValue<QString> m_defaultSavePath;
|
CachedSettingValue<QString> m_savePath;
|
||||||
CachedSettingValue<QString> m_tempPath;
|
CachedSettingValue<QString> m_downloadPath;
|
||||||
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
CachedSettingValue<bool> m_isSubcategoriesEnabled;
|
||||||
CachedSettingValue<bool> m_isTempPathEnabled;
|
CachedSettingValue<bool> m_isDownloadPathEnabled;
|
||||||
CachedSettingValue<bool> m_isAutoTMMDisabledByDefault;
|
CachedSettingValue<bool> m_isAutoTMMDisabledByDefault;
|
||||||
CachedSettingValue<bool> m_isDisableAutoTMMWhenCategoryChanged;
|
CachedSettingValue<bool> m_isDisableAutoTMMWhenCategoryChanged;
|
||||||
CachedSettingValue<bool> m_isDisableAutoTMMWhenDefaultSavePathChanged;
|
CachedSettingValue<bool> m_isDisableAutoTMMWhenDefaultSavePathChanged;
|
||||||
|
@ -780,7 +786,7 @@ namespace BitTorrent
|
||||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||||
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
|
QHash<TorrentID, RemovingTorrentData> m_removingTorrents;
|
||||||
QSet<TorrentID> m_needSaveResumeDataTorrents;
|
QSet<TorrentID> m_needSaveResumeDataTorrents;
|
||||||
QStringMap m_categories;
|
QMap<QString, CategoryOptions> m_categories;
|
||||||
QSet<QString> m_tags;
|
QSet<QString> m_tags;
|
||||||
|
|
||||||
// I/O errored torrents
|
// I/O errored torrents
|
||||||
|
@ -800,6 +806,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
QString m_lastExternalIP;
|
QString m_lastExternalIP;
|
||||||
|
|
||||||
|
bool m_needUpgradeDownloadPath = false;
|
||||||
|
|
||||||
static Session *m_instance;
|
static Session *m_instance;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,11 +126,10 @@ namespace BitTorrent
|
||||||
virtual QString currentTracker() const = 0;
|
virtual QString currentTracker() const = 0;
|
||||||
|
|
||||||
// 1. savePath() - the path where all the files and subfolders of torrent are stored.
|
// 1. savePath() - the path where all the files and subfolders of torrent are stored.
|
||||||
|
// 1.1 downloadPath() - the path where all the files and subfolders of torrent are stored until torrent has finished downloading.
|
||||||
// 2. rootPath() - absolute path of torrent file tree (first common subfolder of torrent files); empty string if torrent has no root folder.
|
// 2. rootPath() - absolute path of torrent file tree (first common subfolder of torrent files); empty string if torrent has no root folder.
|
||||||
// 3. contentPath() - absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents).
|
// 3. contentPath() - absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents).
|
||||||
//
|
//
|
||||||
// These methods have 'actual' parameter (defaults to false) which allow to get actual or final path variant.
|
|
||||||
//
|
|
||||||
// Examples.
|
// Examples.
|
||||||
// Suppose we have three torrent with following structures and save path `/home/user/torrents`:
|
// Suppose we have three torrent with following structures and save path `/home/user/torrents`:
|
||||||
//
|
//
|
||||||
|
@ -168,14 +167,15 @@ namespace BitTorrent
|
||||||
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
|
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
|
||||||
// | C | <empty> | /home/user/torrents/file1 |
|
// | C | <empty> | /home/user/torrents/file1 |
|
||||||
|
|
||||||
virtual QString savePath(bool actual = false) const = 0;
|
|
||||||
virtual QString rootPath(bool actual = false) const = 0;
|
|
||||||
virtual QString contentPath(bool actual = false) const = 0;
|
|
||||||
|
|
||||||
virtual bool useTempPath() const = 0;
|
|
||||||
|
|
||||||
virtual bool isAutoTMMEnabled() const = 0;
|
virtual bool isAutoTMMEnabled() const = 0;
|
||||||
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
virtual void setAutoTMMEnabled(bool enabled) = 0;
|
||||||
|
virtual QString savePath() const = 0;
|
||||||
|
virtual void setSavePath(const QString &savePath) = 0;
|
||||||
|
virtual QString downloadPath() const = 0;
|
||||||
|
virtual void setDownloadPath(const QString &downloadPath) = 0;
|
||||||
|
virtual QString actualStorageLocation() const = 0;
|
||||||
|
virtual QString rootPath() const = 0;
|
||||||
|
virtual QString contentPath() const = 0;
|
||||||
virtual QString category() const = 0;
|
virtual QString category() const = 0;
|
||||||
virtual bool belongsToCategory(const QString &category) const = 0;
|
virtual bool belongsToCategory(const QString &category) const = 0;
|
||||||
virtual bool setCategory(const QString &category) = 0;
|
virtual bool setCategory(const QString &category) = 0;
|
||||||
|
@ -273,7 +273,6 @@ namespace BitTorrent
|
||||||
virtual void setFirstLastPiecePriority(bool enabled) = 0;
|
virtual void setFirstLastPiecePriority(bool enabled) = 0;
|
||||||
virtual void pause() = 0;
|
virtual void pause() = 0;
|
||||||
virtual void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) = 0;
|
virtual void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) = 0;
|
||||||
virtual void move(QString path) = 0;
|
|
||||||
virtual void forceReannounce(int index = -1) = 0;
|
virtual void forceReannounce(int index = -1) = 0;
|
||||||
virtual void forceDHTAnnounce() = 0;
|
virtual void forceDHTAnnounce() = 0;
|
||||||
virtual void forceRecheck() = 0;
|
virtual void forceRecheck() = 0;
|
||||||
|
|
|
@ -252,7 +252,8 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||||
, m_infoHash(m_nativeHandle.info_hash())
|
, m_infoHash(m_nativeHandle.info_hash())
|
||||||
#endif
|
#endif
|
||||||
, m_name(params.name)
|
, m_name(params.name)
|
||||||
, m_savePath(Utils::Fs::toNativePath(params.savePath))
|
, m_savePath(params.savePath)
|
||||||
|
, m_downloadPath(params.downloadPath)
|
||||||
, m_category(params.category)
|
, m_category(params.category)
|
||||||
, m_tags(params.tags)
|
, m_tags(params.tags)
|
||||||
, m_ratioLimit(params.ratioLimit)
|
, m_ratioLimit(params.ratioLimit)
|
||||||
|
@ -261,13 +262,10 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||||
, m_contentLayout(params.contentLayout)
|
, m_contentLayout(params.contentLayout)
|
||||||
, m_hasSeedStatus(params.hasSeedStatus)
|
, m_hasSeedStatus(params.hasSeedStatus)
|
||||||
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
||||||
, m_useAutoTMM(params.savePath.isEmpty())
|
, m_useAutoTMM(params.useAutoTMM)
|
||||||
, m_isStopped(params.stopped)
|
, m_isStopped(params.stopped)
|
||||||
, m_ltAddTorrentParams(params.ltAddTorrentParams)
|
, m_ltAddTorrentParams(params.ltAddTorrentParams)
|
||||||
{
|
{
|
||||||
if (m_useAutoTMM)
|
|
||||||
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
|
||||||
|
|
||||||
if (m_ltAddTorrentParams.ti)
|
if (m_ltAddTorrentParams.ti)
|
||||||
{
|
{
|
||||||
// Initialize it only if torrent is added with metadata.
|
// Initialize it only if torrent is added with metadata.
|
||||||
|
@ -293,7 +291,7 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
|
||||||
if (hasMetadata())
|
if (hasMetadata())
|
||||||
applyFirstLastPiecePriority(m_hasFirstLastPiecePriority);
|
applyFirstLastPiecePriority(m_hasFirstLastPiecePriority);
|
||||||
|
|
||||||
// TODO: Remove the following upgrade code in v.4.4
|
// TODO: Remove the following upgrade code in v4.4
|
||||||
// == BEGIN UPGRADE CODE ==
|
// == BEGIN UPGRADE CODE ==
|
||||||
const QString spath = actualStorageLocation();
|
const QString spath = actualStorageLocation();
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
@ -396,15 +394,52 @@ QString TorrentImpl::currentTracker() const
|
||||||
return QString::fromStdString(m_nativeStatus.current_tracker);
|
return QString::fromStdString(m_nativeStatus.current_tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::savePath(bool actual) const
|
QString TorrentImpl::savePath() const
|
||||||
{
|
{
|
||||||
if (actual)
|
return isAutoTMMEnabled() ? m_session->categorySavePath(category()) : m_savePath;
|
||||||
return Utils::Fs::toUniformPath(actualStorageLocation());
|
|
||||||
else
|
|
||||||
return Utils::Fs::toUniformPath(m_savePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::rootPath(bool actual) const
|
void TorrentImpl::setSavePath(const QString &path)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isAutoTMMEnabled());
|
||||||
|
|
||||||
|
const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, m_session->savePath()));
|
||||||
|
if (resolvedPath == savePath())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_savePath = resolvedPath;
|
||||||
|
|
||||||
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
|
||||||
|
const bool isFinished = isSeed() || m_hasSeedStatus;
|
||||||
|
if (isFinished)
|
||||||
|
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentImpl::downloadPath() const
|
||||||
|
{
|
||||||
|
return isAutoTMMEnabled() ? m_session->categoryDownloadPath(category()) : m_downloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::setDownloadPath(const QString &path)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isAutoTMMEnabled());
|
||||||
|
|
||||||
|
const QString resolvedPath = ((path.isEmpty() || QDir::isAbsolutePath(path))
|
||||||
|
? path : Utils::Fs::resolvePath(path, m_session->downloadPath()));
|
||||||
|
if (resolvedPath == m_downloadPath)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_downloadPath = resolvedPath;
|
||||||
|
|
||||||
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
|
||||||
|
const bool isFinished = isSeed() || m_hasSeedStatus;
|
||||||
|
if (!isFinished)
|
||||||
|
moveStorage((m_downloadPath.isEmpty() ? savePath() : m_downloadPath), MoveStorageMode::KeepExistingFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentImpl::rootPath() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
|
@ -413,19 +448,19 @@ QString TorrentImpl::rootPath(bool actual) const
|
||||||
if (relativeRootPath.isEmpty())
|
if (relativeRootPath.isEmpty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return QDir(savePath(actual)).absoluteFilePath(relativeRootPath);
|
return QDir(actualStorageLocation()).absoluteFilePath(relativeRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::contentPath(const bool actual) const
|
QString TorrentImpl::contentPath() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (filesCount() == 1)
|
if (filesCount() == 1)
|
||||||
return QDir(savePath(actual)).absoluteFilePath(filePath(0));
|
return QDir(actualStorageLocation()).absoluteFilePath(filePath(0));
|
||||||
|
|
||||||
const QString rootPath = this->rootPath(actual);
|
const QString rootPath = this->rootPath();
|
||||||
return (rootPath.isEmpty() ? savePath(actual) : rootPath);
|
return (rootPath.isEmpty() ? actualStorageLocation() : rootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentImpl::isAutoTMMEnabled() const
|
bool TorrentImpl::isAutoTMMEnabled() const
|
||||||
|
@ -435,19 +470,25 @@ bool TorrentImpl::isAutoTMMEnabled() const
|
||||||
|
|
||||||
void TorrentImpl::setAutoTMMEnabled(bool enabled)
|
void TorrentImpl::setAutoTMMEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
if (m_useAutoTMM == enabled) return;
|
if (m_useAutoTMM == enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
m_useAutoTMM = enabled;
|
m_useAutoTMM = enabled;
|
||||||
|
if (!m_useAutoTMM)
|
||||||
|
{
|
||||||
|
m_savePath = m_session->categorySavePath(category());
|
||||||
|
m_downloadPath = m_session->categoryDownloadPath(category());
|
||||||
|
}
|
||||||
|
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
m_session->handleTorrentSavingModeChanged(this);
|
m_session->handleTorrentSavingModeChanged(this);
|
||||||
|
|
||||||
if (m_useAutoTMM)
|
adjustStorageLocation();
|
||||||
move_impl(m_session->categorySavePath(m_category), MoveStorageMode::Overwrite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentImpl::actualStorageLocation() const
|
QString TorrentImpl::actualStorageLocation() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(m_nativeStatus.save_path);
|
return Utils::Fs::toUniformPath(QString::fromStdString(m_nativeStatus.save_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::setAutoManaged(const bool enable)
|
void TorrentImpl::setAutoManaged(const bool enable)
|
||||||
|
@ -774,7 +815,7 @@ QStringList TorrentImpl::absoluteFilePaths() const
|
||||||
{
|
{
|
||||||
if (!hasMetadata()) return {};
|
if (!hasMetadata()) return {};
|
||||||
|
|
||||||
const QDir saveDir {savePath(true)};
|
const QDir saveDir {actualStorageLocation()};
|
||||||
QStringList res;
|
QStringList res;
|
||||||
res.reserve(filesCount());
|
res.reserve(filesCount());
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
@ -1351,7 +1392,7 @@ bool TorrentImpl::setCategory(const QString &category)
|
||||||
if (m_useAutoTMM)
|
if (m_useAutoTMM)
|
||||||
{
|
{
|
||||||
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
|
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
|
||||||
move_impl(m_session->categorySavePath(m_category), MoveStorageMode::Overwrite);
|
adjustStorageLocation();
|
||||||
else
|
else
|
||||||
setAutoTMMEnabled(false);
|
setAutoTMMEnabled(false);
|
||||||
}
|
}
|
||||||
|
@ -1360,41 +1401,6 @@ bool TorrentImpl::setCategory(const QString &category)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::move(QString path)
|
|
||||||
{
|
|
||||||
if (m_useAutoTMM)
|
|
||||||
{
|
|
||||||
m_useAutoTMM = false;
|
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentSavingModeChanged(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = Utils::Fs::toUniformPath(path.trimmed());
|
|
||||||
if (path.isEmpty())
|
|
||||||
path = m_session->defaultSavePath();
|
|
||||||
if (!path.endsWith('/'))
|
|
||||||
path += '/';
|
|
||||||
|
|
||||||
move_impl(path, MoveStorageMode::KeepExistingFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::move_impl(QString path, const MoveStorageMode mode)
|
|
||||||
{
|
|
||||||
if (path == savePath()) return;
|
|
||||||
|
|
||||||
path = Utils::Fs::toNativePath(path);
|
|
||||||
if (!useTempPath())
|
|
||||||
{
|
|
||||||
moveStorage(path, mode);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_savePath = path;
|
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
|
||||||
m_session->handleTorrentSavePathChanged(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::forceReannounce(const int index)
|
void TorrentImpl::forceReannounce(const int index)
|
||||||
{
|
{
|
||||||
m_nativeHandle.force_reannounce(0, index);
|
m_nativeHandle.force_reannounce(0, index);
|
||||||
|
@ -1611,7 +1617,7 @@ void TorrentImpl::resume(const TorrentOperatingMode mode)
|
||||||
|
|
||||||
void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
|
void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
|
||||||
{
|
{
|
||||||
if (m_session->addMoveTorrentStorageJob(this, newPath, mode))
|
if (m_session->addMoveTorrentStorageJob(this, Utils::Fs::toNativePath(newPath), mode))
|
||||||
{
|
{
|
||||||
m_storageIsMoving = true;
|
m_storageIsMoving = true;
|
||||||
updateStatus();
|
updateStatus();
|
||||||
|
@ -1629,18 +1635,18 @@ void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||||
updateStatus(nativeStatus);
|
updateStatus(nativeStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentImpl::handleDownloadPathChanged()
|
||||||
|
{
|
||||||
|
adjustStorageLocation();
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob)
|
void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob)
|
||||||
{
|
{
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
m_storageIsMoving = hasOutstandingJob;
|
m_storageIsMoving = hasOutstandingJob;
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
const QString newPath = QString::fromStdString(m_nativeStatus.save_path);
|
|
||||||
if (!useTempPath() && (newPath != m_savePath))
|
|
||||||
{
|
|
||||||
m_savePath = newPath;
|
|
||||||
m_session->handleTorrentSavePathChanged(this);
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_storageIsMoving)
|
if (!m_storageIsMoving)
|
||||||
{
|
{
|
||||||
|
@ -1717,7 +1723,7 @@ void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
|
||||||
else if (progress() == 1.0)
|
else if (progress() == 1.0)
|
||||||
m_hasSeedStatus = true;
|
m_hasSeedStatus = true;
|
||||||
|
|
||||||
adjustActualSavePath();
|
adjustStorageLocation();
|
||||||
manageIncompleteFiles();
|
manageIncompleteFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1735,7 +1741,7 @@ void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p
|
||||||
updateStatus();
|
updateStatus();
|
||||||
m_hasSeedStatus = true;
|
m_hasSeedStatus = true;
|
||||||
|
|
||||||
adjustActualSavePath();
|
adjustStorageLocation();
|
||||||
manageIncompleteFiles();
|
manageIncompleteFiles();
|
||||||
|
|
||||||
m_session->handleTorrentNeedSaveResumeData(this);
|
m_session->handleTorrentNeedSaveResumeData(this);
|
||||||
|
@ -1778,7 +1784,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
||||||
|
|
||||||
QStringList filePaths = metadata.filePaths();
|
QStringList filePaths = metadata.filePaths();
|
||||||
applyContentLayout(filePaths, m_contentLayout);
|
applyContentLayout(filePaths, m_contentLayout);
|
||||||
m_session->findIncompleteFiles(metadata, m_savePath, filePaths);
|
m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1812,7 +1818,6 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params ¶ms)
|
||||||
LoadTorrentParams resumeData;
|
LoadTorrentParams resumeData;
|
||||||
resumeData.name = m_name;
|
resumeData.name = m_name;
|
||||||
resumeData.category = m_category;
|
resumeData.category = m_category;
|
||||||
resumeData.savePath = m_useAutoTMM ? "" : m_savePath;
|
|
||||||
resumeData.tags = m_tags;
|
resumeData.tags = m_tags;
|
||||||
resumeData.contentLayout = m_contentLayout;
|
resumeData.contentLayout = m_contentLayout;
|
||||||
resumeData.ratioLimit = m_ratioLimit;
|
resumeData.ratioLimit = m_ratioLimit;
|
||||||
|
@ -1822,6 +1827,12 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params ¶ms)
|
||||||
resumeData.stopped = m_isStopped;
|
resumeData.stopped = m_isStopped;
|
||||||
resumeData.operatingMode = m_operatingMode;
|
resumeData.operatingMode = m_operatingMode;
|
||||||
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
|
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
|
||||||
|
resumeData.useAutoTMM = m_useAutoTMM;
|
||||||
|
if (!resumeData.useAutoTMM)
|
||||||
|
{
|
||||||
|
resumeData.savePath = m_savePath;
|
||||||
|
resumeData.downloadPath = m_downloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||||
}
|
}
|
||||||
|
@ -1956,15 +1967,10 @@ void TorrentImpl::handlePerformanceAlert(const lt::performance_alert *p) const
|
||||||
, Log::INFO);
|
, Log::INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::handleTempPathChanged()
|
void TorrentImpl::handleCategoryOptionsChanged()
|
||||||
{
|
|
||||||
adjustActualSavePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::handleCategorySavePathChanged()
|
|
||||||
{
|
{
|
||||||
if (m_useAutoTMM)
|
if (m_useAutoTMM)
|
||||||
move_impl(m_session->categorySavePath(m_category), MoveStorageMode::Overwrite);
|
adjustStorageLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::handleAppendExtensionToggled()
|
void TorrentImpl::handleAppendExtensionToggled()
|
||||||
|
@ -2069,39 +2075,13 @@ void TorrentImpl::manageIncompleteFiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentImpl::adjustActualSavePath()
|
void TorrentImpl::adjustStorageLocation()
|
||||||
{
|
{
|
||||||
if (!isMoveInProgress())
|
const QString downloadPath = this->downloadPath();
|
||||||
adjustActualSavePath_impl();
|
const bool isFinished = isSeed() || m_hasSeedStatus;
|
||||||
else
|
const QDir targetDir {((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath)};
|
||||||
m_moveFinishedTriggers.append([this]() { adjustActualSavePath_impl(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::adjustActualSavePath_impl()
|
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
|
||||||
{
|
|
||||||
const bool needUseTempDir = useTempPath();
|
|
||||||
const QDir tempDir {m_session->torrentTempPath(m_torrentInfo)};
|
|
||||||
const QDir currentDir {actualStorageLocation()};
|
|
||||||
const QDir targetDir {needUseTempDir ? tempDir : QDir {savePath()}};
|
|
||||||
|
|
||||||
if (targetDir == currentDir) return;
|
|
||||||
|
|
||||||
if (!needUseTempDir)
|
|
||||||
{
|
|
||||||
if ((currentDir == tempDir) && (currentDir != QDir {m_session->tempPath()}))
|
|
||||||
{
|
|
||||||
// torrent without root folder still has it in its temporary save path
|
|
||||||
// so its temp path isn't equal to temp path root
|
|
||||||
const QString currentDirPath = currentDir.absolutePath();
|
|
||||||
m_moveFinishedTriggers.append([currentDirPath]
|
|
||||||
{
|
|
||||||
qDebug() << "Removing torrent temp folder:" << currentDirPath;
|
|
||||||
Utils::Fs::smartRemoveEmptyFolderTree(currentDirPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
moveStorage(Utils::Fs::toNativePath(targetDir.absolutePath()), MoveStorageMode::Overwrite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lt::torrent_handle TorrentImpl::nativeHandle() const
|
lt::torrent_handle TorrentImpl::nativeHandle() const
|
||||||
|
@ -2114,11 +2094,6 @@ bool TorrentImpl::isMoveInProgress() const
|
||||||
return m_storageIsMoving;
|
return m_storageIsMoving;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentImpl::useTempPath() const
|
|
||||||
{
|
|
||||||
return m_session->isTempPathEnabled() && !(isSeed() || m_hasSeedStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentImpl::updateStatus()
|
void TorrentImpl::updateStatus()
|
||||||
{
|
{
|
||||||
updateStatus(m_nativeHandle.status());
|
updateStatus(m_nativeHandle.status());
|
||||||
|
|
|
@ -99,14 +99,15 @@ namespace BitTorrent
|
||||||
qlonglong wastedSize() const override;
|
qlonglong wastedSize() const override;
|
||||||
QString currentTracker() const override;
|
QString currentTracker() const override;
|
||||||
|
|
||||||
QString savePath(bool actual = false) const override;
|
|
||||||
QString rootPath(bool actual = false) const override;
|
|
||||||
QString contentPath(bool actual = false) const override;
|
|
||||||
|
|
||||||
bool useTempPath() const override;
|
|
||||||
|
|
||||||
bool isAutoTMMEnabled() const override;
|
bool isAutoTMMEnabled() const override;
|
||||||
void setAutoTMMEnabled(bool enabled) override;
|
void setAutoTMMEnabled(bool enabled) override;
|
||||||
|
QString savePath() const override;
|
||||||
|
void setSavePath(const QString &path) override;
|
||||||
|
QString downloadPath() const override;
|
||||||
|
void setDownloadPath(const QString &path) override;
|
||||||
|
QString actualStorageLocation() const override;
|
||||||
|
QString rootPath() const override;
|
||||||
|
QString contentPath() const override;
|
||||||
QString category() const override;
|
QString category() const override;
|
||||||
bool belongsToCategory(const QString &category) const override;
|
bool belongsToCategory(const QString &category) const override;
|
||||||
bool setCategory(const QString &category) override;
|
bool setCategory(const QString &category) override;
|
||||||
|
@ -201,7 +202,6 @@ namespace BitTorrent
|
||||||
void setFirstLastPiecePriority(bool enabled) override;
|
void setFirstLastPiecePriority(bool enabled) override;
|
||||||
void pause() override;
|
void pause() override;
|
||||||
void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) override;
|
void resume(TorrentOperatingMode mode = TorrentOperatingMode::AutoManaged) override;
|
||||||
void move(QString path) override;
|
|
||||||
void forceReannounce(int index = -1) override;
|
void forceReannounce(int index = -1) override;
|
||||||
void forceDHTAnnounce() override;
|
void forceDHTAnnounce() override;
|
||||||
void forceRecheck() override;
|
void forceRecheck() override;
|
||||||
|
@ -232,15 +232,13 @@ namespace BitTorrent
|
||||||
|
|
||||||
void handleAlert(const lt::alert *a);
|
void handleAlert(const lt::alert *a);
|
||||||
void handleStateUpdate(const lt::torrent_status &nativeStatus);
|
void handleStateUpdate(const lt::torrent_status &nativeStatus);
|
||||||
void handleTempPathChanged();
|
void handleDownloadPathChanged();
|
||||||
void handleCategorySavePathChanged();
|
void handleCategoryOptionsChanged();
|
||||||
void handleAppendExtensionToggled();
|
void handleAppendExtensionToggled();
|
||||||
void saveResumeData();
|
void saveResumeData();
|
||||||
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
void handleMoveStorageJobFinished(bool hasOutstandingJob);
|
||||||
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
|
void fileSearchFinished(const QString &savePath, const QStringList &fileNames);
|
||||||
|
|
||||||
QString actualStorageLocation() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using EventTrigger = std::function<void ()>;
|
using EventTrigger = std::function<void ()>;
|
||||||
|
|
||||||
|
@ -272,9 +270,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
void setAutoManaged(bool enable);
|
void setAutoManaged(bool enable);
|
||||||
|
|
||||||
void adjustActualSavePath();
|
void adjustStorageLocation();
|
||||||
void adjustActualSavePath_impl();
|
|
||||||
void move_impl(QString path, MoveStorageMode mode);
|
|
||||||
void moveStorage(const QString &newPath, MoveStorageMode mode);
|
void moveStorage(const QString &newPath, MoveStorageMode mode);
|
||||||
void manageIncompleteFiles();
|
void manageIncompleteFiles();
|
||||||
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
|
||||||
|
@ -308,6 +304,7 @@ namespace BitTorrent
|
||||||
// Persistent data
|
// Persistent data
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_savePath;
|
QString m_savePath;
|
||||||
|
QString m_downloadPath;
|
||||||
QString m_category;
|
QString m_category;
|
||||||
TagSet m_tags;
|
TagSet m_tags;
|
||||||
qreal m_ratioLimit;
|
qreal m_ratioLimit;
|
||||||
|
|
|
@ -88,8 +88,6 @@ QString Profile::location(const SpecialFolder folder) const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.endsWith(QLatin1Char('/')))
|
|
||||||
result += QLatin1Char('/');
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,8 @@ const QString OPTION_RECURSIVE {QStringLiteral("recursive")};
|
||||||
const QString PARAM_CATEGORY {QStringLiteral("category")};
|
const QString PARAM_CATEGORY {QStringLiteral("category")};
|
||||||
const QString PARAM_TAGS {QStringLiteral("tags")};
|
const QString PARAM_TAGS {QStringLiteral("tags")};
|
||||||
const QString PARAM_SAVEPATH {QStringLiteral("save_path")};
|
const QString PARAM_SAVEPATH {QStringLiteral("save_path")};
|
||||||
|
const QString PARAM_USEDOWNLOADPATH {QStringLiteral("use_download_path")};
|
||||||
|
const QString PARAM_DOWNLOADPATH {QStringLiteral("download_path")};
|
||||||
const QString PARAM_OPERATINGMODE {QStringLiteral("operating_mode")};
|
const QString PARAM_OPERATINGMODE {QStringLiteral("operating_mode")};
|
||||||
const QString PARAM_STOPPED {QStringLiteral("stopped")};
|
const QString PARAM_STOPPED {QStringLiteral("stopped")};
|
||||||
const QString PARAM_SKIPCHECKING {QStringLiteral("skip_checking")};
|
const QString PARAM_SKIPCHECKING {QStringLiteral("skip_checking")};
|
||||||
|
@ -136,6 +138,8 @@ namespace
|
||||||
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
params.category = jsonObj.value(PARAM_CATEGORY).toString();
|
||||||
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
params.tags = parseTagSet(jsonObj.value(PARAM_TAGS).toArray());
|
||||||
params.savePath = jsonObj.value(PARAM_SAVEPATH).toString();
|
params.savePath = jsonObj.value(PARAM_SAVEPATH).toString();
|
||||||
|
params.useDownloadPath = getOptionalBool(jsonObj, PARAM_USEDOWNLOADPATH);
|
||||||
|
params.downloadPath = jsonObj.value(PARAM_DOWNLOADPATH).toString();
|
||||||
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
params.addForced = (getEnum<BitTorrent::TorrentOperatingMode>(jsonObj, PARAM_OPERATINGMODE) == BitTorrent::TorrentOperatingMode::Forced);
|
||||||
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
params.addPaused = getOptionalBool(jsonObj, PARAM_STOPPED);
|
||||||
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
params.skipChecking = jsonObj.value(PARAM_SKIPCHECKING).toBool();
|
||||||
|
@ -155,6 +159,7 @@ namespace
|
||||||
{PARAM_CATEGORY, params.category},
|
{PARAM_CATEGORY, params.category},
|
||||||
{PARAM_TAGS, serializeTagSet(params.tags)},
|
{PARAM_TAGS, serializeTagSet(params.tags)},
|
||||||
{PARAM_SAVEPATH, params.savePath},
|
{PARAM_SAVEPATH, params.savePath},
|
||||||
|
{PARAM_DOWNLOADPATH, params.downloadPath},
|
||||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||||
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
? BitTorrent::TorrentOperatingMode::Forced : BitTorrent::TorrentOperatingMode::AutoManaged)},
|
||||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||||
|
@ -170,6 +175,8 @@ namespace
|
||||||
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
jsonObj[PARAM_CONTENTLAYOUT] = Utils::String::fromEnum(*params.contentLayout);
|
||||||
if (params.useAutoTMM)
|
if (params.useAutoTMM)
|
||||||
jsonObj[PARAM_AUTOTMM] = *params.useAutoTMM;
|
jsonObj[PARAM_AUTOTMM] = *params.useAutoTMM;
|
||||||
|
if (params.useDownloadPath)
|
||||||
|
jsonObj[PARAM_USEDOWNLOADPATH] = *params.useDownloadPath;
|
||||||
|
|
||||||
return jsonObj;
|
return jsonObj;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,9 +72,14 @@ QString Utils::Fs::toUniformPath(const QString &path)
|
||||||
return QDir::fromNativeSeparators(path);
|
return QDir::fromNativeSeparators(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
QString Utils::Fs::resolvePath(const QString &relativePath, const QString &basePath)
|
||||||
* Returns the file extension part of a file name.
|
{
|
||||||
*/
|
Q_ASSERT(QDir::isRelativePath(relativePath));
|
||||||
|
Q_ASSERT(QDir::isAbsolutePath(basePath));
|
||||||
|
|
||||||
|
return (relativePath.isEmpty() ? basePath : QDir(basePath).absoluteFilePath(relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
QString Utils::Fs::fileExtension(const QString &filename)
|
QString Utils::Fs::fileExtension(const QString &filename)
|
||||||
{
|
{
|
||||||
const QString name = filename.endsWith(QB_EXT)
|
const QString name = filename.endsWith(QB_EXT)
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace Utils::Fs
|
||||||
* with the OS being run.
|
* with the OS being run.
|
||||||
*/
|
*/
|
||||||
QString toNativePath(const QString &path);
|
QString toNativePath(const QString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a path to a string suitable for processing.
|
* Converts a path to a string suitable for processing.
|
||||||
* This function makes sure the directory separator used is independent
|
* This function makes sure the directory separator used is independent
|
||||||
|
@ -50,7 +51,16 @@ namespace Utils::Fs
|
||||||
*/
|
*/
|
||||||
QString toUniformPath(const QString &path);
|
QString toUniformPath(const QString &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `path is relative then resolves it against `basePath`, otherwise returns the `path` itself
|
||||||
|
*/
|
||||||
|
QString resolvePath(const QString &relativePath, const QString &basePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file extension part of a file name.
|
||||||
|
*/
|
||||||
QString fileExtension(const QString &filename);
|
QString fileExtension(const QString &filename);
|
||||||
|
|
||||||
QString fileName(const QString &filePath);
|
QString fileName(const QString &filePath);
|
||||||
QString folderName(const QString &filePath);
|
QString folderName(const QString &filePath);
|
||||||
qint64 computePathSize(const QString &path);
|
qint64 computePathSize(const QString &path);
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace
|
||||||
const QString KEY_ENABLED = QStringLiteral(SETTINGS_KEY("Enabled"));
|
const QString KEY_ENABLED = QStringLiteral(SETTINGS_KEY("Enabled"));
|
||||||
const QString KEY_TOPLEVEL = QStringLiteral(SETTINGS_KEY("TopLevel"));
|
const QString KEY_TOPLEVEL = QStringLiteral(SETTINGS_KEY("TopLevel"));
|
||||||
const QString KEY_SAVEPATHHISTORY = QStringLiteral(SETTINGS_KEY("SavePathHistory"));
|
const QString KEY_SAVEPATHHISTORY = QStringLiteral(SETTINGS_KEY("SavePathHistory"));
|
||||||
|
const QString KEY_DOWNLOADPATHHISTORY = QStringLiteral(SETTINGS_KEY("DownloadPathHistory"));
|
||||||
const QString KEY_SAVEPATHHISTORYLENGTH = QStringLiteral(SETTINGS_KEY("SavePathHistoryLength"));
|
const QString KEY_SAVEPATHHISTORYLENGTH = QStringLiteral(SETTINGS_KEY("SavePathHistoryLength"));
|
||||||
|
|
||||||
// just a shortcut
|
// just a shortcut
|
||||||
|
@ -120,6 +121,45 @@ namespace
|
||||||
const BitTorrent::TorrentInfo &m_torrentInfo;
|
const BitTorrent::TorrentInfo &m_torrentInfo;
|
||||||
QStringList &m_filePaths;
|
QStringList &m_filePaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// savePath is a folder, not an absolute file path
|
||||||
|
int indexOfPath(const FileSystemPathComboEdit *fsPathEdit, const QString &savePath)
|
||||||
|
{
|
||||||
|
const QDir saveDir {savePath};
|
||||||
|
for (int i = 0; i < fsPathEdit->count(); ++i)
|
||||||
|
{
|
||||||
|
if (QDir(fsPathEdit->item(i)) == saveDir)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPath(FileSystemPathComboEdit *fsPathEdit, const QString &newPath)
|
||||||
|
{
|
||||||
|
int existingIndex = indexOfPath(fsPathEdit, newPath);
|
||||||
|
if (existingIndex < 0)
|
||||||
|
{
|
||||||
|
// New path, prepend to combo box
|
||||||
|
fsPathEdit->insertItem(0, newPath);
|
||||||
|
existingIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsPathEdit->setCurrentIndex(existingIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePathHistory(const QString &settingsKey, const QString &path, const int maxLength)
|
||||||
|
{
|
||||||
|
// Add last used save path to the front of history
|
||||||
|
|
||||||
|
auto pathList = settings()->loadValue<QStringList>(settingsKey);
|
||||||
|
const int selectedSavePathIndex = pathList.indexOf(path);
|
||||||
|
if (selectedSavePathIndex > 0)
|
||||||
|
pathList.removeAt(selectedSavePathIndex);
|
||||||
|
if (selectedSavePathIndex != 0)
|
||||||
|
pathList.prepend(path);
|
||||||
|
|
||||||
|
settings()->storeValue(settingsKey, QStringList(pathList.mid(0, maxLength)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int AddNewTorrentDialog::minPathHistoryLength;
|
const int AddNewTorrentDialog::minPathHistoryLength;
|
||||||
|
@ -155,13 +195,19 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
|
||||||
|
|
||||||
const auto *session = BitTorrent::Session::instance();
|
const auto *session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
|
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
|
||||||
|
m_ui->downloadPath->setMaxVisibleItems(20);
|
||||||
|
|
||||||
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
|
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
|
||||||
|
|
||||||
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
|
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
|
||||||
m_ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
|
m_ui->comboTTM->setCurrentIndex(session->isAutoTMMDisabledByDefault() ? 0 : 1);
|
||||||
m_ui->comboTTM->blockSignals(false);
|
m_ui->comboTTM->blockSignals(false);
|
||||||
populateSavePathComboBox();
|
|
||||||
connect(m_ui->savePath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onSavePathChanged);
|
connect(m_ui->savePath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onSavePathChanged);
|
||||||
|
connect(m_ui->downloadPath, &FileSystemPathEdit::selectedPathChanged, this, &AddNewTorrentDialog::onDownloadPathChanged);
|
||||||
|
connect(m_ui->groupBoxDownloadPath, &QGroupBox::toggled, this, &AddNewTorrentDialog::onUseDownloadPathChanged);
|
||||||
|
|
||||||
m_ui->checkBoxRememberLastSavePath->setChecked(m_storeRememberLastSavePath);
|
m_ui->checkBoxRememberLastSavePath->setChecked(m_storeRememberLastSavePath);
|
||||||
|
|
||||||
|
@ -176,7 +222,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
|
||||||
m_ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
|
m_ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
|
||||||
|
|
||||||
// Load categories
|
// Load categories
|
||||||
QStringList categories = session->categories().keys();
|
QStringList categories = session->categories();
|
||||||
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||||
const QString defaultCategory = m_storeDefaultCategory;
|
const QString defaultCategory = m_storeDefaultCategory;
|
||||||
|
|
||||||
|
@ -344,6 +390,7 @@ bool AddNewTorrentDialog::loadTorrentImpl()
|
||||||
m_ui->labelInfohash2Data->setText(m_torrentInfo.infoHash().v2().isValid() ? m_torrentInfo.infoHash().v2().toString() : tr("N/A"));
|
m_ui->labelInfohash2Data->setText(m_torrentInfo.infoHash().v2().isValid() ? m_torrentInfo.infoHash().v2().toString() : tr("N/A"));
|
||||||
setupTreeview();
|
setupTreeview();
|
||||||
TMMChanged(m_ui->comboTTM->currentIndex());
|
TMMChanged(m_ui->comboTTM->currentIndex());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,36 +456,6 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
|
||||||
raise();
|
raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::saveSavePathHistory() const
|
|
||||||
{
|
|
||||||
// Get current history
|
|
||||||
auto history = settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY);
|
|
||||||
QVector<QDir> historyDirs;
|
|
||||||
for (const QString &path : asConst(history))
|
|
||||||
historyDirs << QDir {path};
|
|
||||||
|
|
||||||
const QDir selectedSavePath {m_ui->savePath->selectedPath()};
|
|
||||||
const int selectedSavePathIndex = historyDirs.indexOf(selectedSavePath);
|
|
||||||
if (selectedSavePathIndex > 0)
|
|
||||||
history.removeAt(selectedSavePathIndex);
|
|
||||||
if (selectedSavePathIndex != 0)
|
|
||||||
// Add last used save path to the front of history
|
|
||||||
history.push_front(selectedSavePath.absolutePath());
|
|
||||||
|
|
||||||
// Save history
|
|
||||||
settings()->storeValue(KEY_SAVEPATHHISTORY, QStringList {history.mid(0, savePathHistoryLength())});
|
|
||||||
}
|
|
||||||
|
|
||||||
// savePath is a folder, not an absolute file path
|
|
||||||
int AddNewTorrentDialog::indexOfSavePath(const QString &savePath)
|
|
||||||
{
|
|
||||||
QDir saveDir(savePath);
|
|
||||||
for (int i = 0; i < m_ui->savePath->count(); ++i)
|
|
||||||
if (QDir(m_ui->savePath->item(i)) == saveDir)
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddNewTorrentDialog::updateDiskSpaceLabel()
|
void AddNewTorrentDialog::updateDiskSpaceLabel()
|
||||||
{
|
{
|
||||||
// Determine torrent size
|
// Determine torrent size
|
||||||
|
@ -472,18 +489,42 @@ void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
|
||||||
{
|
{
|
||||||
Q_UNUSED(newPath);
|
Q_UNUSED(newPath);
|
||||||
// Remember index
|
// Remember index
|
||||||
m_oldIndex = m_ui->savePath->currentIndex();
|
m_savePathIndex = m_ui->savePath->currentIndex();
|
||||||
updateDiskSpaceLabel();
|
updateDiskSpaceLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddNewTorrentDialog::onDownloadPathChanged(const QString &newPath)
|
||||||
|
{
|
||||||
|
Q_UNUSED(newPath);
|
||||||
|
// Remember index
|
||||||
|
const int currentPathIndex = m_ui->downloadPath->currentIndex();
|
||||||
|
if (currentPathIndex >= 0)
|
||||||
|
m_downloadPathIndex = m_ui->downloadPath->currentIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddNewTorrentDialog::onUseDownloadPathChanged(const bool checked)
|
||||||
|
{
|
||||||
|
m_useDownloadPath = checked;
|
||||||
|
m_ui->downloadPath->setCurrentIndex(checked ? m_downloadPathIndex : -1);
|
||||||
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::categoryChanged(int index)
|
void AddNewTorrentDialog::categoryChanged(int index)
|
||||||
{
|
{
|
||||||
Q_UNUSED(index);
|
Q_UNUSED(index);
|
||||||
|
|
||||||
if (m_ui->comboTTM->currentIndex() == 1)
|
if (m_ui->comboTTM->currentIndex() == 1)
|
||||||
{
|
{
|
||||||
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
|
const QString categoryName = m_ui->categoryComboBox->currentText();
|
||||||
|
|
||||||
|
const QString savePath = btSession->categorySavePath(categoryName);
|
||||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
||||||
|
|
||||||
|
const QString downloadPath = btSession->categoryDownloadPath(categoryName);
|
||||||
|
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
|
||||||
|
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(!m_ui->downloadPath->selectedPath().isEmpty());
|
||||||
|
|
||||||
updateDiskSpaceLabel();
|
updateDiskSpaceLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,19 +554,6 @@ void AddNewTorrentDialog::contentLayoutChanged(const int index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::setSavePath(const QString &newPath)
|
|
||||||
{
|
|
||||||
int existingIndex = indexOfSavePath(newPath);
|
|
||||||
if (existingIndex < 0)
|
|
||||||
{
|
|
||||||
// New path, prepend to combo box
|
|
||||||
m_ui->savePath->insertItem(0, newPath);
|
|
||||||
existingIndex = 0;
|
|
||||||
}
|
|
||||||
m_ui->savePath->setCurrentIndex(existingIndex);
|
|
||||||
onSavePathChanged(newPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddNewTorrentDialog::saveTorrentFile()
|
void AddNewTorrentDialog::saveTorrentFile()
|
||||||
{
|
{
|
||||||
Q_ASSERT(hasMetadata());
|
Q_ASSERT(hasMetadata());
|
||||||
|
@ -555,22 +583,78 @@ bool AddNewTorrentDialog::hasMetadata() const
|
||||||
return m_torrentInfo.isValid();
|
return m_torrentInfo.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::populateSavePathComboBox()
|
void AddNewTorrentDialog::populateSavePaths()
|
||||||
{
|
{
|
||||||
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
|
|
||||||
|
m_ui->savePath->blockSignals(true);
|
||||||
m_ui->savePath->clear();
|
m_ui->savePath->clear();
|
||||||
|
const auto savePathHistory = settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY);
|
||||||
|
if (savePathHistory.size() > 0)
|
||||||
|
{
|
||||||
|
for (const QString &path : savePathHistory)
|
||||||
|
m_ui->savePath->addItem(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->savePath->addItem(btSession->savePath());
|
||||||
|
}
|
||||||
|
|
||||||
// Load save path history
|
if (m_savePathIndex >= 0)
|
||||||
const auto savePathHistory {settings()->loadValue<QStringList>(KEY_SAVEPATHHISTORY)};
|
{
|
||||||
for (const QString &savePath : savePathHistory)
|
m_ui->savePath->setCurrentIndex(std::min(m_savePathIndex, (m_ui->savePath->count() - 1)));
|
||||||
m_ui->savePath->addItem(savePath);
|
}
|
||||||
|
else
|
||||||
const QString defSavePath {BitTorrent::Session::instance()->defaultSavePath()};
|
{
|
||||||
|
|
||||||
if (!m_torrentParams.savePath.isEmpty())
|
if (!m_torrentParams.savePath.isEmpty())
|
||||||
setSavePath(m_torrentParams.savePath);
|
setPath(m_ui->savePath, m_torrentParams.savePath);
|
||||||
else if (!m_storeRememberLastSavePath)
|
else if (!m_storeRememberLastSavePath)
|
||||||
setSavePath(defSavePath);
|
setPath(m_ui->savePath, btSession->savePath());
|
||||||
// else last used save path will be selected since it is the first in the list
|
else
|
||||||
|
m_ui->savePath->setCurrentIndex(0);
|
||||||
|
|
||||||
|
m_savePathIndex = m_ui->savePath->currentIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->savePath->blockSignals(false);
|
||||||
|
|
||||||
|
m_ui->downloadPath->blockSignals(true);
|
||||||
|
m_ui->downloadPath->clear();
|
||||||
|
const auto downloadPathHistory = settings()->loadValue<QStringList>(KEY_DOWNLOADPATHHISTORY);
|
||||||
|
if (downloadPathHistory.size() > 0)
|
||||||
|
{
|
||||||
|
for (const QString &path : downloadPathHistory)
|
||||||
|
m_ui->downloadPath->addItem(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->downloadPath->addItem(btSession->downloadPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_downloadPathIndex >= 0)
|
||||||
|
{
|
||||||
|
m_ui->downloadPath->setCurrentIndex(m_useDownloadPath ? std::min(m_downloadPathIndex, (m_ui->downloadPath->count() - 1)) : -1);
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(m_useDownloadPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const bool useDownloadPath = m_torrentParams.useDownloadPath.value_or(btSession->isDownloadPathEnabled());
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(useDownloadPath);
|
||||||
|
|
||||||
|
if (!m_torrentParams.downloadPath.isEmpty())
|
||||||
|
setPath(m_ui->downloadPath, m_torrentParams.downloadPath);
|
||||||
|
else if (!m_storeRememberLastSavePath)
|
||||||
|
setPath(m_ui->downloadPath, btSession->downloadPath());
|
||||||
|
else
|
||||||
|
m_ui->downloadPath->setCurrentIndex(0);
|
||||||
|
|
||||||
|
m_downloadPathIndex = m_ui->downloadPath->currentIndex();
|
||||||
|
if (!useDownloadPath)
|
||||||
|
m_ui->downloadPath->setCurrentIndex(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->downloadPath->blockSignals(false);
|
||||||
|
m_ui->groupBoxDownloadPath->blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
|
||||||
|
@ -694,16 +778,21 @@ void AddNewTorrentDialog::accept()
|
||||||
m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked();
|
m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked();
|
||||||
m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
|
m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked();
|
||||||
|
|
||||||
QString savePath = m_ui->savePath->selectedPath();
|
const bool useAutoTMM = (m_ui->comboTTM->currentIndex() == 1); // 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||||
if (m_ui->comboTTM->currentIndex() != 1)
|
m_torrentParams.useAutoTMM = useAutoTMM;
|
||||||
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
if (!useAutoTMM)
|
||||||
m_torrentParams.useAutoTMM = false;
|
|
||||||
m_torrentParams.savePath = savePath;
|
|
||||||
saveSavePathHistory();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
m_torrentParams.useAutoTMM = true;
|
const QString savePath = m_ui->savePath->selectedPath();
|
||||||
|
m_torrentParams.savePath = savePath;
|
||||||
|
updatePathHistory(KEY_SAVEPATHHISTORY, savePath, savePathHistoryLength());
|
||||||
|
|
||||||
|
m_torrentParams.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
||||||
|
if (m_torrentParams.useDownloadPath)
|
||||||
|
{
|
||||||
|
const QString downloadPath = m_ui->downloadPath->selectedPath();
|
||||||
|
m_torrentParams.downloadPath = downloadPath;
|
||||||
|
updatePathHistory(KEY_DOWNLOADPATHHISTORY, downloadPath, savePathHistoryLength());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
|
setEnabled(!m_ui->checkBoxNeverShow->isChecked());
|
||||||
|
@ -855,18 +944,28 @@ void AddNewTorrentDialog::TMMChanged(int index)
|
||||||
{
|
{
|
||||||
if (index != 1)
|
if (index != 1)
|
||||||
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||||
populateSavePathComboBox();
|
populateSavePaths();
|
||||||
m_ui->groupBoxSavePath->setEnabled(true);
|
m_ui->groupBoxSavePath->setEnabled(true);
|
||||||
m_ui->savePath->blockSignals(false);
|
|
||||||
m_ui->savePath->setCurrentIndex(m_oldIndex < m_ui->savePath->count() ? m_oldIndex : m_ui->savePath->count() - 1);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
const auto *session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
m_ui->groupBoxSavePath->setEnabled(false);
|
m_ui->groupBoxSavePath->setEnabled(false);
|
||||||
|
|
||||||
m_ui->savePath->blockSignals(true);
|
m_ui->savePath->blockSignals(true);
|
||||||
m_ui->savePath->clear();
|
m_ui->savePath->clear();
|
||||||
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
|
const QString savePath = session->categorySavePath(m_ui->categoryComboBox->currentText());
|
||||||
m_ui->savePath->addItem(savePath);
|
m_ui->savePath->addItem(savePath);
|
||||||
|
|
||||||
|
m_ui->downloadPath->blockSignals(true);
|
||||||
|
m_ui->downloadPath->clear();
|
||||||
|
const QString downloadPath = session->categoryDownloadPath(m_ui->categoryComboBox->currentText());
|
||||||
|
m_ui->downloadPath->addItem(downloadPath);
|
||||||
|
|
||||||
|
m_ui->groupBoxDownloadPath->blockSignals(true);
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(!downloadPath.isEmpty());
|
||||||
|
|
||||||
updateDiskSpaceLabel();
|
updateDiskSpaceLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,8 @@ private slots:
|
||||||
void displayContentTreeMenu(const QPoint &);
|
void displayContentTreeMenu(const QPoint &);
|
||||||
void updateDiskSpaceLabel();
|
void updateDiskSpaceLabel();
|
||||||
void onSavePathChanged(const QString &newPath);
|
void onSavePathChanged(const QString &newPath);
|
||||||
|
void onDownloadPathChanged(const QString &newPath);
|
||||||
|
void onUseDownloadPathChanged(bool checked);
|
||||||
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
|
void updateMetadata(const BitTorrent::TorrentInfo &metadata);
|
||||||
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
|
void handleDownloadFinished(const Net::DownloadResult &downloadResult);
|
||||||
void TMMChanged(int index);
|
void TMMChanged(int index);
|
||||||
|
@ -97,14 +99,11 @@ private:
|
||||||
bool loadTorrentFile(const QString &torrentPath);
|
bool loadTorrentFile(const QString &torrentPath);
|
||||||
bool loadTorrentImpl();
|
bool loadTorrentImpl();
|
||||||
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
|
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
|
||||||
void populateSavePathComboBox();
|
void populateSavePaths();
|
||||||
void saveSavePathHistory() const;
|
|
||||||
int indexOfSavePath(const QString &savePath);
|
|
||||||
void loadState();
|
void loadState();
|
||||||
void saveState();
|
void saveState();
|
||||||
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
|
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
|
||||||
void setupTreeview();
|
void setupTreeview();
|
||||||
void setSavePath(const QString &newPath);
|
|
||||||
void saveTorrentFile();
|
void saveTorrentFile();
|
||||||
bool hasMetadata() const;
|
bool hasMetadata() const;
|
||||||
|
|
||||||
|
@ -115,7 +114,9 @@ private:
|
||||||
PropListDelegate *m_contentDelegate = nullptr;
|
PropListDelegate *m_contentDelegate = nullptr;
|
||||||
BitTorrent::MagnetUri m_magnetURI;
|
BitTorrent::MagnetUri m_magnetURI;
|
||||||
BitTorrent::TorrentInfo m_torrentInfo;
|
BitTorrent::TorrentInfo m_torrentInfo;
|
||||||
int m_oldIndex = 0;
|
int m_savePathIndex = -1;
|
||||||
|
int m_downloadPathIndex = -1;
|
||||||
|
bool m_useDownloadPath = false;
|
||||||
std::unique_ptr<TorrentFileGuard> m_torrentGuard;
|
std::unique_ptr<TorrentFileGuard> m_torrentGuard;
|
||||||
BitTorrent::AddTorrentParams m_torrentParams;
|
BitTorrent::AddTorrentParams m_torrentParams;
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,40 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
|
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
<item alignment="Qt::AlignRight">
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBoxDownloadPath">
|
||||||
|
<property name="title">
|
||||||
|
<string>Use another path for incomplete torrent</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="FileSystemPathComboEdit" name="downloadPath" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="layoutRememberLastSavePath">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
|
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Remember last used save path</string>
|
<string>Remember last used save path</string>
|
||||||
|
@ -91,6 +124,8 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -404,40 +404,33 @@ void CategoryFilterModel::populate()
|
||||||
|
|
||||||
// Uncategorized torrents
|
// Uncategorized torrents
|
||||||
using Torrent = BitTorrent::Torrent;
|
using Torrent = BitTorrent::Torrent;
|
||||||
m_rootItem->addChild(
|
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
|
||||||
UID_UNCATEGORIZED
|
, [](Torrent *torrent) { return torrent->category().isEmpty(); });
|
||||||
, new CategoryModelItem(
|
m_rootItem->addChild(UID_UNCATEGORIZED, new CategoryModelItem(nullptr, tr("Uncategorized"), torrentsCount));
|
||||||
nullptr, tr("Uncategorized")
|
|
||||||
, std::count_if(torrents.begin(), torrents.end()
|
|
||||||
, [](Torrent *torrent) { return torrent->category().isEmpty(); })));
|
|
||||||
|
|
||||||
using Torrent = BitTorrent::Torrent;
|
using BitTorrent::Torrent;
|
||||||
const QStringMap categories = session->categories();
|
for (const QString &categoryName : asConst(session->categories()))
|
||||||
for (auto i = categories.cbegin(); i != categories.cend(); ++i)
|
|
||||||
{
|
{
|
||||||
const QString &category = i.key();
|
|
||||||
if (m_isSubcategoriesEnabled)
|
if (m_isSubcategoriesEnabled)
|
||||||
{
|
{
|
||||||
CategoryModelItem *parent = m_rootItem;
|
CategoryModelItem *parent = m_rootItem;
|
||||||
for (const QString &subcat : asConst(session->expandCategory(category)))
|
for (const QString &subcat : asConst(session->expandCategory(categoryName)))
|
||||||
{
|
{
|
||||||
const QString subcatName = shortName(subcat);
|
const QString subcatName = shortName(subcat);
|
||||||
if (!parent->hasChild(subcatName))
|
if (!parent->hasChild(subcatName))
|
||||||
{
|
{
|
||||||
new CategoryModelItem(
|
const int torrentsCount = std::count_if(torrents.cbegin(), torrents.cend()
|
||||||
parent, subcatName
|
, [subcat](Torrent *torrent) { return torrent->category() == subcat; });
|
||||||
, std::count_if(torrents.cbegin(), torrents.cend()
|
new CategoryModelItem(parent, subcatName, torrentsCount);
|
||||||
, [subcat](Torrent *torrent) { return torrent->category() == subcat; }));
|
|
||||||
}
|
}
|
||||||
parent = parent->child(subcatName);
|
parent = parent->child(subcatName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
new CategoryModelItem(
|
const int torrentsCount = std::count_if(torrents.begin(), torrents.end()
|
||||||
m_rootItem, category
|
, [categoryName](Torrent *torrent) { return torrent->belongsToCategory(categoryName); });
|
||||||
, std::count_if(torrents.begin(), torrents.end()
|
new CategoryModelItem(m_rootItem, categoryName, torrentsCount);
|
||||||
, [category](Torrent *torrent) { return torrent->belongsToCategory(category); }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ void CategoryFilterWidget::removeCategory()
|
||||||
void CategoryFilterWidget::removeUnusedCategories()
|
void CategoryFilterWidget::removeUnusedCategories()
|
||||||
{
|
{
|
||||||
auto session = BitTorrent::Session::instance();
|
auto session = BitTorrent::Session::instance();
|
||||||
for (const QString &category : asConst(session->categories().keys()))
|
for (const QString &category : asConst(session->categories()))
|
||||||
{
|
{
|
||||||
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
|
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
|
||||||
session->removeCategory(category);
|
session->removeCategory(category);
|
||||||
|
|
|
@ -247,6 +247,18 @@ void FileSystemPathEdit::setFileNameFilter(const QString &val)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FileSystemPathEdit::placeholder() const
|
||||||
|
{
|
||||||
|
Q_D(const FileSystemPathEdit);
|
||||||
|
return d->m_editor->placeholder();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemPathEdit::setPlaceholder(const QString &val)
|
||||||
|
{
|
||||||
|
Q_D(FileSystemPathEdit);
|
||||||
|
d->m_editor->setPlaceholder(val);
|
||||||
|
}
|
||||||
|
|
||||||
bool FileSystemPathEdit::briefBrowseButtonCaption() const
|
bool FileSystemPathEdit::briefBrowseButtonCaption() const
|
||||||
{
|
{
|
||||||
Q_D(const FileSystemPathEdit);
|
Q_D(const FileSystemPathEdit);
|
||||||
|
|
|
@ -71,6 +71,9 @@ public:
|
||||||
QString fileNameFilter() const;
|
QString fileNameFilter() const;
|
||||||
void setFileNameFilter(const QString &val);
|
void setFileNameFilter(const QString &val);
|
||||||
|
|
||||||
|
QString placeholder() const;
|
||||||
|
void setPlaceholder(const QString &val);
|
||||||
|
|
||||||
/// The browse button caption is "..." if true, and "Browse" otherwise
|
/// The browse button caption is "..." if true, and "Browse" otherwise
|
||||||
bool briefBrowseButtonCaption() const;
|
bool briefBrowseButtonCaption() const;
|
||||||
void setBriefBrowseButtonCaption(bool brief);
|
void setBriefBrowseButtonCaption(bool brief);
|
||||||
|
|
|
@ -240,6 +240,16 @@ void Private::FileLineEdit::setValidator(QValidator *validator)
|
||||||
QLineEdit::setValidator(validator);
|
QLineEdit::setValidator(validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Private::FileLineEdit::placeholder() const
|
||||||
|
{
|
||||||
|
return placeholderText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Private::FileLineEdit::setPlaceholder(const QString &val)
|
||||||
|
{
|
||||||
|
setPlaceholderText(val);
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *Private::FileLineEdit::widget()
|
QWidget *Private::FileLineEdit::widget()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
|
@ -346,6 +356,16 @@ void Private::FileComboEdit::setValidator(QValidator *validator)
|
||||||
lineEdit()->setValidator(validator);
|
lineEdit()->setValidator(validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Private::FileComboEdit::placeholder() const
|
||||||
|
{
|
||||||
|
return lineEdit()->placeholderText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Private::FileComboEdit::setPlaceholder(const QString &val)
|
||||||
|
{
|
||||||
|
lineEdit()->setPlaceholderText(val);
|
||||||
|
}
|
||||||
|
|
||||||
void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
|
void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
|
||||||
{
|
{
|
||||||
static_cast<FileLineEdit *>(lineEdit())->setFilenameFilters(filters);
|
static_cast<FileLineEdit *>(lineEdit())->setFilenameFilters(filters);
|
||||||
|
|
|
@ -105,6 +105,8 @@ namespace Private
|
||||||
virtual void setFilenameFilters(const QStringList &filters) = 0;
|
virtual void setFilenameFilters(const QStringList &filters) = 0;
|
||||||
virtual void setBrowseAction(QAction *action) = 0;
|
virtual void setBrowseAction(QAction *action) = 0;
|
||||||
virtual void setValidator(QValidator *validator) = 0;
|
virtual void setValidator(QValidator *validator) = 0;
|
||||||
|
virtual QString placeholder() const = 0;
|
||||||
|
virtual void setPlaceholder(const QString &val) = 0;
|
||||||
virtual QWidget *widget() = 0;
|
virtual QWidget *widget() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,6 +123,8 @@ namespace Private
|
||||||
void setFilenameFilters(const QStringList &filters) override;
|
void setFilenameFilters(const QStringList &filters) override;
|
||||||
void setBrowseAction(QAction *action) override;
|
void setBrowseAction(QAction *action) override;
|
||||||
void setValidator(QValidator *validator) override;
|
void setValidator(QValidator *validator) override;
|
||||||
|
QString placeholder() const override;
|
||||||
|
void setPlaceholder(const QString &val) override;
|
||||||
QWidget *widget() override;
|
QWidget *widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -149,6 +153,8 @@ namespace Private
|
||||||
void setFilenameFilters(const QStringList &filters) override;
|
void setFilenameFilters(const QStringList &filters) override;
|
||||||
void setBrowseAction(QAction *action) override;
|
void setBrowseAction(QAction *action) override;
|
||||||
void setValidator(QValidator *validator) override;
|
void setValidator(QValidator *validator) override;
|
||||||
|
QString placeholder() const override;
|
||||||
|
void setPlaceholder(const QString &val) override;
|
||||||
QWidget *widget() override;
|
QWidget *widget() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -354,7 +354,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||||
connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->comboTorrentCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->comboCategoryDefaultPathChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->comboCategoryChanged, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->textTempPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textDownloadPath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkAppendqB, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkPreallocateAll, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkRecursiveDownload, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
|
@ -372,8 +372,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||||
connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->textExportDirFin, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->actionTorrentDlOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
connect(m_ui->actionTorrentFnOnDblClBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->checkTempFolder, &QAbstractButton::toggled, m_ui->textTempPath, &QWidget::setEnabled);
|
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled);
|
||||||
connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
|
||||||
connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
|
||||||
|
@ -559,8 +559,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||||
m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
|
m_ui->textSavePath->setDialogCaption(tr("Choose a save directory"));
|
||||||
m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
m_ui->textSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
|
||||||
m_ui->textTempPath->setDialogCaption(tr("Choose a save directory"));
|
m_ui->textDownloadPath->setDialogCaption(tr("Choose a save directory"));
|
||||||
m_ui->textTempPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
m_ui->textDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
|
||||||
// disable mouse wheel event on widgets to avoid mis-selection
|
// disable mouse wheel event on widgets to avoid mis-selection
|
||||||
auto *wheelEventEater = new WheelEventEater(this);
|
auto *wheelEventEater = new WheelEventEater(this);
|
||||||
|
@ -731,14 +731,14 @@ void OptionsDialog::saveOptions()
|
||||||
auto session = BitTorrent::Session::instance();
|
auto session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
// Downloads preferences
|
// Downloads preferences
|
||||||
session->setDefaultSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
|
session->setSavePath(Utils::Fs::expandPathAbs(m_ui->textSavePath->selectedPath()));
|
||||||
session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
|
session->setSubcategoriesEnabled(m_ui->checkUseSubcategories->isChecked());
|
||||||
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
|
session->setAutoTMMDisabledByDefault(m_ui->comboSavingMode->currentIndex() == 0);
|
||||||
session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1);
|
session->setDisableAutoTMMWhenCategoryChanged(m_ui->comboTorrentCategoryChanged->currentIndex() == 1);
|
||||||
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
session->setDisableAutoTMMWhenCategorySavePathChanged(m_ui->comboCategoryChanged->currentIndex() == 1);
|
||||||
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
session->setDisableAutoTMMWhenDefaultSavePathChanged(m_ui->comboCategoryDefaultPathChanged->currentIndex() == 1);
|
||||||
session->setTempPathEnabled(m_ui->checkTempFolder->isChecked());
|
session->setDownloadPathEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||||
session->setTempPath(Utils::Fs::expandPathAbs(m_ui->textTempPath->selectedPath()));
|
session->setDownloadPath(Utils::Fs::expandPathAbs(m_ui->textDownloadPath->selectedPath()));
|
||||||
session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
|
session->setAppendExtensionEnabled(m_ui->checkAppendqB->isChecked());
|
||||||
session->setPreallocationEnabled(preAllocateAllFiles());
|
session->setPreallocationEnabled(preAllocateAllFiles());
|
||||||
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
|
pref->disableRecursiveDownload(!m_ui->checkRecursiveDownload->isChecked());
|
||||||
|
@ -997,16 +997,16 @@ void OptionsDialog::loadOptions()
|
||||||
m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
|
m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
|
||||||
m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
|
m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
|
||||||
|
|
||||||
m_ui->textSavePath->setSelectedPath(session->defaultSavePath());
|
m_ui->textSavePath->setSelectedPath(session->savePath());
|
||||||
m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
|
m_ui->checkUseSubcategories->setChecked(session->isSubcategoriesEnabled());
|
||||||
m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
|
m_ui->comboSavingMode->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
|
||||||
m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged());
|
m_ui->comboTorrentCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategoryChanged());
|
||||||
m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
|
m_ui->comboCategoryChanged->setCurrentIndex(session->isDisableAutoTMMWhenCategorySavePathChanged());
|
||||||
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
m_ui->comboCategoryDefaultPathChanged->setCurrentIndex(session->isDisableAutoTMMWhenDefaultSavePathChanged());
|
||||||
m_ui->checkTempFolder->setChecked(session->isTempPathEnabled());
|
m_ui->checkUseDownloadPath->setChecked(session->isDownloadPathEnabled());
|
||||||
m_ui->textTempPath->setEnabled(m_ui->checkTempFolder->isChecked());
|
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||||
m_ui->textTempPath->setEnabled(m_ui->checkTempFolder->isChecked());
|
m_ui->textDownloadPath->setEnabled(m_ui->checkUseDownloadPath->isChecked());
|
||||||
m_ui->textTempPath->setSelectedPath(Utils::Fs::toNativePath(session->tempPath()));
|
m_ui->textDownloadPath->setSelectedPath(Utils::Fs::toNativePath(session->downloadPath()));
|
||||||
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
|
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
|
||||||
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
|
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
|
||||||
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
|
||||||
|
|
|
@ -1105,16 +1105,6 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="FileSystemPathLineEdit" name="textTempPath" native="true"/>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="checkExportDirFin">
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy .torrent files for finished downloads to:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="labelSavePath">
|
<widget class="QLabel" name="labelSavePath">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -1122,6 +1112,19 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="FileSystemPathLineEdit" name="textSavePath" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="checkUseDownloadPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use another path for incomplete torrents:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="FileSystemPathLineEdit" name="textDownloadPath" native="true"/>
|
||||||
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="checkExportDir">
|
<widget class="QCheckBox" name="checkExportDir">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -1129,21 +1132,18 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="checkTempFolder">
|
<widget class="FileSystemPathLineEdit" name="textExportDir" native="true"/>
|
||||||
<property name="text">
|
|
||||||
<string>Keep incomplete torrents in:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="FileSystemPathLineEdit" name="textExportDirFin" native="true"/>
|
<widget class="FileSystemPathLineEdit" name="textExportDirFin" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="3" column="0">
|
||||||
<widget class="FileSystemPathLineEdit" name="textSavePath" native="true"/>
|
<widget class="QCheckBox" name="checkExportDirFin">
|
||||||
</item>
|
<property name="text">
|
||||||
<item row="2" column="1">
|
<string>Copy .torrent files for finished downloads to:</string>
|
||||||
<widget class="FileSystemPathLineEdit" name="textExportDir" native="true"/>
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
@ -3490,8 +3490,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
|
||||||
<tabstop>checkAdditionDialog</tabstop>
|
<tabstop>checkAdditionDialog</tabstop>
|
||||||
<tabstop>checkAdditionDialogFront</tabstop>
|
<tabstop>checkAdditionDialogFront</tabstop>
|
||||||
<tabstop>checkPreallocateAll</tabstop>
|
<tabstop>checkPreallocateAll</tabstop>
|
||||||
<tabstop>checkTempFolder</tabstop>
|
<tabstop>checkUseDownloadPath</tabstop>
|
||||||
<tabstop>textTempPath</tabstop>
|
<tabstop>textDownloadPath</tabstop>
|
||||||
<tabstop>checkAppendqB</tabstop>
|
<tabstop>checkAppendqB</tabstop>
|
||||||
<tabstop>scanFoldersView</tabstop>
|
<tabstop>scanFoldersView</tabstop>
|
||||||
<tabstop>addWatchedFolderButton</tabstop>
|
<tabstop>addWatchedFolderButton</tabstop>
|
||||||
|
|
|
@ -581,11 +581,12 @@ void PropertiesWidget::loadUrlSeeds()
|
||||||
|
|
||||||
QString PropertiesWidget::getFullPath(const QModelIndex &index) const
|
QString PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
|
const QDir saveDir {m_torrent->actualStorageLocation()};
|
||||||
|
|
||||||
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
|
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
|
||||||
{
|
{
|
||||||
const int fileIdx = m_propListModel->getFileIndex(index);
|
const int fileIdx = m_propListModel->getFileIndex(index);
|
||||||
const QString filename {m_torrent->filePath(fileIdx)};
|
const QString filename {m_torrent->filePath(fileIdx)};
|
||||||
const QDir saveDir {m_torrent->savePath(true)};
|
|
||||||
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(filename))};
|
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(filename))};
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
@ -596,7 +597,6 @@ QString PropertiesWidget::getFullPath(const QModelIndex &index) const
|
||||||
for (QModelIndex modelIdx = m_propListModel->parent(nameIndex); modelIdx.isValid(); modelIdx = modelIdx.parent())
|
for (QModelIndex modelIdx = m_propListModel->parent(nameIndex); modelIdx.isValid(); modelIdx = modelIdx.parent())
|
||||||
folderPath.prepend(modelIdx.data().toString() + '/');
|
folderPath.prepend(modelIdx.data().toString() + '/');
|
||||||
|
|
||||||
const QDir saveDir {m_torrent->savePath(true)};
|
|
||||||
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(folderPath))};
|
const QString fullPath {Utils::Fs::expandPath(saveDir.absoluteFilePath(folderPath))};
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,7 +330,7 @@ void AutomatedRssDownloader::clearRuleDefinitionBox()
|
||||||
void AutomatedRssDownloader::initCategoryCombobox()
|
void AutomatedRssDownloader::initCategoryCombobox()
|
||||||
{
|
{
|
||||||
// Load torrent categories
|
// Load torrent categories
|
||||||
QStringList categories = BitTorrent::Session::instance()->categories().keys();
|
QStringList categories = BitTorrent::Session::instance()->categories();
|
||||||
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||||
m_ui->comboCategory->addItem("");
|
m_ui->comboCategory->addItem("");
|
||||||
m_ui->comboCategory->addItems(categories);
|
m_ui->comboCategory->addItems(categories);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
#include "ui_torrentcategorydialog.h"
|
#include "ui_torrentcategorydialog.h"
|
||||||
|
|
||||||
TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
|
TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
|
||||||
|
@ -39,15 +40,20 @@ TorrentCategoryDialog::TorrentCategoryDialog(QWidget *parent)
|
||||||
, m_ui {new Ui::TorrentCategoryDialog}
|
, m_ui {new Ui::TorrentCategoryDialog}
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
m_ui->comboSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
m_ui->comboSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
m_ui->comboSavePath->setDialogCaption(tr("Choose save path"));
|
m_ui->comboSavePath->setDialogCaption(tr("Choose save path"));
|
||||||
|
|
||||||
|
m_ui->comboDownloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
m_ui->comboDownloadPath->setDialogCaption(tr("Choose download path"));
|
||||||
|
m_ui->comboDownloadPath->setEnabled(false);
|
||||||
|
m_ui->labelDownloadPath->setEnabled(false);
|
||||||
|
|
||||||
// disable save button
|
// disable save button
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||||
connect(m_ui->textCategoryName, &QLineEdit::textChanged, this, [this](const QString &text)
|
|
||||||
{
|
connect(m_ui->textCategoryName, &QLineEdit::textChanged, this, &TorrentCategoryDialog::categoryNameChanged);
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
|
connect(m_ui->comboUseDownloadPath, &QComboBox::currentIndexChanged, this, &TorrentCategoryDialog::useDownloadPathChanged);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentCategoryDialog::~TorrentCategoryDialog()
|
TorrentCategoryDialog::~TorrentCategoryDialog()
|
||||||
|
@ -57,20 +63,21 @@ TorrentCategoryDialog::~TorrentCategoryDialog()
|
||||||
|
|
||||||
QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &parentCategoryName)
|
QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &parentCategoryName)
|
||||||
{
|
{
|
||||||
|
using BitTorrent::CategoryOptions;
|
||||||
using BitTorrent::Session;
|
using BitTorrent::Session;
|
||||||
|
|
||||||
QString newCategoryName {parentCategoryName};
|
QString newCategoryName = parentCategoryName;
|
||||||
if (!newCategoryName.isEmpty())
|
if (!newCategoryName.isEmpty())
|
||||||
newCategoryName += QLatin1Char('/');
|
newCategoryName += QLatin1Char('/');
|
||||||
newCategoryName += tr("New Category");
|
newCategoryName += tr("New Category");
|
||||||
|
|
||||||
TorrentCategoryDialog dialog(parent);
|
TorrentCategoryDialog dialog {parent};
|
||||||
dialog.setCategoryName(newCategoryName);
|
dialog.setCategoryName(newCategoryName);
|
||||||
while (dialog.exec() == TorrentCategoryDialog::Accepted)
|
while (dialog.exec() == TorrentCategoryDialog::Accepted)
|
||||||
{
|
{
|
||||||
newCategoryName = dialog.categoryName();
|
newCategoryName = dialog.categoryName();
|
||||||
|
|
||||||
if (!BitTorrent::Session::isValidCategoryName(newCategoryName))
|
if (!Session::isValidCategoryName(newCategoryName))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
parent, tr("Invalid category name")
|
parent, tr("Invalid category name")
|
||||||
|
@ -78,7 +85,7 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
|
||||||
"Category name cannot start/end with '/'.\n"
|
"Category name cannot start/end with '/'.\n"
|
||||||
"Category name cannot contain '//' sequence."));
|
"Category name cannot contain '//' sequence."));
|
||||||
}
|
}
|
||||||
else if (BitTorrent::Session::instance()->categories().contains(newCategoryName))
|
else if (Session::instance()->categories().contains(newCategoryName))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
parent, tr("Category creation error")
|
parent, tr("Category creation error")
|
||||||
|
@ -87,7 +94,7 @@ QString TorrentCategoryDialog::createCategory(QWidget *parent, const QString &pa
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Session::instance()->addCategory(newCategoryName, dialog.savePath());
|
Session::instance()->addCategory(newCategoryName, dialog.categoryOptions());
|
||||||
return newCategoryName;
|
return newCategoryName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,10 +112,10 @@ void TorrentCategoryDialog::editCategory(QWidget *parent, const QString &categor
|
||||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
dialog->setCategoryNameEditable(false);
|
dialog->setCategoryNameEditable(false);
|
||||||
dialog->setCategoryName(categoryName);
|
dialog->setCategoryName(categoryName);
|
||||||
dialog->setSavePath(Session::instance()->categories()[categoryName]);
|
dialog->setCategoryOptions(Session::instance()->categoryOptions(categoryName));
|
||||||
connect(dialog, &TorrentCategoryDialog::accepted, parent, [dialog, categoryName]()
|
connect(dialog, &TorrentCategoryDialog::accepted, parent, [dialog, categoryName]()
|
||||||
{
|
{
|
||||||
Session::instance()->editCategory(categoryName, dialog->savePath());
|
Session::instance()->editCategory(categoryName, dialog->categoryOptions());
|
||||||
});
|
});
|
||||||
dialog->open();
|
dialog->open();
|
||||||
}
|
}
|
||||||
|
@ -128,12 +135,59 @@ void TorrentCategoryDialog::setCategoryName(const QString &categoryName)
|
||||||
m_ui->textCategoryName->setText(categoryName);
|
m_ui->textCategoryName->setText(categoryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentCategoryDialog::savePath() const
|
BitTorrent::CategoryOptions TorrentCategoryDialog::categoryOptions() const
|
||||||
{
|
{
|
||||||
return m_ui->comboSavePath->selectedPath();
|
BitTorrent::CategoryOptions categoryOptions;
|
||||||
|
categoryOptions.savePath = m_ui->comboSavePath->selectedPath();
|
||||||
|
if (m_ui->comboUseDownloadPath->currentIndex() == 1)
|
||||||
|
categoryOptions.downloadPath = {true, m_ui->comboDownloadPath->selectedPath()};
|
||||||
|
else if (m_ui->comboUseDownloadPath->currentIndex() == 2)
|
||||||
|
categoryOptions.downloadPath = {false};
|
||||||
|
|
||||||
|
return categoryOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentCategoryDialog::setSavePath(const QString &savePath)
|
void TorrentCategoryDialog::setCategoryOptions(const BitTorrent::CategoryOptions &categoryOptions)
|
||||||
{
|
{
|
||||||
m_ui->comboSavePath->setSelectedPath(savePath);
|
m_ui->comboSavePath->setSelectedPath(categoryOptions.savePath);
|
||||||
|
if (categoryOptions.downloadPath)
|
||||||
|
{
|
||||||
|
m_ui->comboUseDownloadPath->setCurrentIndex(categoryOptions.downloadPath->enabled ? 1 : 2);
|
||||||
|
m_ui->comboDownloadPath->setSelectedPath(categoryOptions.downloadPath->enabled ? categoryOptions.downloadPath->path : QString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->comboUseDownloadPath->setCurrentIndex(0);
|
||||||
|
m_ui->comboDownloadPath->setSelectedPath({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCategoryDialog::categoryNameChanged(const QString &categoryName)
|
||||||
|
{
|
||||||
|
const QString categoryPath = Utils::Fs::toValidFileSystemName(categoryName, true);
|
||||||
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
|
m_ui->comboSavePath->setPlaceholder(Utils::Fs::resolvePath(categoryPath, btSession->savePath()));
|
||||||
|
|
||||||
|
const int index = m_ui->comboUseDownloadPath->currentIndex();
|
||||||
|
const bool useDownloadPath = (index == 1) || ((index == 0) && BitTorrent::Session::instance()->isDownloadPathEnabled());
|
||||||
|
if (useDownloadPath)
|
||||||
|
m_ui->comboDownloadPath->setPlaceholder(Utils::Fs::resolvePath(categoryPath, btSession->downloadPath()));
|
||||||
|
|
||||||
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!categoryName.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCategoryDialog::useDownloadPathChanged(const int index)
|
||||||
|
{
|
||||||
|
if (const QString selectedPath = m_ui->comboDownloadPath->selectedPath(); !selectedPath.isEmpty())
|
||||||
|
m_lastEnteredDownloadPath = selectedPath;
|
||||||
|
|
||||||
|
m_ui->labelDownloadPath->setEnabled(index == 1);
|
||||||
|
m_ui->comboDownloadPath->setEnabled(index == 1);
|
||||||
|
m_ui->comboDownloadPath->setSelectedPath((index == 1) ? m_lastEnteredDownloadPath : QString());
|
||||||
|
|
||||||
|
const QString categoryName = m_ui->textCategoryName->text();
|
||||||
|
const QString categoryPath = Utils::Fs::resolvePath(Utils::Fs::toValidFileSystemName(categoryName, true)
|
||||||
|
, BitTorrent::Session::instance()->downloadPath());
|
||||||
|
const bool useDownloadPath = (index == 1) || ((index == 0) && BitTorrent::Session::instance()->isDownloadPathEnabled());
|
||||||
|
m_ui->comboDownloadPath->setPlaceholder(useDownloadPath ? categoryPath : QString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2017, 2021 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -30,6 +30,11 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct CategoryOptions;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class TorrentCategoryDialog;
|
class TorrentCategoryDialog;
|
||||||
|
@ -50,9 +55,14 @@ public:
|
||||||
void setCategoryNameEditable(bool editable);
|
void setCategoryNameEditable(bool editable);
|
||||||
QString categoryName() const;
|
QString categoryName() const;
|
||||||
void setCategoryName(const QString &categoryName);
|
void setCategoryName(const QString &categoryName);
|
||||||
QString savePath() const;
|
void setCategoryOptions(const BitTorrent::CategoryOptions &categoryOptions);
|
||||||
void setSavePath(const QString &savePath);
|
BitTorrent::CategoryOptions categoryOptions() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void categoryNameChanged(const QString &categoryName);
|
||||||
|
void useDownloadPathChanged(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::TorrentCategoryDialog *m_ui;
|
Ui::TorrentCategoryDialog *m_ui;
|
||||||
|
QString m_lastEnteredDownloadPath;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>493</width>
|
||||||
<height>100</height>
|
<height>208</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -29,10 +29,13 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="labelCategoryName">
|
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
|
||||||
<property name="text">
|
<property name="sizePolicy">
|
||||||
<string>Name:</string>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -46,10 +49,86 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="0" column="0">
|
||||||
<widget class="FileSystemPathComboEdit" name="comboSavePath" native="true">
|
<widget class="QLabel" name="labelCategoryName">
|
||||||
|
<property name="text">
|
||||||
|
<string>Name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Save path for incomplete torrents:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelUseDownloadPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use another path for incomplete torrents:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboUseDownloadPath">
|
||||||
|
<property name="maxVisibleItems">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Default</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Yes</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>No</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelDownloadPath">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Path:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="FileSystemPathComboEdit" name="comboDownloadPath" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
|
@ -58,8 +137,11 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -65,19 +65,33 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
, m_storeDialogSize {SETTINGS_KEY("Size")}
|
, m_storeDialogSize {SETTINGS_KEY("Size")}
|
||||||
, m_currentCategoriesString {QString::fromLatin1("--%1--").arg(tr("Currently used categories"))}
|
, m_currentCategoriesString {QString::fromLatin1("--%1--").arg(tr("Currently used categories"))}
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(!torrents.empty());
|
||||||
|
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
||||||
Q_ASSERT(!torrents.empty());
|
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
|
||||||
|
|
||||||
const auto *session = BitTorrent::Session::instance();
|
const auto *session = BitTorrent::Session::instance();
|
||||||
bool allSameUpLimit = true, allSameDownLimit = true, allSameRatio = true, allSameSeedingTime = true
|
bool allSameUpLimit = true;
|
||||||
, allTorrentsArePrivate = true, allSameDHT = true, allSamePEX = true, allSameLSD = true
|
bool allSameDownLimit = true;
|
||||||
, allSameSequential = true, allSameFirstLastPieces = true, allSameAutoTMM = true, allSameSavePath = true;
|
bool allSameRatio = true;
|
||||||
|
bool allSameSeedingTime = true;
|
||||||
|
bool allTorrentsArePrivate = true;
|
||||||
|
bool allSameDHT = true;
|
||||||
|
bool allSamePEX = true;
|
||||||
|
bool allSameLSD = true;
|
||||||
|
bool allSameSequential = true;
|
||||||
|
bool allSameFirstLastPieces = true;
|
||||||
|
bool allSameAutoTMM = true;
|
||||||
|
bool allSameSavePath = true;
|
||||||
|
bool allSameDownloadPath = true;
|
||||||
|
|
||||||
const bool isFirstTorrentAutoTMMEnabled = torrents[0]->isAutoTMMEnabled();
|
const bool isFirstTorrentAutoTMMEnabled = torrents[0]->isAutoTMMEnabled();
|
||||||
const QString firstTorrentSavePath = torrents[0]->savePath();
|
const QString firstTorrentSavePath = torrents[0]->savePath();
|
||||||
|
const QString firstTorrentDownloadPath = torrents[0]->downloadPath();
|
||||||
const QString firstTorrentCategory = torrents[0]->category();
|
const QString firstTorrentCategory = torrents[0]->category();
|
||||||
|
|
||||||
const int firstTorrentUpLimit = std::max(0, torrents[0]->uploadLimit());
|
const int firstTorrentUpLimit = std::max(0, torrents[0]->uploadLimit());
|
||||||
|
@ -107,6 +121,11 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
if (torrent->savePath() != firstTorrentSavePath)
|
if (torrent->savePath() != firstTorrentSavePath)
|
||||||
allSameSavePath = false;
|
allSameSavePath = false;
|
||||||
}
|
}
|
||||||
|
if (allSameDownloadPath)
|
||||||
|
{
|
||||||
|
if (torrent->downloadPath() != firstTorrentDownloadPath)
|
||||||
|
allSameDownloadPath = false;
|
||||||
|
}
|
||||||
if (m_allSameCategory)
|
if (m_allSameCategory)
|
||||||
{
|
{
|
||||||
if (torrent->category() != firstTorrentCategory)
|
if (torrent->category() != firstTorrentCategory)
|
||||||
|
@ -172,6 +191,16 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
if (allSameSavePath)
|
if (allSameSavePath)
|
||||||
m_ui->savePath->setSelectedPath(firstTorrentSavePath);
|
m_ui->savePath->setSelectedPath(firstTorrentSavePath);
|
||||||
|
|
||||||
|
if (allSameDownloadPath)
|
||||||
|
{
|
||||||
|
m_ui->downloadPath->setSelectedPath(firstTorrentDownloadPath);
|
||||||
|
m_ui->checkUseDownloadPath->setChecked(!firstTorrentDownloadPath.isEmpty());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_allSameCategory)
|
if (!m_allSameCategory)
|
||||||
{
|
{
|
||||||
m_ui->comboCategory->addItem(m_currentCategoriesString);
|
m_ui->comboCategory->addItem(m_currentCategoriesString);
|
||||||
|
@ -185,7 +214,7 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
}
|
}
|
||||||
m_ui->comboCategory->addItem(QString());
|
m_ui->comboCategory->addItem(QString());
|
||||||
|
|
||||||
m_categories = session->categories().keys();
|
m_categories = session->categories();
|
||||||
std::sort(m_categories.begin(), m_categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
std::sort(m_categories.begin(), m_categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||||
for (const QString &category : asConst(m_categories))
|
for (const QString &category : asConst(m_categories))
|
||||||
{
|
{
|
||||||
|
@ -324,12 +353,14 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
m_initialValues =
|
m_initialValues =
|
||||||
{
|
{
|
||||||
m_ui->savePath->selectedPath(),
|
m_ui->savePath->selectedPath(),
|
||||||
|
m_ui->downloadPath->selectedPath(),
|
||||||
m_ui->comboCategory->currentText(),
|
m_ui->comboCategory->currentText(),
|
||||||
getRatio(),
|
getRatio(),
|
||||||
getSeedingTime(),
|
getSeedingTime(),
|
||||||
m_ui->spinUploadLimit->value(),
|
m_ui->spinUploadLimit->value(),
|
||||||
m_ui->spinDownloadLimit->value(),
|
m_ui->spinDownloadLimit->value(),
|
||||||
m_ui->checkAutoTMM->checkState(),
|
m_ui->checkAutoTMM->checkState(),
|
||||||
|
m_ui->checkUseDownloadPath->checkState(),
|
||||||
m_ui->checkDisableDHT->checkState(),
|
m_ui->checkDisableDHT->checkState(),
|
||||||
m_ui->checkDisablePEX->checkState(),
|
m_ui->checkDisablePEX->checkState(),
|
||||||
m_ui->checkDisableLSD->checkState(),
|
m_ui->checkDisableLSD->checkState(),
|
||||||
|
@ -339,9 +370,11 @@ TorrentOptionsDialog::TorrentOptionsDialog(QWidget *parent, const QVector<BitTor
|
||||||
|
|
||||||
// Needs to be called after the initial values struct is initialized
|
// Needs to be called after the initial values struct is initialized
|
||||||
handleTMMChanged();
|
handleTMMChanged();
|
||||||
|
handleUseDownloadPathChanged();
|
||||||
handleRatioTypeChanged();
|
handleRatioTypeChanged();
|
||||||
|
|
||||||
connect(m_ui->checkAutoTMM, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleTMMChanged);
|
connect(m_ui->checkAutoTMM, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleTMMChanged);
|
||||||
|
connect(m_ui->checkUseDownloadPath, &QCheckBox::clicked, this, &TorrentOptionsDialog::handleUseDownloadPathChanged);
|
||||||
connect(m_ui->comboCategory, &QComboBox::activated, this, &TorrentOptionsDialog::handleCategoryChanged);
|
connect(m_ui->comboCategory, &QComboBox::activated, this, &TorrentOptionsDialog::handleCategoryChanged);
|
||||||
|
|
||||||
// Sync up/down speed limit sliders with their corresponding spinboxes
|
// Sync up/down speed limit sliders with their corresponding spinboxes
|
||||||
|
@ -380,11 +413,28 @@ void TorrentOptionsDialog::accept()
|
||||||
BitTorrent::Torrent *torrent = session->findTorrent(id);
|
BitTorrent::Torrent *torrent = session->findTorrent(id);
|
||||||
if (!torrent) continue;
|
if (!torrent) continue;
|
||||||
|
|
||||||
const QString savePath = m_ui->savePath->selectedPath();
|
|
||||||
if (m_initialValues.autoTMM != m_ui->checkAutoTMM->checkState())
|
if (m_initialValues.autoTMM != m_ui->checkAutoTMM->checkState())
|
||||||
torrent->setAutoTMMEnabled(m_ui->checkAutoTMM->isChecked());
|
torrent->setAutoTMMEnabled(m_ui->checkAutoTMM->isChecked());
|
||||||
if (!m_ui->checkAutoTMM->isChecked() && (m_initialValues.savePath != savePath))
|
|
||||||
torrent->move(Utils::Fs::expandPathAbs(savePath));
|
if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
|
||||||
|
{
|
||||||
|
const QString savePath = m_ui->savePath->selectedPath();
|
||||||
|
if (m_initialValues.savePath != savePath)
|
||||||
|
torrent->setSavePath(Utils::Fs::expandPathAbs(savePath));
|
||||||
|
|
||||||
|
const Qt::CheckState useDownloadPathState = m_ui->checkUseDownloadPath->checkState();
|
||||||
|
if (useDownloadPathState == Qt::Checked)
|
||||||
|
{
|
||||||
|
const QString downloadPath = m_ui->downloadPath->selectedPath();
|
||||||
|
if (m_initialValues.downloadPath != downloadPath)
|
||||||
|
torrent->setDownloadPath(Utils::Fs::expandPathAbs(downloadPath));
|
||||||
|
}
|
||||||
|
else if (useDownloadPathState == Qt::Unchecked)
|
||||||
|
{
|
||||||
|
torrent->setDownloadPath(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QString category = m_ui->comboCategory->currentText();
|
const QString category = m_ui->comboCategory->currentText();
|
||||||
// index 0 is always the current category
|
// index 0 is always the current category
|
||||||
if ((m_initialValues.category != category) || (m_ui->comboCategory->currentIndex() != 0))
|
if ((m_initialValues.category != category) || (m_ui->comboCategory->currentIndex() != 0))
|
||||||
|
@ -487,33 +537,48 @@ void TorrentOptionsDialog::handleTMMChanged()
|
||||||
{
|
{
|
||||||
if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
|
if (m_ui->checkAutoTMM->checkState() == Qt::Unchecked)
|
||||||
{
|
{
|
||||||
m_ui->labelSavePath->setEnabled(true);
|
m_ui->groupBoxSavePath->setEnabled(true);
|
||||||
m_ui->savePath->setEnabled(true);
|
|
||||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(m_initialValues.savePath));
|
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(m_initialValues.savePath));
|
||||||
|
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(m_initialValues.downloadPath));
|
||||||
|
m_ui->checkUseDownloadPath->setCheckState(m_initialValues.useDownloadPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_ui->labelSavePath->setEnabled(false);
|
m_ui->groupBoxSavePath->setEnabled(false);
|
||||||
m_ui->savePath->setEnabled(false);
|
|
||||||
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
|
if (m_ui->checkAutoTMM->checkState() == Qt::Checked)
|
||||||
{
|
{
|
||||||
if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
|
if (!m_allSameCategory && (m_ui->comboCategory->currentIndex() == 0))
|
||||||
{
|
{
|
||||||
m_ui->savePath->setSelectedPath(QString());
|
m_ui->savePath->setSelectedPath(QString());
|
||||||
|
m_ui->downloadPath->setSelectedPath(QString());
|
||||||
|
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
|
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
|
||||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
||||||
|
const QString downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
|
||||||
|
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
|
||||||
|
m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // partially checked
|
else // partially checked
|
||||||
{
|
{
|
||||||
m_ui->savePath->setSelectedPath(QString());
|
m_ui->savePath->setSelectedPath(QString());
|
||||||
|
m_ui->downloadPath->setSelectedPath(QString());
|
||||||
|
m_ui->checkUseDownloadPath->setCheckState(Qt::PartiallyChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorrentOptionsDialog::handleUseDownloadPathChanged()
|
||||||
|
{
|
||||||
|
const bool isChecked = m_ui->checkUseDownloadPath->checkState() == Qt::Checked;
|
||||||
|
m_ui->downloadPath->setEnabled(isChecked);
|
||||||
|
if (isChecked && m_ui->downloadPath->selectedPath().isEmpty())
|
||||||
|
m_ui->downloadPath->setSelectedPath(BitTorrent::Session::instance()->downloadPath());
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentOptionsDialog::handleRatioTypeChanged()
|
void TorrentOptionsDialog::handleRatioTypeChanged()
|
||||||
{
|
{
|
||||||
if ((m_initialValues.ratio == MIXED_SHARE_LIMITS) || (m_initialValues.seedingTime == MIXED_SHARE_LIMITS))
|
if ((m_initialValues.ratio == MIXED_SHARE_LIMITS) || (m_initialValues.seedingTime == MIXED_SHARE_LIMITS))
|
||||||
|
|
|
@ -62,6 +62,7 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void handleCategoryChanged(int index);
|
void handleCategoryChanged(int index);
|
||||||
void handleTMMChanged();
|
void handleTMMChanged();
|
||||||
|
void handleUseDownloadPathChanged();
|
||||||
|
|
||||||
void handleUpSpeedLimitChanged();
|
void handleUpSpeedLimitChanged();
|
||||||
void handleDownSpeedLimitChanged();
|
void handleDownSpeedLimitChanged();
|
||||||
|
@ -82,12 +83,14 @@ private:
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
QString savePath;
|
QString savePath;
|
||||||
|
QString downloadPath;
|
||||||
QString category;
|
QString category;
|
||||||
qreal ratio;
|
qreal ratio;
|
||||||
int seedingTime;
|
int seedingTime;
|
||||||
int upSpeedLimit;
|
int upSpeedLimit;
|
||||||
int downSpeedLimit;
|
int downSpeedLimit;
|
||||||
Qt::CheckState autoTMM;
|
Qt::CheckState autoTMM;
|
||||||
|
Qt::CheckState useDownloadPath;
|
||||||
Qt::CheckState disableDHT;
|
Qt::CheckState disableDHT;
|
||||||
Qt::CheckState disablePEX;
|
Qt::CheckState disablePEX;
|
||||||
Qt::CheckState disableLSD;
|
Qt::CheckState disableLSD;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Torrent Options</string>
|
<string>Torrent Options</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkAutoTMM">
|
<widget class="QCheckBox" name="checkAutoTMM">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
@ -24,33 +24,35 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBoxSavePath">
|
||||||
|
<property name="title">
|
||||||
|
<string>Save at</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkUseDownloadPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use another path for incomplete torrent</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="FileSystemPathComboEdit" name="downloadPath" native="true">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="labelSavePath">
|
|
||||||
<property name="text">
|
|
||||||
<string>Save path:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="FileSystemPathLineEdit" name="savePath" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="labelCategory">
|
|
||||||
<property name="text">
|
|
||||||
<string>Category:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QComboBox" name="comboCategory">
|
<widget class="QComboBox" name="comboCategory">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
@ -69,6 +71,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="labelCategory">
|
||||||
|
<property name="text">
|
||||||
|
<string>Category:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -310,7 +319,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>FileSystemPathLineEdit</class>
|
<class>FileSystemPathComboEdit</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>gui/fspathedit.h</header>
|
<header>gui/fspathedit.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
|
|
|
@ -106,12 +106,12 @@ namespace
|
||||||
void openDestinationFolder(const BitTorrent::Torrent *const torrent)
|
void openDestinationFolder(const BitTorrent::Torrent *const torrent)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
MacUtils::openFiles({torrent->contentPath(true)});
|
MacUtils::openFiles({torrent->contentPath()});
|
||||||
#else
|
#else
|
||||||
if (torrent->filesCount() == 1)
|
if (torrent->filesCount() == 1)
|
||||||
Utils::Gui::openFolderSelect(torrent->contentPath(true));
|
Utils::Gui::openFolderSelect(torrent->contentPath());
|
||||||
else
|
else
|
||||||
Utils::Gui::openPath(torrent->contentPath(true));
|
Utils::Gui::openPath(torrent->contentPath());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,22 +525,22 @@ void TransferListWidget::openSelectedTorrentsFolder() const
|
||||||
// folders prehilighted for opening, so we use a custom method.
|
// folders prehilighted for opening, so we use a custom method.
|
||||||
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
|
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
|
||||||
{
|
{
|
||||||
QString path = torrent->contentPath(true);
|
const QString contentPath = QDir(torrent->actualStorageLocation()).absoluteFilePath(torrent->contentPath());
|
||||||
pathsList.insert(path);
|
pathsList.insert(contentPath);
|
||||||
}
|
}
|
||||||
MacUtils::openFiles(pathsList);
|
MacUtils::openFiles(pathsList);
|
||||||
#else
|
#else
|
||||||
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
|
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
|
||||||
{
|
{
|
||||||
QString path = torrent->contentPath(true);
|
const QString contentPath = torrent->contentPath();
|
||||||
if (!pathsList.contains(path))
|
if (!pathsList.contains(contentPath))
|
||||||
{
|
{
|
||||||
if (torrent->filesCount() == 1)
|
if (torrent->filesCount() == 1)
|
||||||
Utils::Gui::openFolderSelect(path);
|
Utils::Gui::openFolderSelect(contentPath);
|
||||||
else
|
else
|
||||||
Utils::Gui::openPath(path);
|
Utils::Gui::openPath(contentPath);
|
||||||
}
|
}
|
||||||
pathsList.insert(path);
|
pathsList.insert(contentPath);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_MACOS
|
#endif // Q_OS_MACOS
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
|
||||||
: QDialog {parent}
|
: QDialog {parent}
|
||||||
, m_ui {new Ui::WatchedFolderOptionsDialog}
|
, m_ui {new Ui::WatchedFolderOptionsDialog}
|
||||||
, m_savePath {watchedFolderOptions.addTorrentParams.savePath}
|
, m_savePath {watchedFolderOptions.addTorrentParams.savePath}
|
||||||
|
, m_downloadPath {watchedFolderOptions.addTorrentParams.downloadPath}
|
||||||
, m_storeDialogSize {SETTINGS_KEY("DialogSize")}
|
, m_storeDialogSize {SETTINGS_KEY("DialogSize")}
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
@ -51,13 +52,18 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
|
||||||
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
||||||
|
|
||||||
|
const auto *session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
|
m_ui->downloadPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
|
m_ui->downloadPath->setDialogCaption(tr("Choose save path"));
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(watchedFolderOptions.addTorrentParams.useDownloadPath.value_or(session->isDownloadPathEnabled()));
|
||||||
|
|
||||||
connect(m_ui->comboTTM, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onTMMChanged);
|
connect(m_ui->comboTTM, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onTMMChanged);
|
||||||
connect(m_ui->categoryComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onCategoryChanged);
|
connect(m_ui->categoryComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &WatchedFolderOptionsDialog::onCategoryChanged);
|
||||||
|
|
||||||
m_ui->checkBoxRecursive->setChecked(watchedFolderOptions.recursive);
|
m_ui->checkBoxRecursive->setChecked(watchedFolderOptions.recursive);
|
||||||
populateSavePathComboBox();
|
populateSavePaths();
|
||||||
|
|
||||||
const auto *session = BitTorrent::Session::instance();
|
|
||||||
const BitTorrent::AddTorrentParams &torrentParams = watchedFolderOptions.addTorrentParams;
|
const BitTorrent::AddTorrentParams &torrentParams = watchedFolderOptions.addTorrentParams;
|
||||||
m_ui->startTorrentCheckBox->setChecked(!torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
|
m_ui->startTorrentCheckBox->setChecked(!torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
|
||||||
m_ui->skipCheckingCheckBox->setChecked(torrentParams.skipChecking);
|
m_ui->skipCheckingCheckBox->setChecked(torrentParams.skipChecking);
|
||||||
|
@ -66,7 +72,7 @@ WatchedFolderOptionsDialog::WatchedFolderOptionsDialog(
|
||||||
static_cast<int>(torrentParams.contentLayout.value_or(session->torrentContentLayout())));
|
static_cast<int>(torrentParams.contentLayout.value_or(session->torrentContentLayout())));
|
||||||
|
|
||||||
// Load categories
|
// Load categories
|
||||||
QStringList categories = session->categories().keys();
|
QStringList categories = session->categories();
|
||||||
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
|
||||||
|
|
||||||
if (!torrentParams.category.isEmpty())
|
if (!torrentParams.category.isEmpty())
|
||||||
|
@ -96,10 +102,16 @@ TorrentFilesWatcher::WatchedFolderOptions WatchedFolderOptionsDialog::watchedFol
|
||||||
watchedFolderOptions.recursive = m_ui->checkBoxRecursive->isChecked();
|
watchedFolderOptions.recursive = m_ui->checkBoxRecursive->isChecked();
|
||||||
|
|
||||||
BitTorrent::AddTorrentParams ¶ms = watchedFolderOptions.addTorrentParams;
|
BitTorrent::AddTorrentParams ¶ms = watchedFolderOptions.addTorrentParams;
|
||||||
params.useAutoTMM = (m_ui->comboTTM->currentIndex() == 1);
|
const bool useAutoTMM = (m_ui->comboTTM->currentIndex() == 1);
|
||||||
if (!*params.useAutoTMM)
|
if (!useAutoTMM)
|
||||||
|
{
|
||||||
params.savePath = m_ui->savePath->selectedPath();
|
params.savePath = m_ui->savePath->selectedPath();
|
||||||
params.category = m_ui->categoryComboBox->currentText();;
|
params.useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
||||||
|
if (params.useDownloadPath)
|
||||||
|
params.downloadPath = m_ui->downloadPath->selectedPath();
|
||||||
|
}
|
||||||
|
params.useAutoTMM = useAutoTMM;
|
||||||
|
params.category = m_ui->categoryComboBox->currentText();
|
||||||
params.addPaused = !m_ui->startTorrentCheckBox->isChecked();
|
params.addPaused = !m_ui->startTorrentCheckBox->isChecked();
|
||||||
params.skipChecking = m_ui->skipCheckingCheckBox->isChecked();
|
params.skipChecking = m_ui->skipCheckingCheckBox->isChecked();
|
||||||
params.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
|
params.contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex());
|
||||||
|
@ -121,34 +133,60 @@ void WatchedFolderOptionsDialog::onCategoryChanged(const int index)
|
||||||
{
|
{
|
||||||
Q_UNUSED(index);
|
Q_UNUSED(index);
|
||||||
|
|
||||||
const QString category = m_ui->categoryComboBox->currentText();
|
|
||||||
if (m_ui->comboTTM->currentIndex() == 1)
|
if (m_ui->comboTTM->currentIndex() == 1)
|
||||||
{
|
{
|
||||||
const QString savePath = BitTorrent::Session::instance()->categorySavePath(category);
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
|
const QString categoryName = m_ui->categoryComboBox->currentText();
|
||||||
|
|
||||||
|
const QString savePath = btSession->categorySavePath(categoryName);
|
||||||
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
|
||||||
|
|
||||||
|
const QString finishedSavePath = btSession->categoryDownloadPath(categoryName);
|
||||||
|
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(finishedSavePath));
|
||||||
|
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(!finishedSavePath.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchedFolderOptionsDialog::populateSavePathComboBox()
|
void WatchedFolderOptionsDialog::populateSavePaths()
|
||||||
{
|
{
|
||||||
const QString defSavePath {BitTorrent::Session::instance()->defaultSavePath()};
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
m_ui->savePath->setSelectedPath(!m_savePath.isEmpty() ? m_savePath : defSavePath);
|
|
||||||
|
const QString defaultSavePath {btSession->savePath()};
|
||||||
|
m_ui->savePath->setSelectedPath(!m_savePath.isEmpty() ? m_savePath : defaultSavePath);
|
||||||
|
|
||||||
|
const QString defaultFinishedSavePath {btSession->downloadPath()};
|
||||||
|
m_ui->downloadPath->setSelectedPath(!m_downloadPath.isEmpty() ? m_downloadPath : defaultFinishedSavePath);
|
||||||
|
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(m_useDownloadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchedFolderOptionsDialog::onTMMChanged(const int index)
|
void WatchedFolderOptionsDialog::onTMMChanged(const int index)
|
||||||
{
|
{
|
||||||
if (index != 1)
|
if (index != 1)
|
||||||
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
{ // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||||
populateSavePathComboBox();
|
populateSavePaths();
|
||||||
m_ui->groupBoxSavePath->setEnabled(true);
|
m_ui->groupBoxSavePath->setEnabled(true);
|
||||||
m_ui->savePath->blockSignals(false);
|
m_ui->savePath->blockSignals(false);
|
||||||
|
m_ui->downloadPath->blockSignals(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_ui->groupBoxSavePath->setEnabled(false);
|
m_ui->groupBoxSavePath->setEnabled(false);
|
||||||
|
|
||||||
|
const auto *btSession = BitTorrent::Session::instance();
|
||||||
|
|
||||||
m_ui->savePath->blockSignals(true);
|
m_ui->savePath->blockSignals(true);
|
||||||
m_savePath = m_ui->savePath->selectedPath();
|
m_savePath = m_ui->savePath->selectedPath();
|
||||||
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
|
const QString savePath = btSession->categorySavePath(m_ui->categoryComboBox->currentText());
|
||||||
m_ui->savePath->setSelectedPath(savePath);
|
m_ui->savePath->setSelectedPath(savePath);
|
||||||
|
|
||||||
|
m_ui->downloadPath->blockSignals(true);
|
||||||
|
m_downloadPath = m_ui->downloadPath->selectedPath();
|
||||||
|
const QString finishedSavePath = btSession->categoryDownloadPath(m_ui->categoryComboBox->currentText());
|
||||||
|
m_ui->downloadPath->setSelectedPath(finishedSavePath);
|
||||||
|
|
||||||
|
m_useDownloadPath = m_ui->groupBoxDownloadPath->isChecked();
|
||||||
|
m_ui->groupBoxDownloadPath->setChecked(!finishedSavePath.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ public:
|
||||||
TorrentFilesWatcher::WatchedFolderOptions watchedFolderOptions() const;
|
TorrentFilesWatcher::WatchedFolderOptions watchedFolderOptions() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void populateSavePathComboBox();
|
void populateSavePaths();
|
||||||
void loadState();
|
void loadState();
|
||||||
void saveState();
|
void saveState();
|
||||||
void onTMMChanged(int index);
|
void onTMMChanged(int index);
|
||||||
|
@ -58,5 +58,7 @@ private:
|
||||||
|
|
||||||
Ui::WatchedFolderOptionsDialog *m_ui;
|
Ui::WatchedFolderOptionsDialog *m_ui;
|
||||||
QString m_savePath;
|
QString m_savePath;
|
||||||
|
QString m_downloadPath;
|
||||||
|
bool m_useDownloadPath = false;
|
||||||
SettingValue<QSize> m_storeDialogSize;
|
SettingValue<QSize> m_storeDialogSize;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>462</width>
|
<width>462</width>
|
||||||
<height>325</height>
|
<height>364</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -119,6 +119,24 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="FileSystemPathLineEdit" name="savePath" native="true"/>
|
<widget class="FileSystemPathLineEdit" name="savePath" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBoxDownloadPath">
|
||||||
|
<property name="title">
|
||||||
|
<string>Use another path for incomplete torrents</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="FileSystemPathLineEdit" name="downloadPath" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -296,6 +314,12 @@
|
||||||
<header>gui/fspathedit.h</header>
|
<header>gui/fspathedit.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>FileSystemPathLineEdit</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>gui/fspathedit.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
|
|
|
@ -112,9 +112,9 @@ void AppController::preferencesAction()
|
||||||
data["torrent_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenCategoryChanged();
|
data["torrent_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenCategoryChanged();
|
||||||
data["save_path_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenDefaultSavePathChanged();
|
data["save_path_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenDefaultSavePathChanged();
|
||||||
data["category_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenCategorySavePathChanged();
|
data["category_changed_tmm_enabled"] = !session->isDisableAutoTMMWhenCategorySavePathChanged();
|
||||||
data["save_path"] = Utils::Fs::toNativePath(session->defaultSavePath());
|
data["save_path"] = Utils::Fs::toNativePath(session->savePath());
|
||||||
data["temp_path_enabled"] = session->isTempPathEnabled();
|
data["temp_path_enabled"] = session->isDownloadPathEnabled();
|
||||||
data["temp_path"] = Utils::Fs::toNativePath(session->tempPath());
|
data["temp_path"] = Utils::Fs::toNativePath(session->downloadPath());
|
||||||
data["export_dir"] = Utils::Fs::toNativePath(session->torrentExportDirectory());
|
data["export_dir"] = Utils::Fs::toNativePath(session->torrentExportDirectory());
|
||||||
data["export_dir_fin"] = Utils::Fs::toNativePath(session->finishedTorrentExportDirectory());
|
data["export_dir_fin"] = Utils::Fs::toNativePath(session->finishedTorrentExportDirectory());
|
||||||
|
|
||||||
|
@ -399,11 +399,11 @@ void AppController::setPreferencesAction()
|
||||||
if (hasKey("category_changed_tmm_enabled"))
|
if (hasKey("category_changed_tmm_enabled"))
|
||||||
session->setDisableAutoTMMWhenCategorySavePathChanged(!it.value().toBool());
|
session->setDisableAutoTMMWhenCategorySavePathChanged(!it.value().toBool());
|
||||||
if (hasKey("save_path"))
|
if (hasKey("save_path"))
|
||||||
session->setDefaultSavePath(it.value().toString());
|
session->setSavePath(it.value().toString());
|
||||||
if (hasKey("temp_path_enabled"))
|
if (hasKey("temp_path_enabled"))
|
||||||
session->setTempPathEnabled(it.value().toBool());
|
session->setDownloadPathEnabled(it.value().toBool());
|
||||||
if (hasKey("temp_path"))
|
if (hasKey("temp_path"))
|
||||||
session->setTempPath(it.value().toString());
|
session->setDownloadPath(it.value().toString());
|
||||||
if (hasKey("export_dir"))
|
if (hasKey("export_dir"))
|
||||||
session->setTorrentExportDirectory(it.value().toString());
|
session->setTorrentExportDirectory(it.value().toString());
|
||||||
if (hasKey("export_dir_fin"))
|
if (hasKey("export_dir_fin"))
|
||||||
|
@ -866,7 +866,7 @@ void AppController::setPreferencesAction()
|
||||||
|
|
||||||
void AppController::defaultSavePathAction()
|
void AppController::defaultSavePathAction()
|
||||||
{
|
{
|
||||||
setResult(BitTorrent::Session::instance()->defaultSavePath());
|
setResult(BitTorrent::Session::instance()->savePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppController::networkInterfaceListAction()
|
void AppController::networkInterfaceListAction()
|
||||||
|
|
|
@ -33,6 +33,6 @@
|
||||||
|
|
||||||
void FreeDiskSpaceChecker::check()
|
void FreeDiskSpaceChecker::check()
|
||||||
{
|
{
|
||||||
const qint64 freeDiskSpace = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->defaultSavePath());
|
const qint64 freeDiskSpace = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
|
||||||
emit checked(freeDiskSpace);
|
emit checked(freeDiskSpace);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent)
|
||||||
{KEY_TORRENT_SUPER_SEEDING, torrent.superSeeding()},
|
{KEY_TORRENT_SUPER_SEEDING, torrent.superSeeding()},
|
||||||
{KEY_TORRENT_FORCE_START, torrent.isForced()},
|
{KEY_TORRENT_FORCE_START, torrent.isForced()},
|
||||||
{KEY_TORRENT_SAVE_PATH, Utils::Fs::toNativePath(torrent.savePath())},
|
{KEY_TORRENT_SAVE_PATH, Utils::Fs::toNativePath(torrent.savePath())},
|
||||||
|
{KEY_TORRENT_DOWNLOAD_PATH, Utils::Fs::toNativePath(torrent.downloadPath())},
|
||||||
{KEY_TORRENT_CONTENT_PATH, Utils::Fs::toNativePath(torrent.contentPath())},
|
{KEY_TORRENT_CONTENT_PATH, Utils::Fs::toNativePath(torrent.contentPath())},
|
||||||
{KEY_TORRENT_ADDED_ON, torrent.addedTime().toSecsSinceEpoch()},
|
{KEY_TORRENT_ADDED_ON, torrent.addedTime().toSecsSinceEpoch()},
|
||||||
{KEY_TORRENT_COMPLETION_ON, torrent.completedTime().toSecsSinceEpoch()},
|
{KEY_TORRENT_COMPLETION_ON, torrent.completedTime().toSecsSinceEpoch()},
|
||||||
|
|
|
@ -61,6 +61,7 @@ inline const char KEY_TORRENT_TAGS[] = "tags";
|
||||||
inline const char KEY_TORRENT_SUPER_SEEDING[] = "super_seeding";
|
inline const char KEY_TORRENT_SUPER_SEEDING[] = "super_seeding";
|
||||||
inline const char KEY_TORRENT_FORCE_START[] = "force_start";
|
inline const char KEY_TORRENT_FORCE_START[] = "force_start";
|
||||||
inline const char KEY_TORRENT_SAVE_PATH[] = "save_path";
|
inline const char KEY_TORRENT_SAVE_PATH[] = "save_path";
|
||||||
|
inline const char KEY_TORRENT_DOWNLOAD_PATH[] = "download_path";
|
||||||
inline const char KEY_TORRENT_CONTENT_PATH[] = "content_path";
|
inline const char KEY_TORRENT_CONTENT_PATH[] = "content_path";
|
||||||
inline const char KEY_TORRENT_ADDED_ON[] = "added_on";
|
inline const char KEY_TORRENT_ADDED_ON[] = "added_on";
|
||||||
inline const char KEY_TORRENT_COMPLETION_ON[] = "completion_on";
|
inline const char KEY_TORRENT_COMPLETION_ON[] = "completion_on";
|
||||||
|
|
|
@ -209,6 +209,7 @@ namespace
|
||||||
case QMetaType::ULongLong:
|
case QMetaType::ULongLong:
|
||||||
case QMetaType::UInt:
|
case QMetaType::UInt:
|
||||||
case QMetaType::QDateTime:
|
case QMetaType::QDateTime:
|
||||||
|
case QMetaType::Nullptr:
|
||||||
if (prevData[key] != value)
|
if (prevData[key] != value)
|
||||||
syncData[key] = value;
|
syncData[key] = value;
|
||||||
break;
|
break;
|
||||||
|
@ -426,6 +427,7 @@ SyncController::~SyncController()
|
||||||
// - "uploaded_session": Amount of data uploaded since program open
|
// - "uploaded_session": Amount of data uploaded since program open
|
||||||
// - "amount_left": Amount of data left to download
|
// - "amount_left": Amount of data left to download
|
||||||
// - "save_path": Torrent save path
|
// - "save_path": Torrent save path
|
||||||
|
// - "download_path": Torrent download path
|
||||||
// - "completed": Amount of data completed
|
// - "completed": Amount of data completed
|
||||||
// - "max_ratio": Upload max share ratio
|
// - "max_ratio": Upload max share ratio
|
||||||
// - "max_seeding_time": Upload max seeding time
|
// - "max_seeding_time": Upload max seeding time
|
||||||
|
@ -496,15 +498,13 @@ void SyncController::maindataAction()
|
||||||
data["torrents"] = torrents;
|
data["torrents"] = torrents;
|
||||||
|
|
||||||
QVariantHash categories;
|
QVariantHash categories;
|
||||||
const QStringMap categoriesList = session->categories();
|
const QStringList categoriesList = session->categories();
|
||||||
for (auto it = categoriesList.cbegin(); it != categoriesList.cend(); ++it)
|
for (const auto &categoryName : categoriesList)
|
||||||
{
|
{
|
||||||
const QString &key = it.key();
|
const BitTorrent::CategoryOptions categoryOptions = session->categoryOptions(categoryName);
|
||||||
categories[key] = QVariantMap
|
QJsonObject category = categoryOptions.toJSON();
|
||||||
{
|
category.insert(QLatin1String("name"), categoryName);
|
||||||
{"name", key},
|
categories[categoryName] = category.toVariantMap();
|
||||||
{"savePath", it.value()}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
data["categories"] = categories;
|
data["categories"] = categories;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "base/bittorrent/categoryoptions.h"
|
||||||
#include "base/bittorrent/common.h"
|
#include "base/bittorrent/common.h"
|
||||||
#include "base/bittorrent/downloadpriority.h"
|
#include "base/bittorrent/downloadpriority.h"
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
|
@ -103,6 +104,7 @@ const char KEY_PROP_ADDITION_DATE[] = "addition_date";
|
||||||
const char KEY_PROP_COMPLETION_DATE[] = "completion_date";
|
const char KEY_PROP_COMPLETION_DATE[] = "completion_date";
|
||||||
const char KEY_PROP_CREATION_DATE[] = "creation_date";
|
const char KEY_PROP_CREATION_DATE[] = "creation_date";
|
||||||
const char KEY_PROP_SAVE_PATH[] = "save_path";
|
const char KEY_PROP_SAVE_PATH[] = "save_path";
|
||||||
|
const char KEY_PROP_DOWNLOAD_PATH[] = "download_path";
|
||||||
const char KEY_PROP_COMMENT[] = "comment";
|
const char KEY_PROP_COMMENT[] = "comment";
|
||||||
|
|
||||||
// File keys
|
// File keys
|
||||||
|
@ -372,6 +374,7 @@ void TorrentsController::infoAction()
|
||||||
// - "completion_date": Torrent completion date
|
// - "completion_date": Torrent completion date
|
||||||
// - "creation_date": Torrent creation date
|
// - "creation_date": Torrent creation date
|
||||||
// - "save_path": Torrent save path
|
// - "save_path": Torrent save path
|
||||||
|
// - "download_path": Torrent download path
|
||||||
// - "comment": Torrent comment
|
// - "comment": Torrent comment
|
||||||
void TorrentsController::propertiesAction()
|
void TorrentsController::propertiesAction()
|
||||||
{
|
{
|
||||||
|
@ -430,6 +433,7 @@ void TorrentsController::propertiesAction()
|
||||||
dataDict[KEY_PROP_CREATION_DATE] = -1;
|
dataDict[KEY_PROP_CREATION_DATE] = -1;
|
||||||
}
|
}
|
||||||
dataDict[KEY_PROP_SAVE_PATH] = Utils::Fs::toNativePath(torrent->savePath());
|
dataDict[KEY_PROP_SAVE_PATH] = Utils::Fs::toNativePath(torrent->savePath());
|
||||||
|
dataDict[KEY_PROP_DOWNLOAD_PATH] = Utils::Fs::toNativePath(torrent->downloadPath());
|
||||||
dataDict[KEY_PROP_COMMENT] = torrent->comment();
|
dataDict[KEY_PROP_COMMENT] = torrent->comment();
|
||||||
|
|
||||||
setResult(dataDict);
|
setResult(dataDict);
|
||||||
|
@ -643,6 +647,8 @@ void TorrentsController::addAction()
|
||||||
const bool firstLastPiece = parseBool(params()["firstLastPiecePrio"]).value_or(false);
|
const bool firstLastPiece = parseBool(params()["firstLastPiecePrio"]).value_or(false);
|
||||||
const std::optional<bool> addPaused = parseBool(params()["paused"]);
|
const std::optional<bool> addPaused = parseBool(params()["paused"]);
|
||||||
const QString savepath = params()["savepath"].trimmed();
|
const QString savepath = params()["savepath"].trimmed();
|
||||||
|
const QString downloadPath = params()["downloadPath"].trimmed();
|
||||||
|
const std::optional<bool> useDownloadPath = parseBool(params()["useDownloadPath"]);
|
||||||
const QString category = params()["category"];
|
const QString category = params()["category"];
|
||||||
const QStringList tags = params()["tags"].split(',', Qt::SkipEmptyParts);
|
const QStringList tags = params()["tags"].split(',', Qt::SkipEmptyParts);
|
||||||
const QString torrentName = params()["rename"].trimmed();
|
const QString torrentName = params()["rename"].trimmed();
|
||||||
|
@ -682,6 +688,8 @@ void TorrentsController::addAction()
|
||||||
addTorrentParams.addPaused = addPaused;
|
addTorrentParams.addPaused = addPaused;
|
||||||
addTorrentParams.contentLayout = contentLayout;
|
addTorrentParams.contentLayout = contentLayout;
|
||||||
addTorrentParams.savePath = savepath;
|
addTorrentParams.savePath = savepath;
|
||||||
|
addTorrentParams.downloadPath = downloadPath;
|
||||||
|
addTorrentParams.useDownloadPath = useDownloadPath;
|
||||||
addTorrentParams.category = category;
|
addTorrentParams.category = category;
|
||||||
addTorrentParams.tags.insert(tags.cbegin(), tags.cend());
|
addTorrentParams.tags.insert(tags.cbegin(), tags.cend());
|
||||||
addTorrentParams.name = torrentName;
|
addTorrentParams.name = torrentName;
|
||||||
|
@ -1095,7 +1103,58 @@ void TorrentsController::setLocationAction()
|
||||||
{
|
{
|
||||||
LogMsg(tr("WebUI Set location: moving \"%1\", from \"%2\" to \"%3\"")
|
LogMsg(tr("WebUI Set location: moving \"%1\", from \"%2\" to \"%3\"")
|
||||||
.arg(torrent->name(), Utils::Fs::toNativePath(torrent->savePath()), Utils::Fs::toNativePath(newLocation)));
|
.arg(torrent->name(), Utils::Fs::toNativePath(torrent->savePath()), Utils::Fs::toNativePath(newLocation)));
|
||||||
torrent->move(Utils::Fs::expandPathAbs(newLocation));
|
torrent->setAutoTMMEnabled(false);
|
||||||
|
torrent->setSavePath(Utils::Fs::expandPathAbs(newLocation));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentsController::setSavePathAction()
|
||||||
|
{
|
||||||
|
requireParams({"id", "path"});
|
||||||
|
|
||||||
|
const QStringList ids {params()["id"].split('|')};
|
||||||
|
const QString newPath {params()["path"]};
|
||||||
|
|
||||||
|
if (newPath.isEmpty())
|
||||||
|
throw APIError(APIErrorType::BadParams, tr("Save path cannot be empty"));
|
||||||
|
|
||||||
|
// try to create the directory if it does not exist
|
||||||
|
if (!QDir(newPath).mkpath("."))
|
||||||
|
throw APIError(APIErrorType::Conflict, tr("Cannot create target directory"));
|
||||||
|
|
||||||
|
// check permissions
|
||||||
|
if (!QFileInfo(newPath).isWritable())
|
||||||
|
throw APIError(APIErrorType::AccessDenied, tr("Cannot write to directory"));
|
||||||
|
|
||||||
|
applyToTorrents(ids, [&newPath](BitTorrent::Torrent *const torrent)
|
||||||
|
{
|
||||||
|
if (!torrent->isAutoTMMEnabled())
|
||||||
|
torrent->setSavePath(newPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentsController::setDownloadPathAction()
|
||||||
|
{
|
||||||
|
requireParams({"id", "path"});
|
||||||
|
|
||||||
|
const QStringList ids {params()["id"].split('|')};
|
||||||
|
const QString newPath {params()["path"]};
|
||||||
|
|
||||||
|
if (!newPath.isEmpty())
|
||||||
|
{
|
||||||
|
// try to create the directory if it does not exist
|
||||||
|
if (!QDir(newPath).mkpath("."))
|
||||||
|
throw APIError(APIErrorType::Conflict, tr("Cannot create target directory"));
|
||||||
|
|
||||||
|
// check permissions
|
||||||
|
if (!QFileInfo(newPath).isWritable())
|
||||||
|
throw APIError(APIErrorType::AccessDenied, tr("Cannot write to directory"));
|
||||||
|
}
|
||||||
|
|
||||||
|
applyToTorrents(ids, [&newPath](BitTorrent::Torrent *const torrent)
|
||||||
|
{
|
||||||
|
if (!torrent->isAutoTMMEnabled())
|
||||||
|
torrent->setDownloadPath(newPath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1164,16 +1223,24 @@ void TorrentsController::createCategoryAction()
|
||||||
{
|
{
|
||||||
requireParams({"category"});
|
requireParams({"category"});
|
||||||
|
|
||||||
const QString category {params()["category"]};
|
const QString category = params()["category"];
|
||||||
const QString savePath {params()["savePath"]};
|
|
||||||
|
|
||||||
if (category.isEmpty())
|
if (category.isEmpty())
|
||||||
throw APIError(APIErrorType::BadParams, tr("Category cannot be empty"));
|
throw APIError(APIErrorType::BadParams, tr("Category cannot be empty"));
|
||||||
|
|
||||||
if (!BitTorrent::Session::isValidCategoryName(category))
|
if (!BitTorrent::Session::isValidCategoryName(category))
|
||||||
throw APIError(APIErrorType::Conflict, tr("Incorrect category name"));
|
throw APIError(APIErrorType::Conflict, tr("Incorrect category name"));
|
||||||
|
|
||||||
if (!BitTorrent::Session::instance()->addCategory(category, savePath))
|
const QString savePath = params()["savePath"];
|
||||||
|
const auto useDownloadPath = parseBool(params()["downloadPathEnabled"]);
|
||||||
|
BitTorrent::CategoryOptions categoryOptions;
|
||||||
|
categoryOptions.savePath = savePath;
|
||||||
|
if (useDownloadPath.has_value())
|
||||||
|
{
|
||||||
|
const QString downloadPath = params()["downloadPath"];
|
||||||
|
categoryOptions.downloadPath = {useDownloadPath.value(), downloadPath};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BitTorrent::Session::instance()->addCategory(category, categoryOptions))
|
||||||
throw APIError(APIErrorType::Conflict, tr("Unable to create category"));
|
throw APIError(APIErrorType::Conflict, tr("Unable to create category"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1181,13 +1248,21 @@ void TorrentsController::editCategoryAction()
|
||||||
{
|
{
|
||||||
requireParams({"category", "savePath"});
|
requireParams({"category", "savePath"});
|
||||||
|
|
||||||
const QString category {params()["category"]};
|
const QString category = params()["category"];
|
||||||
const QString savePath {params()["savePath"]};
|
|
||||||
|
|
||||||
if (category.isEmpty())
|
if (category.isEmpty())
|
||||||
throw APIError(APIErrorType::BadParams, tr("Category cannot be empty"));
|
throw APIError(APIErrorType::BadParams, tr("Category cannot be empty"));
|
||||||
|
|
||||||
if (!BitTorrent::Session::instance()->editCategory(category, savePath))
|
const QString savePath = params()["savePath"];
|
||||||
|
const auto useDownloadPath = parseBool(params()["downloadPathEnabled"]);
|
||||||
|
BitTorrent::CategoryOptions categoryOptions;
|
||||||
|
categoryOptions.savePath = savePath;
|
||||||
|
if (useDownloadPath.has_value())
|
||||||
|
{
|
||||||
|
const QString downloadPath = params()["downloadPath"];
|
||||||
|
categoryOptions.downloadPath = {useDownloadPath.value(), downloadPath};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BitTorrent::Session::instance()->editCategory(category, categoryOptions))
|
||||||
throw APIError(APIErrorType::Conflict, tr("Unable to edit category"));
|
throw APIError(APIErrorType::Conflict, tr("Unable to edit category"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,16 +1277,16 @@ void TorrentsController::removeCategoriesAction()
|
||||||
|
|
||||||
void TorrentsController::categoriesAction()
|
void TorrentsController::categoriesAction()
|
||||||
{
|
{
|
||||||
|
const auto session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
QJsonObject categories;
|
QJsonObject categories;
|
||||||
const QStringMap categoriesMap = BitTorrent::Session::instance()->categories();
|
const QStringList categoriesList = session->categories();
|
||||||
for (auto it = categoriesMap.cbegin(); it != categoriesMap.cend(); ++it)
|
for (const auto &categoryName : categoriesList)
|
||||||
{
|
{
|
||||||
const auto &key = it.key();
|
const BitTorrent::CategoryOptions categoryOptions = session->categoryOptions(categoryName);
|
||||||
categories[key] = QJsonObject
|
QJsonObject category = categoryOptions.toJSON();
|
||||||
{
|
category.insert(QLatin1String("name"), categoryName);
|
||||||
{"name", key},
|
categories[categoryName] = category;
|
||||||
{"savePath", it.value()}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setResult(categories);
|
setResult(categories);
|
||||||
|
|
|
@ -78,6 +78,8 @@ private slots:
|
||||||
void topPrioAction();
|
void topPrioAction();
|
||||||
void bottomPrioAction();
|
void bottomPrioAction();
|
||||||
void setLocationAction();
|
void setLocationAction();
|
||||||
|
void setSavePathAction();
|
||||||
|
void setDownloadPathAction();
|
||||||
void setAutoManagementAction();
|
void setAutoManagementAction();
|
||||||
void setSuperSeedingAction();
|
void setSuperSeedingAction();
|
||||||
void setForceStartAction();
|
void setForceStartAction();
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "base/utils/version.h"
|
#include "base/utils/version.h"
|
||||||
|
|
||||||
inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 3};
|
inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 4};
|
||||||
|
|
||||||
class APIController;
|
class APIController;
|
||||||
class WebApplication;
|
class WebApplication;
|
||||||
|
|
Loading…
Reference in a new issue