Revamp "Automated RSS downloader" dialog

This commit is contained in:
Vladimir Golovnev (Glassez)
2023-04-11 12:17:48 +03:00
parent 0a87bb368f
commit 905f141657
9 changed files with 464 additions and 618 deletions

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017, 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -50,6 +50,7 @@
#include "base/utils/compare.h"
#include "base/utils/io.h"
#include "base/utils/string.h"
#include "gui/addtorrentparamswidget.h"
#include "gui/autoexpandabledialog.h"
#include "gui/torrentcategorydialog.h"
#include "gui/uithememanager.h"
@@ -61,37 +62,44 @@ const QString EXT_LEGACY = u".rssrules"_qs;
AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
: QDialog(parent)
, m_formatFilterJSON(u"%1 (*%2)"_qs.arg(tr("Rules"), EXT_JSON))
, m_formatFilterLegacy(u"%1 (*%2)"_qs.arg(tr("Rules (legacy)"), EXT_LEGACY))
, m_ui(new Ui::AutomatedRssDownloader)
, m_formatFilterJSON {u"%1 (*%2)"_qs.arg(tr("Rules"), EXT_JSON)}
, m_formatFilterLegacy {u"%1 (*%2)"_qs.arg(tr("Rules (legacy)"), EXT_LEGACY)}
, m_ui {new Ui::AutomatedRssDownloader}
, m_addTorrentParamsWidget {new AddTorrentParamsWidget}
, m_storeDialogSize {u"RssFeedDownloader/geometrySize"_qs}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
, m_storeHSplitterSize {u"GUI/Qt6/RSSFeedDownloader/HSplitterSizes"_qs}
, m_storeMainSplitterState {u"GUI/Qt6/RSSFeedDownloader/HSplitterSizes"_qs}
, m_storeRuleDefSplitterState {u"GUI/Qt6/RSSFeedDownloader/RuleDefSplitterState"_qs}
#else
, m_storeHSplitterSize {u"RssFeedDownloader/qt5/hsplitterSizes"_qs}
, m_storeMainSplitterState {u"RssFeedDownloader/qt5/hsplitterSizes"_qs}
, m_storeRuleDefSplitterState {u"RssFeedDownloader/qt5/RuleDefSplitterState"_qs}
#endif
{
m_ui->setupUi(this);
m_ui->torrentParametersGroupBox->layout()->addWidget(m_addTorrentParamsWidget);
connect(m_ui->addRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onAddRuleBtnClicked);
connect(m_ui->removeRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked);
connect(m_ui->exportBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onExportBtnClicked);
connect(m_ui->importBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onImportBtnClicked);
connect(m_ui->renameRuleBtn, &QPushButton::clicked, this, &AutomatedRssDownloader::onRenameRuleBtnClicked);
// Icons
m_ui->renameRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-rename"_qs));
m_ui->removeRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs));
m_ui->addRuleBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
m_ui->addCategoryBtn->setIcon(UIThemeManager::instance()->getIcon(u"list-add"_qs));
// Ui Settings
m_ui->listRules->setSortingEnabled(true);
m_ui->listRules->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->treeMatchingArticles->setSortingEnabled(true);
m_ui->treeMatchingArticles->sortByColumn(0, Qt::AscendingOrder);
m_ui->hsplitter->setCollapsible(0, false);
m_ui->hsplitter->setCollapsible(1, false);
m_ui->hsplitter->setCollapsible(2, true); // Only the preview list is collapsible
m_ui->lineSavePath->setDialogCaption(tr("Destination directory"));
m_ui->lineSavePath->setMode(FileSystemPathEdit::Mode::DirectorySave);
m_ui->ruleList->setSortingEnabled(true);
m_ui->ruleList->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->matchingArticlesTree->setSortingEnabled(true);
m_ui->matchingArticlesTree->sortByColumn(0, Qt::AscendingOrder);
m_ui->mainSplitter->setCollapsible(0, false);
m_ui->mainSplitter->setCollapsible(1, false);
m_ui->mainSplitter->setCollapsible(2, true); // Only the preview list is collapsible
connect(m_ui->checkRegex, &QAbstractButton::toggled, this, &AutomatedRssDownloader::updateFieldsToolTips);
connect(m_ui->listRules, &QWidget::customContextMenuRequested, this, &AutomatedRssDownloader::displayRulesListMenu);
connect(m_ui->ruleList, &QWidget::customContextMenuRequested, this, &AutomatedRssDownloader::displayRulesListMenu);
m_episodeRegex = new QRegularExpression(u"^(^\\d{1,4}x(\\d{1,4}(-(\\d{1,4})?)?;){1,}){1,1}"_qs
, QRegularExpression::CaseInsensitiveOption);
@@ -106,7 +114,6 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
+ u"<li>" + tr("Infinite range: <b>1x25-;</b> matches episodes 25 and upward of season one, and all episodes of later seasons") + u"</li>" + u"</ul></li></ul>";
m_ui->lineEFilter->setToolTip(tip);
initCategoryCombobox();
loadSettings();
connect(RSS::AutoDownloader::instance(), &RSS::AutoDownloader::ruleAdded, this, &AutomatedRssDownloader::handleRuleAdded);
@@ -130,27 +137,28 @@ AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
connect(m_ui->listFeeds, &QListWidget::itemChanged, this, &AutomatedRssDownloader::handleFeedCheckStateChange);
connect(m_ui->listRules, &QListWidget::itemSelectionChanged, this, &AutomatedRssDownloader::updateRuleDefinitionBox);
connect(m_ui->listRules, &QListWidget::itemChanged, this, &AutomatedRssDownloader::handleRuleCheckStateChange);
connect(m_ui->ruleList, &QListWidget::itemSelectionChanged, this, &AutomatedRssDownloader::updateRuleDefinitionBox);
connect(m_ui->ruleList, &QListWidget::itemChanged, this, &AutomatedRssDownloader::handleRuleCheckStateChange);
const auto *editHotkey = new QShortcut(Qt::Key_F2, m_ui->listRules, nullptr, nullptr, Qt::WidgetShortcut);
const auto *editHotkey = new QShortcut(Qt::Key_F2, m_ui->ruleList, nullptr, nullptr, Qt::WidgetShortcut);
connect(editHotkey, &QShortcut::activated, this, &AutomatedRssDownloader::renameSelectedRule);
const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, m_ui->listRules, nullptr, nullptr, Qt::WidgetShortcut);
connect(deleteHotkey, &QShortcut::activated, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, m_ui->ruleList, nullptr, nullptr, Qt::WidgetShortcut);
connect(deleteHotkey, &QShortcut::activated, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked);
connect(m_ui->listRules, &QAbstractItemView::doubleClicked, this, &AutomatedRssDownloader::renameSelectedRule);
connect(m_ui->ruleList, &QAbstractItemView::doubleClicked, this, &AutomatedRssDownloader::renameSelectedRule);
loadFeedList();
m_ui->listRules->blockSignals(true);
m_ui->ruleList->blockSignals(true);
for (const RSS::AutoDownloadRule &rule : asConst(RSS::AutoDownloader::instance()->rules()))
createRuleItem(rule);
m_ui->listRules->blockSignals(false);
m_ui->ruleList->blockSignals(false);
updateRuleDefinitionBox();
if (RSS::AutoDownloader::instance()->isProcessingEnabled())
m_ui->labelWarn->hide();
connect(RSS::AutoDownloader::instance(), &RSS::AutoDownloader::processingStateChanged
, this, &AutomatedRssDownloader::handleProcessingStateChanged);
}
@@ -170,19 +178,23 @@ void AutomatedRssDownloader::loadSettings()
if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid())
resize(dialogSize);
if (const QByteArray hSplitterSize = m_storeHSplitterSize; !hSplitterSize.isEmpty())
m_ui->hsplitter->restoreState(hSplitterSize);
if (const QByteArray mainSplitterSize = m_storeMainSplitterState; !mainSplitterSize.isEmpty())
m_ui->mainSplitter->restoreState(mainSplitterSize);
if (const QByteArray ruleDefSplitterSize = m_storeRuleDefSplitterState; !ruleDefSplitterSize.isEmpty())
m_ui->ruleDefSplitter->restoreState(ruleDefSplitterSize);
}
void AutomatedRssDownloader::saveSettings()
{
m_storeDialogSize = size();
m_storeHSplitterSize = m_ui->hsplitter->saveState();
m_storeMainSplitterState = m_ui->mainSplitter->saveState();
m_storeRuleDefSplitterState = m_ui->ruleDefSplitter->saveState();
}
void AutomatedRssDownloader::createRuleItem(const RSS::AutoDownloadRule &rule)
{
QListWidgetItem *item = new QListWidgetItem(rule.name(), m_ui->listRules);
QListWidgetItem *item = new QListWidgetItem(rule.name(), m_ui->ruleList);
m_itemsByRuleName.insert(rule.name(), item);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(rule.isEnabled() ? Qt::Checked : Qt::Unchecked);
@@ -211,7 +223,7 @@ void AutomatedRssDownloader::updateFeedList()
if (m_currentRuleItem)
selection << m_currentRuleItem;
else
selection = m_ui->listRules->selectedItems();
selection = m_ui->ruleList->selectedItems();
bool enable = !selection.isEmpty();
@@ -248,7 +260,7 @@ void AutomatedRssDownloader::updateFeedList()
void AutomatedRssDownloader::updateRuleDefinitionBox()
{
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
const QList<QListWidgetItem *> selection = m_ui->ruleList->selectedItems();
QListWidgetItem *currentRuleItem = ((selection.count() == 1) ? selection.first() : nullptr);
// Enable the edit rule button but only if we have 1 rule selected
@@ -261,7 +273,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
{
saveEditedRule(); // Save previous rule first
m_currentRuleItem = currentRuleItem;
//m_ui->listRules->setCurrentItem(m_currentRuleItem);
//m_ui->ruleList->setCurrentItem(m_currentRuleItem);
}
// Update rule definition box
@@ -269,31 +281,20 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
{
m_currentRule = RSS::AutoDownloader::instance()->ruleByName(m_currentRuleItem->text());
m_addTorrentParamsWidget->setAddTorrentParams(m_currentRule.addTorrentParams());
m_ui->lineContains->setText(m_currentRule.mustContain());
m_ui->lineNotContains->setText(m_currentRule.mustNotContain());
if (!m_currentRule.episodeFilter().isEmpty())
m_ui->lineEFilter->setText(m_currentRule.episodeFilter());
else
m_ui->lineEFilter->clear();
m_ui->checkBoxSaveDiffDir->setChecked(!m_currentRule.savePath().isEmpty());
m_ui->lineSavePath->setSelectedPath(m_currentRule.savePath());
m_ui->checkRegex->blockSignals(true);
m_ui->checkRegex->setChecked(m_currentRule.useRegex());
m_ui->checkRegex->blockSignals(false);
m_ui->checkSmart->blockSignals(true);
m_ui->checkSmart->setChecked(m_currentRule.useSmartFilter());
m_ui->checkSmart->blockSignals(false);
m_ui->comboCategory->setCurrentIndex(m_ui->comboCategory->findText(m_currentRule.assignedCategory()));
if (m_currentRule.assignedCategory().isEmpty())
m_ui->comboCategory->clearEditText();
int index = 0;
if (m_currentRule.addPaused().has_value())
index = (*m_currentRule.addPaused() ? 1 : 2);
m_ui->comboAddPaused->setCurrentIndex(index);
index = 0;
if (m_currentRule.torrentContentLayout())
index = static_cast<int>(*m_currentRule.torrentContentLayout()) + 1;
m_ui->comboContentLayout->setCurrentIndex(index);
m_ui->spinIgnorePeriod->setValue(m_currentRule.ignoreDays());
QDateTime dateTime = m_currentRule.lastMatch();
QString lMatch;
@@ -307,13 +308,13 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
updateEpisodeFilterValidity();
updateFieldsToolTips(m_ui->checkRegex->isChecked());
m_ui->ruleDefBox->setEnabled(true);
m_ui->ruleScrollArea->setEnabled(true);
}
else
{
m_currentRule = RSS::AutoDownloadRule();
clearRuleDefinitionBox();
m_ui->ruleDefBox->setEnabled(false);
m_ui->ruleScrollArea->setEnabled(false);
}
updateFeedList();
@@ -322,38 +323,23 @@ void AutomatedRssDownloader::updateRuleDefinitionBox()
void AutomatedRssDownloader::clearRuleDefinitionBox()
{
m_addTorrentParamsWidget->setAddTorrentParams({});
m_ui->lineContains->clear();
m_ui->lineNotContains->clear();
m_ui->lineEFilter->clear();
m_ui->checkBoxSaveDiffDir->setChecked(false);
m_ui->lineSavePath->clear();
m_ui->comboCategory->clearEditText();
m_ui->comboCategory->setCurrentIndex(-1);
m_ui->checkRegex->setChecked(false);
m_ui->checkSmart->setChecked(false);
m_ui->spinIgnorePeriod->setValue(0);
m_ui->comboAddPaused->clearEditText();
m_ui->comboAddPaused->setCurrentIndex(-1);
m_ui->comboContentLayout->clearEditText();
m_ui->comboContentLayout->setCurrentIndex(-1);
updateFieldsToolTips(m_ui->checkRegex->isChecked());
updateMustLineValidity();
updateMustNotLineValidity();
updateEpisodeFilterValidity();
}
void AutomatedRssDownloader::initCategoryCombobox()
{
// Load torrent categories
QStringList categories = BitTorrent::Session::instance()->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
m_ui->comboCategory->addItem(u""_qs);
m_ui->comboCategory->addItems(categories);
}
void AutomatedRssDownloader::updateEditedRule()
{
if (!m_currentRuleItem || !m_ui->ruleDefBox->isEnabled()) return;
if (!m_currentRuleItem || !m_ui->ruleScrollArea->isEnabled())
return;
m_currentRule.setEnabled(m_currentRuleItem->checkState() != Qt::Unchecked);
m_currentRule.setUseRegex(m_ui->checkRegex->isChecked());
@@ -361,32 +347,20 @@ void AutomatedRssDownloader::updateEditedRule()
m_currentRule.setMustContain(m_ui->lineContains->text());
m_currentRule.setMustNotContain(m_ui->lineNotContains->text());
m_currentRule.setEpisodeFilter(m_ui->lineEFilter->text());
m_currentRule.setSavePath(m_ui->checkBoxSaveDiffDir->isChecked() ? m_ui->lineSavePath->selectedPath() : Path());
m_currentRule.setCategory(m_ui->comboCategory->currentText());
std::optional<bool> addPaused;
if (m_ui->comboAddPaused->currentIndex() == 1)
addPaused = true;
else if (m_ui->comboAddPaused->currentIndex() == 2)
addPaused = false;
m_currentRule.setAddPaused(addPaused);
std::optional<BitTorrent::TorrentContentLayout> contentLayout;
if (m_ui->comboContentLayout->currentIndex() > 0)
contentLayout = static_cast<BitTorrent::TorrentContentLayout>(m_ui->comboContentLayout->currentIndex() - 1);
m_currentRule.setTorrentContentLayout(contentLayout);
m_currentRule.setIgnoreDays(m_ui->spinIgnorePeriod->value());
m_currentRule.setAddTorrentParams(m_addTorrentParamsWidget->addTorrentParams());
}
void AutomatedRssDownloader::saveEditedRule()
{
if (!m_currentRuleItem || !m_ui->ruleDefBox->isEnabled()) return;
if (!m_currentRuleItem || !m_ui->ruleScrollArea->isEnabled()) return;
updateEditedRule();
RSS::AutoDownloader::instance()->insertRule(m_currentRule);
}
void AutomatedRssDownloader::on_addRuleBtn_clicked()
void AutomatedRssDownloader::onAddRuleBtnClicked()
{
// saveEditedRule();
@@ -406,9 +380,9 @@ void AutomatedRssDownloader::on_addRuleBtn_clicked()
RSS::AutoDownloader::instance()->insertRule(RSS::AutoDownloadRule(ruleName));
}
void AutomatedRssDownloader::on_removeRuleBtn_clicked()
void AutomatedRssDownloader::onRemoveRuleBtnClicked()
{
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
const QList<QListWidgetItem *> selection = m_ui->ruleList->selectedItems();
if (selection.isEmpty()) return;
// Ask for confirmation
@@ -423,23 +397,12 @@ void AutomatedRssDownloader::on_removeRuleBtn_clicked()
RSS::AutoDownloader::instance()->removeRule(item->text());
}
void AutomatedRssDownloader::on_addCategoryBtn_clicked()
{
const QString newCategoryName = TorrentCategoryDialog::createCategory(this);
if (!newCategoryName.isEmpty())
{
m_ui->comboCategory->addItem(newCategoryName);
m_ui->comboCategory->setCurrentText(newCategoryName);
}
}
void AutomatedRssDownloader::on_renameRuleBtn_clicked()
void AutomatedRssDownloader::onRenameRuleBtnClicked()
{
renameSelectedRule();
}
void AutomatedRssDownloader::on_exportBtn_clicked()
void AutomatedRssDownloader::onExportBtnClicked()
{
if (RSS::AutoDownloader::instance()->rules().isEmpty())
{
@@ -482,7 +445,7 @@ void AutomatedRssDownloader::on_exportBtn_clicked()
}
}
void AutomatedRssDownloader::on_importBtn_clicked()
void AutomatedRssDownloader::onImportBtnClicked()
{
QString selectedFilter {m_formatFilterJSON};
const Path path {QFileDialog::getOpenFileName(
@@ -525,16 +488,16 @@ void AutomatedRssDownloader::displayRulesListMenu()
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction(UIThemeManager::instance()->getIcon(u"list-add"_qs), tr("Add new rule...")
, this, &AutomatedRssDownloader::on_addRuleBtn_clicked);
, this, &AutomatedRssDownloader::onAddRuleBtnClicked);
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
const QList<QListWidgetItem *> selection = m_ui->ruleList->selectedItems();
if (!selection.isEmpty())
{
if (selection.count() == 1)
{
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete rule")
, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked);
menu->addSeparator();
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Rename rule...")
, this, &AutomatedRssDownloader::renameSelectedRule);
@@ -542,7 +505,7 @@ void AutomatedRssDownloader::displayRulesListMenu()
else
{
menu->addAction(UIThemeManager::instance()->getIcon(u"edit-clear"_qs, u"list-remove"_qs), tr("Delete selected rules")
, this, &AutomatedRssDownloader::on_removeRuleBtn_clicked);
, this, &AutomatedRssDownloader::onRemoveRuleBtnClicked);
}
menu->addSeparator();
@@ -555,7 +518,7 @@ void AutomatedRssDownloader::displayRulesListMenu()
void AutomatedRssDownloader::renameSelectedRule()
{
const QList<QListWidgetItem *> selection = m_ui->listRules->selectedItems();
const QList<QListWidgetItem *> selection = m_ui->ruleList->selectedItems();
if (selection.isEmpty()) return;
QListWidgetItem *item = selection.first();
@@ -583,7 +546,7 @@ void AutomatedRssDownloader::renameSelectedRule()
void AutomatedRssDownloader::handleRuleCheckStateChange(QListWidgetItem *ruleItem)
{
m_ui->listRules->setCurrentItem(ruleItem);
m_ui->ruleList->setCurrentItem(ruleItem);
}
void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
@@ -604,7 +567,7 @@ void AutomatedRssDownloader::clearSelectedRuleDownloadedEpisodeList()
void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feedItem)
{
const QString feedURL = feedItem->data(Qt::UserRole).toString();
for (QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems()))
for (QListWidgetItem *ruleItem : asConst(m_ui->ruleList->selectedItems()))
{
RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem
? m_currentRule
@@ -627,9 +590,9 @@ void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feedIte
void AutomatedRssDownloader::updateMatchingArticles()
{
m_ui->treeMatchingArticles->clear();
m_ui->matchingArticlesTree->clear();
for (const QListWidgetItem *ruleItem : asConst(m_ui->listRules->selectedItems()))
for (const QListWidgetItem *ruleItem : asConst(m_ui->ruleList->selectedItems()))
{
RSS::AutoDownloadRule rule = (ruleItem == m_currentRuleItem
? m_currentRule
@@ -654,13 +617,13 @@ void AutomatedRssDownloader::updateMatchingArticles()
void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStringList &articles)
{
// Turn off sorting while inserting
m_ui->treeMatchingArticles->setSortingEnabled(false);
m_ui->matchingArticlesTree->setSortingEnabled(false);
// Check if this feed is already in the tree
QTreeWidgetItem *treeFeedItem = nullptr;
for (int i = 0; i < m_ui->treeMatchingArticles->topLevelItemCount(); ++i)
for (int i = 0; i < m_ui->matchingArticlesTree->topLevelItemCount(); ++i)
{
QTreeWidgetItem *item = m_ui->treeMatchingArticles->topLevelItem(i);
QTreeWidgetItem *item = m_ui->matchingArticlesTree->topLevelItem(i);
if (item->data(0, Qt::UserRole).toString() == feed->url())
{
treeFeedItem = item;
@@ -678,7 +641,7 @@ void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStrin
treeFeedItem->setFont(0, f);
treeFeedItem->setData(0, Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"directory"_qs));
treeFeedItem->setData(0, Qt::UserRole, feed->url());
m_ui->treeMatchingArticles->addTopLevelItem(treeFeedItem);
m_ui->matchingArticlesTree->addTopLevelItem(treeFeedItem);
}
// Insert the articles
@@ -695,9 +658,9 @@ void AutomatedRssDownloader::addFeedArticlesToTree(RSS::Feed *feed, const QStrin
}
}
m_ui->treeMatchingArticles->expandItem(treeFeedItem);
m_ui->treeMatchingArticles->sortItems(0, Qt::AscendingOrder);
m_ui->treeMatchingArticles->setSortingEnabled(true);
m_ui->matchingArticlesTree->expandItem(treeFeedItem);
m_ui->matchingArticlesTree->sortItems(0, Qt::AscendingOrder);
m_ui->matchingArticlesTree->setSortingEnabled(true);
}
void AutomatedRssDownloader::updateFieldsToolTips(bool regex)

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017, 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -51,6 +51,8 @@ namespace Ui
class AutomatedRssDownloader;
}
class AddTorrentParamsWidget;
class AutomatedRssDownloader : public QDialog
{
Q_OBJECT
@@ -61,13 +63,11 @@ public:
~AutomatedRssDownloader() override;
private slots:
void on_addRuleBtn_clicked();
void on_removeRuleBtn_clicked();
void on_addCategoryBtn_clicked();
void on_exportBtn_clicked();
void on_importBtn_clicked();
void on_renameRuleBtn_clicked();
void onAddRuleBtnClicked();
void onRemoveRuleBtnClicked();
void onExportBtnClicked();
void onImportBtnClicked();
void onRenameRuleBtnClicked();
void handleRuleCheckStateChange(QListWidgetItem *ruleItem);
void handleFeedCheckStateChange(QListWidgetItem *feedItem);
void displayRulesListMenu();
@@ -90,7 +90,6 @@ private:
void loadSettings();
void saveSettings();
void createRuleItem(const RSS::AutoDownloadRule &rule);
void initCategoryCombobox();
void clearRuleDefinitionBox();
void updateEditedRule();
void updateMatchingArticles();
@@ -103,6 +102,7 @@ private:
const QString m_formatFilterLegacy;
Ui::AutomatedRssDownloader *m_ui = nullptr;
AddTorrentParamsWidget *m_addTorrentParamsWidget = nullptr;
QListWidgetItem *m_currentRuleItem = nullptr;
QSet<std::pair<QString, QString>> m_treeListEntries;
RSS::AutoDownloadRule m_currentRule;
@@ -110,5 +110,6 @@ private:
QRegularExpression *m_episodeRegex = nullptr;
SettingValue<QSize> m_storeDialogSize;
SettingValue<QByteArray> m_storeHSplitterSize;
SettingValue<QByteArray> m_storeMainSplitterState;
SettingValue<QByteArray> m_storeRuleDefSplitterState;
};

View File

@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>818</width>
<height>571</height>
<height>588</height>
</rect>
</property>
<property name="windowTitle">
<string>RSS Downloader</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="labelWarn">
<property name="font">
@@ -33,7 +33,7 @@
</widget>
</item>
<item>
<widget class="QSplitter" name="hsplitter">
<widget class="QSplitter" name="mainSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@@ -43,15 +43,14 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<widget class="QWidget" name="ruleListLayoutWidget">
<layout class="QVBoxLayout" name="ruleListLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="ruleListHeaderLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="ruleListLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@@ -99,7 +98,7 @@
</layout>
</item>
<item>
<widget class="QListWidget" name="listRules">
<widget class="QListWidget" name="ruleList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@@ -107,313 +106,240 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget1">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="ruleDefBox">
<property name="title">
<string>Rule Definition</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="checkRegex">
<property name="text">
<string>Use Regular Expressions</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Must Contain:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Must Not Contain:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblEFilter">
<property name="text">
<string>Episode Filter:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="lblEFilterStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelMustNotStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineContains"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelMustStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="labelEpFilterStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineNotContains"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEFilter"/>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkSmart">
<property name="toolTip">
<string>Smart Episode Filter will check the episode number to prevent downloading of duplicates.
Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also support - as a separator)</string>
</property>
<property name="text">
<string>Use Smart Episode Filter</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboCategory">
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="addCategoryBtn"/>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxSaveDiffDir">
<property name="text">
<string>Save to a Different Directory</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_6">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save to:</string>
</property>
</widget>
</item>
<item>
<widget class="FileSystemPathLineEdit" name="lineSavePath" native="true"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="lblIgnoreDays">
<property name="text">
<string extracomment="... X days">Ignore Subsequent Matches for (0 to Disable)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinIgnorePeriod">
<property name="enabled">
<bool>true</bool>
</property>
<property name="specialValueText">
<string>Disabled</string>
</property>
<property name="suffix">
<string> days</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>365</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblLastMatch">
<property name="enabled">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="lblAddPaused">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add Paused:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboAddPaused">
<item>
<property name="text">
<string>Use global settings</string>
</property>
</item>
<item>
<property name="text">
<string>Always</string>
</property>
</item>
<item>
<property name="text">
<string>Never</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="lblContentLayout">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Torrent content layout:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboContentLayout">
<item>
<property name="text">
<string>Use global settings</string>
</property>
</item>
<item>
<property name="text">
<string>Original</string>
</property>
</item>
<item>
<property name="text">
<string>Create subfolder</string>
</property>
</item>
<item>
<property name="text">
<string>Don't create subfolder</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<widget class="QSplitter" name="ruleDefSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QScrollArea" name="ruleScrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>329</width>
<height>243</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8" stretch="0,0,0,0,0,0,1">
<item>
<widget class="QLabel" name="lblListFeeds">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<widget class="QCheckBox" name="checkRegex">
<property name="text">
<string>Apply Rule to Feeds:</string>
<string>Use Regular Expressions</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFeeds"/>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelMustContain">
<property name="text">
<string>Must Contain:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelMustNotContain">
<property name="text">
<string>Must Not Contain:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblEFilter">
<property name="text">
<string>Episode Filter:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="lblEFilterStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelMustNotStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineContains"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelMustStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="labelEpFilterStat">
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineNotContains"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEFilter"/>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkSmart">
<property name="toolTip">
<string>Smart Episode Filter will check the episode number to prevent downloading of duplicates.
Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also support - as a separator)</string>
</property>
<property name="text">
<string>Use Smart Episode Filter</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="ignorePeriodLayout">
<item>
<widget class="QLabel" name="lblIgnoreDays">
<property name="text">
<string extracomment="... X days">Ignore Subsequent Matches for (0 to Disable)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinIgnorePeriod">
<property name="enabled">
<bool>true</bool>
</property>
<property name="specialValueText">
<string>Disabled</string>
</property>
<property name="suffix">
<string> days</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>365</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblLastMatch">
<property name="enabled">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="torrentParametersGroupBox">
<property name="title">
<string>Torrent parameters</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="lblListFeeds">
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Apply Rule to Feeds:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFeeds"/>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="layoutWidget2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<widget class="QWidget" name="matchingArticlesLayoutWidget">
<layout class="QVBoxLayout" name="matchingArticlesLayout">
<item>
<widget class="QLabel" name="label_3">
<widget class="QLabel" name="matchingArticlesLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@@ -423,7 +349,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeMatchingArticles">
<widget class="QTreeWidget" name="matchingArticlesTree">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
@@ -439,7 +365,7 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<layout class="QHBoxLayout" name="buttonsLayout">
<item>
<widget class="QPushButton" name="importBtn">
<property name="enabled">
@@ -474,31 +400,17 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSystemPathLineEdit</class>
<extends>QWidget</extends>
<header>gui/fspathedit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>renameRuleBtn</tabstop>
<tabstop>removeRuleBtn</tabstop>
<tabstop>addRuleBtn</tabstop>
<tabstop>listRules</tabstop>
<tabstop>checkRegex</tabstop>
<tabstop>checkSmart</tabstop>
<tabstop>ruleList</tabstop>
<tabstop>lineContains</tabstop>
<tabstop>lineNotContains</tabstop>
<tabstop>lineEFilter</tabstop>
<tabstop>comboCategory</tabstop>
<tabstop>checkBoxSaveDiffDir</tabstop>
<tabstop>lineSavePath</tabstop>
<tabstop>spinIgnorePeriod</tabstop>
<tabstop>comboAddPaused</tabstop>
<tabstop>comboContentLayout</tabstop>
<tabstop>listFeeds</tabstop>
<tabstop>treeMatchingArticles</tabstop>
<tabstop>matchingArticlesTree</tabstop>
<tabstop>importBtn</tabstop>
<tabstop>exportBtn</tabstop>
</tabstops>
@@ -536,37 +448,5 @@ Supports the formats: S01E01, 1x1, 2017.12.31 and 31.12.2017 (Date formats also
</hint>
</hints>
</connection>
<connection>
<sender>checkBoxSaveDiffDir</sender>
<signal>toggled(bool)</signal>
<receiver>label_6</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>304</x>
<y>171</y>
</hint>
<hint type="destinationlabel">
<x>377</x>
<y>205</y>
</hint>
</hints>
</connection>
<connection>
<sender>checkBoxSaveDiffDir</sender>
<signal>toggled(bool)</signal>
<receiver>lineSavePath</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>474</x>
<y>174</y>
</hint>
<hint type="destinationlabel">
<x>476</x>
<y>204</y>
</hint>
</hints>
</connection>
</connections>
</ui>