Implement Advanced Saving Management subsystem

Closes #4696
This commit is contained in:
Vladimir Golovnev (Glassez)
2016-02-09 11:56:48 +03:00
parent d05d5a85a5
commit dd34663224
59 changed files with 1796 additions and 1280 deletions

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -80,7 +80,7 @@ AddTorrentData::AddTorrentData()
AddTorrentData::AddTorrentData(const AddTorrentParams &params)
: 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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -91,7 +91,7 @@ namespace BitTorrent
NativePtr nativeInfo() const;
private:
NativeConstPtr m_nativeInfo;
NativePtr m_nativeInfo;
};
}