Merge pull request #800 from Gelmir/smart_ep

Implement episode filter for rss downloader
This commit is contained in:
sledgehammer999 2014-11-16 14:48:56 +02:00
commit 9088b1af84
5 changed files with 127 additions and 0 deletions

View file

@ -68,6 +68,21 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer<RssManager>& 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);
QString tip = "<p>" + tr("Matches articles based on episode filter.") + "</p><p><b>" + tr("Example: ") +
"1x2;8-15;5;30-;</b>" + tr(" will match 2, 5, 8 through 15, 30 and onward episodes of season one", "example X will match") + "</p>";
tip += "<p>" + tr("Episode filter rules: ") + "</p><ul><li>" + tr("Season number is a mandatory non-zero value") + "</li>" +
"<li>" + tr("Episode number is a mandatory non-zero value") + "</li>" +
"<li>" + tr("Filter must end with semicolon") + "</li>" +
"<li>" + tr("Three range types for episodes are supported: ") + "</li>" + "<li><ul>"
"<li>" + tr("Single number: <b>1x25;</b> matches episode 25 of season one") + "</li>" +
"<li>" + tr("Normal range: <b>1x25-40;</b> matches episodes 25 through 40 of season one") + "</li>" +
"<li>" + tr("Infinite range: <b>1x25-;</b> matches 40 and onward episodes of season one") + "</li>" + "</ul></li></ul>";
ui->lineEFilter->setToolTip(tip);
initLabelCombobox();
loadFeedList();
loadSettings();
@ -94,6 +109,8 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer<RssManager>& 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 +130,7 @@ AutomatedRssDownloader::~AutomatedRssDownloader()
delete deleteHotkey;
delete ui;
delete m_editableRuleList;
delete m_episodeValidator;
}
void AutomatedRssDownloader::loadSettings()
@ -223,6 +241,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 +324,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

View file

@ -34,6 +34,7 @@
#include <QDialog>
#include <QWeakPointer>
#include <QShortcut>
#include <QRegExpValidator>
#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;
};

View file

@ -216,6 +216,45 @@
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="lineEFilter"/>
</item>
<item>
<widget class="QLabel" name="lblEFilterStat">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblEFilter">
<property name="text">
<string>Episode filter:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View file

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

View file

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