mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-31 20:58:07 -06:00
@@ -3,7 +3,6 @@
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
#include "base/qinisettings.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/bittorrent/sessionstatus.h"
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "statistics.h"
|
||||
@@ -76,40 +75,9 @@ void Statistics::save() const
|
||||
|
||||
void Statistics::load()
|
||||
{
|
||||
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
||||
// This code reads the data from there, writes it to the new file, and removes the keys
|
||||
// from the old file. This code should be removed after some time has passed.
|
||||
// e.g. When we reach v3.3.0
|
||||
// Don't forget to remove:
|
||||
// 1. Preferences::getStats()
|
||||
// 2. Preferences::removeStats()
|
||||
// 3. #include "base/preferences.h"
|
||||
Preferences* const pref = Preferences::instance();
|
||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||
QVariantHash v = pref->getStats();
|
||||
|
||||
// Let's test if the qbittorrent.ini holds the key
|
||||
if (!v.isEmpty()) {
|
||||
m_dirty = true;
|
||||
|
||||
// If the user has used qbt > 3.1.5 and then reinstalled/used
|
||||
// qbt < 3.1.6, there will be stats in qbittorrent-data.ini too
|
||||
// so we need to merge those 2.
|
||||
if (s.contains("Stats/AllStats")) {
|
||||
QVariantHash tmp = s.value("Stats/AllStats").toHash();
|
||||
v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong();
|
||||
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
|
||||
}
|
||||
}
|
||||
else {
|
||||
v = s.value("Stats/AllStats").toHash();
|
||||
}
|
||||
QVariantHash v = s.value("Stats/AllStats").toHash();
|
||||
|
||||
m_alltimeDL = v["AlltimeDL"].toULongLong();
|
||||
m_alltimeUL = v["AlltimeUL"].toULongLong();
|
||||
|
||||
if (m_dirty) {
|
||||
save();
|
||||
pref->removeStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,6 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "session.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDateTime>
|
||||
@@ -44,6 +40,7 @@ using namespace BitTorrent;
|
||||
#include <QProcess>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QRegExp>
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
@@ -70,6 +67,7 @@ using namespace BitTorrent;
|
||||
#include "base/utils/string.h"
|
||||
#include "base/unicodestrings.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/torrentfilter.h"
|
||||
#include "base/net/downloadmanager.h"
|
||||
@@ -94,20 +92,73 @@ static const char RESUME_FOLDER[] = "BT_backup";
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
static bool readFile(const QString &path, QByteArray &buf);
|
||||
static bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri);
|
||||
#define SETTINGS_KEY(name) "BitTorrent/Session/" name
|
||||
const QString KEY_CATEGORIES = SETTINGS_KEY("Categories");
|
||||
const QString KEY_MAXRATIOACTION = SETTINGS_KEY("MaxRatioAction");
|
||||
const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath");
|
||||
const QString KEY_TEMPPATH = SETTINGS_KEY("TempPath");
|
||||
const QString KEY_SUBCATEGORIESENABLED = SETTINGS_KEY("SubcategoriesEnabled");
|
||||
const QString KEY_TEMPPATHENABLED = SETTINGS_KEY("TempPathEnabled");
|
||||
const QString KEY_DISABLEASMBYDEFAULT = SETTINGS_KEY("DisableASMByDefault");
|
||||
const QString KEY_DISABLEASMONCATEGORYCHANGED = SETTINGS_KEY("DisableASMTriggers/CategoryChanged");
|
||||
const QString KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/DefaultSavePathChanged");
|
||||
const QString KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/CategorySavePathChanged");
|
||||
const QString KEY_ADDTORRENTPAUSED = SETTINGS_KEY("AddTorrentPaused");
|
||||
|
||||
static void torrentQueuePositionUp(const libt::torrent_handle &handle);
|
||||
static void torrentQueuePositionDown(const libt::torrent_handle &handle);
|
||||
static void torrentQueuePositionTop(const libt::torrent_handle &handle);
|
||||
static void torrentQueuePositionBottom(const libt::torrent_handle &handle);
|
||||
namespace
|
||||
{
|
||||
bool readFile(const QString &path, QByteArray &buf);
|
||||
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri);
|
||||
|
||||
void torrentQueuePositionUp(const libt::torrent_handle &handle);
|
||||
void torrentQueuePositionDown(const libt::torrent_handle &handle);
|
||||
void torrentQueuePositionTop(const libt::torrent_handle &handle);
|
||||
void torrentQueuePositionBottom(const libt::torrent_handle &handle);
|
||||
|
||||
QStringMap map_cast(const QVariantMap &map)
|
||||
{
|
||||
QStringMap result;
|
||||
foreach (const QString &key, map.keys())
|
||||
result[key] = map.value(key).toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap map_cast(const QStringMap &map)
|
||||
{
|
||||
QVariantMap result;
|
||||
foreach (const QString &key, map.keys())
|
||||
result[key] = map.value(key);
|
||||
return result;
|
||||
}
|
||||
|
||||
QString normalizePath(QString path, const QString &defaultPath = Utils::Fs::QDesktopServicesDownloadLocation())
|
||||
{
|
||||
path = Utils::Fs::fromNativePath(path.trimmed());
|
||||
return !path.isEmpty() ? path : defaultPath;
|
||||
}
|
||||
|
||||
QStringMap expandCategories(const QStringMap &categories)
|
||||
{
|
||||
QStringMap expanded = categories;
|
||||
|
||||
foreach (const QString &category, categories.keys()) {
|
||||
foreach (const QString &subcat, Session::expandCategory(category)) {
|
||||
if (!expanded.contains(subcat))
|
||||
expanded[subcat] = "";
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
}
|
||||
|
||||
// Session
|
||||
|
||||
Session *Session::m_instance = 0;
|
||||
Session *Session::m_instance = nullptr;
|
||||
|
||||
Session::Session(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_settings(SettingsStorage::instance())
|
||||
, m_LSDEnabled(false)
|
||||
, m_DHTEnabled(false)
|
||||
, m_PeXEnabled(false)
|
||||
@@ -118,10 +169,8 @@ Session::Session(QObject *parent)
|
||||
, m_globalMaxRatio(-1)
|
||||
, m_numResumeData(0)
|
||||
, m_extraLimit(0)
|
||||
, m_appendLabelToSavePath(false)
|
||||
, m_appendExtension(false)
|
||||
, m_refreshInterval(0)
|
||||
, m_highRatioAction(MaxRatioAction::Pause)
|
||||
{
|
||||
Preferences* const pref = Preferences::instance();
|
||||
Logger* const logger = Logger::instance();
|
||||
@@ -174,6 +223,13 @@ Session::Session(QObject *parent)
|
||||
m_nativeSession->add_extension(&libt::create_ut_pex_plugin);
|
||||
m_nativeSession->add_extension(&libt::create_smart_ban_plugin);
|
||||
|
||||
m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap());
|
||||
if (isSubcategoriesEnabled()) {
|
||||
// if subcategories support changed manually
|
||||
m_categories = expandCategories(m_categories);
|
||||
m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
|
||||
}
|
||||
|
||||
m_refreshTimer = new QTimer(this);
|
||||
m_refreshTimer->setInterval(2000);
|
||||
connect(m_refreshTimer, SIGNAL(timeout()), SLOT(refresh()));
|
||||
@@ -185,6 +241,10 @@ Session::Session(QObject *parent)
|
||||
|
||||
m_statistics = new Statistics(this);
|
||||
|
||||
m_maxRatioAction = static_cast<MaxRatioAction>(m_settings->loadValue(KEY_MAXRATIOACTION, Pause).toInt());
|
||||
m_defaultSavePath = normalizePath(m_settings->loadValue(KEY_DEFAULTSAVEPATH).toString());
|
||||
m_tempPath = normalizePath(m_settings->loadValue(KEY_TEMPPATH).toString(), m_defaultSavePath + "/temp");
|
||||
|
||||
// Apply user settings to BitTorrent session
|
||||
configure();
|
||||
connect(pref, SIGNAL(changed()), SLOT(configure()));
|
||||
@@ -231,7 +291,14 @@ bool Session::isQueueingEnabled() const
|
||||
|
||||
bool Session::isTempPathEnabled() const
|
||||
{
|
||||
return !m_tempPath.isEmpty();
|
||||
return m_settings->loadValue(KEY_TEMPPATHENABLED, false).toBool();
|
||||
}
|
||||
|
||||
void Session::setTempPathEnabled(bool enabled)
|
||||
{
|
||||
m_settings->storeValue(KEY_TEMPPATHENABLED, enabled);
|
||||
foreach (TorrentHandle *const torrent, m_torrents)
|
||||
torrent->handleTempPathChanged();
|
||||
}
|
||||
|
||||
bool Session::isAppendExtensionEnabled() const
|
||||
@@ -239,11 +306,6 @@ bool Session::isAppendExtensionEnabled() const
|
||||
return m_appendExtension;
|
||||
}
|
||||
|
||||
bool Session::useAppendLabelToSavePath() const
|
||||
{
|
||||
return m_appendLabelToSavePath;
|
||||
}
|
||||
|
||||
QString Session::defaultSavePath() const
|
||||
{
|
||||
return m_defaultSavePath;
|
||||
@@ -254,6 +316,200 @@ QString Session::tempPath() const
|
||||
return m_tempPath;
|
||||
}
|
||||
|
||||
bool Session::isValidCategoryName(const QString &name)
|
||||
{
|
||||
QRegExp re(R"#(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)#");
|
||||
if (!name.isEmpty() && (re.indexIn(name) != 0)) {
|
||||
qDebug() << "Incorrect category name:" << name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList Session::expandCategory(const QString &category)
|
||||
{
|
||||
QStringList result;
|
||||
if (!isValidCategoryName(category))
|
||||
return result;
|
||||
|
||||
int index = 0;
|
||||
while ((index = category.indexOf('/', index)) >= 0) {
|
||||
result << category.left(index);
|
||||
++index;
|
||||
}
|
||||
result << category;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList Session::categories() const
|
||||
{
|
||||
return m_categories.keys();
|
||||
}
|
||||
|
||||
QString Session::categorySavePath(const QString &categoryName) const
|
||||
{
|
||||
QString basePath = m_defaultSavePath;
|
||||
QString path = m_categories.value(categoryName);
|
||||
if (categoryName.isEmpty()) return basePath;
|
||||
|
||||
if (path.isEmpty()) // use implicit save path
|
||||
path = Utils::Fs::toValidFileSystemName(categoryName, true);
|
||||
|
||||
if (!QDir::isAbsolutePath(path))
|
||||
path = QString("%1/%2").arg(basePath).arg(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Session::addCategory(const QString &name, const QString &savePath)
|
||||
{
|
||||
if (name.isEmpty()) return false;
|
||||
if (!isValidCategoryName(name) || m_categories.contains(name))
|
||||
return false;
|
||||
|
||||
if (isSubcategoriesEnabled()) {
|
||||
foreach (const QString &parent, expandCategory(name)) {
|
||||
if ((parent != name) && !m_categories.contains(parent)) {
|
||||
m_categories[parent] = "";
|
||||
emit categoryAdded(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_categories[name] = savePath;
|
||||
m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
|
||||
emit categoryAdded(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Session::editCategory(const QString &name, const QString &savePath)
|
||||
{
|
||||
if (!m_categories.contains(name)) return false;
|
||||
if (categorySavePath(name) == savePath) return false;
|
||||
|
||||
m_categories[name] = savePath;
|
||||
if (isDisableASMWhenCategorySavePathChanged()) {
|
||||
foreach (TorrentHandle *const torrent, torrents())
|
||||
if (torrent->category() == name)
|
||||
torrent->setASMEnabled(false);
|
||||
}
|
||||
else {
|
||||
foreach (TorrentHandle *const torrent, torrents())
|
||||
if (torrent->category() == name)
|
||||
torrent->handleCategorySavePathChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Session::removeCategory(const QString &name)
|
||||
{
|
||||
foreach (TorrentHandle *const torrent, torrents())
|
||||
if (torrent->belongsToCategory(name))
|
||||
torrent->setCategory("");
|
||||
|
||||
// remove stored category and its subcategories if exist
|
||||
bool result = false;
|
||||
if (isSubcategoriesEnabled()) {
|
||||
// remove subcategories
|
||||
QString test = name + "/";
|
||||
foreach (const QString &category, m_categories.keys()) {
|
||||
if (category.startsWith(test)) {
|
||||
m_categories.remove(category);
|
||||
result = true;
|
||||
emit categoryRemoved(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = (m_categories.remove(name) > 0) || result;
|
||||
|
||||
if (result) {
|
||||
// update stored categories
|
||||
m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
|
||||
emit categoryRemoved(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Session::isSubcategoriesEnabled() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_SUBCATEGORIESENABLED, false).toBool();
|
||||
}
|
||||
|
||||
void Session::setSubcategoriesEnabled(bool value)
|
||||
{
|
||||
if (isSubcategoriesEnabled() == value) return;
|
||||
|
||||
if (value) {
|
||||
// expand categories to include all parent categories
|
||||
m_categories = expandCategories(m_categories);
|
||||
// update stored categories
|
||||
m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
|
||||
}
|
||||
else {
|
||||
// reload categories
|
||||
m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap());
|
||||
}
|
||||
|
||||
m_settings->storeValue(KEY_SUBCATEGORIESENABLED, value);
|
||||
emit subcategoriesSupportChanged();
|
||||
}
|
||||
|
||||
bool Session::isASMDisabledByDefault() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_DISABLEASMBYDEFAULT, true).toBool();
|
||||
}
|
||||
|
||||
void Session::setASMDisabledByDefault(bool value)
|
||||
{
|
||||
m_settings->storeValue(KEY_DISABLEASMBYDEFAULT, value);
|
||||
}
|
||||
|
||||
bool Session::isDisableASMWhenCategoryChanged() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_DISABLEASMONCATEGORYCHANGED, false).toBool();
|
||||
}
|
||||
|
||||
void Session::setDisableASMWhenCategoryChanged(bool value)
|
||||
{
|
||||
m_settings->storeValue(KEY_DISABLEASMONCATEGORYCHANGED, value);
|
||||
}
|
||||
|
||||
bool Session::isDisableASMWhenDefaultSavePathChanged() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, true).toBool();
|
||||
}
|
||||
|
||||
void Session::setDisableASMWhenDefaultSavePathChanged(bool value)
|
||||
{
|
||||
m_settings->storeValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, value);
|
||||
}
|
||||
|
||||
bool Session::isDisableASMWhenCategorySavePathChanged() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, true).toBool();
|
||||
}
|
||||
|
||||
void Session::setDisableASMWhenCategorySavePathChanged(bool value)
|
||||
{
|
||||
m_settings->storeValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, value);
|
||||
}
|
||||
|
||||
bool Session::isAddTorrentPaused() const
|
||||
{
|
||||
return m_settings->loadValue(KEY_ADDTORRENTPAUSED, false).toBool();
|
||||
}
|
||||
|
||||
void Session::setAddTorrentPaused(bool value)
|
||||
{
|
||||
m_settings->storeValue(KEY_ADDTORRENTPAUSED, value);
|
||||
}
|
||||
|
||||
qreal Session::globalMaxRatio() const
|
||||
{
|
||||
return m_globalMaxRatio;
|
||||
@@ -442,22 +698,12 @@ void Session::configure()
|
||||
setListeningPort();
|
||||
}
|
||||
|
||||
// * Save path
|
||||
setDefaultSavePath(pref->getSavePath());
|
||||
|
||||
// * Temp path
|
||||
if (pref->isTempPathEnabled())
|
||||
setDefaultTempPath(pref->getTempPath());
|
||||
else
|
||||
setDefaultTempPath();
|
||||
|
||||
uint newRefreshInterval = pref->getRefreshInterval();
|
||||
if (newRefreshInterval != m_refreshInterval) {
|
||||
m_refreshInterval = newRefreshInterval;
|
||||
m_refreshTimer->setInterval(m_refreshInterval);
|
||||
}
|
||||
|
||||
setAppendLabelToSavePath(pref->appendTorrentLabel());
|
||||
setAppendExtension(pref->useIncompleteFilesExtension());
|
||||
preAllocateAllFiles(pref->preAllocateAllFiles());
|
||||
|
||||
@@ -587,7 +833,6 @@ void Session::configure()
|
||||
}
|
||||
|
||||
// * Maximum ratio
|
||||
m_highRatioAction = pref->getMaxRatioAction();
|
||||
setGlobalMaxRatio(pref->getGlobalMaxRatio());
|
||||
|
||||
// Ip Filter
|
||||
@@ -690,7 +935,7 @@ void Session::processBigRatios()
|
||||
|
||||
if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) {
|
||||
Logger* const logger = Logger::instance();
|
||||
if (m_highRatioAction == MaxRatioAction::Remove) {
|
||||
if (m_maxRatioAction == Remove) {
|
||||
logger->addMessage(tr("'%1' reached the maximum ratio you set. Removing...").arg(torrent->name()));
|
||||
deleteTorrent(torrent->hash());
|
||||
}
|
||||
@@ -974,18 +1219,19 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
||||
const TorrentInfo &torrentInfo, const QByteArray &fastresumeData)
|
||||
{
|
||||
if (!addData.resumed) {
|
||||
// manage save path
|
||||
QString defaultSavePath = this->defaultSavePath();
|
||||
if (addData.savePath.isEmpty())
|
||||
addData.savePath = defaultSavePath;
|
||||
if (!addData.savePath.endsWith("/"))
|
||||
addData.savePath += "/";
|
||||
if (useAppendLabelToSavePath()) {
|
||||
if ((addData.savePath == defaultSavePath) && !addData.label.isEmpty())
|
||||
addData.savePath += QString("%1/").arg(addData.label);
|
||||
if (addData.savePath.isEmpty() && isASMDisabledByDefault())
|
||||
addData.savePath = m_defaultSavePath;
|
||||
}
|
||||
|
||||
if (!addData.category.isEmpty()) {
|
||||
if (!m_categories.contains(addData.category) && !addCategory(addData.category)) {
|
||||
qWarning() << "Couldn't create category" << addData.category;
|
||||
addData.category = "";
|
||||
}
|
||||
}
|
||||
|
||||
addData.savePath = Utils::Fs::fromNativePath(addData.savePath);
|
||||
|
||||
libt::add_torrent_params p;
|
||||
InfoHash hash;
|
||||
std::vector<char> buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size());
|
||||
@@ -1082,7 +1328,9 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
||||
// Set actual save path (e.g. temporary folder)
|
||||
if (isTempPathEnabled() && !addData.disableTempPath && !addData.hasSeedStatus)
|
||||
savePath = m_tempPath;
|
||||
else
|
||||
else if (addData.savePath.isEmpty()) // using Advanced mode
|
||||
savePath = categorySavePath(addData.category);
|
||||
else // using Simple mode
|
||||
savePath = addData.savePath;
|
||||
|
||||
p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath));
|
||||
@@ -1341,51 +1589,32 @@ void Session::saveResumeData()
|
||||
}
|
||||
}
|
||||
|
||||
void Session::setDefaultSavePath(const QString &path)
|
||||
void Session::setDefaultSavePath(QString path)
|
||||
{
|
||||
if (path.isEmpty()) return;
|
||||
path = normalizePath(path);
|
||||
if (m_defaultSavePath == path) return;
|
||||
|
||||
m_defaultSavePath = Utils::Fs::fromNativePath(path);
|
||||
if (!m_defaultSavePath.endsWith("/"))
|
||||
m_defaultSavePath += "/";
|
||||
m_defaultSavePath = path;
|
||||
m_settings->storeValue(KEY_DEFAULTSAVEPATH, m_defaultSavePath);
|
||||
|
||||
if (isDisableASMWhenDefaultSavePathChanged())
|
||||
foreach (TorrentHandle *const torrent, torrents())
|
||||
torrent->setASMEnabled(false);
|
||||
else
|
||||
foreach (TorrentHandle *const torrent, torrents())
|
||||
torrent->handleCategorySavePathChanged();
|
||||
}
|
||||
|
||||
void Session::setDefaultTempPath(const QString &path)
|
||||
void Session::setTempPath(QString path)
|
||||
{
|
||||
QString tempPath;
|
||||
path = normalizePath(path, m_defaultSavePath + "/temp");
|
||||
if (m_tempPath == path) return;
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
tempPath = Utils::Fs::fromNativePath(path);
|
||||
if (!tempPath.endsWith("/"))
|
||||
tempPath += "/";
|
||||
}
|
||||
m_tempPath = path;
|
||||
m_settings->storeValue(KEY_TEMPPATH, m_tempPath);
|
||||
|
||||
if (m_tempPath != tempPath) {
|
||||
m_tempPath = tempPath;
|
||||
foreach (TorrentHandle *const torrent, m_torrents)
|
||||
torrent->handleTempPathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Session::setAppendLabelToSavePath(bool append)
|
||||
{
|
||||
if (m_appendLabelToSavePath != append) {
|
||||
m_appendLabelToSavePath = append;
|
||||
foreach (TorrentHandle *const torrent, m_torrents) {
|
||||
QString label = torrent->label();
|
||||
if (label.isEmpty()) continue;
|
||||
|
||||
QString testedOldSavePath = m_defaultSavePath;
|
||||
QString newSavePath = m_defaultSavePath;
|
||||
if (!m_appendLabelToSavePath)
|
||||
testedOldSavePath += QString("%1/").arg(label);
|
||||
else
|
||||
newSavePath += QString("%1/").arg(label);
|
||||
|
||||
if (torrent->savePath() == testedOldSavePath)
|
||||
torrent->move(newSavePath);
|
||||
}
|
||||
}
|
||||
foreach (TorrentHandle *const torrent, m_torrents)
|
||||
torrent->handleTempPathChanged();
|
||||
}
|
||||
|
||||
void Session::setAppendExtension(bool append)
|
||||
@@ -1536,6 +1765,19 @@ bool Session::isListening() const
|
||||
return m_nativeSession->is_listening();
|
||||
}
|
||||
|
||||
MaxRatioAction Session::maxRatioAction() const
|
||||
{
|
||||
return m_maxRatioAction;
|
||||
}
|
||||
|
||||
void Session::setMaxRatioAction(MaxRatioAction act)
|
||||
{
|
||||
if (m_maxRatioAction != act) {
|
||||
m_maxRatioAction = act;
|
||||
m_settings->storeValue(KEY_MAXRATIOACTION, act);
|
||||
}
|
||||
}
|
||||
|
||||
// Torrents will a ratio superior to the given value will
|
||||
// be automatically deleted
|
||||
void Session::setGlobalMaxRatio(qreal ratio)
|
||||
@@ -1586,22 +1828,14 @@ void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent)
|
||||
emit torrentSavePathChanged(torrent);
|
||||
}
|
||||
|
||||
void Session::handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel)
|
||||
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory)
|
||||
{
|
||||
if (m_appendLabelToSavePath) {
|
||||
QString testedOldSavePath = m_defaultSavePath;
|
||||
if (!oldLabel.isEmpty())
|
||||
testedOldSavePath += QString("%1/").arg(oldLabel);
|
||||
QString newLabel = torrent->label();
|
||||
if (torrent->savePath() == testedOldSavePath) {
|
||||
QString newSavePath = m_defaultSavePath;
|
||||
if (!newLabel.isEmpty())
|
||||
newSavePath += QString("%1/").arg(newLabel);
|
||||
torrent->move(newSavePath);
|
||||
}
|
||||
}
|
||||
emit torrentCategoryChanged(torrent, oldCategory);
|
||||
}
|
||||
|
||||
emit torrentLabelChanged(torrent, oldLabel);
|
||||
void Session::handleTorrentSavingModeChanged(TorrentHandle * const torrent)
|
||||
{
|
||||
emit torrentSavingModeChanged(torrent);
|
||||
}
|
||||
|
||||
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers)
|
||||
@@ -2138,7 +2372,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
|
||||
|
||||
bool addPaused = data.addPaused;
|
||||
if (data.addPaused == TriStateBool::Undefined)
|
||||
addPaused = pref->addTorrentsInPause();
|
||||
addPaused = isAddTorrentPaused();
|
||||
|
||||
// Start torrent because it was added in paused state
|
||||
if (!addPaused)
|
||||
@@ -2351,86 +2585,90 @@ void Session::handleStateUpdateAlert(libt::state_update_alert *p)
|
||||
emit torrentsUpdated();
|
||||
}
|
||||
|
||||
bool readFile(const QString &path, QByteArray &buf)
|
||||
namespace
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString()));
|
||||
return false;
|
||||
bool readFile(const QString &path, QByteArray &buf)
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = file.readAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
buf = file.readAll();
|
||||
return true;
|
||||
}
|
||||
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri)
|
||||
{
|
||||
torrentData = AddTorrentData();
|
||||
torrentData.resumed = true;
|
||||
torrentData.skipChecking = false;
|
||||
|
||||
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri)
|
||||
{
|
||||
out = AddTorrentData();
|
||||
out.resumed = true;
|
||||
out.skipChecking = false;
|
||||
libt::lazy_entry fast;
|
||||
libt::error_code ec;
|
||||
libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec);
|
||||
if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false;
|
||||
|
||||
libt::lazy_entry fast;
|
||||
libt::error_code ec;
|
||||
libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec);
|
||||
if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false;
|
||||
torrentData.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath")));
|
||||
torrentData.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
|
||||
// **************************************************************************************
|
||||
// Workaround to convert legacy label to category
|
||||
// TODO: Should be removed in future
|
||||
torrentData.category = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label"));
|
||||
if (torrentData.category.isEmpty())
|
||||
// **************************************************************************************
|
||||
torrentData.category = Utils::String::fromStdString(fast.dict_find_string_value("qBt-category"));
|
||||
torrentData.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name"));
|
||||
torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
|
||||
torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
|
||||
|
||||
out.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath")));
|
||||
if (out.savePath.isEmpty()) {
|
||||
Logger::instance()->addMessage("Empty torrent save path was loaded from .fastresume file! Using default one...", Log::WARNING);
|
||||
out.savePath = Preferences::instance()->getSavePath();
|
||||
magnetUri = MagnetUri(Utils::String::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
|
||||
torrentData.addPaused = fast.dict_find_int_value("qBt-paused");
|
||||
torrentData.addForced = fast.dict_find_int_value("qBt-forced");
|
||||
|
||||
prio = fast.dict_find_int_value("qBt-queuePosition");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
out.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
|
||||
out.label = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label"));
|
||||
out.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name"));
|
||||
out.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
|
||||
out.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
|
||||
|
||||
magnetUri = MagnetUri(Utils::String::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
|
||||
out.addPaused = fast.dict_find_int_value("qBt-paused");
|
||||
out.addForced = fast.dict_find_int_value("qBt-forced");
|
||||
|
||||
prio = fast.dict_find_int_value("qBt-queuePosition");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void torrentQueuePositionUp(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_up();
|
||||
void torrentQueuePositionUp(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_up();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
void torrentQueuePositionDown(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_down();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
void torrentQueuePositionTop(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_top();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
void torrentQueuePositionBottom(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_bottom();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
|
||||
void torrentQueuePositionDown(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_down();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
void torrentQueuePositionTop(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_top();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
void torrentQueuePositionBottom(const libt::torrent_handle &handle)
|
||||
{
|
||||
try {
|
||||
handle.queue_position_bottom();
|
||||
}
|
||||
catch (std::exception &exc) {
|
||||
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QPointer>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
@@ -110,8 +111,19 @@ class FilterParserThread;
|
||||
class BandwidthScheduler;
|
||||
class Statistics;
|
||||
class ResumeDataSavingManager;
|
||||
class SettingsStorage;
|
||||
|
||||
typedef QPair<QString, QString> QStringPair;
|
||||
enum MaxRatioAction
|
||||
{
|
||||
Pause,
|
||||
Remove
|
||||
};
|
||||
|
||||
enum TorrentExportFolder
|
||||
{
|
||||
Regular,
|
||||
Finished
|
||||
};
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
@@ -127,7 +139,7 @@ namespace BitTorrent
|
||||
struct AddTorrentParams
|
||||
{
|
||||
QString name;
|
||||
QString label;
|
||||
QString category;
|
||||
QString savePath;
|
||||
bool disableTempPath = false; // e.g. for imported torrents
|
||||
bool sequential = false;
|
||||
@@ -165,11 +177,49 @@ namespace BitTorrent
|
||||
bool isPexEnabled() const;
|
||||
bool isQueueingEnabled() const;
|
||||
qreal globalMaxRatio() const;
|
||||
bool isTempPathEnabled() const;
|
||||
bool isAppendExtensionEnabled() const;
|
||||
bool useAppendLabelToSavePath() const;
|
||||
|
||||
QString defaultSavePath() const;
|
||||
void setDefaultSavePath(QString path);
|
||||
QString tempPath() const;
|
||||
void setTempPath(QString path);
|
||||
bool isTempPathEnabled() const;
|
||||
void setTempPathEnabled(bool enabled);
|
||||
|
||||
static bool isValidCategoryName(const QString &name);
|
||||
// returns category itself and all top level categories
|
||||
static QStringList expandCategory(const QString &category);
|
||||
|
||||
QStringList categories() const;
|
||||
QString categorySavePath(const QString &categoryName) const;
|
||||
bool addCategory(const QString &name, const QString &savePath = "");
|
||||
bool editCategory(const QString &name, const QString &savePath);
|
||||
bool removeCategory(const QString &name);
|
||||
bool isSubcategoriesEnabled() const;
|
||||
void setSubcategoriesEnabled(bool value);
|
||||
|
||||
// Advanced Saving Management subsystem (ASM)
|
||||
//
|
||||
// Each torrent can be either in Simple mode or in Advanced mode
|
||||
// In Simple mode torrent has explicit save path
|
||||
// In Advanced Mode torrent has implicit save path (based on Default
|
||||
// save path and Category save path)
|
||||
// In Advanced Mode torrent save path can be changed in following cases:
|
||||
// 1. Default save path changed
|
||||
// 2. Torrent category save path changed
|
||||
// 3. Torrent category changed
|
||||
// (unless otherwise is specified)
|
||||
bool isASMDisabledByDefault() const;
|
||||
void setASMDisabledByDefault(bool value);
|
||||
bool isDisableASMWhenCategoryChanged() const;
|
||||
void setDisableASMWhenCategoryChanged(bool value);
|
||||
bool isDisableASMWhenDefaultSavePathChanged() const;
|
||||
void setDisableASMWhenDefaultSavePathChanged(bool value);
|
||||
bool isDisableASMWhenCategorySavePathChanged() const;
|
||||
void setDisableASMWhenCategorySavePathChanged(bool value);
|
||||
|
||||
bool isAddTorrentPaused() const;
|
||||
void setAddTorrentPaused(bool value);
|
||||
|
||||
TorrentHandle *findTorrent(const InfoHash &hash) const;
|
||||
QHash<InfoHash, TorrentHandle *> torrents() const;
|
||||
@@ -184,6 +234,9 @@ namespace BitTorrent
|
||||
int uploadRateLimit() const;
|
||||
bool isListening() const;
|
||||
|
||||
MaxRatioAction maxRatioAction() const;
|
||||
void setMaxRatioAction(MaxRatioAction act);
|
||||
|
||||
void changeSpeedLimitMode(bool alternative);
|
||||
void setDownloadRateLimit(int rate);
|
||||
void setUploadRateLimit(int rate);
|
||||
@@ -208,7 +261,8 @@ namespace BitTorrent
|
||||
// TorrentHandle interface
|
||||
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
||||
void handleTorrentSavingModeChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
|
||||
void handleTorrentPaused(TorrentHandle *const torrent);
|
||||
void handleTorrentResumed(TorrentHandle *const torrent);
|
||||
@@ -236,7 +290,8 @@ namespace BitTorrent
|
||||
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory);
|
||||
void torrentSavingModeChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void allTorrentsFinished();
|
||||
void metadataLoaded(const BitTorrent::TorrentInfo &info);
|
||||
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
|
||||
@@ -254,6 +309,9 @@ namespace BitTorrent
|
||||
void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless);
|
||||
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
||||
void downloadFromUrlFinished(const QString &url);
|
||||
void categoryAdded(const QString &categoryName);
|
||||
void categoryRemoved(const QString &categoryName);
|
||||
void subcategoriesSupportChanged();
|
||||
|
||||
private slots:
|
||||
void configure();
|
||||
@@ -287,8 +345,6 @@ namespace BitTorrent
|
||||
void adjustLimits(libtorrent::session_settings &sessionSettings);
|
||||
const QStringList getListeningIPs();
|
||||
void setListeningPort();
|
||||
void setDefaultSavePath(const QString &path);
|
||||
void setDefaultTempPath(const QString &path = QString());
|
||||
void preAllocateAllFiles(bool b);
|
||||
void setMaxConnectionsPerTorrent(int max);
|
||||
void setMaxUploadsPerTorrent(int max);
|
||||
@@ -296,7 +352,6 @@ namespace BitTorrent
|
||||
void enableDHT(bool enable);
|
||||
void changeSpeedLimitMode_impl(bool alternative);
|
||||
|
||||
void setAppendLabelToSavePath(bool append);
|
||||
void setAppendExtension(bool append);
|
||||
|
||||
void startUpTorrents();
|
||||
@@ -333,6 +388,8 @@ namespace BitTorrent
|
||||
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
|
||||
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
|
||||
|
||||
SettingsStorage *m_settings;
|
||||
|
||||
// BitTorrent
|
||||
libtorrent::session *m_nativeSession;
|
||||
|
||||
@@ -346,10 +403,9 @@ namespace BitTorrent
|
||||
qreal m_globalMaxRatio;
|
||||
int m_numResumeData;
|
||||
int m_extraLimit;
|
||||
bool m_appendLabelToSavePath;
|
||||
bool m_appendExtension;
|
||||
uint m_refreshInterval;
|
||||
MaxRatioAction m_highRatioAction;
|
||||
MaxRatioAction m_maxRatioAction;
|
||||
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
|
||||
QString m_defaultSavePath;
|
||||
QString m_tempPath;
|
||||
@@ -376,6 +432,7 @@ namespace BitTorrent
|
||||
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||
TorrentStatusReport m_torrentStatusReport;
|
||||
QStringMap m_categories;
|
||||
|
||||
QMutex m_alertsMutex;
|
||||
QWaitCondition m_alertsWaitCondition;
|
||||
|
||||
@@ -80,7 +80,7 @@ AddTorrentData::AddTorrentData()
|
||||
AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
||||
: resumed(false)
|
||||
, name(params.name)
|
||||
, label(params.label)
|
||||
, category(params.category)
|
||||
, savePath(params.savePath)
|
||||
, disableTempPath(params.disableTempPath)
|
||||
, sequential(params.sequential)
|
||||
@@ -201,7 +201,8 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
||||
, m_renameCount(0)
|
||||
, m_name(data.name)
|
||||
, m_savePath(Utils::Fs::toNativePath(data.savePath))
|
||||
, m_label(data.label)
|
||||
, m_category(data.category)
|
||||
, m_useASM(data.savePath.isEmpty())
|
||||
, m_hasSeedStatus(data.hasSeedStatus)
|
||||
, m_ratioLimit(data.ratioLimit)
|
||||
, m_tempPathDisabled(data.disableTempPath)
|
||||
@@ -209,7 +210,8 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
||||
, m_pauseAfterRecheck(false)
|
||||
, m_needSaveResumeData(false)
|
||||
{
|
||||
Q_ASSERT(!m_savePath.isEmpty());
|
||||
if (m_useASM)
|
||||
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
||||
|
||||
updateStatus();
|
||||
m_hash = InfoHash(m_nativeStatus.info_hash);
|
||||
@@ -330,6 +332,22 @@ QString TorrentHandle::contentPath(bool actual) const
|
||||
return rootPath(actual);
|
||||
}
|
||||
|
||||
bool TorrentHandle::isASMEnabled() const
|
||||
{
|
||||
return m_useASM;
|
||||
}
|
||||
|
||||
void TorrentHandle::setASMEnabled(bool enabled)
|
||||
{
|
||||
if (m_useASM == enabled) return;
|
||||
|
||||
m_useASM = enabled;
|
||||
m_session->handleTorrentSavingModeChanged(this);
|
||||
|
||||
if (m_useASM)
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
}
|
||||
|
||||
QString TorrentHandle::nativeActualSavePath() const
|
||||
{
|
||||
return Utils::String::fromStdString(m_nativeStatus.save_path);
|
||||
@@ -507,9 +525,22 @@ qreal TorrentHandle::progress() const
|
||||
return progress;
|
||||
}
|
||||
|
||||
QString TorrentHandle::label() const
|
||||
QString TorrentHandle::category() const
|
||||
{
|
||||
return m_label;
|
||||
return m_category;
|
||||
}
|
||||
|
||||
bool TorrentHandle::belongsToCategory(const QString &category) const
|
||||
{
|
||||
if (m_category.isEmpty()) return category.isEmpty();
|
||||
if (!Session::isValidCategoryName(category)) return false;
|
||||
|
||||
if (m_category == category) return true;
|
||||
|
||||
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDateTime TorrentHandle::addedTime() const
|
||||
@@ -1110,17 +1141,41 @@ void TorrentHandle::setName(const QString &name)
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentHandle::setLabel(const QString &label)
|
||||
bool TorrentHandle::setCategory(const QString &category)
|
||||
{
|
||||
if (m_label != label) {
|
||||
QString oldLabel = m_label;
|
||||
m_label = label;
|
||||
if (m_category != category) {
|
||||
if (!category.isEmpty()) {
|
||||
if (!Session::isValidCategoryName(category)) return false;
|
||||
if (!m_session->categories().contains(category))
|
||||
if (!m_session->addCategory(category))
|
||||
return false;
|
||||
}
|
||||
|
||||
QString oldCategory = m_category;
|
||||
m_category = category;
|
||||
m_needSaveResumeData = true;
|
||||
m_session->handleTorrentLabelChanged(this, oldLabel);
|
||||
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
||||
|
||||
if (m_useASM) {
|
||||
if (!m_session->isDisableASMWhenCategoryChanged())
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
else
|
||||
setASMEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TorrentHandle::move(QString path)
|
||||
{
|
||||
m_useASM = false;
|
||||
m_session->handleTorrentSavingModeChanged(this);
|
||||
|
||||
move_impl(path);
|
||||
}
|
||||
|
||||
void TorrentHandle::move_impl(QString path)
|
||||
{
|
||||
path = Utils::Fs::toNativePath(path);
|
||||
if (path == savePath()) return;
|
||||
@@ -1469,9 +1524,9 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
|
||||
resumeData["qBt-paused"] = isPaused();
|
||||
resumeData["qBt-forced"] = isForced();
|
||||
}
|
||||
resumeData["qBt-savePath"] = Utils::String::toStdString(m_savePath);
|
||||
resumeData["qBt-savePath"] = m_useASM ? "" : Utils::String::toStdString(m_savePath);
|
||||
resumeData["qBt-ratioLimit"] = Utils::String::toStdString(QString::number(m_ratioLimit));
|
||||
resumeData["qBt-label"] = Utils::String::toStdString(m_label);
|
||||
resumeData["qBt-category"] = Utils::String::toStdString(m_category);
|
||||
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
|
||||
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
||||
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
||||
@@ -1532,12 +1587,6 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
||||
|
||||
updateStatus();
|
||||
|
||||
if (filesCount() == 1) {
|
||||
// Single-file torrent
|
||||
// Renaming a file corresponds to changing the save path
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
}
|
||||
|
||||
--m_renameCount;
|
||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
||||
m_moveFinishedTriggers.takeFirst()();
|
||||
@@ -1598,6 +1647,12 @@ void TorrentHandle::handleTempPathChanged()
|
||||
adjustActualSavePath();
|
||||
}
|
||||
|
||||
void TorrentHandle::handleCategorySavePathChanged()
|
||||
{
|
||||
if (m_useASM)
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
}
|
||||
|
||||
void TorrentHandle::handleAppendExtensionToggled()
|
||||
{
|
||||
if (!hasMetadata()) return;
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace BitTorrent
|
||||
bool resumed;
|
||||
// for both new and resumed torrents
|
||||
QString name;
|
||||
QString label;
|
||||
QString category;
|
||||
QString savePath;
|
||||
bool disableTempPath;
|
||||
bool sequential;
|
||||
@@ -227,11 +227,16 @@ namespace BitTorrent
|
||||
QString rootPath(bool actual = false) const;
|
||||
QString contentPath(bool actual = false) const;
|
||||
|
||||
bool isASMEnabled() const;
|
||||
void setASMEnabled(bool enabled);
|
||||
QString category() const;
|
||||
bool belongsToCategory(const QString &category) const;
|
||||
bool setCategory(const QString &category);
|
||||
|
||||
int filesCount() const;
|
||||
int piecesCount() const;
|
||||
int piecesHave() const;
|
||||
qreal progress() const;
|
||||
QString label() const;
|
||||
QDateTime addedTime() const;
|
||||
qreal ratioLimit() const;
|
||||
|
||||
@@ -307,7 +312,6 @@ namespace BitTorrent
|
||||
qlonglong nextAnnounce() const;
|
||||
|
||||
void setName(const QString &name);
|
||||
void setLabel(const QString &label);
|
||||
void setSequentialDownload(bool b);
|
||||
void toggleSequentialDownload();
|
||||
void setFirstLastPiecePriority(bool b);
|
||||
@@ -344,6 +348,7 @@ namespace BitTorrent
|
||||
void handleAlert(libtorrent::alert *a);
|
||||
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
|
||||
void handleTempPathChanged();
|
||||
void handleCategorySavePathChanged();
|
||||
void handleAppendExtensionToggled();
|
||||
void saveResumeData();
|
||||
|
||||
@@ -379,6 +384,7 @@ namespace BitTorrent
|
||||
|
||||
void adjustActualSavePath();
|
||||
void adjustActualSavePath_impl();
|
||||
void move_impl(QString path);
|
||||
void moveStorage(const QString &newPath);
|
||||
void appendExtensionsToIncompleteFiles();
|
||||
void removeExtensionsFromIncompleteFiles();
|
||||
@@ -405,10 +411,12 @@ namespace BitTorrent
|
||||
QQueue<EventTrigger> m_moveFinishedTriggers;
|
||||
int m_renameCount;
|
||||
|
||||
bool m_useASM;
|
||||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
bool m_hasSeedStatus;
|
||||
qreal m_ratioLimit;
|
||||
bool m_tempPathDisabled;
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo)
|
||||
: m_nativeInfo(nativeInfo)
|
||||
{
|
||||
m_nativeInfo = boost::const_pointer_cast<libt::torrent_info>(nativeInfo);
|
||||
}
|
||||
|
||||
TorrentInfo::TorrentInfo(const TorrentInfo &other)
|
||||
@@ -219,5 +219,5 @@ void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||
|
||||
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
|
||||
{
|
||||
return *reinterpret_cast<const NativePtr *>(&m_nativeInfo);
|
||||
return m_nativeInfo;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace BitTorrent
|
||||
NativePtr nativeInfo() const;
|
||||
|
||||
private:
|
||||
NativeConstPtr m_nativeInfo;
|
||||
NativePtr m_nativeInfo;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <QHostAddress>
|
||||
#include <QVector>
|
||||
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
#include "base/types.h"
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <QCryptographicHash>
|
||||
#include <QPair>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#include <QApplication>
|
||||
@@ -62,7 +63,6 @@ Preferences* Preferences::m_instance = 0;
|
||||
Preferences::Preferences()
|
||||
: m_randomPort(rand() % 64512 + 1024)
|
||||
{
|
||||
qRegisterMetaTypeStreamOperators<MaxRatioAction>("MaxRatioAction");
|
||||
}
|
||||
|
||||
Preferences *Preferences::instance()
|
||||
@@ -277,40 +277,6 @@ void Preferences::setWinStartup(bool b)
|
||||
#endif
|
||||
|
||||
// Downloads
|
||||
QString Preferences::getSavePath() const
|
||||
{
|
||||
QString save_path = value("Preferences/Downloads/SavePath").toString();
|
||||
if (!save_path.isEmpty())
|
||||
return Utils::Fs::fromNativePath(save_path);
|
||||
return Utils::Fs::QDesktopServicesDownloadLocation();
|
||||
}
|
||||
|
||||
void Preferences::setSavePath(const QString &save_path)
|
||||
{
|
||||
setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path));
|
||||
}
|
||||
|
||||
bool Preferences::isTempPathEnabled() const
|
||||
{
|
||||
return value("Preferences/Downloads/TempPathEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setTempPathEnabled(bool enabled)
|
||||
{
|
||||
setValue("Preferences/Downloads/TempPathEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getTempPath() const
|
||||
{
|
||||
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
||||
}
|
||||
|
||||
void Preferences::setTempPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
bool Preferences::useIncompleteFilesExtension() const
|
||||
{
|
||||
return value("Preferences/Downloads/UseIncompleteExtension", false).toBool();
|
||||
@@ -321,16 +287,6 @@ void Preferences::useIncompleteFilesExtension(bool enabled)
|
||||
setValue("Preferences/Downloads/UseIncompleteExtension", enabled);
|
||||
}
|
||||
|
||||
bool Preferences::appendTorrentLabel() const
|
||||
{
|
||||
return value("Preferences/Downloads/AppendLabel", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setAppendTorrentLabel(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/AppendLabel", b);
|
||||
}
|
||||
|
||||
QString Preferences::lastLocationPath() const
|
||||
{
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
||||
@@ -351,36 +307,6 @@ void Preferences::preAllocateAllFiles(bool enabled)
|
||||
return setValue("Preferences/Downloads/PreAllocation", enabled);
|
||||
}
|
||||
|
||||
bool Preferences::useAdditionDialog() const
|
||||
{
|
||||
return value("Preferences/Downloads/NewAdditionDialog", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::useAdditionDialog(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/NewAdditionDialog", b);
|
||||
}
|
||||
|
||||
bool Preferences::additionDialogFront() const
|
||||
{
|
||||
return value("Preferences/Downloads/NewAdditionDialogFront", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::additionDialogFront(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/NewAdditionDialogFront", b);
|
||||
}
|
||||
|
||||
bool Preferences::addTorrentsInPause() const
|
||||
{
|
||||
return value("Preferences/Downloads/StartInPause", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::addTorrentsInPause(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/StartInPause", b);
|
||||
}
|
||||
|
||||
QVariantHash Preferences::getScanDirs() const
|
||||
{
|
||||
return value("Preferences/Downloads/ScanDirsV2").toHash();
|
||||
@@ -878,16 +804,6 @@ void Preferences::setGlobalMaxRatio(qreal ratio)
|
||||
setValue("Preferences/Bittorrent/MaxRatio", ratio);
|
||||
}
|
||||
|
||||
MaxRatioAction Preferences::getMaxRatioAction() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(MaxRatioAction::Pause)).value<MaxRatioAction>();
|
||||
}
|
||||
|
||||
void Preferences::setMaxRatioAction(MaxRatioAction act)
|
||||
{
|
||||
setValue("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(act));
|
||||
}
|
||||
|
||||
// IP Filter
|
||||
bool Preferences::isFilteringEnabled() const
|
||||
{
|
||||
@@ -1501,51 +1417,6 @@ void Preferences::useSystemIconTheme(bool enabled)
|
||||
}
|
||||
#endif
|
||||
|
||||
QStringList Preferences::getTorrentLabels() const
|
||||
{
|
||||
return value("TransferListFilters/customLabels").toStringList();
|
||||
}
|
||||
|
||||
void Preferences::setTorrentLabels(const QStringList& labels)
|
||||
{
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabelExternal(const QString &label)
|
||||
{
|
||||
addTorrentLabel(label);
|
||||
QString toEmit = label;
|
||||
emit externalLabelAdded(toEmit);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabel(const QString& label)
|
||||
{
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (labels.contains(label))
|
||||
return;
|
||||
labels << label;
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
void Preferences::removeTorrentLabel(const QString& label)
|
||||
{
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (!labels.contains(label))
|
||||
return;
|
||||
labels.removeOne(label);
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
QString Preferences::getDefaultLabel() const
|
||||
{
|
||||
return value("Preferences/Downloads/DefaultLabel").toString();
|
||||
}
|
||||
|
||||
void Preferences::setDefaultLabel(const QString &defaultLabel)
|
||||
{
|
||||
setValue("Preferences/Downloads/DefaultLabel", defaultLabel);
|
||||
}
|
||||
|
||||
bool Preferences::recursiveDownloadDisabled() const
|
||||
{
|
||||
return value("Preferences/Advanced/DisableRecursiveDownload", false).toBool();
|
||||
@@ -1904,63 +1775,6 @@ void Preferences::setTrayIconStyle(TrayIcon::Style style)
|
||||
|
||||
// Stuff that don't appear in the Options GUI but are saved
|
||||
// in the same file.
|
||||
QByteArray Preferences::getAddNewTorrentDialogState() const
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
return value("AddNewTorrentDialog/qt5/treeHeaderState").toByteArray();
|
||||
#else
|
||||
return value("AddNewTorrentDialog/treeHeaderState").toByteArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogState(const QByteArray &state)
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
setValue("AddNewTorrentDialog/qt5/treeHeaderState", state);
|
||||
#else
|
||||
setValue("AddNewTorrentDialog/treeHeaderState", state);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Preferences::getAddNewTorrentDialogPos() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/y", -1).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogPos(const int &pos)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/y", pos);
|
||||
}
|
||||
|
||||
int Preferences::getAddNewTorrentDialogWidth() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/width", -1).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogWidth(const int &width)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/width", width);
|
||||
}
|
||||
|
||||
bool Preferences::getAddNewTorrentDialogExpanded() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/expanded", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogExpanded(const bool expanded)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/expanded", expanded);
|
||||
}
|
||||
|
||||
QStringList Preferences::getAddNewTorrentDialogPathHistory() const
|
||||
{
|
||||
return value("TorrentAdditionDlg/save_path_history").toStringList();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogPathHistory(const QStringList &history)
|
||||
{
|
||||
setValue("TorrentAdditionDlg/save_path_history", history);
|
||||
}
|
||||
|
||||
QDateTime Preferences::getDNSLastUpd() const
|
||||
{
|
||||
@@ -2320,14 +2134,14 @@ void Preferences::setStatusFilterState(const bool checked)
|
||||
setValue("TransferListFilters/statusFilterState", checked);
|
||||
}
|
||||
|
||||
bool Preferences::getLabelFilterState() const
|
||||
bool Preferences::getCategoryFilterState() const
|
||||
{
|
||||
return value("TransferListFilters/labelFilterState", true).toBool();
|
||||
return value("TransferListFilters/CategoryFilterState", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setLabelFilterState(const bool checked)
|
||||
void Preferences::setCategoryFilterState(const bool checked)
|
||||
{
|
||||
setValue("TransferListFilters/labelFilterState", checked);
|
||||
setValue("TransferListFilters/CategoryFilterState", checked);
|
||||
}
|
||||
|
||||
bool Preferences::getTrackerFilterState() const
|
||||
@@ -2499,7 +2313,19 @@ void Preferences::upgrade()
|
||||
|
||||
setNetworkCookies(cookies);
|
||||
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (!labels.isEmpty()) {
|
||||
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
|
||||
foreach (const QString &label, labels) {
|
||||
if (!categories.contains(label))
|
||||
categories[label] = "";
|
||||
}
|
||||
setValue("BitTorrent/Session/Categories", categories);
|
||||
SettingsStorage::instance()->removeValue("TransferListFilters/customLabels");
|
||||
}
|
||||
|
||||
SettingsStorage::instance()->removeValue("Rss/hosts_cookies");
|
||||
SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel");
|
||||
}
|
||||
|
||||
void Preferences::apply()
|
||||
|
||||
@@ -106,7 +106,6 @@ class Preferences: public QObject
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
void externalLabelAdded(QString&);
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
@@ -152,28 +151,12 @@ public:
|
||||
#endif
|
||||
|
||||
// Downloads
|
||||
QString getSavePath() const;
|
||||
void setSavePath(const QString &save_path);
|
||||
bool isTempPathEnabled() const;
|
||||
void setTempPathEnabled(bool enabled);
|
||||
QString getTempPath() const;
|
||||
void setTempPath(const QString &path);
|
||||
QString getDefaultLabel() const;
|
||||
void setDefaultLabel(const QString &defaultLabel);
|
||||
bool useIncompleteFilesExtension() const;
|
||||
void useIncompleteFilesExtension(bool enabled);
|
||||
bool appendTorrentLabel() const;
|
||||
void setAppendTorrentLabel(bool b);
|
||||
QString lastLocationPath() const;
|
||||
void setLastLocationPath(const QString &path);
|
||||
bool preAllocateAllFiles() const;
|
||||
void preAllocateAllFiles(bool enabled);
|
||||
bool useAdditionDialog() const;
|
||||
void useAdditionDialog(bool b);
|
||||
bool additionDialogFront() const;
|
||||
void additionDialogFront(bool b);
|
||||
bool addTorrentsInPause() const;
|
||||
void addTorrentsInPause(bool b);
|
||||
QVariantHash getScanDirs() const;
|
||||
void setScanDirs(const QVariantHash &dirs);
|
||||
QString getScanDirsLastPath() const;
|
||||
@@ -275,8 +258,6 @@ public:
|
||||
void setTrackersList(const QString &val);
|
||||
qreal getGlobalMaxRatio() const;
|
||||
void setGlobalMaxRatio(qreal ratio);
|
||||
MaxRatioAction getMaxRatioAction() const;
|
||||
void setMaxRatioAction(MaxRatioAction act);
|
||||
|
||||
// IP Filter
|
||||
bool isFilteringEnabled() const;
|
||||
@@ -400,11 +381,6 @@ public:
|
||||
bool useSystemIconTheme() const;
|
||||
void useSystemIconTheme(bool enabled);
|
||||
#endif
|
||||
QStringList getTorrentLabels() const;
|
||||
void setTorrentLabels(const QStringList& labels);
|
||||
void addTorrentLabelExternal(const QString &label);
|
||||
void addTorrentLabel(const QString& label);
|
||||
void removeTorrentLabel(const QString& label);
|
||||
bool recursiveDownloadDisabled() const;
|
||||
void disableRecursiveDownload(bool disable = true);
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -437,19 +413,8 @@ public:
|
||||
TrayIcon::Style trayIconStyle() const;
|
||||
void setTrayIconStyle(TrayIcon::Style style);
|
||||
|
||||
|
||||
// Stuff that don't appear in the Options GUI but are saved
|
||||
// in the same file.
|
||||
QByteArray getAddNewTorrentDialogState() const;
|
||||
void setAddNewTorrentDialogState(const QByteArray &state);
|
||||
int getAddNewTorrentDialogPos() const;
|
||||
void setAddNewTorrentDialogPos(const int &pos);
|
||||
int getAddNewTorrentDialogWidth() const;
|
||||
void setAddNewTorrentDialogWidth(const int &width);
|
||||
bool getAddNewTorrentDialogExpanded() const;
|
||||
void setAddNewTorrentDialogExpanded(const bool expanded);
|
||||
QStringList getAddNewTorrentDialogPathHistory() const;
|
||||
void setAddNewTorrentDialogPathHistory(const QStringList &history);
|
||||
QDateTime getDNSLastUpd() const;
|
||||
void setDNSLastUpd(const QDateTime &date);
|
||||
QString getDNSLastIP() const;
|
||||
@@ -511,7 +476,7 @@ public:
|
||||
QByteArray getTorImportGeometry() const;
|
||||
void setTorImportGeometry(const QByteArray &geometry);
|
||||
bool getStatusFilterState() const;
|
||||
bool getLabelFilterState() const;
|
||||
bool getCategoryFilterState() const;
|
||||
bool getTrackerFilterState() const;
|
||||
int getTransSelFilter() const;
|
||||
void setTransSelFilter(const int &index);
|
||||
@@ -548,7 +513,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void setStatusFilterState(bool checked);
|
||||
void setLabelFilterState(bool checked);
|
||||
void setCategoryFilterState(bool checked);
|
||||
void setTrackerFilterState(bool checked);
|
||||
|
||||
void apply();
|
||||
|
||||
@@ -178,7 +178,7 @@ DownloadRulePtr DownloadRule::fromVariantHash(const QVariantHash &ruleHash)
|
||||
rule->setRssFeeds(ruleHash.value("affected_feeds").toStringList());
|
||||
rule->setEnabled(ruleHash.value("enabled", false).toBool());
|
||||
rule->setSavePath(ruleHash.value("save_path").toString());
|
||||
rule->setLabel(ruleHash.value("label_assigned").toString());
|
||||
rule->setCategory(ruleHash.value("category_assigned").toString());
|
||||
rule->setAddPaused(AddPausedState(ruleHash.value("add_paused").toUInt()));
|
||||
rule->setLastMatch(ruleHash.value("last_match").toDateTime());
|
||||
rule->setIgnoreDays(ruleHash.value("ignore_days").toInt());
|
||||
@@ -194,7 +194,7 @@ QVariantHash DownloadRule::toVariantHash() const
|
||||
hash["save_path"] = m_savePath;
|
||||
hash["affected_feeds"] = m_rssFeeds;
|
||||
hash["enabled"] = m_enabled;
|
||||
hash["label_assigned"] = m_label;
|
||||
hash["category_assigned"] = m_category;
|
||||
hash["use_regex"] = m_useRegex;
|
||||
hash["add_paused"] = m_apstate;
|
||||
hash["episode_filter"] = m_episodeFilter;
|
||||
@@ -210,10 +210,7 @@ bool DownloadRule::operator==(const DownloadRule &other) const
|
||||
|
||||
void DownloadRule::setSavePath(const QString &savePath)
|
||||
{
|
||||
if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath())))
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
else
|
||||
m_savePath = QString();
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
}
|
||||
|
||||
DownloadRule::AddPausedState DownloadRule::addPaused() const
|
||||
@@ -226,14 +223,14 @@ void DownloadRule::setAddPaused(const DownloadRule::AddPausedState &aps)
|
||||
m_apstate = aps;
|
||||
}
|
||||
|
||||
QString DownloadRule::label() const
|
||||
QString DownloadRule::category() const
|
||||
{
|
||||
return m_label;
|
||||
return m_category;
|
||||
}
|
||||
|
||||
void DownloadRule::setLabel(const QString &label)
|
||||
void DownloadRule::setCategory(const QString &category)
|
||||
{
|
||||
m_label = label;
|
||||
m_category = category;
|
||||
}
|
||||
|
||||
bool DownloadRule::isEnabled() const
|
||||
|
||||
@@ -69,8 +69,8 @@ namespace Rss
|
||||
void setSavePath(const QString &savePath);
|
||||
AddPausedState addPaused() const;
|
||||
void setAddPaused(const AddPausedState &aps);
|
||||
QString label() const;
|
||||
void setLabel(const QString &label);
|
||||
QString category() const;
|
||||
void setCategory(const QString &category);
|
||||
bool isEnabled() const;
|
||||
void setEnabled(bool enable);
|
||||
void setLastMatch(const QDateTime &d);
|
||||
@@ -93,7 +93,7 @@ namespace Rss
|
||||
QStringList m_mustNotContain;
|
||||
QString m_episodeFilter;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
bool m_enabled;
|
||||
QStringList m_rssFeeds;
|
||||
bool m_useRegex;
|
||||
|
||||
@@ -391,7 +391,7 @@ void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article)
|
||||
|
||||
BitTorrent::AddTorrentParams params;
|
||||
params.savePath = matchingRule->savePath();
|
||||
params.label = matchingRule->label();
|
||||
params.category = matchingRule->category();
|
||||
if (matchingRule->addPaused() == DownloadRule::ALWAYS_PAUSED)
|
||||
params.addPaused = TriStateBool::True;
|
||||
else if (matchingRule->addPaused() == DownloadRule::NEVER_PAUSED)
|
||||
|
||||
@@ -47,9 +47,44 @@ namespace
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef QBT_USES_QT5
|
||||
typedef QHash<QString, QString> MappingTable;
|
||||
#else
|
||||
class MappingTable: public QHash<QString, QString>
|
||||
{
|
||||
public:
|
||||
MappingTable(std::initializer_list<std::pair<QString, QString>> list)
|
||||
{
|
||||
reserve(static_cast<int>(list.size()));
|
||||
for (std::initializer_list<std::pair<QString, QString>>::const_iterator it = list.begin(); it != list.end(); ++it)
|
||||
insert(it->first, it->second);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
QString mapKey(const QString &key)
|
||||
{
|
||||
static const QHash<QString, QString> keyMapping = {};
|
||||
static const MappingTable keyMapping = {
|
||||
|
||||
{ "BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction" },
|
||||
{ "BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath" },
|
||||
{ "BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath" },
|
||||
{ "BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled" },
|
||||
{ "BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause" },
|
||||
#ifdef QBT_USES_QT5
|
||||
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState" },
|
||||
#else
|
||||
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState" },
|
||||
#endif
|
||||
{ "AddNewTorrentDialog/Width", "AddNewTorrentDialog/width" },
|
||||
{ "AddNewTorrentDialog/Position", "AddNewTorrentDialog/y" },
|
||||
{ "AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded" },
|
||||
{ "AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history" },
|
||||
{ "AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog" },
|
||||
{ "AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront" }
|
||||
|
||||
};
|
||||
|
||||
return keyMapping.value(key, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "bittorrent/torrenthandle.h"
|
||||
#include "torrentfilter.h"
|
||||
|
||||
const QString TorrentFilter::AnyLabel;
|
||||
const QString TorrentFilter::AnyCategory;
|
||||
const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString());
|
||||
|
||||
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
|
||||
@@ -49,16 +49,16 @@ TorrentFilter::TorrentFilter()
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label)
|
||||
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString category)
|
||||
: m_type(type)
|
||||
, m_label(label)
|
||||
, m_category(category)
|
||||
, m_hashSet(hashSet)
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label)
|
||||
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString category)
|
||||
: m_type(All)
|
||||
, m_label(label)
|
||||
, m_category(category)
|
||||
, m_hashSet(hashSet)
|
||||
{
|
||||
setTypeByName(filter);
|
||||
@@ -108,13 +108,13 @@ bool TorrentFilter::setHashSet(const QStringSet &hashSet)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TorrentFilter::setLabel(const QString &label)
|
||||
bool TorrentFilter::setCategory(const QString &category)
|
||||
{
|
||||
// QString::operator==() doesn't distinguish between empty and null strings.
|
||||
if ((m_label != label)
|
||||
|| (m_label.isNull() && !label.isNull())
|
||||
|| (!m_label.isNull() && label.isNull())) {
|
||||
m_label = label;
|
||||
if ((m_category != category)
|
||||
|| (m_category.isNull() && !category.isNull())
|
||||
|| (!m_category.isNull() && category.isNull())) {
|
||||
m_category = category;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ bool TorrentFilter::match(TorrentHandle *const torrent) const
|
||||
{
|
||||
if (!torrent) return false;
|
||||
|
||||
return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent));
|
||||
return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent));
|
||||
}
|
||||
|
||||
bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const
|
||||
@@ -160,9 +160,8 @@ bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const
|
||||
else return m_hashSet.contains(torrent->hash());
|
||||
}
|
||||
|
||||
bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const
|
||||
bool TorrentFilter::matchCategory(BitTorrent::TorrentHandle *const torrent) const
|
||||
{
|
||||
if (m_label.isNull()) return true;
|
||||
else if (m_label.isEmpty()) return torrent->label().isEmpty();
|
||||
else return (torrent->label() == m_label);
|
||||
if (m_category.isNull()) return true;
|
||||
else return (torrent->belongsToCategory(m_category));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
Errored
|
||||
};
|
||||
|
||||
static const QString AnyLabel;
|
||||
static const QString AnyCategory;
|
||||
static const QStringSet AnyHash;
|
||||
|
||||
static const TorrentFilter DownloadingTorrent;
|
||||
@@ -71,24 +71,24 @@ public:
|
||||
static const TorrentFilter ErroredTorrent;
|
||||
|
||||
TorrentFilter();
|
||||
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
||||
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||
// category: pass empty string for "no category" or null string (QString()) for "any category"
|
||||
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString category = AnyCategory);
|
||||
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString category = AnyCategory);
|
||||
|
||||
bool setType(Type type);
|
||||
bool setTypeByName(const QString &filter);
|
||||
bool setHashSet(const QStringSet &hashSet);
|
||||
bool setLabel(const QString &label);
|
||||
bool setCategory(const QString &category);
|
||||
|
||||
bool match(BitTorrent::TorrentHandle *const torrent) const;
|
||||
|
||||
private:
|
||||
bool matchState(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchHash(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchLabel(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchCategory(BitTorrent::TorrentHandle *const torrent) const;
|
||||
|
||||
Type m_type;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
QStringSet m_hashSet;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,25 +29,10 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDataStream>
|
||||
#include <QMap>
|
||||
|
||||
const qlonglong MAX_ETA = 8640000;
|
||||
|
||||
enum class MaxRatioAction
|
||||
{
|
||||
Pause,
|
||||
Remove
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MaxRatioAction)
|
||||
|
||||
enum class TorrentExportFolder
|
||||
{
|
||||
Regular,
|
||||
Finished
|
||||
};
|
||||
|
||||
enum class ShutdownAction
|
||||
{
|
||||
None,
|
||||
@@ -56,19 +41,6 @@ enum class ShutdownAction
|
||||
Hibernate
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline QDataStream &operator<<(QDataStream &out, const T &val)
|
||||
{
|
||||
return (out << static_cast<int>(val));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QDataStream &operator>>(QDataStream &in, T &val)
|
||||
{
|
||||
int tmp;
|
||||
in >> tmp;
|
||||
val = static_cast<T>(tmp);
|
||||
return in;
|
||||
}
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
|
||||
#endif // TYPES_H
|
||||
|
||||
@@ -250,22 +250,23 @@ bool Utils::Fs::sameFiles(const QString& path1, const QString& path2)
|
||||
return same;
|
||||
}
|
||||
|
||||
QString Utils::Fs::toValidFileSystemName(const QString &filename)
|
||||
QString Utils::Fs::toValidFileSystemName(const QString &name, bool allowSeparators)
|
||||
{
|
||||
static const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
QRegExp regex(allowSeparators ? "[:?\"*<>|]+" : "[\\\\/:?\"*<>|]+");
|
||||
|
||||
QString validName = filename.trimmed();
|
||||
QString validName = name.trimmed();
|
||||
validName.replace(regex, " ");
|
||||
qDebug() << "toValidFileSystemName:" << filename << "=>" << validName;
|
||||
qDebug() << "toValidFileSystemName:" << name << "=>" << validName;
|
||||
|
||||
return validName;
|
||||
}
|
||||
|
||||
bool Utils::Fs::isValidFileSystemName(const QString& filename)
|
||||
bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
|
||||
{
|
||||
if (filename.isEmpty()) return false;
|
||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
return !filename.contains(regex);
|
||||
if (name.isEmpty()) return false;
|
||||
|
||||
QRegExp regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]");
|
||||
return !name.contains(regex);
|
||||
}
|
||||
|
||||
qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path)
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace Utils
|
||||
QString folderName(const QString& file_path);
|
||||
qint64 computePathSize(const QString& path);
|
||||
bool sameFiles(const QString& path1, const QString& path2);
|
||||
QString toValidFileSystemName(const QString &filename);
|
||||
bool isValidFileSystemName(const QString& filename);
|
||||
QString toValidFileSystemName(const QString &name, bool allowSeparators = false);
|
||||
bool isValidFileSystemName(const QString& name, bool allowSeparators = false);
|
||||
qlonglong freeDiskSpaceOnPath(QString path);
|
||||
QString branchPath(const QString& file_path, QString* removed = 0);
|
||||
bool sameFileNames(const QString& first, const QString& second);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include <QSize>
|
||||
#include "base/types.h"
|
||||
|
||||
/* Miscellaneous functions that can be useful */
|
||||
|
||||
Reference in New Issue
Block a user