diff --git a/src/app/upgrade.cpp b/src/app/upgrade.cpp index 66ff575a7..7d53cd417 100644 --- a/src/app/upgrade.cpp +++ b/src/app/upgrade.cpp @@ -29,12 +29,15 @@ #include "upgrade.h" #include +#include #include +#include "base/bittorrent/torrentcontentlayout.h" #include "base/logger.h" #include "base/profile.h" #include "base/settingsstorage.h" #include "base/utils/fs.h" +#include "base/utils/string.h" namespace { @@ -79,11 +82,32 @@ namespace , QLatin1String("Preferences/WebUI/HTTPS/KeyPath") , Utils::Fs::toNativePath(configPath + QLatin1String("WebUIPrivateKey.pem"))); } + + void upgradeTorrentContentLayout() + { + const QString oldKey {QLatin1String {"BitTorrent/Session/CreateTorrentSubfolder"}}; + const QString newKey {QLatin1String {"BitTorrent/Session/TorrentContentLayout"}}; + + SettingsStorage *settingsStorage {SettingsStorage::instance()}; + const QVariant oldData {settingsStorage->loadValue(oldKey)}; + const QString newData {settingsStorage->loadValue(newKey).toString()}; + + if (!newData.isEmpty() || !oldData.isValid()) + return; + + const bool createSubfolder = oldData.toBool(); + const BitTorrent::TorrentContentLayout torrentContentLayout = + (createSubfolder ? BitTorrent::TorrentContentLayout::Original : BitTorrent::TorrentContentLayout::NoSubfolder); + + settingsStorage->storeValue(newKey, Utils::String::fromEnum(torrentContentLayout)); + settingsStorage->removeValue(oldKey); + } } bool upgrade(const bool /*ask*/) { exportWebUIHttpsFiles(); + upgradeTorrentContentLayout(); return true; } diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 1c9222263..5e2552cbc 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(qbt_base STATIC bittorrent/sessionstatus.h bittorrent/speedmonitor.h bittorrent/statistics.h + bittorrent/torrentcontentlayout.h bittorrent/torrentcreatorthread.h bittorrent/torrenthandle.h bittorrent/torrenthandleimpl.h diff --git a/src/base/base.pri b/src/base/base.pri index 4fa643eb3..13ace978a 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -23,6 +23,7 @@ HEADERS += \ $$PWD/bittorrent/sessionstatus.h \ $$PWD/bittorrent/speedmonitor.h \ $$PWD/bittorrent/statistics.h \ + $$PWD/bittorrent/torrentcontentlayout.h \ $$PWD/bittorrent/torrentcreatorthread.h \ $$PWD/bittorrent/torrenthandle.h \ $$PWD/bittorrent/torrenthandleimpl.h \ diff --git a/src/base/bittorrent/addtorrentparams.h b/src/base/bittorrent/addtorrentparams.h index 70f2095d8..2cdabb8ae 100644 --- a/src/base/bittorrent/addtorrentparams.h +++ b/src/base/bittorrent/addtorrentparams.h @@ -28,12 +28,15 @@ #pragma once +#include + #include #include #include #include "base/tristatebool.h" #include "torrenthandle.h" +#include "torrentcontentlayout.h" namespace BitTorrent { @@ -52,7 +55,7 @@ namespace BitTorrent TriStateBool addPaused; QVector filePriorities; // used if TorrentInfo is set bool skipChecking = false; - TriStateBool createSubfolder; + boost::optional contentLayout; TriStateBool useAutoTMM; int uploadLimit = -1; int downloadLimit = -1; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index cc3fe58a5..01ae2de47 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -396,7 +396,7 @@ Session::Session(QObject *parent) , m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;}) , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY("GlobalMaxSeedingMinutes"), -1, lowerLimited(-1)) , m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false) - , m_isKeepTorrentTopLevelFolder(BITTORRENT_SESSION_KEY("CreateTorrentSubfolder"), true) + , m_torrentContentLayout(BITTORRENT_SESSION_KEY("TorrentContentLayout"), TorrentContentLayout::Original) , m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false) , m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500) , m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false) @@ -2094,9 +2094,9 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr loadTorrentParams.tags = addTorrentParams.tags; loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority; loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping - loadTorrentParams.hasRootFolder = ((addTorrentParams.createSubfolder == TriStateBool::Undefined) - ? isKeepTorrentTopLevelFolder() - : (addTorrentParams.createSubfolder == TriStateBool::True)); + loadTorrentParams.contentLayout = (addTorrentParams.contentLayout + ? *addTorrentParams.contentLayout + : torrentContentLayout()); loadTorrentParams.forced = (addTorrentParams.addForced == TriStateBool::True); loadTorrentParams.paused = ((addTorrentParams.addPaused == TriStateBool::Undefined) ? isAddTorrentPaused() @@ -2161,8 +2161,7 @@ bool Session::addTorrent_impl(const AddTorrentParams &addTorrentParams, const Ma const QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath; if (hasMetadata) { - if (!loadTorrentParams.hasRootFolder) - metadata.stripRootFolder(); + metadata.setContentLayout(loadTorrentParams.contentLayout); if (!loadTorrentParams.hasSeedStatus) { @@ -4234,9 +4233,27 @@ bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &m Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath")))); torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus"); torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority"); - torrentParams.hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME); + // TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x. + // === BEGIN DEPRECATED CODE === // + const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout"); + if (contentLayoutNode.type() == lt::bdecode_node::string_t) + { + const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value()); + torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original); + } + else + { + const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder"); + torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder); + } + // === END DEPRECATED CODE === // + // === BEGIN REPLACEMENT CODE === // +// torrentParams.contentLayout = Utils::String::parse( +// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default); + // === END REPLACEMENT CODE === // + const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit"); if (ratioLimitString.empty()) torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0; @@ -4467,14 +4484,14 @@ std::vector Session::getPendingAlerts(const lt::time_duration time) return alerts; } -bool Session::isKeepTorrentTopLevelFolder() const +TorrentContentLayout Session::torrentContentLayout() const { - return m_isKeepTorrentTopLevelFolder; + return m_torrentContentLayout; } -void Session::setKeepTorrentTopLevelFolder(const bool value) +void Session::setTorrentContentLayout(const TorrentContentLayout value) { - m_isKeepTorrentTopLevelFolder = value; + m_torrentContentLayout = value; } // Read alerts sent by the BitTorrent session diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 9750a8ac8..7fb51b5bc 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -114,7 +114,7 @@ namespace BitTorrent // Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised // since `Q_NAMESPACE` cannot be used when the same namespace resides at different files. // https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779 - namespace SessionSettingsEnums + inline namespace SessionSettingsEnums { Q_NAMESPACE @@ -160,7 +160,6 @@ namespace BitTorrent Q_ENUM_NS(OSMemoryPriority) #endif } - using namespace SessionSettingsEnums; struct SessionMetricIndices { @@ -274,8 +273,8 @@ namespace BitTorrent void setPeXEnabled(bool enabled); bool isAddTorrentPaused() const; void setAddTorrentPaused(bool value); - bool isKeepTorrentTopLevelFolder() const; - void setKeepTorrentTopLevelFolder(bool value); + TorrentContentLayout torrentContentLayout() const; + void setTorrentContentLayout(TorrentContentLayout value); bool isTrackerEnabled() const; void setTrackerEnabled(bool enabled); bool isAppendExtensionEnabled() const; @@ -702,7 +701,7 @@ namespace BitTorrent CachedSettingValue m_globalMaxRatio; CachedSettingValue m_globalMaxSeedingMinutes; CachedSettingValue m_isAddTorrentPaused; - CachedSettingValue m_isKeepTorrentTopLevelFolder; + CachedSettingValue m_torrentContentLayout; CachedSettingValue m_isAppendExtensionEnabled; CachedSettingValue m_refreshInterval; CachedSettingValue m_isPreallocationEnabled; diff --git a/src/base/bittorrent/torrentcontentlayout.h b/src/base/bittorrent/torrentcontentlayout.h new file mode 100644 index 000000000..2483997f4 --- /dev/null +++ b/src/base/bittorrent/torrentcontentlayout.h @@ -0,0 +1,51 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2020 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#pragma once + +#include + +namespace BitTorrent +{ + // Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised + // since `Q_NAMESPACE` cannot be used when the same namespace resides at different files. + // https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779 + inline namespace TorrentContentLayoutNS + { + Q_NAMESPACE + + enum class TorrentContentLayout + { + Original, + Subfolder, + NoSubfolder + }; + + Q_ENUM_NS(TorrentContentLayout) + } +} diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 76234af08..7fea30c55 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -177,8 +177,6 @@ namespace BitTorrent virtual bool removeTag(const QString &tag) = 0; virtual void removeAllTags() = 0; - virtual bool hasRootFolder() const = 0; - virtual int filesCount() const = 0; virtual int piecesCount() const = 0; virtual int piecesHave() const = 0; diff --git a/src/base/bittorrent/torrenthandleimpl.cpp b/src/base/bittorrent/torrenthandleimpl.cpp index 85c750a6a..2424e5f16 100644 --- a/src/base/bittorrent/torrenthandleimpl.cpp +++ b/src/base/bittorrent/torrenthandleimpl.cpp @@ -115,8 +115,8 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, lt::session *nativeSessio , m_ratioLimit(params.ratioLimit) , m_seedingTimeLimit(params.seedingTimeLimit) , m_operatingMode(params.forced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged) + , m_contentLayout(params.contentLayout) , m_hasSeedStatus(params.hasSeedStatus) - , m_hasRootFolder(params.hasRootFolder) , m_hasFirstLastPiecePriority(params.firstLastPiecePriority) , m_useAutoTMM(params.savePath.isEmpty()) , m_isStopped(params.paused) @@ -136,16 +136,8 @@ TorrentHandleImpl::TorrentHandleImpl(Session *session, lt::session *nativeSessio updateStatus(); if (hasMetadata()) - { applyFirstLastPiecePriority(m_hasFirstLastPiecePriority); - if (!params.restored) - { - if (filesCount() == 1) - m_hasRootFolder = false; - } - } - // TODO: Remove the following upgrade code in v.4.4 // == BEGIN UPGRADE CODE == const QString spath = actualStorageLocation(); @@ -259,7 +251,7 @@ QString TorrentHandleImpl::savePath(bool actual) const QString TorrentHandleImpl::rootPath(bool actual) const { - if ((filesCount() > 1) && !hasRootFolder()) + if (!hasMetadata()) return {}; const QString firstFilePath = filePath(0); @@ -272,10 +264,13 @@ QString TorrentHandleImpl::rootPath(bool actual) const QString TorrentHandleImpl::contentPath(const bool actual) const { + if (!hasMetadata()) + return {}; + if (filesCount() == 1) return QDir(savePath(actual)).absoluteFilePath(filePath(0)); - if (hasRootFolder()) + if (m_torrentInfo.hasRootFolder()) return rootPath(actual); return savePath(actual); @@ -297,11 +292,6 @@ void TorrentHandleImpl::setAutoTMMEnabled(bool enabled) move_impl(m_session->categorySavePath(m_category), MoveStorageMode::Overwrite); } -bool TorrentHandleImpl::hasRootFolder() const -{ - return m_hasRootFolder; -} - QString TorrentHandleImpl::actualStorageLocation() const { return QString::fromStdString(m_nativeStatus.save_path); @@ -1581,8 +1571,7 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale m_ltAddTorrentParams.verified_pieces.clear(); TorrentInfo metadata = TorrentInfo {m_nativeHandle.torrent_file()}; - if (!m_hasRootFolder) - metadata.stripRootFolder(); + metadata.setContentLayout(m_contentLayout); m_session->findIncompleteFiles(metadata, m_savePath); } @@ -1614,7 +1603,7 @@ void TorrentHandleImpl::handleSaveResumeDataAlert(const lt::save_resume_data_ale resumeData["qBt-tags"] = setToEntryList(m_tags); resumeData["qBt-name"] = m_name.toStdString(); resumeData["qBt-seedStatus"] = m_hasSeedStatus; - resumeData["qBt-hasRootFolder"] = m_hasRootFolder; + resumeData["qBt-contentLayout"] = Utils::String::fromEnum(m_contentLayout).toStdString(); resumeData["qBt-firstLastPiecePriority"] = m_hasFirstLastPiecePriority; m_session->handleTorrentResumeDataReady(this, resumeDataPtr); diff --git a/src/base/bittorrent/torrenthandleimpl.h b/src/base/bittorrent/torrenthandleimpl.h index ddbf953fa..314dbbd19 100644 --- a/src/base/bittorrent/torrenthandleimpl.h +++ b/src/base/bittorrent/torrenthandleimpl.h @@ -62,12 +62,13 @@ namespace BitTorrent QString category; QSet tags; QString savePath; + TorrentContentLayout contentLayout = TorrentContentLayout::Original; bool firstLastPiecePriority = false; bool hasSeedStatus = false; - bool hasRootFolder = true; bool forced = false; bool paused = false; + qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO; int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME; @@ -129,8 +130,6 @@ namespace BitTorrent bool removeTag(const QString &tag) override; void removeAllTags() override; - bool hasRootFolder() const override; - int filesCount() const override; int piecesCount() const override; int piecesHave() const override; @@ -319,10 +318,10 @@ namespace BitTorrent qreal m_ratioLimit; int m_seedingTimeLimit; TorrentOperatingMode m_operatingMode; + TorrentContentLayout m_contentLayout; bool m_hasSeedStatus; bool m_fastresumeDataRejected = false; bool m_hasMissingFiles = false; - bool m_hasRootFolder; bool m_hasFirstLastPiecePriority = false; bool m_useAutoTMM; bool m_isStopped; diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp index 1c29ff15e..333951da7 100644 --- a/src/base/bittorrent/torrentinfo.cpp +++ b/src/base/bittorrent/torrentinfo.cpp @@ -52,6 +52,29 @@ using namespace BitTorrent; +namespace +{ + QString getRootFolder(const QStringList &filePaths) + { + QString rootFolder; + for (const QString &filePath : filePaths) + { + if (QDir::isAbsolutePath(filePath)) continue; + + const auto filePathElements = filePath.splitRef('/'); + // if at least one file has no root folder, no common root folder exists + if (filePathElements.count() <= 1) return {}; + + if (rootFolder.isEmpty()) + rootFolder = filePathElements.at(0).toString(); + else if (rootFolder != filePathElements.at(0)) + return {}; + } + + return rootFolder; + } +} + TorrentInfo::TorrentInfo(std::shared_ptr nativeInfo) { m_nativeInfo = std::const_pointer_cast(nativeInfo); @@ -412,23 +435,7 @@ int TorrentInfo::fileIndex(const QString &fileName) const QString TorrentInfo::rootFolder() const { - QString rootFolder; - for (int i = 0; i < filesCount(); ++i) - { - const QString filePath = this->filePath(i); - if (QDir::isAbsolutePath(filePath)) continue; - - const auto filePathElements = filePath.splitRef('/'); - // if at least one file has no root folder, no common root folder exists - if (filePathElements.count() <= 1) return ""; - - if (rootFolder.isEmpty()) - rootFolder = filePathElements.at(0).toString(); - else if (rootFolder != filePathElements.at(0)) - return ""; - } - - return rootFolder; + return getRootFolder(filePaths()); } bool TorrentInfo::hasRootFolder() const @@ -436,10 +443,26 @@ bool TorrentInfo::hasRootFolder() const return !rootFolder().isEmpty(); } -void TorrentInfo::stripRootFolder() +void TorrentInfo::setContentLayout(const TorrentContentLayout layout) { - if (!hasRootFolder()) return; + switch (layout) + { + case TorrentContentLayout::Original: + setContentLayout(defaultContentLayout()); + break; + case TorrentContentLayout::Subfolder: + if (rootFolder().isEmpty()) + addRootFolder(); + break; + case TorrentContentLayout::NoSubfolder: + if (!rootFolder().isEmpty()) + stripRootFolder(); + break; + } +} +void TorrentInfo::stripRootFolder() +{ lt::file_storage files = m_nativeInfo->files(); // Solution for case of renamed root folder @@ -456,6 +479,32 @@ void TorrentInfo::stripRootFolder() m_nativeInfo->remap_files(files); } +void TorrentInfo::addRootFolder() +{ + const QString rootFolder = name(); + Q_ASSERT(!rootFolder.isEmpty()); + + const std::string rootPrefix = Utils::Fs::toNativePath(rootFolder + QLatin1Char {'/'}).toStdString(); + lt::file_storage files = m_nativeInfo->files(); + files.set_name(rootFolder.toStdString()); + for (int i = 0; i < files.num_files(); ++i) + files.rename_file(lt::file_index_t {i}, rootPrefix + files.file_path(lt::file_index_t {i})); + m_nativeInfo->remap_files(files); +} + +TorrentContentLayout TorrentInfo::defaultContentLayout() const +{ + QStringList origFilePaths; + origFilePaths.reserve(filesCount()); + for (int i = 0; i < filesCount(); ++i) + origFilePaths << origFilePath(i); + + const QString origRootFolder = getRootFolder(origFilePaths); + return (origRootFolder.isEmpty() + ? TorrentContentLayout::NoSubfolder + : TorrentContentLayout::Subfolder); +} + std::shared_ptr TorrentInfo::nativeInfo() const { return m_nativeInfo; diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h index dcf2b9b4c..8a36ee9e2 100644 --- a/src/base/bittorrent/torrentinfo.h +++ b/src/base/bittorrent/torrentinfo.h @@ -34,6 +34,7 @@ #include #include "base/indexrange.h" +#include "torrentcontentlayout.h" class QByteArray; class QDateTime; @@ -94,13 +95,17 @@ namespace BitTorrent QString rootFolder() const; bool hasRootFolder() const; - void stripRootFolder(); + void setContentLayout(TorrentContentLayout layout); std::shared_ptr nativeInfo() const; private: // returns file index or -1 if fileName is not found int fileIndex(const QString &fileName) const; + void stripRootFolder(); + void addRootFolder(); + TorrentContentLayout defaultContentLayout() const; + std::shared_ptr m_nativeInfo; }; } diff --git a/src/base/rss/rss_autodownloader.cpp b/src/base/rss/rss_autodownloader.cpp index c93651093..889a8183f 100644 --- a/src/base/rss/rss_autodownloader.cpp +++ b/src/base/rss/rss_autodownloader.cpp @@ -396,7 +396,7 @@ void AutoDownloader::processJob(const QSharedPointer &job) params.savePath = rule.savePath(); params.category = rule.assignedCategory(); params.addPaused = rule.addPaused(); - params.createSubfolder = rule.createSubfolder(); + params.contentLayout = rule.torrentContentLayout(); if (!rule.savePath().isEmpty()) params.useAutoTMM = TriStateBool::False; const auto torrentURL = job->articleData.value(Article::KeyTorrentURL).toString(); diff --git a/src/base/rss/rss_autodownloadrule.cpp b/src/base/rss/rss_autodownloadrule.cpp index ba9c5d18a..afe0d1e5e 100644 --- a/src/base/rss/rss_autodownloadrule.cpp +++ b/src/base/rss/rss_autodownloadrule.cpp @@ -40,11 +40,11 @@ #include #include -#include "../global.h" -#include "../preferences.h" -#include "../tristatebool.h" -#include "../utils/fs.h" -#include "../utils/string.h" +#include "base/global.h" +#include "base/preferences.h" +#include "base/tristatebool.h" +#include "base/utils/fs.h" +#include "base/utils/string.h" #include "rss_article.h" #include "rss_autodownloader.h" #include "rss_feed.h" @@ -91,6 +91,21 @@ namespace default: return 0; // default } } + + boost::optional jsonValueToContentLayout(const QJsonValue &jsonVal) + { + const QString str = jsonVal.toString(); + if (str.isEmpty()) + return {}; + return Utils::String::toEnum(str, BitTorrent::TorrentContentLayout::Original); + } + + QJsonValue contentLayoutToJsonValue(const boost::optional contentLayout) + { + if (!contentLayout) + return {}; + return Utils::String::fromEnum(*contentLayout); + } } const QString Str_Name(QStringLiteral("name")); @@ -106,6 +121,7 @@ const QString Str_LastMatch(QStringLiteral("lastMatch")); const QString Str_IgnoreDays(QStringLiteral("ignoreDays")); const QString Str_AddPaused(QStringLiteral("addPaused")); const QString Str_CreateSubfolder(QStringLiteral("createSubfolder")); +const QString Str_ContentLayout(QStringLiteral("torrentContentLayout")); const QString Str_SmartFilter(QStringLiteral("smartFilter")); const QString Str_PreviouslyMatched(QStringLiteral("previouslyMatchedEpisodes")); @@ -127,7 +143,7 @@ namespace RSS QString savePath; QString category; TriStateBool addPaused = TriStateBool::Undefined; - TriStateBool createSubfolder = TriStateBool::Undefined; + boost::optional contentLayout; bool smartFilter = false; QStringList previouslyMatchedEpisodes; @@ -149,7 +165,7 @@ namespace RSS && (savePath == other.savePath) && (category == other.category) && (addPaused == other.addPaused) - && (createSubfolder == other.createSubfolder) + && (contentLayout == other.contentLayout) && (smartFilter == other.smartFilter); } }; @@ -462,7 +478,7 @@ QJsonObject AutoDownloadRule::toJsonObject() const , {Str_LastMatch, lastMatch().toString(Qt::RFC2822Date)} , {Str_IgnoreDays, ignoreDays()} , {Str_AddPaused, triStateBoolToJsonValue(addPaused())} - , {Str_CreateSubfolder, triStateBoolToJsonValue(createSubfolder())} + , {Str_ContentLayout, contentLayoutToJsonValue(torrentContentLayout())} , {Str_SmartFilter, useSmartFilter()} , {Str_PreviouslyMatched, QJsonArray::fromStringList(previouslyMatchedEpisodes())}}; } @@ -479,7 +495,29 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co rule.setSavePath(jsonObj.value(Str_SavePath).toString()); rule.setCategory(jsonObj.value(Str_AssignedCategory).toString()); rule.setAddPaused(jsonValueToTriStateBool(jsonObj.value(Str_AddPaused))); - rule.setCreateSubfolder(jsonValueToTriStateBool(jsonObj.value(Str_CreateSubfolder))); + + // TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x. + // === BEGIN DEPRECATED CODE === // + if (jsonObj.contains(Str_ContentLayout)) + { + rule.setTorrentContentLayout(jsonValueToContentLayout(jsonObj.value(Str_ContentLayout))); + } + else + { + const TriStateBool createSubfolder = jsonValueToTriStateBool(jsonObj.value(Str_CreateSubfolder)); + boost::optional contentLayout; + if (createSubfolder == TriStateBool::True) + contentLayout = BitTorrent::TorrentContentLayout::Original; + else if (createSubfolder == TriStateBool::False) + contentLayout = BitTorrent::TorrentContentLayout::NoSubfolder; + + rule.setTorrentContentLayout(contentLayout); + } + // === END DEPRECATED CODE === // + // === BEGIN REPLACEMENT CODE === // +// rule.setTorrentContentLayout(jsonValueToContentLayout(jsonObj.value(Str_ContentLayout))); + // === END REPLACEMENT CODE === // + rule.setLastMatch(QDateTime::fromString(jsonObj.value(Str_LastMatch).toString(), Qt::RFC2822Date)); rule.setIgnoreDays(jsonObj.value(Str_IgnoreDays).toInt()); rule.setUseSmartFilter(jsonObj.value(Str_SmartFilter).toBool(false)); @@ -611,14 +649,14 @@ void AutoDownloadRule::setAddPaused(const TriStateBool addPaused) m_dataPtr->addPaused = addPaused; } -TriStateBool AutoDownloadRule::createSubfolder() const +boost::optional AutoDownloadRule::torrentContentLayout() const { - return m_dataPtr->createSubfolder; + return m_dataPtr->contentLayout; } -void AutoDownloadRule::setCreateSubfolder(const TriStateBool createSubfolder) +void AutoDownloadRule::setTorrentContentLayout(const boost::optional contentLayout) { - m_dataPtr->createSubfolder = createSubfolder; + m_dataPtr->contentLayout = contentLayout; } QString AutoDownloadRule::assignedCategory() const diff --git a/src/base/rss/rss_autodownloadrule.h b/src/base/rss/rss_autodownloadrule.h index 2ab2746e1..0d3fe298f 100644 --- a/src/base/rss/rss_autodownloadrule.h +++ b/src/base/rss/rss_autodownloadrule.h @@ -29,9 +29,13 @@ #pragma once +#include + #include #include +#include "base/bittorrent/torrentcontentlayout.h" + class QDateTime; class QJsonObject; class QRegularExpression; @@ -79,8 +83,8 @@ namespace RSS void setSavePath(const QString &savePath); TriStateBool addPaused() const; void setAddPaused(TriStateBool addPaused); - TriStateBool createSubfolder() const; - void setCreateSubfolder(TriStateBool createSubfolder); + boost::optional torrentContentLayout() const; + void setTorrentContentLayout(boost::optional contentLayout); QString assignedCategory() const; void setCategory(const QString &category); diff --git a/src/base/settingvalue.h b/src/base/settingvalue.h index e36bd42aa..e03823755 100644 --- a/src/base/settingvalue.h +++ b/src/base/settingvalue.h @@ -78,13 +78,13 @@ public: private: // regular load/save pair - template ::value, int>::type = 0> + template ::value, int> = 0> U loadValue(const U &defaultValue) { return SettingsStorage::instance()->loadValue(m_keyName, defaultValue).template value(); } - template ::value, int>::type = 0> + template ::value, int> = 0> void storeValue(const U &value) { SettingsStorage::instance()->storeValue(m_keyName, value); @@ -92,16 +92,16 @@ private: // load/save pair for an enum // saves literal value of the enum constant, obtained from QMetaEnum - template ::value, int>::type = 0> + template ::value, int> = 0> U loadValue(const U &defaultValue) { - return Utils::String::parse(SettingsStorage::instance()->loadValue(m_keyName).toString(), defaultValue); + return Utils::String::toEnum(SettingsStorage::instance()->loadValue(m_keyName).toString(), defaultValue); } - template ::value, int>::type = 0> + template ::value, int> = 0> void storeValue(const U &value) { - SettingsStorage::instance()->storeValue(m_keyName, Utils::String::serialize(value)); + SettingsStorage::instance()->storeValue(m_keyName, Utils::String::fromEnum(value)); } const QString m_keyName; diff --git a/src/base/utils/string.h b/src/base/utils/string.h index a8373e5dd..72d75cf68 100644 --- a/src/base/utils/string.h +++ b/src/base/utils/string.h @@ -73,20 +73,25 @@ namespace Utils QString join(const QVector &strings, const QString &separator); - template ::value, int>::type = 0> - QString serialize(const U &value) + template ::value, int> = 0> + QString fromEnum(const T &value) { - return QString::fromLatin1(QMetaEnum::fromType().valueToKey(static_cast(value))); + static_assert(std::is_same>::value, + "Enumeration underlying type has to be int."); + + const auto metaEnum = QMetaEnum::fromType(); + return QString::fromLatin1(metaEnum.valueToKey(static_cast(value))); } - template ::value, int>::type = 0> - U parse(const QString &serializedValue, const U &defaultValue) + template ::value, int> = 0> + T toEnum(const QString &serializedValue, const T &defaultValue) { - static_assert(std::is_same::type>::value, + static_assert(std::is_same>::value, "Enumeration underlying type has to be int."); + const auto metaEnum = QMetaEnum::fromType(); bool ok = false; - const U value = static_cast(QMetaEnum::fromType().keyToValue(serializedValue.toLatin1().constData(), &ok)); + const T value = static_cast(metaEnum.keyToValue(serializedValue.toLatin1().constData(), &ok)); return (ok ? value : defaultValue); } } diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index d95190c8f..ea8176684 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -123,12 +123,8 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP const bool rememberLastSavePath = settings()->loadValue(KEY_REMEMBERLASTSAVEPATH, false).toBool(); m_ui->checkBoxRememberLastSavePath->setChecked(rememberLastSavePath); - if (m_torrentParams.createSubfolder == TriStateBool::True) - m_ui->keepTopLevelFolderCheckBox->setChecked(true); - else if (m_torrentParams.createSubfolder == TriStateBool::False) - m_ui->keepTopLevelFolderCheckBox->setChecked(false); - else - m_ui->keepTopLevelFolderCheckBox->setChecked(session->isKeepTorrentTopLevelFolder()); + m_ui->contentLayoutComboBox->setCurrentIndex( + static_cast(m_torrentParams.contentLayout ? *m_torrentParams.contentLayout : session->torrentContentLayout())); m_ui->sequentialCheckBox->setChecked(m_torrentParams.sequential); m_ui->firstLastCheckBox->setChecked(m_torrentParams.firstLastPiecePriority); @@ -308,7 +304,6 @@ bool AddNewTorrentDialog::loadTorrentImpl() m_ui->labelHashData->setText(infoHash); setupTreeview(); TMMChanged(m_ui->comboTTM->currentIndex()); - m_ui->keepTopLevelFolderCheckBox->setEnabled(m_torrentInfo.hasRootFolder()); return true; } @@ -579,7 +574,7 @@ void AddNewTorrentDialog::accept() m_torrentParams.filePriorities = m_contentModel->model()->getFilePriorities(); m_torrentParams.addPaused = TriStateBool(!m_ui->startTorrentCheckBox->isChecked()); - m_torrentParams.createSubfolder = TriStateBool(m_ui->keepTopLevelFolderCheckBox->isChecked()); + m_torrentParams.contentLayout = static_cast(m_ui->contentLayoutComboBox->currentIndex()); m_torrentParams.sequential = m_ui->sequentialCheckBox->isChecked(); m_torrentParams.firstLastPiecePriority = m_ui->firstLastCheckBox->isChecked(); @@ -640,7 +635,6 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &metadata // Update UI setupTreeview(); setMetadataProgressIndicator(false, tr("Metadata retrieval complete")); - m_ui->keepTopLevelFolderCheckBox->setEnabled(m_torrentInfo.hasRootFolder()); } void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText) diff --git a/src/gui/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui index ca25fc535..ef10963b7 100644 --- a/src/gui/addnewtorrentdialog.ui +++ b/src/gui/addnewtorrentdialog.ui @@ -135,7 +135,7 @@ - + When checked, the .torrent file will not be deleted despite the settings at the "Download" page of the options dialog @@ -152,7 +152,7 @@ - + Skip hash check @@ -166,13 +166,6 @@ - - - - Keep top-level folder - - - @@ -195,6 +188,52 @@ + + + + + + Content layout: + + + + + + + 0 + + + + Original + + + + + Create subfolder + + + + + Don't create subfolder + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index b92872433..e4a72c8f8 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -358,7 +358,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->checkAdditionDialog, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAdditionDialogFront, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkStartPaused, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); - connect(m_ui->checkKeepTopLevelFolder, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->contentLayoutComboBox, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->deleteTorrentBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->deleteCancelledTorrentBox, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkExportDir, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -752,7 +752,7 @@ void OptionsDialog::saveOptions() AddNewTorrentDialog::setEnabled(useAdditionDialog()); AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked()); session->setAddTorrentPaused(addTorrentsInPause()); - session->setKeepTorrentTopLevelFolder(m_ui->checkKeepTopLevelFolder->isChecked()); + session->setTorrentContentLayout(static_cast(m_ui->contentLayoutComboBox->currentIndex())); ScanFoldersModel::instance()->removeFromFSWatcher(m_removedScanDirs); ScanFoldersModel::instance()->addToFSWatcher(m_addedScanDirs); ScanFoldersModel::instance()->makePersistent(); @@ -1000,7 +1000,7 @@ void OptionsDialog::loadOptions() m_ui->checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled()); m_ui->checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel()); m_ui->checkStartPaused->setChecked(session->isAddTorrentPaused()); - m_ui->checkKeepTopLevelFolder->setChecked(session->isKeepTorrentTopLevelFolder()); + m_ui->contentLayoutComboBox->setCurrentIndex(static_cast(session->torrentContentLayout())); const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode(); m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never); m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always); diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 8df7c72f3..47ac147eb 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -769,14 +769,50 @@ - - - Keep top-level folder - - - true - - + + + + + Torrent content layout: + + + + + + + 0 + + + + Original + + + + + Create subfolder + + + + + Don't create subfolder + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 1e3eb6f2d..1a0a3f640 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -275,11 +275,9 @@ void AutomatedRssDownloader::updateRuleDefinitionBox() index = 2; m_ui->comboAddPaused->setCurrentIndex(index); index = 0; - if (m_currentRule.createSubfolder() == TriStateBool::True) - index = 1; - else if (m_currentRule.createSubfolder() == TriStateBool::False) - index = 2; - m_ui->comboCreateSubfolder->setCurrentIndex(index); + if (m_currentRule.torrentContentLayout()) + index = static_cast(*m_currentRule.torrentContentLayout()) + 1; + m_ui->comboContentLayout->setCurrentIndex(index); m_ui->spinIgnorePeriod->setValue(m_currentRule.ignoreDays()); QDateTime dateTime = m_currentRule.lastMatch(); QString lMatch; @@ -320,8 +318,8 @@ void AutomatedRssDownloader::clearRuleDefinitionBox() m_ui->spinIgnorePeriod->setValue(0); m_ui->comboAddPaused->clearEditText(); m_ui->comboAddPaused->setCurrentIndex(-1); - m_ui->comboCreateSubfolder->clearEditText(); - m_ui->comboCreateSubfolder->setCurrentIndex(-1); + m_ui->comboContentLayout->clearEditText(); + m_ui->comboContentLayout->setCurrentIndex(-1); updateFieldsToolTips(m_ui->checkRegex->isChecked()); updateMustLineValidity(); updateMustNotLineValidity(); @@ -355,12 +353,12 @@ void AutomatedRssDownloader::updateEditedRule() else if (m_ui->comboAddPaused->currentIndex() == 2) addPaused = TriStateBool::False; m_currentRule.setAddPaused(addPaused); - TriStateBool createSubfolder; // Undefined by default - if (m_ui->comboCreateSubfolder->currentIndex() == 1) - createSubfolder = TriStateBool::True; - else if (m_ui->comboCreateSubfolder->currentIndex() == 2) - createSubfolder = TriStateBool::False; - m_currentRule.setCreateSubfolder(createSubfolder); + + boost::optional contentLayout; + if (m_ui->comboContentLayout->currentIndex() > 0) + contentLayout = static_cast(m_ui->comboContentLayout->currentIndex() - 1); + m_currentRule.setTorrentContentLayout(contentLayout); + m_currentRule.setIgnoreDays(m_ui->spinIgnorePeriod->value()); } diff --git a/src/gui/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui index d41f61b28..b17c71bbc 100644 --- a/src/gui/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -325,7 +325,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also - + 0 @@ -333,12 +333,12 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also - Keep top-level folder: + Torrent content layout: - + Use global settings @@ -346,12 +346,17 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also - Always + Original - Never + Create subfolder + + + + + Don't create subfolder @@ -471,7 +476,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also lineSavePath spinIgnorePeriod comboAddPaused - comboCreateSubfolder + comboContentLayout listFeeds treeMatchingArticles importBtn diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index a5298e7a3..17b6767de 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -56,6 +56,7 @@ #include "base/utils/misc.h" #include "base/utils/net.h" #include "base/utils/password.h" +#include "base/utils/string.h" #include "../webapplication.h" void AppController::webapiVersionAction() @@ -100,7 +101,7 @@ void AppController::preferencesAction() // Downloads // When adding a torrent - data["create_subfolder_enabled"] = session->isKeepTorrentTopLevelFolder(); + data["torrent_content_layout"] = Utils::String::fromEnum(session->torrentContentLayout()); data["start_paused_enabled"] = session->isAddTorrentPaused(); data["auto_delete_mode"] = static_cast(TorrentFileGuard::autoDeleteMode()); data["preallocate_all"] = session->isPreallocationEnabled(); @@ -355,8 +356,8 @@ void AppController::setPreferencesAction() // Downloads // When adding a torrent - if (hasKey("create_subfolder_enabled")) - session->setKeepTorrentTopLevelFolder(it.value().toBool()); + if (hasKey("torrent_content_layout")) + session->setTorrentContentLayout(Utils::String::toEnum(it.value().toString(), BitTorrent::TorrentContentLayout::Original)); if (hasKey("start_paused_enabled")) session->setAddTorrentPaused(it.value().toBool()); if (hasKey("auto_delete_mode")) diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 71b2c13da..fa9d74f89 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -568,7 +568,6 @@ void TorrentsController::addAction() const bool seqDownload = parseBool(params()["sequentialDownload"], false); const bool firstLastPiece = parseBool(params()["firstLastPiecePrio"], false); const TriStateBool addPaused = parseTriStateBool(params()["paused"]); - const TriStateBool rootFolder = parseTriStateBool(params()["root_folder"]); const QString savepath = params()["savepath"].trimmed(); const QString category = params()["category"]; const QSet tags = List::toSet(params()["tags"].split(',', QString::SkipEmptyParts)); @@ -578,6 +577,11 @@ void TorrentsController::addAction() const int dlLimit = params()["dlLimit"].toInt(); const TriStateBool autoTMM = parseTriStateBool(params()["autoTMM"]); + const QString contentLayoutParam = params()["contentLayout"]; + const boost::optional contentLayout = (!contentLayoutParam.isEmpty() + ? Utils::String::toEnum(contentLayoutParam, BitTorrent::TorrentContentLayout::Original) + : boost::optional {}); + QList cookies; if (!cookie.isEmpty()) { @@ -601,7 +605,7 @@ void TorrentsController::addAction() params.sequential = seqDownload; params.firstLastPiecePriority = firstLastPiece; params.addPaused = addPaused; - params.createSubfolder = rootFolder; + params.contentLayout = contentLayout; params.savePath = savepath; params.category = category; params.tags = tags; diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index efc5de649..38c617c9e 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -43,7 +43,7 @@ #include "base/utils/net.h" #include "base/utils/version.h" -constexpr Utils::Version API_VERSION {2, 6, 2}; +constexpr Utils::Version API_VERSION {2, 7, 0}; class APIController; class WebApplication; diff --git a/src/webui/www/private/download.html b/src/webui/www/private/download.html index 3a625d65f..960aa1dff 100644 --- a/src/webui/www/private/download.html +++ b/src/webui/www/private/download.html @@ -89,11 +89,14 @@ - + - - + diff --git a/src/webui/www/private/scripts/download.js b/src/webui/www/private/scripts/download.js index 1fa8a7e0d..f6198cc23 100644 --- a/src/webui/www/private/scripts/download.js +++ b/src/webui/www/private/scripts/download.js @@ -82,6 +82,16 @@ window.qBittorrent.Download = (function() { else { $('autoTMM').selectedIndex = 0; } + + if (pref.torrent_content_layout === "Subfolder") { + $('contentLayout').selectedIndex = 1; + } + else if (pref.torrent_content_layout === "NoSubfolder") { + $('contentLayout').selectedIndex = 2; + } + else { + $('contentLayout').selectedIndex = 0; + } } }).send(); }; diff --git a/src/webui/www/private/upload.html b/src/webui/www/private/upload.html index 28f98d84e..b73f8545e 100644 --- a/src/webui/www/private/upload.html +++ b/src/webui/www/private/upload.html @@ -77,11 +77,14 @@ - + - - + diff --git a/src/webui/www/private/views/rssDownloader.html b/src/webui/www/private/views/rssDownloader.html index 958f81efc..e4989d193 100644 --- a/src/webui/www/private/views/rssDownloader.html +++ b/src/webui/www/private/views/rssDownloader.html @@ -248,13 +248,14 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also @@ -586,15 +587,18 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also break; } - switch ($('creatSubfolderCombobox').value) { - case 'default': - rulesList[rule].createSubfolder = null; + switch ($('contentLayoutCombobox').value) { + case 'Default': + rulesList[rule].torrentContentLayout = null; break; - case 'always': - rulesList[rule].createSubfolder = true; + case 'Original': + rulesList[rule].torrentContentLayout = 'Original'; break; - case 'never': - rulesList[rule].createSubfolder = false; + case 'Subfolder': + rulesList[rule].torrentContentLayout = 'Subfolder'; + break; + case 'NoSubfolder': + rulesList[rule].torrentContentLayout = 'NoSubfolder'; break; } @@ -660,7 +664,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also $('saveToText').disabled = true; $('ignoreDaysValue').disabled = true; $('addPausedCombobox').disabled = true; - $('creatSubfolderCombobox').disabled = true; + $('contentLayoutCombobox').disabled = true; // reset all boxes $('useRegEx').checked = false; @@ -674,7 +678,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also $('ignoreDaysValue').value = 0; $('lastMatchText').innerHTML = 'QBT_TR(Last Match: Unknown)QBT_TR[CONTEXT=AutomatedRssDownloader]'; $('addPausedCombobox').value = 'default'; - $('creatSubfolderCombobox').value = 'default'; + $('contentLayoutCombobox').value = 'Default'; rssDownloaderFeedSelectionTable.clear(); rssDownloaderArticlesTable.clear(); @@ -696,7 +700,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also $('saveToText').disabled = rulesList[ruleName].savePath ? false : true; $('ignoreDaysValue').disabled = false; $('addPausedCombobox').disabled = false; - $('creatSubfolderCombobox').disabled = false; + $('contentLayoutCombobox').disabled = false; // load rule settings $('useRegEx').checked = rulesList[ruleName].useRegex; @@ -725,10 +729,10 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also else $('addPausedCombobox').value = rulesList[ruleName].addPaused ? 'always' : 'never'; - if (rulesList[ruleName].createSubfolder === null) - $('creatSubfolderCombobox').value = 'default'; + if (rulesList[ruleName].torrentContentLayout === null) + $('contentLayoutCombobox').value = 'Default'; else - $('creatSubfolderCombobox').value = rulesList[ruleName].createSubfolder ? 'always' : 'never'; + $('contentLayoutCombobox').value = rulesList[ruleName].torrentContentLayout; setElementTitles();
- + - + + + +