diff --git a/src/base/rss/rss_autodownloader.cpp b/src/base/rss/rss_autodownloader.cpp index be97eee57..8d0a43dc5 100644 --- a/src/base/rss/rss_autodownloader.cpp +++ b/src/base/rss/rss_autodownloader.cpp @@ -64,6 +64,7 @@ const QString ConfFolderName(QStringLiteral("rss")); const QString RulesFileName(QStringLiteral("download_rules.json")); const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing")); +const QString SettingsKey_SmartEpisodeFilter(QStringLiteral("RSS/AutoDownloader/SmartEpisodeFilter")); namespace { @@ -95,6 +96,11 @@ using namespace RSS; QPointer AutoDownloader::m_instance = nullptr; +QString computeSmartFilterRegex(const QStringList &filters) +{ + return QString("(?:_|\\b)(?:%1)(?:_|\\b)").arg(filters.join(QString(")|(?:"))); +} + AutoDownloader::AutoDownloader() : m_processingEnabled(SettingsStorage::instance()->loadValue(SettingsKey_ProcessingEnabled, false).toBool()) , m_processingTimer(new QTimer(this)) @@ -123,6 +129,13 @@ AutoDownloader::AutoDownloader() connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed , this, &AutoDownloader::handleTorrentDownloadFailed); + // initialise the smart episode regex + const QString regex = computeSmartFilterRegex(smartEpisodeFilters()); + m_smartEpisodeRegex = QRegularExpression(regex, + QRegularExpression::CaseInsensitiveOption + | QRegularExpression::ExtendedPatternSyntaxOption + | QRegularExpression::UseUnicodePropertiesOption); + load(); m_processingTimer->setSingleShot(true); @@ -266,6 +279,37 @@ void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data) insertRule(AutoDownloadRule::fromLegacyDict(val.toHash())); } +QStringList AutoDownloader::smartEpisodeFilters() const +{ + const QVariant filtersSetting = SettingsStorage::instance()->loadValue(SettingsKey_SmartEpisodeFilter); + + if (filtersSetting.isNull()) { + QStringList filters = { + "s(\\d+)e(\\d+)", // Format 1: s01e01 + "(\\d+)x(\\d+)", // Format 2: 01x01 + "(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", // Format 3: 2017.01.01 + "(\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4})" // Format 4: 01.01.2017 + }; + + return filters; + } + + return filtersSetting.toStringList(); +} + +QRegularExpression AutoDownloader::smartEpisodeRegex() const +{ + return m_smartEpisodeRegex; +} + +void AutoDownloader::setSmartEpisodeFilters(const QStringList &filters) +{ + SettingsStorage::instance()->storeValue(SettingsKey_SmartEpisodeFilter, filters); + + const QString regex = computeSmartFilterRegex(filters); + m_smartEpisodeRegex.setPattern(regex); +} + void AutoDownloader::process() { if (m_processingQueue.isEmpty()) return; // processing was disabled diff --git a/src/base/rss/rss_autodownloader.h b/src/base/rss/rss_autodownloader.h index ba9c0ac39..5a56b488e 100644 --- a/src/base/rss/rss_autodownloader.h +++ b/src/base/rss/rss_autodownloader.h @@ -35,6 +35,7 @@ #include #include #include +#include #include class QThread; @@ -80,6 +81,10 @@ namespace RSS bool isProcessingEnabled() const; void setProcessingEnabled(bool enabled); + QStringList smartEpisodeFilters() const; + void setSmartEpisodeFilters(const QStringList &filters); + QRegularExpression smartEpisodeRegex() const; + bool hasRule(const QString &ruleName) const; AutoDownloadRule ruleByName(const QString &ruleName) const; QList rules() const; @@ -132,5 +137,6 @@ namespace RSS QHash> m_waitingJobs; bool m_dirty = false; QBasicTimer m_savingTimer; + QRegularExpression m_smartEpisodeRegex; }; } diff --git a/src/base/rss/rss_autodownloadrule.cpp b/src/base/rss/rss_autodownloadrule.cpp index fb92d0212..dda3c2d07 100644 --- a/src/base/rss/rss_autodownloadrule.cpp +++ b/src/base/rss/rss_autodownloadrule.cpp @@ -45,6 +45,7 @@ #include "../utils/string.h" #include "rss_feed.h" #include "rss_article.h" +#include "rss_autodownloader.h" namespace { @@ -150,40 +151,26 @@ using namespace RSS; QString computeEpisodeName(const QString &article) { - const QRegularExpression episodeRegex( - "(?:^|[^a-z0-9])(?:" - - //Format 1: s01e01 - "(?:s(\\d+)e(\\d+))|" - - //Format 2: 01x01 - "(?:(\\d+)x(\\d+))|" - - //Format 3: 2017.01.01 - "((?:\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})|" - - //Format 4: 01.01.2017 - "(?:\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4}))" - - ")(?:[^a-z0-9]|$)", - QRegularExpression::CaseInsensitiveOption - | QRegularExpression::ExtendedPatternSyntaxOption); - - QRegularExpressionMatch match = episodeRegex.match(article); + const QRegularExpression episodeRegex = AutoDownloader::instance()->smartEpisodeRegex(); + const QRegularExpressionMatch match = episodeRegex.match(article); // See if we can extract an season/episode number or date from the title - if (!match.hasMatch()) { + if (!match.hasMatch()) return QString(); - } - int lastCapturedIndex = match.lastCapturedIndex(); - if (lastCapturedIndex == 5) { - return match.captured(5); - } - else { - return QString("%1x%2").arg(match.captured(lastCapturedIndex - 1).toInt()) - .arg(match.captured(lastCapturedIndex).toInt()); + QStringList ret; + for (int i = 1; i <= match.lastCapturedIndex(); ++i) { + QString cap = match.captured(i); + + if (cap.isEmpty()) + continue; + + bool isInt = false; + int x = cap.toInt(&isInt); + + ret.append(isInt ? QString::number(x) : cap); } + return ret.join('x'); } AutoDownloadRule::AutoDownloadRule(const QString &name) @@ -383,14 +370,14 @@ bool AutoDownloadRule::matches(const QString &articleTitle) const if (useSmartFilter()) { // now see if this episode has been downloaded before - QString episodeStr = computeEpisodeName(articleTitle); + const QString episodeStr = computeEpisodeName(articleTitle); if (!episodeStr.isEmpty()) { bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr); bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive); - if (previouslyMatched && !isRepack) { + if (previouslyMatched && !isRepack) return false; - } + m_dataPtr->lastComputedEpisode = episodeStr; } } diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index a5e0b15a8..3397fb29a 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -372,6 +372,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) // RSS tab connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); connect(m_ui->checkRSSAutoDownloaderEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); + connect(m_ui->textSmartEpisodeFilters, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->btnEditRules, &QPushButton::clicked, [this]() { AutomatedRssDownloader(this).exec(); }); @@ -548,6 +549,7 @@ void OptionsDialog::saveOptions() RSS::Session::instance()->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value()); RSS::Session::instance()->setProcessingEnabled(m_ui->checkRSSEnable->isChecked()); RSS::AutoDownloader::instance()->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->isChecked()); + RSS::AutoDownloader::instance()->setSmartEpisodeFilters(m_ui->textSmartEpisodeFilters->toPlainText().split('\n', QString::SplitBehavior::SkipEmptyParts)); auto session = BitTorrent::Session::instance(); @@ -772,6 +774,8 @@ void OptionsDialog::loadOptions() m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled()); m_ui->checkRSSAutoDownloaderEnable->setChecked(RSS::AutoDownloader::instance()->isProcessingEnabled()); + m_ui->textSmartEpisodeFilters->setPlainText(RSS::AutoDownloader::instance()->smartEpisodeFilters().join('\n')); + m_ui->spinRSSRefreshInterval->setValue(RSS::Session::instance()->refreshInterval()); m_ui->spinRSSMaxArticlesPerFeed->setValue(RSS::Session::instance()->maxArticlesPerFeed()); diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index 9118a2fc4..f46de905e 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -2699,6 +2699,18 @@ + + + + RSS Smart Episode Filters + + + + + + + + @@ -2707,7 +2719,7 @@ 20 - 267 + 200 diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index e82b3d096..133659961 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -532,7 +532,7 @@ void AutomatedRssDownloader::handleRuleCheckStateChange(QListWidgetItem *ruleIte void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList() { - QMessageBox::StandardButton reply = QMessageBox::question( + const QMessageBox::StandardButton reply = QMessageBox::question( this, tr("Clear downloaded episodes"), tr("Are you sure you want to clear the list of downloaded episodes for the selected rule?"),