From b8fc4158706278e772806106addc70725d21068c Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 22 Nov 2017 20:25:12 +0300 Subject: [PATCH] Implement Import/Export RSS rules in legacy format --- src/base/rss/rss_autodownloader.cpp | 32 +++++++++++- src/base/rss/rss_autodownloader.h | 3 ++ src/base/rss/rss_autodownloadrule.cpp | 68 +++++++++++++++++++------- src/base/rss/rss_autodownloadrule.h | 4 +- src/gui/rss/automatedrssdownloader.cpp | 61 +++++++++++++++-------- src/gui/rss/automatedrssdownloader.ui | 4 +- 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/src/base/rss/rss_autodownloader.cpp b/src/base/rss/rss_autodownloader.cpp index 7707983de..8edfe2e70 100644 --- a/src/base/rss/rss_autodownloader.cpp +++ b/src/base/rss/rss_autodownloader.cpp @@ -28,6 +28,7 @@ #include "rss_autodownloader.h" +#include #include #include #include @@ -174,6 +175,35 @@ void AutoDownloader::removeRule(const QString &ruleName) } } +QByteArray AutoDownloader::exportRulesToLegacyFormat() const +{ + QVariantHash dict; + for (const auto &rule : rules()) + dict[rule.name()] = rule.toLegacyDict(); + + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << dict; + + return data; +} + +bool AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data) +{ + QDataStream in(data); + in.setVersion(QDataStream::Qt_4_5); + QVariantHash dict; + in >> dict; + if (in.status() != QDataStream::Ok) + return false; + + for (const QVariant &val : dict) + insertRule(AutoDownloadRule::fromLegacyDict(val.toHash())); + + return true; +} + void AutoDownloader::process() { if (m_processingQueue.isEmpty()) return; // processing was disabled @@ -317,7 +347,7 @@ void AutoDownloader::loadRulesLegacy() SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss")); QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash(); foreach (const QVariant &ruleVar, rules) { - auto rule = AutoDownloadRule::fromVariantHash(ruleVar.toHash()); + auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash()); if (!rule.name().isEmpty()) insertRule(rule); } diff --git a/src/base/rss/rss_autodownloader.h b/src/base/rss/rss_autodownloader.h index 50919f978..26947678b 100644 --- a/src/base/rss/rss_autodownloader.h +++ b/src/base/rss/rss_autodownloader.h @@ -73,6 +73,9 @@ namespace RSS bool renameRule(const QString &ruleName, const QString &newRuleName); void removeRule(const QString &ruleName); + QByteArray exportRulesToLegacyFormat() const; + bool importRulesFromLegacyFormat(const QByteArray &data); + signals: void processingStateChanged(bool enabled); void ruleAdded(const QString &ruleName); diff --git a/src/base/rss/rss_autodownloadrule.cpp b/src/base/rss/rss_autodownloadrule.cpp index 48c194621..83a9c343e 100644 --- a/src/base/rss/rss_autodownloadrule.cpp +++ b/src/base/rss/rss_autodownloadrule.cpp @@ -63,11 +63,29 @@ namespace QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool) { switch (static_cast(triStateBool)) { - case 0: return false; break; - case 1: return true; break; + case 0: return false; + case 1: return true; default: return QJsonValue(); } } + + TriStateBool addPausedLegacyToTriStateBool(int val) + { + switch (val) { + case 1: return TriStateBool::True; // always + case 2: return TriStateBool::False; // never + default: return TriStateBool::Undefined; // default + } + } + + int triStateBoolToAddPausedLegacy(const TriStateBool &triStateBool) + { + switch (static_cast(triStateBool)) { + case 0: return 2; // never + case 1: return 1; // always + default: return 0; // default + } + } } const QString Str_Name(QStringLiteral("name")); @@ -378,21 +396,37 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co return rule; } -AutoDownloadRule AutoDownloadRule::fromVariantHash(const QVariantHash &varHash) -{ - AutoDownloadRule rule(varHash.value("name").toString()); - - rule.setUseRegex(varHash.value("use_regex", false).toBool()); - rule.setMustContain(varHash.value("must_contain").toString()); - rule.setMustNotContain(varHash.value("must_not_contain").toString()); - rule.setEpisodeFilter(varHash.value("episode_filter").toString()); - rule.setFeedURLs(varHash.value("affected_feeds").toStringList()); - rule.setEnabled(varHash.value("enabled", false).toBool()); - rule.setSavePath(varHash.value("save_path").toString()); - rule.setCategory(varHash.value("category_assigned").toString()); - rule.setAddPaused(TriStateBool(varHash.value("add_paused").toInt() - 1)); - rule.setLastMatch(varHash.value("last_match").toDateTime()); - rule.setIgnoreDays(varHash.value("ignore_days").toInt()); +QVariantHash AutoDownloadRule::toLegacyDict() const +{ + return {{"name", name()}, + {"must_contain", mustContain()}, + {"must_not_contain", mustNotContain()}, + {"save_path", savePath()}, + {"affected_feeds", feedURLs()}, + {"enabled", isEnabled()}, + {"category_assigned", assignedCategory()}, + {"use_regex", useRegex()}, + {"add_paused", triStateBoolToAddPausedLegacy(addPaused())}, + {"episode_filter", episodeFilter()}, + {"last_match", lastMatch()}, + {"ignore_days", ignoreDays()}}; +} + +AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict) +{ + AutoDownloadRule rule(dict.value("name").toString()); + + rule.setUseRegex(dict.value("use_regex", false).toBool()); + rule.setMustContain(dict.value("must_contain").toString()); + rule.setMustNotContain(dict.value("must_not_contain").toString()); + rule.setEpisodeFilter(dict.value("episode_filter").toString()); + rule.setFeedURLs(dict.value("affected_feeds").toStringList()); + rule.setEnabled(dict.value("enabled", false).toBool()); + rule.setSavePath(dict.value("save_path").toString()); + rule.setCategory(dict.value("category_assigned").toString()); + rule.setAddPaused(addPausedLegacyToTriStateBool(dict.value("add_paused").toInt())); + rule.setLastMatch(dict.value("last_match").toDateTime()); + rule.setIgnoreDays(dict.value("ignore_days").toInt()); return rule; } diff --git a/src/base/rss/rss_autodownloadrule.h b/src/base/rss/rss_autodownloadrule.h index 6b79fe3a5..b28d7b47a 100644 --- a/src/base/rss/rss_autodownloadrule.h +++ b/src/base/rss/rss_autodownloadrule.h @@ -84,7 +84,9 @@ namespace RSS QJsonObject toJsonObject() const; static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = ""); - static AutoDownloadRule fromVariantHash(const QVariantHash &varHash); + + QVariantHash toLegacyDict() const; + static AutoDownloadRule fromLegacyDict(const QVariantHash &dict); private: bool matches(const QString &articleTitle, const QString &expression) const; diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index a97ebcbb9..9f1995c86 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -384,31 +384,50 @@ void AutomatedRssDownloader::on_browseSP_clicked() void AutomatedRssDownloader::on_exportBtn_clicked() { -// if (m_editableRuleList->isEmpty()) { -// QMessageBox::warning(this, tr("Invalid action"), tr("The list is empty, there is nothing to export.")); -// return; -// } -// // Ask for a save path -// QString save_path = QFileDialog::getSaveFileName(this, tr("Where would you like to save the list?"), QDir::homePath(), tr("Rules list (*.rssrules)")); -// if (save_path.isEmpty()) return; -// if (!save_path.endsWith(".rssrules", Qt::CaseInsensitive)) -// save_path += ".rssrules"; -// if (!m_editableRuleList->serialize(save_path)) { -// QMessageBox::warning(this, tr("I/O Error"), tr("Failed to create the destination file")); -// return; -// } + if (RSS::AutoDownloader::instance()->rules().isEmpty()) { + QMessageBox::warning(this, tr("Invalid action") + , tr("The list is empty, there is nothing to export.")); + return; + } + + QString path = QFileDialog::getSaveFileName( + this, tr("Where would you like to save the list?") + , QDir::homePath(), tr("Rules list (legacy)") + QString(" (*.rssrules)")); + if (path.isEmpty()) return; + + if (!path.endsWith(".rssrules", Qt::CaseInsensitive)) + path += ".rssrules"; + + QFile file(path); + if (!file.open(QFile::WriteOnly) + || (file.write(RSS::AutoDownloader::instance()->exportRulesToLegacyFormat()) == -1)) { + QMessageBox::critical( + this, tr("I/O Error") + , tr("Failed to create the destination file. Reason: %1").arg(file.errorString())); + } } void AutomatedRssDownloader::on_importBtn_clicked() { -// // Ask for filter path -// QString load_path = QFileDialog::getOpenFileName(this, tr("Please point to the RSS download rules file"), QDir::homePath(), tr("Rules list") + QString(" (*.rssrules *.filters)")); -// if (load_path.isEmpty() || !QFile::exists(load_path)) return; -// // Load it -// if (!m_editableRuleList->unserialize(load_path)) { -// QMessageBox::warning(this, tr("Import Error"), tr("Failed to import the selected rules file")); -// return; -// } + QString path = QFileDialog::getOpenFileName( + this, tr("Please point to the RSS download rules file") + , QDir::homePath(), tr("Rules list (legacy)") + QString(" (*.rssrules)")); + if (path.isEmpty() || !QFile::exists(path)) + return; + + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::critical( + this, tr("I/O Error") + , tr("Failed to open the file. Reason: %1").arg(file.errorString())); + return; + } + + if (!RSS::AutoDownloader::instance()->importRulesFromLegacyFormat(file.readAll())) { + QMessageBox::critical( + this, tr("Import Error") + , tr("Failed to import the selected rules file.")); + } } void AutomatedRssDownloader::displayRulesListMenu() diff --git a/src/gui/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui index 6c9db0cdf..e77b1385a 100644 --- a/src/gui/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -386,7 +386,7 @@ - false + true &Import... @@ -396,7 +396,7 @@ - false + true &Export...