Browse Source

Allow to assign priority to RSS download rule

PR #19000.
adaptive-webui-19844
Vladimir Golovnev 1 year ago committed by GitHub
parent
commit
a5e8af5070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 75
      src/base/rss/rss_autodownloader.cpp
  2. 6
      src/base/rss/rss_autodownloader.h
  3. 18
      src/base/rss/rss_autodownloadrule.cpp
  4. 3
      src/base/rss/rss_autodownloadrule.h
  5. 1
      src/gui/mainwindow.cpp
  6. 13
      src/gui/rss/automatedrssdownloader.cpp
  7. 33
      src/gui/rss/automatedrssdownloader.ui
  8. 2
      src/webui/api/rsscontroller.cpp

75
src/base/rss/rss_autodownloader.cpp

@ -28,6 +28,8 @@
#include "rss_autodownloader.h" #include "rss_autodownloader.h"
#include <queue>
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
@ -166,25 +168,27 @@ AutoDownloader *AutoDownloader::instance()
bool AutoDownloader::hasRule(const QString &ruleName) const bool AutoDownloader::hasRule(const QString &ruleName) const
{ {
return m_rules.contains(ruleName); return m_rulesByName.contains(ruleName);
} }
AutoDownloadRule AutoDownloader::ruleByName(const QString &ruleName) const AutoDownloadRule AutoDownloader::ruleByName(const QString &ruleName) const
{ {
return m_rules.value(ruleName, AutoDownloadRule(u"Unknown Rule"_qs)); const auto index = m_rulesByName.value(ruleName, -1);
return m_rules.value(index, AutoDownloadRule(u"Unknown Rule"_qs));
} }
QList<AutoDownloadRule> AutoDownloader::rules() const QList<AutoDownloadRule> AutoDownloader::rules() const
{ {
return m_rules.values(); return m_rules;
} }
void AutoDownloader::insertRule(const AutoDownloadRule &rule) void AutoDownloader::setRule(const AutoDownloadRule &rule)
{ {
if (!hasRule(rule.name())) if (!hasRule(rule.name()))
{ {
// Insert new rule // Insert new rule
setRule_impl(rule); setRule_impl(rule);
sortRules();
m_dirty = true; m_dirty = true;
store(); store();
emit ruleAdded(rule.name()); emit ruleAdded(rule.name());
@ -194,6 +198,7 @@ void AutoDownloader::insertRule(const AutoDownloadRule &rule)
{ {
// Update existing rule // Update existing rule
setRule_impl(rule); setRule_impl(rule);
sortRules();
m_dirty = true; m_dirty = true;
storeDeferred(); storeDeferred();
emit ruleChanged(rule.name()); emit ruleChanged(rule.name());
@ -203,12 +208,12 @@ void AutoDownloader::insertRule(const AutoDownloadRule &rule)
bool AutoDownloader::renameRule(const QString &ruleName, const QString &newRuleName) bool AutoDownloader::renameRule(const QString &ruleName, const QString &newRuleName)
{ {
if (!hasRule(ruleName)) return false; if (!hasRule(ruleName) || hasRule(newRuleName))
if (hasRule(newRuleName)) return false; return false;
AutoDownloadRule rule = m_rules.take(ruleName); const auto index = m_rulesByName.take(ruleName);
rule.setName(newRuleName); m_rules[index].setName(newRuleName);
m_rules.insert(newRuleName, rule); m_rulesByName.insert(newRuleName, index);
m_dirty = true; m_dirty = true;
store(); store();
emit ruleRenamed(newRuleName, ruleName); emit ruleRenamed(newRuleName, ruleName);
@ -217,14 +222,22 @@ bool AutoDownloader::renameRule(const QString &ruleName, const QString &newRuleN
void AutoDownloader::removeRule(const QString &ruleName) void AutoDownloader::removeRule(const QString &ruleName)
{ {
if (m_rules.contains(ruleName)) if (!hasRule(ruleName))
{ return;
emit ruleAboutToBeRemoved(ruleName); emit ruleAboutToBeRemoved(ruleName);
m_rules.remove(ruleName);
const auto index = m_rulesByName.take(ruleName);
m_rules.removeAt(index);
for (qsizetype i = index; i < m_rules.size(); ++i)
{
const AutoDownloadRule &rule = m_rules[i];
m_rulesByName[rule.name()] = i;
}
m_dirty = true; m_dirty = true;
store(); store();
} }
}
QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const
{ {
@ -261,7 +274,7 @@ QByteArray AutoDownloader::exportRulesToJSONFormat() const
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data) void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
{ {
for (const auto &rule : asConst(rulesFromJSON(data))) for (const auto &rule : asConst(rulesFromJSON(data)))
insertRule(rule); setRule(rule);
} }
QByteArray AutoDownloader::exportRulesToLegacyFormat() const QByteArray AutoDownloader::exportRulesToLegacyFormat() const
@ -288,7 +301,7 @@ void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
throw ParsingError(tr("Invalid data format")); throw ParsingError(tr("Invalid data format"));
for (const QVariant &val : asConst(dict)) for (const QVariant &val : asConst(dict))
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash())); setRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
} }
QStringList AutoDownloader::smartEpisodeFilters() const QStringList AutoDownloader::smartEpisodeFilters() const
@ -399,7 +412,31 @@ void AutoDownloader::handleFeedURLChanged(Feed *feed, const QString &oldURL)
void AutoDownloader::setRule_impl(const AutoDownloadRule &rule) void AutoDownloader::setRule_impl(const AutoDownloadRule &rule)
{ {
m_rules.insert(rule.name(), rule); const QString ruleName = rule.name();
const auto index = m_rulesByName.value(ruleName, -1);
if (index < 0)
{
m_rules.append(rule);
m_rulesByName[ruleName] = m_rules.size() - 1;
}
else
{
m_rules[index] = rule;
}
}
void AutoDownloader::sortRules()
{
std::sort(m_rules.begin(), m_rules.end(), [](const AutoDownloadRule &lhs, const AutoDownloadRule &rhs)
{
return (lhs.priority() < rhs.priority());
});
for (qsizetype i = 0; i < m_rules.size(); ++i)
{
const AutoDownloadRule &rule = m_rules[i];
m_rulesByName[rule.name()] = i;
}
} }
void AutoDownloader::addJobForArticle(const Article *article) void AutoDownloader::addJobForArticle(const Article *article)
@ -430,6 +467,9 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
m_dirty = true; m_dirty = true;
storeDeferred(); storeDeferred();
LogMsg(tr("RSS article '%1' is accepted by rule '%2'. Trying to add torrent...")
.arg(job->articleData.value(Article::KeyTitle).toString(), rule.name()));
const auto torrentURL = job->articleData.value(Article::KeyTorrentURL).toString(); const auto torrentURL = job->articleData.value(Article::KeyTorrentURL).toString();
BitTorrent::Session::instance()->addTorrent(torrentURL, rule.addTorrentParams()); BitTorrent::Session::instance()->addTorrent(torrentURL, rule.addTorrentParams());
@ -477,6 +517,7 @@ void AutoDownloader::loadRules(const QByteArray &data)
const auto rules = rulesFromJSON(data); const auto rules = rulesFromJSON(data);
for (const auto &rule : rules) for (const auto &rule : rules)
setRule_impl(rule); setRule_impl(rule);
sortRules();
} }
catch (const ParsingError &error) catch (const ParsingError &error)
{ {
@ -493,7 +534,7 @@ void AutoDownloader::loadRulesLegacy()
{ {
const auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash()); const auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
if (!rule.name().isEmpty()) if (!rule.name().isEmpty())
insertRule(rule); setRule(rule);
} }
} }

6
src/base/rss/rss_autodownloader.h

@ -94,7 +94,7 @@ namespace RSS
AutoDownloadRule ruleByName(const QString &ruleName) const; AutoDownloadRule ruleByName(const QString &ruleName) const;
QList<AutoDownloadRule> rules() const; QList<AutoDownloadRule> rules() const;
void insertRule(const AutoDownloadRule &rule); void setRule(const AutoDownloadRule &rule);
bool renameRule(const QString &ruleName, const QString &newRuleName); bool renameRule(const QString &ruleName, const QString &newRuleName);
void removeRule(const QString &ruleName); void removeRule(const QString &ruleName);
@ -118,6 +118,7 @@ namespace RSS
private: private:
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
void setRule_impl(const AutoDownloadRule &rule); void setRule_impl(const AutoDownloadRule &rule);
void sortRules();
void resetProcessingQueue(); void resetProcessingQueue();
void startProcessing(); void startProcessing();
void addJobForArticle(const Article *article); void addJobForArticle(const Article *article);
@ -141,7 +142,8 @@ namespace RSS
QTimer *m_processingTimer = nullptr; QTimer *m_processingTimer = nullptr;
Utils::Thread::UniquePtr m_ioThread; Utils::Thread::UniquePtr m_ioThread;
AsyncFileStorage *m_fileStorage = nullptr; AsyncFileStorage *m_fileStorage = nullptr;
QHash<QString, AutoDownloadRule> m_rules; QList<AutoDownloadRule> m_rules;
QHash<QString, qsizetype> m_rulesByName;
QList<QSharedPointer<ProcessingJob>> m_processingQueue; QList<QSharedPointer<ProcessingJob>> m_processingQueue;
QHash<QString, QSharedPointer<ProcessingJob>> m_waitingJobs; QHash<QString, QSharedPointer<ProcessingJob>> m_waitingJobs;
bool m_dirty = false; bool m_dirty = false;

18
src/base/rss/rss_autodownloadrule.cpp

@ -103,6 +103,7 @@ namespace
const QString S_NAME = u"name"_qs; const QString S_NAME = u"name"_qs;
const QString S_ENABLED = u"enabled"_qs; const QString S_ENABLED = u"enabled"_qs;
const QString S_PRIORITY = u"priority"_qs;
const QString S_USE_REGEX = u"useRegex"_qs; const QString S_USE_REGEX = u"useRegex"_qs;
const QString S_MUST_CONTAIN = u"mustContain"_qs; const QString S_MUST_CONTAIN = u"mustContain"_qs;
const QString S_MUST_NOT_CONTAIN = u"mustNotContain"_qs; const QString S_MUST_NOT_CONTAIN = u"mustNotContain"_qs;
@ -126,6 +127,7 @@ namespace RSS
{ {
QString name; QString name;
bool enabled = true; bool enabled = true;
int priority = 0;
QStringList mustContain; QStringList mustContain;
QStringList mustNotContain; QStringList mustNotContain;
@ -147,6 +149,7 @@ namespace RSS
{ {
return (left.name == right.name) return (left.name == right.name)
&& (left.enabled == right.enabled) && (left.enabled == right.enabled)
&& (left.priority == right.priority)
&& (left.mustContain == right.mustContain) && (left.mustContain == right.mustContain)
&& (left.mustNotContain == right.mustNotContain) && (left.mustNotContain == right.mustNotContain)
&& (left.episodeFilter == right.episodeFilter) && (left.episodeFilter == right.episodeFilter)
@ -457,6 +460,7 @@ QJsonObject AutoDownloadRule::toJsonObject() const
const BitTorrent::AddTorrentParams &addTorrentParams = m_dataPtr->addTorrentParams; const BitTorrent::AddTorrentParams &addTorrentParams = m_dataPtr->addTorrentParams;
return {{S_ENABLED, isEnabled()} return {{S_ENABLED, isEnabled()}
, {S_PRIORITY, priority()}
, {S_USE_REGEX, useRegex()} , {S_USE_REGEX, useRegex()}
, {S_MUST_CONTAIN, mustContain()} , {S_MUST_CONTAIN, mustContain()}
, {S_MUST_NOT_CONTAIN, mustNotContain()} , {S_MUST_NOT_CONTAIN, mustNotContain()}
@ -483,11 +487,13 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
{ {
AutoDownloadRule rule {(name.isEmpty() ? jsonObj.value(S_NAME).toString() : name)}; AutoDownloadRule rule {(name.isEmpty() ? jsonObj.value(S_NAME).toString() : name)};
rule.setEnabled(jsonObj.value(S_ENABLED).toBool(true));
rule.setPriority(jsonObj.value(S_PRIORITY).toInt(0));
rule.setUseRegex(jsonObj.value(S_USE_REGEX).toBool(false)); rule.setUseRegex(jsonObj.value(S_USE_REGEX).toBool(false));
rule.setMustContain(jsonObj.value(S_MUST_CONTAIN).toString()); rule.setMustContain(jsonObj.value(S_MUST_CONTAIN).toString());
rule.setMustNotContain(jsonObj.value(S_MUST_NOT_CONTAIN).toString()); rule.setMustNotContain(jsonObj.value(S_MUST_NOT_CONTAIN).toString());
rule.setEpisodeFilter(jsonObj.value(S_EPISODE_FILTER).toString()); rule.setEpisodeFilter(jsonObj.value(S_EPISODE_FILTER).toString());
rule.setEnabled(jsonObj.value(S_ENABLED).toBool(true));
rule.setLastMatch(QDateTime::fromString(jsonObj.value(S_LAST_MATCH).toString(), Qt::RFC2822Date)); rule.setLastMatch(QDateTime::fromString(jsonObj.value(S_LAST_MATCH).toString(), Qt::RFC2822Date));
rule.setIgnoreDays(jsonObj.value(S_IGNORE_DAYS).toInt()); rule.setIgnoreDays(jsonObj.value(S_IGNORE_DAYS).toInt());
rule.setUseSmartFilter(jsonObj.value(S_SMART_FILTER).toBool(false)); rule.setUseSmartFilter(jsonObj.value(S_SMART_FILTER).toBool(false));
@ -665,6 +671,16 @@ void AutoDownloadRule::setEnabled(const bool enable)
m_dataPtr->enabled = enable; m_dataPtr->enabled = enable;
} }
int AutoDownloadRule::priority() const
{
return m_dataPtr->priority;
}
void AutoDownloadRule::setPriority(const int value)
{
m_dataPtr->priority = value;
}
QDateTime AutoDownloadRule::lastMatch() const QDateTime AutoDownloadRule::lastMatch() const
{ {
return m_dataPtr->lastMatch; return m_dataPtr->lastMatch;

3
src/base/rss/rss_autodownloadrule.h

@ -61,6 +61,9 @@ namespace RSS
bool isEnabled() const; bool isEnabled() const;
void setEnabled(bool enable); void setEnabled(bool enable);
int priority() const;
void setPriority(int value);
QString mustContain() const; QString mustContain() const;
void setMustContain(const QString &tokens); void setMustContain(const QString &tokens);
QString mustNotContain() const; QString mustNotContain() const;

1
src/gui/mainwindow.cpp

@ -223,7 +223,6 @@ MainWindow::MainWindow(IGUIApplication *app, WindowState initialState)
// Transfer List tab // Transfer List tab
m_transferListWidget = new TransferListWidget(hSplitter, this); m_transferListWidget = new TransferListWidget(hSplitter, this);
// m_transferListWidget->setStyleSheet("QTreeView {border: none;}"); // borderless
m_propertiesWidget = new PropertiesWidget(hSplitter); m_propertiesWidget = new PropertiesWidget(hSplitter);
connect(m_transferListWidget, &TransferListWidget::currentTorrentChanged, m_propertiesWidget, &PropertiesWidget::loadTorrentInfos); connect(m_transferListWidget, &TransferListWidget::currentTorrentChanged, m_propertiesWidget, &PropertiesWidget::loadTorrentInfos);
hSplitter->addWidget(m_transferListWidget); hSplitter->addWidget(m_transferListWidget);

13
src/gui/rss/automatedrssdownloader.cpp

@ -78,6 +78,9 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->torrentParametersGroupBox->layout()->addWidget(m_addTorrentParamsWidget); m_ui->torrentParametersGroupBox->layout()->addWidget(m_addTorrentParamsWidget);
m_ui->prioritySpinBox->setMinimum(std::numeric_limits<int>::min());
m_ui->prioritySpinBox->setMaximum(std::numeric_limits<int>::max());
connect(m_ui->addRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onAddRuleBtnClicked); connect(m_ui->addRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onAddRuleBtnClicked);
connect(m_ui->removeRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked); connect(m_ui->removeRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked);
connect(m_ui->exportBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onExportBtnClicked); connect(m_ui->exportBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onExportBtnClicked);
@ -281,6 +284,8 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
{ {
m_currentRule = RSS::AutoDownloader::instance()->ruleByName(m_currentRuleItem->text()); m_currentRule = RSS::AutoDownloader::instance()->ruleByName(m_currentRuleItem->text());
m_ui->prioritySpinBox->setValue(m_currentRule.priority());
m_addTorrentParamsWidget->setAddTorrentParams(m_currentRule.addTorrentParams()); m_addTorrentParamsWidget->setAddTorrentParams(m_currentRule.addTorrentParams());
m_ui->lineContains->setText(m_currentRule.mustContain()); m_ui->lineContains->setText(m_currentRule.mustContain());
@ -324,6 +329,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
void AutomatedRssDownloader::clearRuleDefinitionBox() void AutomatedRssDownloader::clearRuleDefinitionBox()
{ {
m_addTorrentParamsWidget->setAddTorrentParams({}); m_addTorrentParamsWidget->setAddTorrentParams({});
m_ui->prioritySpinBox->setValue(0);
m_ui->lineContains->clear(); m_ui->lineContains->clear();
m_ui->lineNotContains->clear(); m_ui->lineNotContains->clear();
m_ui->lineEFilter->clear(); m_ui->lineEFilter->clear();
@ -342,6 +348,7 @@ void AutomatedRssDownloader::updateEditedRule()
return; return;
m_currentRule.setEnabled(m_currentRuleItem->checkState() != Qt::Unchecked); m_currentRule.setEnabled(m_currentRuleItem->checkState() != Qt::Unchecked);
m_currentRule.setPriority(m_ui->prioritySpinBox->value());
m_currentRule.setUseRegex(m_ui->checkRegex->isChecked()); m_currentRule.setUseRegex(m_ui->checkRegex->isChecked());
m_currentRule.setUseSmartFilter(m_ui->checkSmart->isChecked()); m_currentRule.setUseSmartFilter(m_ui->checkSmart->isChecked());
m_currentRule.setMustContain(m_ui->lineContains->text()); m_currentRule.setMustContain(m_ui->lineContains->text());
@ -357,7 +364,7 @@ void AutomatedRssDownloader::saveEditedRule()
if (!m_currentRuleItem || !m_ui->ruleScrollArea->isEnabled()) return; if (!m_currentRuleItem || !m_ui->ruleScrollArea->isEnabled()) return;
updateEditedRule(); updateEditedRule();
RSS::AutoDownloader::instance()->insertRule(m_currentRule); RSS::AutoDownloader::instance()->setRule(m_currentRule);
} }
void AutomatedRssDownloader::onAddRuleBtnClicked() void AutomatedRssDownloader::onAddRuleBtnClicked()
@ -377,7 +384,7 @@ void AutomatedRssDownloader::onAddRuleBtnClicked()
return; return;
} }
RSS::AutoDownloader::instance()->insertRule(RSS::AutoDownloadRule(ruleName)); RSS::AutoDownloader::instance()->setRule(RSS::AutoDownloadRule(ruleName));
} }
void AutomatedRssDownloader::onRemoveRuleBtnClicked() void AutomatedRssDownloader::onRemoveRuleBtnClicked()
@ -580,7 +587,7 @@ void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feedIte
rule.setFeedURLs(affectedFeeds); rule.setFeedURLs(affectedFeeds);
if (ruleItem != m_currentRuleItem) if (ruleItem != m_currentRuleItem)
RSS::AutoDownloader::instance()->insertRule(rule); RSS::AutoDownloader::instance()->setRule(rule);
else else
m_currentRule = rule; m_currentRule = rule;
} }

33
src/gui/rss/automatedrssdownloader.ui

@ -134,11 +134,38 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>329</width> <width>370</width>
<height>243</height> <height>277</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_8" stretch="0,0,0,0,0,0,1"> <layout class="QVBoxLayout" name="verticalLayout_8" stretch="0,0,0,0,0,0,0,1">
<item>
<layout class="QHBoxLayout" name="priorityLayout">
<item>
<widget class="QLabel" name="priorityLabel">
<property name="text">
<string>Priority:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="prioritySpinBox"/>
</item>
<item>
<spacer name="prioritySpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<widget class="QCheckBox" name="checkRegex"> <widget class="QCheckBox" name="checkRegex">
<property name="text"> <property name="text">

2
src/webui/api/rsscontroller.cpp

@ -150,7 +150,7 @@ void RSSController::setRuleAction()
const QByteArray ruleDef {params()[u"ruleDef"_qs].trimmed().toUtf8()}; const QByteArray ruleDef {params()[u"ruleDef"_qs].trimmed().toUtf8()};
const auto jsonObj = QJsonDocument::fromJson(ruleDef).object(); const auto jsonObj = QJsonDocument::fromJson(ruleDef).object();
RSS::AutoDownloader::instance()->insertRule(RSS::AutoDownloadRule::fromJsonObject(jsonObj, ruleName)); RSS::AutoDownloader::instance()->setRule(RSS::AutoDownloadRule::fromJsonObject(jsonObj, ruleName));
} }
void RSSController::renameRuleAction() void RSSController::renameRuleAction()

Loading…
Cancel
Save