mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-02-06 03:44:29 +00:00
Made smart episode filter regular expression configurable
This commit is contained in:
parent
2845a791d0
commit
48cbccff1e
@ -64,6 +64,7 @@ const QString ConfFolderName(QStringLiteral("rss"));
|
|||||||
const QString RulesFileName(QStringLiteral("download_rules.json"));
|
const QString RulesFileName(QStringLiteral("download_rules.json"));
|
||||||
|
|
||||||
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing"));
|
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing"));
|
||||||
|
const QString SettingsKey_SmartEpisodeFilter(QStringLiteral("RSS/AutoDownloader/SmartEpisodeFilter"));
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -95,6 +96,11 @@ using namespace RSS;
|
|||||||
|
|
||||||
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
|
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
|
||||||
|
|
||||||
|
QString computeSmartFilterRegex(const QStringList &filters)
|
||||||
|
{
|
||||||
|
return QString("(?:_|\\b)(?:%1)(?:_|\\b)").arg(filters.join(QString(")|(?:")));
|
||||||
|
}
|
||||||
|
|
||||||
AutoDownloader::AutoDownloader()
|
AutoDownloader::AutoDownloader()
|
||||||
: m_processingEnabled(SettingsStorage::instance()->loadValue(SettingsKey_ProcessingEnabled, false).toBool())
|
: m_processingEnabled(SettingsStorage::instance()->loadValue(SettingsKey_ProcessingEnabled, false).toBool())
|
||||||
, m_processingTimer(new QTimer(this))
|
, m_processingTimer(new QTimer(this))
|
||||||
@ -123,6 +129,13 @@ AutoDownloader::AutoDownloader()
|
|||||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed
|
connect(BitTorrent::Session::instance(), &BitTorrent::Session::downloadFromUrlFailed
|
||||||
, this, &AutoDownloader::handleTorrentDownloadFailed);
|
, this, &AutoDownloader::handleTorrentDownloadFailed);
|
||||||
|
|
||||||
|
// initialise the smart episode regex
|
||||||
|
const QString regex = computeSmartFilterRegex(smartEpisodeFilters());
|
||||||
|
m_smartEpisodeRegex = QRegularExpression(regex,
|
||||||
|
QRegularExpression::CaseInsensitiveOption
|
||||||
|
| QRegularExpression::ExtendedPatternSyntaxOption
|
||||||
|
| QRegularExpression::UseUnicodePropertiesOption);
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
m_processingTimer->setSingleShot(true);
|
m_processingTimer->setSingleShot(true);
|
||||||
@ -266,6 +279,37 @@ void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
|
|||||||
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
|
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()
|
void AutoDownloader::process()
|
||||||
{
|
{
|
||||||
if (m_processingQueue.isEmpty()) return; // processing was disabled
|
if (m_processingQueue.isEmpty()) return; // processing was disabled
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
@ -80,6 +81,10 @@ namespace RSS
|
|||||||
bool isProcessingEnabled() const;
|
bool isProcessingEnabled() const;
|
||||||
void setProcessingEnabled(bool enabled);
|
void setProcessingEnabled(bool enabled);
|
||||||
|
|
||||||
|
QStringList smartEpisodeFilters() const;
|
||||||
|
void setSmartEpisodeFilters(const QStringList &filters);
|
||||||
|
QRegularExpression smartEpisodeRegex() const;
|
||||||
|
|
||||||
bool hasRule(const QString &ruleName) const;
|
bool hasRule(const QString &ruleName) const;
|
||||||
AutoDownloadRule ruleByName(const QString &ruleName) const;
|
AutoDownloadRule ruleByName(const QString &ruleName) const;
|
||||||
QList<AutoDownloadRule> rules() const;
|
QList<AutoDownloadRule> rules() const;
|
||||||
@ -132,5 +137,6 @@ namespace RSS
|
|||||||
QHash<QString, QSharedPointer<ProcessingJob>> m_waitingJobs;
|
QHash<QString, QSharedPointer<ProcessingJob>> m_waitingJobs;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
QBasicTimer m_savingTimer;
|
QBasicTimer m_savingTimer;
|
||||||
|
QRegularExpression m_smartEpisodeRegex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include "../utils/string.h"
|
#include "../utils/string.h"
|
||||||
#include "rss_feed.h"
|
#include "rss_feed.h"
|
||||||
#include "rss_article.h"
|
#include "rss_article.h"
|
||||||
|
#include "rss_autodownloader.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -150,40 +151,26 @@ using namespace RSS;
|
|||||||
|
|
||||||
QString computeEpisodeName(const QString &article)
|
QString computeEpisodeName(const QString &article)
|
||||||
{
|
{
|
||||||
const QRegularExpression episodeRegex(
|
const QRegularExpression episodeRegex = AutoDownloader::instance()->smartEpisodeRegex();
|
||||||
"(?:^|[^a-z0-9])(?:"
|
const QRegularExpressionMatch match = episodeRegex.match(article);
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
// See if we can extract an season/episode number or date from the title
|
// See if we can extract an season/episode number or date from the title
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch())
|
||||||
return QString();
|
return QString();
|
||||||
}
|
|
||||||
|
|
||||||
int lastCapturedIndex = match.lastCapturedIndex();
|
QStringList ret;
|
||||||
if (lastCapturedIndex == 5) {
|
for (int i = 1; i <= match.lastCapturedIndex(); ++i) {
|
||||||
return match.captured(5);
|
QString cap = match.captured(i);
|
||||||
}
|
|
||||||
else {
|
if (cap.isEmpty())
|
||||||
return QString("%1x%2").arg(match.captured(lastCapturedIndex - 1).toInt())
|
continue;
|
||||||
.arg(match.captured(lastCapturedIndex).toInt());
|
|
||||||
|
bool isInt = false;
|
||||||
|
int x = cap.toInt(&isInt);
|
||||||
|
|
||||||
|
ret.append(isInt ? QString::number(x) : cap);
|
||||||
}
|
}
|
||||||
|
return ret.join('x');
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoDownloadRule::AutoDownloadRule(const QString &name)
|
AutoDownloadRule::AutoDownloadRule(const QString &name)
|
||||||
@ -383,14 +370,14 @@ bool AutoDownloadRule::matches(const QString &articleTitle) const
|
|||||||
|
|
||||||
if (useSmartFilter()) {
|
if (useSmartFilter()) {
|
||||||
// now see if this episode has been downloaded before
|
// now see if this episode has been downloaded before
|
||||||
QString episodeStr = computeEpisodeName(articleTitle);
|
const QString episodeStr = computeEpisodeName(articleTitle);
|
||||||
|
|
||||||
if (!episodeStr.isEmpty()) {
|
if (!episodeStr.isEmpty()) {
|
||||||
bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr);
|
bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr);
|
||||||
bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive);
|
bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive);
|
||||||
if (previouslyMatched && !isRepack) {
|
if (previouslyMatched && !isRepack)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
m_dataPtr->lastComputedEpisode = episodeStr;
|
m_dataPtr->lastComputedEpisode = episodeStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,6 +372,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
|||||||
// RSS tab
|
// RSS tab
|
||||||
connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
|
connect(m_ui->checkRSSEnable, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton);
|
||||||
connect(m_ui->checkRSSAutoDownloaderEnable, &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->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
|
||||||
connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
|
connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton);
|
||||||
connect(m_ui->btnEditRules, &QPushButton::clicked, [this]() { AutomatedRssDownloader(this).exec(); });
|
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()->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value());
|
||||||
RSS::Session::instance()->setProcessingEnabled(m_ui->checkRSSEnable->isChecked());
|
RSS::Session::instance()->setProcessingEnabled(m_ui->checkRSSEnable->isChecked());
|
||||||
RSS::AutoDownloader::instance()->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->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();
|
auto session = BitTorrent::Session::instance();
|
||||||
|
|
||||||
@ -772,6 +774,8 @@ void OptionsDialog::loadOptions()
|
|||||||
|
|
||||||
m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled());
|
m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled());
|
||||||
m_ui->checkRSSAutoDownloaderEnable->setChecked(RSS::AutoDownloader::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->spinRSSRefreshInterval->setValue(RSS::Session::instance()->refreshInterval());
|
||||||
m_ui->spinRSSMaxArticlesPerFeed->setValue(RSS::Session::instance()->maxArticlesPerFeed());
|
m_ui->spinRSSMaxArticlesPerFeed->setValue(RSS::Session::instance()->maxArticlesPerFeed());
|
||||||
|
|
||||||
|
@ -2699,6 +2699,18 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupRSSSmartEpisodeFilter">
|
||||||
|
<property name="title">
|
||||||
|
<string>RSS Smart Episode Filters</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_31">
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="textSmartEpisodeFilters"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_5">
|
<spacer name="verticalSpacer_5">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@ -2707,7 +2719,7 @@
|
|||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>267</height>
|
<height>200</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
|
@ -532,7 +532,7 @@ void AutomatedRssDownloader::handleRuleCheckStateChange(QListWidgetItem *ruleIte
|
|||||||
|
|
||||||
void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
|
void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
|
||||||
{
|
{
|
||||||
QMessageBox::StandardButton reply = QMessageBox::question(
|
const QMessageBox::StandardButton reply = QMessageBox::question(
|
||||||
this,
|
this,
|
||||||
tr("Clear downloaded episodes"),
|
tr("Clear downloaded episodes"),
|
||||||
tr("Are you sure you want to clear the list of downloaded episodes for the selected rule?"),
|
tr("Are you sure you want to clear the list of downloaded episodes for the selected rule?"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user