diff --git a/src/rss/automatedrssdownloader.cpp b/src/rss/automatedrssdownloader.cpp index deac84381..843930ea7 100644 --- a/src/rss/automatedrssdownloader.cpp +++ b/src/rss/automatedrssdownloader.cpp @@ -68,6 +68,11 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& m Q_ASSERT(ok); m_ruleList = manager.toStrongRef()->downloadRules(); m_editableRuleList = new RssDownloadRuleList; // Read rule list from disk + m_episodeValidator = new QRegExpValidator( + QRegExp("^(^[1-9]{1,1}\\d{0,3}x([1-9]{1,1}\\d{0,3}(-([1-9]{1,1}\\d{0,3})?)?;){1,}){1,1}", + Qt::CaseInsensitive), + ui->lineEFilter); + ui->lineEFilter->setValidator(m_episodeValidator); initLabelCombobox(); loadFeedList(); loadSettings(); @@ -94,6 +99,8 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& m Q_ASSERT(ok); ok = connect(this, SIGNAL(finished(int)), SLOT(on_finished(int))); Q_ASSERT(ok); + ok = connect(ui->lineEFilter, SIGNAL(textEdited(QString)), SLOT(updateMatchingArticles())); + Q_ASSERT(ok); editHotkey = new QShortcut(QKeySequence("F2"), ui->listRules, 0, 0, Qt::WidgetShortcut); ok = connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRule())); Q_ASSERT(ok); @@ -113,6 +120,7 @@ AutomatedRssDownloader::~AutomatedRssDownloader() delete deleteHotkey; delete ui; delete m_editableRuleList; + delete m_episodeValidator; } void AutomatedRssDownloader::loadSettings() @@ -223,6 +231,11 @@ void AutomatedRssDownloader::updateRuleDefinitionBox() if (rule) { ui->lineContains->setText(rule->mustContain()); ui->lineNotContains->setText(rule->mustNotContain()); + QString ep = rule->episodeFilter(); + if (!ep.isEmpty()) + ui->lineEFilter->setText(ep); + else + ui->lineEFilter->clear(); ui->saveDiffDir_check->setChecked(!rule->savePath().isEmpty()); ui->lineSavePath->setText(fsutils::toNativePath(rule->savePath())); ui->checkRegex->setChecked(rule->useRegex()); @@ -301,6 +314,7 @@ void AutomatedRssDownloader::saveEditedRule() rule->setUseRegex(ui->checkRegex->isChecked()); rule->setMustContain(ui->lineContains->text()); rule->setMustNotContain(ui->lineNotContains->text()); + rule->setEpisodeFilter(ui->lineEFilter->text()); if (ui->saveDiffDir_check->isChecked()) rule->setSavePath(ui->lineSavePath->text()); else diff --git a/src/rss/automatedrssdownloader.h b/src/rss/automatedrssdownloader.h index 1373478d3..9792267b6 100644 --- a/src/rss/automatedrssdownloader.h +++ b/src/rss/automatedrssdownloader.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "rssdownloadrule.h" QT_BEGIN_NAMESPACE @@ -94,6 +95,7 @@ private: QListWidgetItem* m_editedRule; RssDownloadRuleList *m_ruleList; RssDownloadRuleList *m_editableRuleList; + QRegExpValidator *m_episodeValidator; QShortcut *editHotkey; QShortcut *deleteHotkey; }; diff --git a/src/rss/automatedrssdownloader.ui b/src/rss/automatedrssdownloader.ui index 1c37d3a6f..0ad25b5c3 100644 --- a/src/rss/automatedrssdownloader.ui +++ b/src/rss/automatedrssdownloader.ui @@ -216,6 +216,45 @@ + + + + + + + + + + 0 + 0 + + + + + 18 + 18 + + + + + 18 + 18 + + + + + + + + + + + + + Episode filter: + + + diff --git a/src/rss/rssdownloadrule.cpp b/src/rss/rssdownloadrule.cpp index 2be166edb..7e928777c 100644 --- a/src/rss/rssdownloadrule.cpp +++ b/src/rss/rssdownloadrule.cpp @@ -60,6 +60,63 @@ bool RssDownloadRule::matches(const QString &article_title) const return false; } } + if (!m_episodeFilter.isEmpty()) { + qDebug("Checking episode filter"); + QRegExp f("(^\\d{1,4})x(.*;$)"); + int pos = f.indexIn(m_episodeFilter); + if (pos < 0) + return false; + + QString s = f.cap(1); + QStringList eps = f.cap(2).split(";"); + QString expStr; + expStr += "s0?" + s + "[ -_\.]?" + "e0?"; + + foreach (const QString& ep, eps) { + if (ep.isEmpty()) + continue; + + if (ep.indexOf('-') != -1) { // Range detected + QString partialPattern = "s0?" + s + "[ -_\.]?" + "e(0?\\d{1,4})"; + QRegExp reg(partialPattern, Qt::CaseInsensitive); + + if (ep.endsWith('-')) { // Infinite range + int epOurs = ep.left(ep.size() - 1).toInt(); + + // Extract partial match from article and ocmpare as digits + pos = reg.indexIn(article_title); + if (pos != -1) { + int epTheirs = reg.cap(1).toInt(); + if (epTheirs >= epOurs) + return true; + } + } + else { // Normal range + QStringList range = ep.split('-'); + Q_ASSERT(range.size() == 2); + if (range.first().toInt() > range.last().toInt()) + continue; // Ignore this subrule completely + + int epOursFirst = range.first().toInt(); + int epOursLast = range.last().toInt(); + + // Extract partial match from article and ocmpare as digits + pos = reg.indexIn(article_title); + if (pos != -1) { + int epTheirs = reg.cap(1).toInt(); + if (epOursFirst <= epTheirs && epOursLast >= epTheirs) + return true; + } + } + } + else { // Single number + QRegExp reg(expStr + ep + "\\D", Qt::CaseInsensitive); + if (reg.indexIn(article_title) != -1) + return true; + } + } + return false; + } return true; } @@ -86,6 +143,7 @@ RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &rule_has rule->setUseRegex(rule_hash.value("use_regex", false).toBool()); rule->setMustContain(rule_hash.value("must_contain").toString()); rule->setMustNotContain(rule_hash.value("must_not_contain").toString()); + rule->setEpisodeFilter(rule_hash.value("episode_filter").toString()); rule->setRssFeeds(rule_hash.value("affected_feeds").toStringList()); rule->setEnabled(rule_hash.value("enabled", false).toBool()); rule->setSavePath(rule_hash.value("save_path").toString()); @@ -104,6 +162,7 @@ QVariantHash RssDownloadRule::toVariantHash() const hash["enabled"] = m_enabled; hash["label_assigned"] = m_label; hash["use_regex"] = m_useRegex; + hash["episode_filter"] = m_episodeFilter; return hash; } diff --git a/src/rss/rssdownloadrule.h b/src/rss/rssdownloadrule.h index c01022af2..6078de129 100644 --- a/src/rss/rssdownloadrule.h +++ b/src/rss/rssdownloadrule.h @@ -65,6 +65,8 @@ public: inline QString mustNotContain() const { return m_mustNotContain.join(" "); } inline bool useRegex() const { return m_useRegex; } inline void setUseRegex(bool enabled) { m_useRegex = enabled; } + inline QString episodeFilter() const { return m_episodeFilter; } + inline void setEpisodeFilter(const QString& e) { m_episodeFilter = e; } QStringList findMatchingArticles(const RssFeedPtr& feed) const; // Operators bool operator==(const RssDownloadRule &other) const; @@ -73,6 +75,7 @@ private: QString m_name; QStringList m_mustContain; QStringList m_mustNotContain; + QString m_episodeFilter; QString m_savePath; QString m_label; bool m_enabled;