mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-09 14:27:56 +00:00
Made good progress on the new rss feed downloader
This commit is contained in:
parent
d2754fb242
commit
8b83d60732
@ -55,7 +55,7 @@
|
|||||||
enum scheduler_days { EVERY_DAY, WEEK_DAYS, WEEK_ENDS, MON, TUE, WED, THU, FRI, SAT, SUN };
|
enum scheduler_days { EVERY_DAY, WEEK_DAYS, WEEK_ENDS, MON, TUE, WED, THU, FRI, SAT, SUN };
|
||||||
enum maxRatioAction {PAUSE_ACTION, REMOVE_ACTION};
|
enum maxRatioAction {PAUSE_ACTION, REMOVE_ACTION};
|
||||||
namespace Proxy {
|
namespace Proxy {
|
||||||
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5};
|
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Preferences {
|
class Preferences {
|
||||||
@ -1053,6 +1053,27 @@ public:
|
|||||||
return raw_cookies.split(':');
|
return raw_cookies.split(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QStringList getTorrentLabels() {
|
||||||
|
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
return settings.value("TransferListFilters/customLabels").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addTorrentLabel(const QString& label) {
|
||||||
|
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
QStringList labels = settings.value("TransferListFilters/customLabels").toStringList();
|
||||||
|
if(!labels.contains(label))
|
||||||
|
labels << label;
|
||||||
|
settings.setValue("TransferListFilters/customLabels", labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeTorrentLabel(const QString& label) {
|
||||||
|
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
QStringList labels = settings.value("TransferListFilters/customLabels").toStringList();
|
||||||
|
if(labels.contains(label))
|
||||||
|
labels.removeOne(label);
|
||||||
|
settings.setValue("TransferListFilters/customLabels", labels);
|
||||||
|
}
|
||||||
|
|
||||||
static void setHostNameCookies(QString host_name, const QList<QByteArray> &cookies) {
|
static void setHostNameCookies(QString host_name, const QList<QByteArray> &cookies) {
|
||||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
QMap<QString, QVariant> hosts_table = qBTRSS.value("hosts_cookies", QMap<QString, QVariant>()).toMap();
|
QMap<QString, QVariant> hosts_table = qBTRSS.value("hosts_cookies", QMap<QString, QVariant>()).toMap();
|
||||||
|
@ -247,11 +247,8 @@ void QBtSession::handleDownloadFailure(QString url, QString reason) {
|
|||||||
emit downloadFromUrlFailure(url, reason);
|
emit downloadFromUrlFailure(url, reason);
|
||||||
// Clean up
|
// Clean up
|
||||||
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
|
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
|
||||||
const int index = url_skippingDlg.indexOf(qurl);
|
url_skippingDlg.removeOne(qurl);
|
||||||
if(index >= 0)
|
savepathLabel_fromurl.remove(qurl);
|
||||||
url_skippingDlg.removeAt(index);
|
|
||||||
if(savepath_fromurl.contains(qurl))
|
|
||||||
savepath_fromurl.remove(qurl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QBtSession::startTorrentsInPause(bool b) {
|
void QBtSession::startTorrentsInPause(bool b) {
|
||||||
@ -1052,9 +1049,15 @@ QTorrentHandle QBtSession::addTorrent(QString path, bool fromScanDir, QString fr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
QString savePath;
|
QString savePath;
|
||||||
if(!from_url.isEmpty() && savepath_fromurl.contains(QUrl::fromEncoded(from_url.toLocal8Bit()))) {
|
if(!from_url.isEmpty() && savepathLabel_fromurl.contains(QUrl::fromEncoded(from_url.toLocal8Bit()))) {
|
||||||
// Enforcing the save path defined before URL download (from RSS for example)
|
// Enforcing the save path defined before URL download (from RSS for example)
|
||||||
savePath = savepath_fromurl.take(QUrl::fromEncoded(from_url.toLocal8Bit()));
|
QPair<QString, QString> savePath_label = savepathLabel_fromurl.take(QUrl::fromEncoded(from_url.toLocal8Bit()));
|
||||||
|
if(savePath_label.first.isEmpty())
|
||||||
|
savePath = getSavePath(hash, fromScanDir, path, root_folder);
|
||||||
|
else
|
||||||
|
savePath = savePath_label.first;
|
||||||
|
// Remember label
|
||||||
|
TorrentTempData::setLabel(hash, savePath_label.second);
|
||||||
} else {
|
} else {
|
||||||
savePath = getSavePath(hash, fromScanDir, path, root_folder);
|
savePath = getSavePath(hash, fromScanDir, path, root_folder);
|
||||||
}
|
}
|
||||||
@ -2154,7 +2157,7 @@ void QBtSession::readAlerts() {
|
|||||||
QTorrentHandle h(p->handle);
|
QTorrentHandle h(p->handle);
|
||||||
if(h.is_valid()) {
|
if(h.is_valid()) {
|
||||||
// Attempt to remove old folder if empty
|
// Attempt to remove old folder if empty
|
||||||
const QString& old_save_path = TorrentPersistentData::getPreviousPath(h.hash());
|
const QString old_save_path = TorrentPersistentData::getPreviousPath(h.hash());
|
||||||
const QString new_save_path = misc::toQStringU(p->path.c_str());
|
const QString new_save_path = misc::toQStringU(p->path.c_str());
|
||||||
qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path));
|
qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path));
|
||||||
QDir old_save_dir(old_save_path);
|
QDir old_save_dir(old_save_path);
|
||||||
@ -2489,11 +2492,11 @@ void QBtSession::addMagnetSkipAddDlg(QString uri) {
|
|||||||
addMagnetUri(uri, false);
|
addMagnetUri(uri, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QBtSession::downloadUrlAndSkipDialog(QString url, QString save_path) {
|
void QBtSession::downloadUrlAndSkipDialog(QString url, QString save_path, QString label) {
|
||||||
//emit aboutToDownloadFromUrl(url);
|
//emit aboutToDownloadFromUrl(url);
|
||||||
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
|
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
|
||||||
if(!save_path.isEmpty())
|
if(!save_path.isEmpty())
|
||||||
savepath_fromurl[qurl] = save_path;
|
savepathLabel_fromurl[qurl] = qMakePair(save_path, label);
|
||||||
url_skippingDlg << qurl;
|
url_skippingDlg << qurl;
|
||||||
// Launch downloader thread
|
// Launch downloader thread
|
||||||
downloader->downloadUrl(url);
|
downloader->downloadUrl(url);
|
||||||
|
@ -119,7 +119,7 @@ public slots:
|
|||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
void setQueueingEnabled(bool enable);
|
void setQueueingEnabled(bool enable);
|
||||||
void handleDownloadFailure(QString url, QString reason);
|
void handleDownloadFailure(QString url, QString reason);
|
||||||
void downloadUrlAndSkipDialog(QString url, QString save_path=QString::null);
|
void downloadUrlAndSkipDialog(QString url, QString save_path, QString label);
|
||||||
// Session configuration - Setters
|
// Session configuration - Setters
|
||||||
void setListeningPort(int port);
|
void setListeningPort(int port);
|
||||||
void setMaxConnections(int maxConnec);
|
void setMaxConnections(int maxConnec);
|
||||||
@ -209,7 +209,7 @@ private:
|
|||||||
session *s;
|
session *s;
|
||||||
QPointer<QTimer> timerAlerts;
|
QPointer<QTimer> timerAlerts;
|
||||||
QPointer<BandwidthScheduler> bd_scheduler;
|
QPointer<BandwidthScheduler> bd_scheduler;
|
||||||
QMap<QUrl, QString> savepath_fromurl;
|
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl;
|
||||||
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
||||||
QHash<QString, QString> savePathsToRemove;
|
QHash<QString, QString> savePathsToRemove;
|
||||||
QStringList torrentsToPausedAfterChecking;
|
QStringList torrentsToPausedAfterChecking;
|
||||||
|
@ -28,32 +28,230 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include "automatedrssdownloader.h"
|
#include "automatedrssdownloader.h"
|
||||||
#include "ui_automatedrssdownloader.h"
|
#include "ui_automatedrssdownloader.h"
|
||||||
#include "rssfilters.h"
|
#include "rssfilters.h"
|
||||||
#include "rsssettings.h"
|
#include "rsssettings.h"
|
||||||
|
#include "rssdownloadrulelist.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "qinisettings.h"
|
||||||
|
|
||||||
AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) :
|
AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::AutomatedRssDownloader)
|
ui(new Ui::AutomatedRssDownloader)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
loadSettings();
|
ui->listRules->setSortingEnabled(true);
|
||||||
//filters = RssFilters::getFeedFilters(feed_url);
|
m_ruleList = RssDownloadRuleList::instance();
|
||||||
|
initLabelCombobox();
|
||||||
|
loadFeedList();
|
||||||
|
loadSettings();
|
||||||
|
connect(ui->listRules, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(updateRuleDefinitionBox(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
connect(ui->listRules, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(updateFeedList(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
if(ui->listRules->count() > 0)
|
||||||
|
ui->listRules->setCurrentRow(0);
|
||||||
|
else
|
||||||
|
updateRuleDefinitionBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
AutomatedRssDownloader::~AutomatedRssDownloader()
|
AutomatedRssDownloader::~AutomatedRssDownloader()
|
||||||
{
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
// Save current item on exit
|
||||||
|
saveCurrentRule(ui->listRules->currentItem());
|
||||||
saveSettings();
|
saveSettings();
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutomatedRssDownloader::loadSettings()
|
void AutomatedRssDownloader::loadSettings()
|
||||||
{
|
{
|
||||||
|
// TODO: load dialog size and pos
|
||||||
ui->checkEnableDownloader->setChecked(RssSettings::isRssDownloadingEnabled());
|
ui->checkEnableDownloader->setChecked(RssSettings::isRssDownloadingEnabled());
|
||||||
|
// Display download rules
|
||||||
|
loadRulesList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutomatedRssDownloader::saveSettings()
|
void AutomatedRssDownloader::saveSettings()
|
||||||
{
|
{
|
||||||
RssSettings::setRssDownloadingEnabled(ui->checkEnableDownloader->isChecked());
|
RssSettings::setRssDownloadingEnabled(ui->checkEnableDownloader->isChecked());
|
||||||
|
// TODO: Save dialog size and pos
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::loadRulesList()
|
||||||
|
{
|
||||||
|
foreach (const QString &rule_name, m_ruleList->ruleNames()) {
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(rule_name, ui->listRules);
|
||||||
|
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||||
|
if(m_ruleList->getRule(rule_name).isEnabled())
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
else
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::loadFeedList()
|
||||||
|
{
|
||||||
|
const QStringList feed_aliases = RssSettings::getRssFeedsAliases();
|
||||||
|
const QStringList feed_urls = RssSettings::getRssFeedsUrls();
|
||||||
|
for(int i=0; i<feed_aliases.size(); ++i) {
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(feed_aliases.at(i), ui->listFeeds);
|
||||||
|
item->setData(Qt::UserRole, feed_urls.at(i));
|
||||||
|
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AutomatedRssDownloader::getSelectedFeeds() const
|
||||||
|
{
|
||||||
|
QStringList feeds;
|
||||||
|
for(int i=0; i<ui->listFeeds->count(); ++i) {
|
||||||
|
QListWidgetItem *item = ui->listFeeds->item(i);
|
||||||
|
if(item->checkState() != Qt::Unchecked)
|
||||||
|
feeds << item->data(Qt::UserRole).toString();
|
||||||
|
}
|
||||||
|
return feeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::updateFeedList(QListWidgetItem* current, QListWidgetItem* previous)
|
||||||
|
{
|
||||||
|
Q_UNUSED(previous);
|
||||||
|
RssDownloadRule rule = getCurrentRule();
|
||||||
|
const QStringList affected_feeds = rule.rssFeeds();
|
||||||
|
for(int i=0; i<ui->listFeeds->count(); ++i) {
|
||||||
|
QListWidgetItem *item = ui->listFeeds->item(i);
|
||||||
|
const QString feed_url = item->data(Qt::UserRole).toString();
|
||||||
|
if(affected_feeds.contains(feed_url))
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
else
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
ui->listFeeds->setEnabled(current != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutomatedRssDownloader::isRssDownloaderEnabled() const
|
||||||
|
{
|
||||||
|
return ui->checkEnableDownloader->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::updateRuleDefinitionBox(QListWidgetItem* current, QListWidgetItem* previous)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << current << previous;
|
||||||
|
// Save previous item
|
||||||
|
saveCurrentRule(previous);
|
||||||
|
// Update rule definition box
|
||||||
|
RssDownloadRule rule = getCurrentRule();
|
||||||
|
if(rule.isValid()) {
|
||||||
|
ui->lineContains->setText(rule.mustContain());
|
||||||
|
ui->lineNotContains->setText(rule.mustNotContain());
|
||||||
|
ui->saveDiffDir_check->setChecked(!rule.savePath().isEmpty());
|
||||||
|
ui->lineSavePath->setText(rule.savePath());
|
||||||
|
if(rule.label().isEmpty()) {
|
||||||
|
ui->comboLabel->setCurrentIndex(-1);
|
||||||
|
ui->comboLabel->clearEditText();
|
||||||
|
} else {
|
||||||
|
ui->comboLabel->setCurrentIndex(ui->comboLabel->findText(rule.label()));
|
||||||
|
}
|
||||||
|
// Enable
|
||||||
|
ui->ruleDefBox->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
// Clear
|
||||||
|
ui->lineNotContains->clear();
|
||||||
|
ui->saveDiffDir_check->setChecked(false);
|
||||||
|
ui->lineSavePath->clear();
|
||||||
|
ui->comboLabel->clearEditText();
|
||||||
|
if(current) {
|
||||||
|
// Use the rule name as a default for the "contains" field
|
||||||
|
ui->lineContains->setText(current->text());
|
||||||
|
ui->ruleDefBox->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
ui->lineContains->clear();
|
||||||
|
ui->ruleDefBox->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RssDownloadRule AutomatedRssDownloader::getCurrentRule() const
|
||||||
|
{
|
||||||
|
QListWidgetItem * current_item = ui->listRules->currentItem();
|
||||||
|
if(current_item)
|
||||||
|
return m_ruleList->getRule(current_item->text());
|
||||||
|
return RssDownloadRule();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::initLabelCombobox()
|
||||||
|
{
|
||||||
|
// Load custom labels
|
||||||
|
const QStringList customLabels = Preferences::getTorrentLabels();
|
||||||
|
foreach(const QString& label, customLabels) {
|
||||||
|
ui->comboLabel->addItem(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::saveCurrentRule(QListWidgetItem * item)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << item;
|
||||||
|
if(!item) return;
|
||||||
|
RssDownloadRule rule = m_ruleList->getRule(item->text());
|
||||||
|
if(!rule.isValid()) {
|
||||||
|
rule.setName(item->text());
|
||||||
|
}
|
||||||
|
if(item->checkState() == Qt::Unchecked)
|
||||||
|
rule.setEnabled(false);
|
||||||
|
else
|
||||||
|
rule.setEnabled(true);
|
||||||
|
rule.setMustContain(ui->lineContains->text());
|
||||||
|
rule.setMustNotContain(ui->lineNotContains->text());
|
||||||
|
if(ui->saveDiffDir_check->isChecked())
|
||||||
|
rule.setSavePath(ui->lineSavePath->text());
|
||||||
|
else
|
||||||
|
rule.setSavePath("");
|
||||||
|
rule.setLabel(ui->comboLabel->currentText());
|
||||||
|
// Save new label
|
||||||
|
if(!rule.label().isEmpty())
|
||||||
|
Preferences::addTorrentLabel(rule.label());
|
||||||
|
rule.setRssFeeds(getSelectedFeeds());
|
||||||
|
// Save it
|
||||||
|
m_ruleList->saveRule(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::on_addRuleBtn_clicked()
|
||||||
|
{
|
||||||
|
// Ask for a rule name
|
||||||
|
const QString rule = QInputDialog::getText(this, tr("New rule name"), tr("Please type the name of the new download rule."));
|
||||||
|
if(rule.isEmpty()) return;
|
||||||
|
// Check if this rule name already exists
|
||||||
|
if(m_ruleList->getRule(rule).isValid()) {
|
||||||
|
QMessageBox::warning(this, tr("Rule name conflict"), tr("A rule with this name already exists, please choose another name."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Add the new rule to the list
|
||||||
|
QListWidgetItem * item = new QListWidgetItem(rule, ui->listRules);
|
||||||
|
item->setFlags(item->flags()|Qt::ItemIsUserCheckable);
|
||||||
|
item->setCheckState(Qt::Checked); // Enable as a default
|
||||||
|
ui->listRules->setCurrentItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::on_removeRuleBtn_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem * item = ui->listRules->currentItem();
|
||||||
|
if(!item) return;
|
||||||
|
// Ask for confirmation
|
||||||
|
if(QMessageBox::question(this, tr("Rule deletion confirmation"), tr("Are you sure you want to remove the download rule named %1?").arg(item->text())) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
// Actually remove the item
|
||||||
|
ui->listRules->removeItemWidget(item);
|
||||||
|
// Clean up memory
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutomatedRssDownloader::on_browseSP_clicked()
|
||||||
|
{
|
||||||
|
QString save_path = QFileDialog::getExistingDirectory(this, tr("Destination directory"), QDir::homePath());
|
||||||
|
if(!save_path.isEmpty())
|
||||||
|
ui->lineSavePath->setText(save_path);
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,15 @@
|
|||||||
#define AUTOMATEDRSSDOWNLOADER_H
|
#define AUTOMATEDRSSDOWNLOADER_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include "rssdownloadrule.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class AutomatedRssDownloader;
|
class AutomatedRssDownloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RssDownloadRuleList;
|
||||||
|
class QListWidgetItem;
|
||||||
|
|
||||||
class AutomatedRssDownloader : public QDialog
|
class AutomatedRssDownloader : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -44,13 +48,30 @@ class AutomatedRssDownloader : public QDialog
|
|||||||
public:
|
public:
|
||||||
explicit AutomatedRssDownloader(QWidget *parent = 0);
|
explicit AutomatedRssDownloader(QWidget *parent = 0);
|
||||||
~AutomatedRssDownloader();
|
~AutomatedRssDownloader();
|
||||||
|
bool isRssDownloaderEnabled() const;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
|
void loadRulesList();
|
||||||
|
void updateRuleDefinitionBox(QListWidgetItem* current = 0, QListWidgetItem* previous = 0);
|
||||||
|
void saveCurrentRule(QListWidgetItem * item);
|
||||||
|
void loadFeedList();
|
||||||
|
void updateFeedList(QListWidgetItem* current, QListWidgetItem* previous);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_addRuleBtn_clicked();
|
||||||
|
void on_removeRuleBtn_clicked();
|
||||||
|
void on_browseSP_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
RssDownloadRule getCurrentRule() const;
|
||||||
|
void initLabelCombobox();
|
||||||
|
QStringList getSelectedFeeds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::AutomatedRssDownloader *ui;
|
Ui::AutomatedRssDownloader *ui;
|
||||||
|
RssDownloadRuleList *m_ruleList;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AUTOMATEDRSSDOWNLOADER_H
|
#endif // AUTOMATEDRSSDOWNLOADER_H
|
||||||
|
@ -161,23 +161,33 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="lineNotContains"/>
|
<widget class="QLineEdit" name="lineNotContains"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Save torrent to:</string>
|
<string>Save to:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="4" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="lineSavePath"/>
|
<widget class="QLineEdit" name="lineSavePath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="browseSP">
|
<widget class="QToolButton" name="browseSP">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
@ -185,24 +195,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="6" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>Assign label:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QComboBox" name="comboLabel">
|
|
||||||
<property name="editable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0" colspan="2">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
@ -222,6 +215,30 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="saveDiffDir_check">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save to a different directory</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Assign label:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="comboLabel">
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -259,14 +276,45 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<property name="orientation">
|
<item>
|
||||||
<enum>Qt::Horizontal</enum>
|
<widget class="QPushButton" name="importBtn">
|
||||||
</property>
|
<property name="text">
|
||||||
<property name="standardButtons">
|
<string>Import...</string>
|
||||||
<set>QDialogButtonBox::Close</set>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="exportBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -281,8 +329,8 @@
|
|||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>750</x>
|
||||||
<y>254</y>
|
<y>483</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
@ -297,8 +345,8 @@
|
|||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>805</x>
|
||||||
<y>260</y>
|
<y>483</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
@ -306,5 +354,53 @@
|
|||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>saveDiffDir_check</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>saveDiffDir_check</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>
|
||||||
|
<connection>
|
||||||
|
<sender>saveDiffDir_check</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>browseSP</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>544</x>
|
||||||
|
<y>166</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>549</x>
|
||||||
|
<y>209</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -89,6 +89,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="rssDownloaderBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>RSS Downloader...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="settingsButton">
|
<widget class="QPushButton" name="settingsButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -275,15 +282,6 @@ p, li { white-space: pre-wrap; }
|
|||||||
<string>Copy feed URL</string>
|
<string>Copy feed URL</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionRSS_feed_downloader">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../icons.qrc">
|
|
||||||
<normaloff>:/Icons/oxygen/download.png</normaloff>:/Icons/oxygen/download.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>RSS feed downloader...</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionNew_folder">
|
<action name="actionNew_folder">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../icons.qrc">
|
<iconset resource="../icons.qrc">
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
#include <QDragMoveEvent>
|
#include <QDragMoveEvent>
|
||||||
|
|
||||||
#include "rss_imp.h"
|
#include "rss_imp.h"
|
||||||
#include "feeddownloader.h"
|
|
||||||
#include "feedlistwidget.h"
|
#include "feedlistwidget.h"
|
||||||
#include "qbtsession.h"
|
#include "qbtsession.h"
|
||||||
#include "cookiesdlg.h"
|
#include "cookiesdlg.h"
|
||||||
@ -48,6 +47,7 @@
|
|||||||
#include "rssfolder.h"
|
#include "rssfolder.h"
|
||||||
#include "rssarticle.h"
|
#include "rssarticle.h"
|
||||||
#include "rssfeed.h"
|
#include "rssfeed.h"
|
||||||
|
#include "automatedrssdownloader.h"
|
||||||
|
|
||||||
enum NewsCols { NEWS_ICON, NEWS_TITLE_COL, NEWS_URL_COL, NEWS_ID };
|
enum NewsCols { NEWS_ICON, NEWS_TITLE_COL, NEWS_URL_COL, NEWS_ID };
|
||||||
|
|
||||||
@ -79,10 +79,6 @@ void RSSImp::displayRSSListMenu(const QPoint& pos){
|
|||||||
if(listStreams->getItemType(selectedItems.first()) == RssFile::FEED) {
|
if(listStreams->getItemType(selectedItems.first()) == RssFile::FEED) {
|
||||||
myRSSListMenu.addSeparator();
|
myRSSListMenu.addSeparator();
|
||||||
myRSSListMenu.addAction(actionCopy_feed_URL);
|
myRSSListMenu.addAction(actionCopy_feed_URL);
|
||||||
if(selectedItems.size() == 1) {
|
|
||||||
myRSSListMenu.addSeparator();
|
|
||||||
myRSSListMenu.addAction(actionRSS_feed_downloader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
myRSSListMenu.addAction(actionNew_subscription);
|
myRSSListMenu.addAction(actionNew_subscription);
|
||||||
@ -393,15 +389,6 @@ void RSSImp::copySelectedFeedsURL() {
|
|||||||
qApp->clipboard()->setText(URLs.join("\n"));
|
qApp->clipboard()->setText(URLs.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RSSImp::showFeedDownloader() {
|
|
||||||
QTreeWidgetItem* item = listStreams->selectedItems()[0];
|
|
||||||
RssFile* rss_item = listStreams->getRSSItem(item);
|
|
||||||
if(rss_item->getType() == RssFile::FEED) {
|
|
||||||
FeedDownloaderDlg* feedDownloader = new FeedDownloaderDlg(this, listStreams->getItemID(item), rss_item->getName(), BTSession);
|
|
||||||
connect(feedDownloader, SIGNAL(filteringEnabled()), this, SLOT(on_updateAllButton_clicked()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RSSImp::on_markReadButton_clicked() {
|
void RSSImp::on_markReadButton_clicked() {
|
||||||
QList<QTreeWidgetItem*> selectedItems = listStreams->selectedItems();
|
QList<QTreeWidgetItem*> selectedItems = listStreams->selectedItems();
|
||||||
QTreeWidgetItem* item;
|
QTreeWidgetItem* item;
|
||||||
@ -620,7 +607,6 @@ RSSImp::RSSImp(QBtSession *BTSession) : QWidget(), BTSession(BTSession){
|
|||||||
connect(actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked()));
|
connect(actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked()));
|
||||||
connect(actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(on_updateAllButton_clicked()));
|
connect(actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(on_updateAllButton_clicked()));
|
||||||
connect(actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL()));
|
connect(actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL()));
|
||||||
connect(actionRSS_feed_downloader, SIGNAL(triggered()), this, SLOT(showFeedDownloader()));
|
|
||||||
connect(actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked()));
|
connect(actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked()));
|
||||||
// News list actions
|
// News list actions
|
||||||
connect(actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openNewsUrl()));
|
connect(actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openNewsUrl()));
|
||||||
@ -658,3 +644,11 @@ void RSSImp::on_settingsButton_clicked() {
|
|||||||
if(dlg.exec())
|
if(dlg.exec())
|
||||||
updateRefreshInterval(Preferences::getRefreshInterval());
|
updateRefreshInterval(Preferences::getRefreshInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RSSImp::on_rssDownloaderBtn_clicked()
|
||||||
|
{
|
||||||
|
AutomatedRssDownloader dlg(this);
|
||||||
|
dlg.exec();
|
||||||
|
if(dlg.isRssDownloaderEnabled())
|
||||||
|
on_updateAllButton_clicked();
|
||||||
|
}
|
||||||
|
@ -73,7 +73,6 @@ protected slots:
|
|||||||
void fillFeedsList(QTreeWidgetItem *parent=0, RssFolder *rss_parent=0);
|
void fillFeedsList(QTreeWidgetItem *parent=0, RssFolder *rss_parent=0);
|
||||||
void saveSlidersPosition();
|
void saveSlidersPosition();
|
||||||
void restoreSlidersPosition();
|
void restoreSlidersPosition();
|
||||||
void showFeedDownloader();
|
|
||||||
void askNewFolder();
|
void askNewFolder();
|
||||||
void saveFoldersOpenState();
|
void saveFoldersOpenState();
|
||||||
void loadFoldersOpenState();
|
void loadFoldersOpenState();
|
||||||
@ -81,6 +80,9 @@ protected slots:
|
|||||||
void on_actionManage_cookies_triggered();
|
void on_actionManage_cookies_triggered();
|
||||||
void on_settingsButton_clicked();
|
void on_settingsButton_clicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_rssDownloaderBtn_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RssManager *rssmanager;
|
RssManager *rssmanager;
|
||||||
QBtSession *BTSession;
|
QBtSession *BTSession;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
|
|
||||||
#include "rssdownloadrule.h"
|
#include "rssdownloadrule.h"
|
||||||
|
#include "preferences.h"
|
||||||
#include "qinisettings.h"
|
#include "qinisettings.h"
|
||||||
|
|
||||||
RssDownloadRule::RssDownloadRule()
|
RssDownloadRule::RssDownloadRule()
|
||||||
@ -66,7 +67,7 @@ void RssDownloadRule::setMustNotContain(const QString &tokens)
|
|||||||
m_mustNotContain = tokens.split(QRegExp("[\\s|]"));
|
m_mustNotContain = tokens.split(QRegExp("[\\s|]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
RssDownloadRule RssDownloadRule::fromOldFormat(const QHash<QString, QVariant> &rule_hash, const QString &feed_url, const QString &rule_name)
|
RssDownloadRule RssDownloadRule::fromOldFormat(const QVariantHash &rule_hash, const QString &feed_url, const QString &rule_name)
|
||||||
{
|
{
|
||||||
RssDownloadRule rule;
|
RssDownloadRule rule;
|
||||||
rule.setName(rule_name);
|
rule.setName(rule_name);
|
||||||
@ -82,7 +83,7 @@ RssDownloadRule RssDownloadRule::fromOldFormat(const QHash<QString, QVariant> &r
|
|||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
RssDownloadRule RssDownloadRule::fromNewFormat(const QHash<QString, QVariant> &rule_hash)
|
RssDownloadRule RssDownloadRule::fromNewFormat(const QVariantHash &rule_hash)
|
||||||
{
|
{
|
||||||
RssDownloadRule rule;
|
RssDownloadRule rule;
|
||||||
rule.setName(rule_hash.value("name").toString());
|
rule.setName(rule_hash.value("name").toString());
|
||||||
@ -90,16 +91,18 @@ RssDownloadRule RssDownloadRule::fromNewFormat(const QHash<QString, QVariant> &r
|
|||||||
rule.setMustNotContain(rule_hash.value("must_not_contain").toString());
|
rule.setMustNotContain(rule_hash.value("must_not_contain").toString());
|
||||||
rule.setRssFeeds(rule_hash.value("affected_feeds").toStringList());
|
rule.setRssFeeds(rule_hash.value("affected_feeds").toStringList());
|
||||||
rule.setEnabled(rule_hash.value("enabled", false).toBool());
|
rule.setEnabled(rule_hash.value("enabled", false).toBool());
|
||||||
|
rule.setSavePath(rule_hash.value("save_path").toString());
|
||||||
rule.setLabel(rule_hash.value("label_assigned").toString());
|
rule.setLabel(rule_hash.value("label_assigned").toString());
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QString, QVariant> RssDownloadRule::toHash() const
|
QVariantHash RssDownloadRule::toVariantHash() const
|
||||||
{
|
{
|
||||||
QHash<QString, QVariant> hash;
|
QVariantHash hash;
|
||||||
hash["name"] = m_name;
|
hash["name"] = m_name;
|
||||||
hash["must_contain"] = m_mustContain.join(" ");
|
hash["must_contain"] = m_mustContain.join(" ");
|
||||||
hash["must_not_contain"] = m_mustNotContain.join(" ");
|
hash["must_not_contain"] = m_mustNotContain.join(" ");
|
||||||
|
hash["save_path"] = m_savePath;
|
||||||
hash["affected_feeds"] = m_rssFeeds;
|
hash["affected_feeds"] = m_rssFeeds;
|
||||||
hash["enabled"] = m_enabled;
|
hash["enabled"] = m_enabled;
|
||||||
hash["label_assigned"] = m_label;
|
hash["label_assigned"] = m_label;
|
||||||
@ -109,3 +112,11 @@ QHash<QString, QVariant> RssDownloadRule::toHash() const
|
|||||||
bool RssDownloadRule::operator==(const RssDownloadRule &other) {
|
bool RssDownloadRule::operator==(const RssDownloadRule &other) {
|
||||||
return m_name == other.name();
|
return m_name == other.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RssDownloadRule::setSavePath(const QString &save_path)
|
||||||
|
{
|
||||||
|
if(!save_path.isEmpty() && QDir(save_path) != QDir(Preferences::getSavePath()))
|
||||||
|
m_savePath = save_path;
|
||||||
|
else
|
||||||
|
m_savePath = QString();
|
||||||
|
}
|
||||||
|
@ -32,16 +32,16 @@
|
|||||||
#define RSSDOWNLOADRULE_H
|
#define RSSDOWNLOADRULE_H
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QHash>
|
#include <QVariantHash>
|
||||||
|
|
||||||
class RssDownloadRule
|
class RssDownloadRule
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RssDownloadRule();
|
explicit RssDownloadRule();
|
||||||
static RssDownloadRule fromOldFormat(const QHash<QString, QVariant>& rule_hash, const QString &feed_url, const QString &rule_name); // Before v2.5.0
|
static RssDownloadRule fromOldFormat(const QVariantHash& rule_hash, const QString &feed_url, const QString &rule_name); // Before v2.5.0
|
||||||
static RssDownloadRule fromNewFormat(const QHash<QString, QVariant> &rule_hash);
|
static RssDownloadRule fromNewFormat(const QVariantHash &rule_hash);
|
||||||
QHash<QString, QVariant> toHash() const;
|
QVariantHash toVariantHash() const;
|
||||||
bool matches(const QString &article_title) const;
|
bool matches(const QString &article_title) const;
|
||||||
void setMustContain(const QString &tokens);
|
void setMustContain(const QString &tokens);
|
||||||
void setMustNotContain(const QString &tokens);
|
void setMustNotContain(const QString &tokens);
|
||||||
@ -50,11 +50,14 @@ public:
|
|||||||
inline QString name() const { return m_name; }
|
inline QString name() const { return m_name; }
|
||||||
inline void setName(const QString &name) { m_name = name; }
|
inline void setName(const QString &name) { m_name = name; }
|
||||||
inline QString savePath() const { return m_savePath; }
|
inline QString savePath() const { return m_savePath; }
|
||||||
inline void setSavePath(const QString &save_path) { m_savePath = save_path; }
|
void setSavePath(const QString &save_path);
|
||||||
inline QString label() const { return m_label; }
|
inline QString label() const { return m_label; }
|
||||||
inline void setLabel(const QString &_label) { m_label = _label; }
|
inline void setLabel(const QString &_label) { m_label = _label; }
|
||||||
inline bool isEnabled() const { return m_enabled; }
|
inline bool isEnabled() const { return m_enabled; }
|
||||||
inline void setEnabled(bool enable) { m_enabled = enable; }
|
inline void setEnabled(bool enable) { m_enabled = enable; }
|
||||||
|
inline bool isValid() const { return !m_name.isEmpty(); }
|
||||||
|
inline QString mustContain() const { return m_mustContain.join(" "); }
|
||||||
|
inline QString mustNotContain() const { return m_mustNotContain.join(" "); }
|
||||||
// Operators
|
// Operators
|
||||||
bool operator==(const RssDownloadRule &other);
|
bool operator==(const RssDownloadRule &other);
|
||||||
|
|
||||||
|
@ -50,19 +50,20 @@ void RssDownloadRuleList::drop()
|
|||||||
delete m_instance;
|
delete m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RssDownloadRule * RssDownloadRuleList::findMatchingRule(const QString &feed_url, const QString &article_title) const
|
RssDownloadRule RssDownloadRuleList::findMatchingRule(const QString &feed_url, const QString &article_title) const
|
||||||
{
|
{
|
||||||
const QList<RssDownloadRule*> rules = feedRules(feed_url);
|
QStringList rule_names = feedRules(feed_url);
|
||||||
foreach(const RssDownloadRule* rule, rules) {
|
foreach(const QString &rule_name, rule_names) {
|
||||||
if(rule->matches(article_title)) return rule;
|
RssDownloadRule rule = m_rules[rule_name];
|
||||||
|
if(rule.isEnabled() && rule.matches(article_title)) return rule;
|
||||||
}
|
}
|
||||||
return 0;
|
return RssDownloadRule();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssDownloadRuleList::saveRulesToStorage()
|
void RssDownloadRuleList::saveRulesToStorage()
|
||||||
{
|
{
|
||||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
qBTRSS.setValue("download_rules", toVariantList());
|
qBTRSS.setValue("download_rules", toVariantHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssDownloadRuleList::loadRulesFromStorage()
|
void RssDownloadRuleList::loadRulesFromStorage()
|
||||||
@ -77,7 +78,7 @@ void RssDownloadRuleList::loadRulesFromStorage()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Load from new format
|
// Load from new format
|
||||||
loadRulesFromVariantList(qBTRSS.value("download_rules").toList());
|
loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssDownloadRuleList::importRulesInOldFormat(const QHash<QString, QVariant> &rules)
|
void RssDownloadRuleList::importRulesInOldFormat(const QHash<QString, QVariant> &rules)
|
||||||
@ -86,42 +87,84 @@ void RssDownloadRuleList::importRulesInOldFormat(const QHash<QString, QVariant>
|
|||||||
const QHash<QString, QVariant> feed_rules = rules.value(feed_url).toHash();
|
const QHash<QString, QVariant> feed_rules = rules.value(feed_url).toHash();
|
||||||
foreach(const QString &rule_name, feed_rules.keys()) {
|
foreach(const QString &rule_name, feed_rules.keys()) {
|
||||||
RssDownloadRule rule = RssDownloadRule::fromOldFormat(feed_rules.value(rule_name).toHash(), feed_url, rule_name);
|
RssDownloadRule rule = RssDownloadRule::fromOldFormat(feed_rules.value(rule_name).toHash(), feed_url, rule_name);
|
||||||
|
if(!rule.isValid()) continue;
|
||||||
// Check for rule name clash
|
// Check for rule name clash
|
||||||
while(contains(rule)) {
|
while(m_rules.contains(rule.name())) {
|
||||||
rule.setName(rule.name()+"_");
|
rule.setName(rule.name()+"_");
|
||||||
}
|
}
|
||||||
// Add the rule to the list
|
// Add the rule to the list
|
||||||
append(rule);
|
saveRule(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssDownloadRuleList::append(const RssDownloadRule &rule)
|
QVariantHash RssDownloadRuleList::toVariantHash() const
|
||||||
{
|
{
|
||||||
Q_ASSERT(!contains(rule));
|
QVariantHash ret;
|
||||||
QList<RssDownloadRule>::append(rule);
|
foreach(const RssDownloadRule &rule, m_rules.values()) {
|
||||||
|
ret.insert(rule.name(), rule.toVariantHash());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RssDownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h)
|
||||||
|
{
|
||||||
|
foreach(const QVariant& v, h.values()) {
|
||||||
|
RssDownloadRule rule = RssDownloadRule::fromNewFormat(v.toHash());
|
||||||
|
if(!rule.name().isEmpty()) {
|
||||||
|
saveRule(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RssDownloadRuleList::saveRule(const RssDownloadRule &rule)
|
||||||
|
{
|
||||||
|
Q_ASSERT(rule.isValid());
|
||||||
|
m_rules.insert(rule.name(), rule);
|
||||||
// Update feedRules hashtable
|
// Update feedRules hashtable
|
||||||
foreach(const QString &feed_url, rule.rssFeeds()) {
|
foreach(const QString &feed_url, rule.rssFeeds()) {
|
||||||
m_feedRules[feed_url].append(&last());
|
m_feedRules[feed_url].append(rule.name());
|
||||||
}
|
}
|
||||||
|
// Save rules
|
||||||
|
saveRulesToStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantList RssDownloadRuleList::toVariantList() const
|
void RssDownloadRuleList::removeRule(const QString &name)
|
||||||
{
|
{
|
||||||
QVariantList l;
|
if(!m_rules.contains(name)) return;
|
||||||
QList<RssDownloadRule>::const_iterator it;
|
const RssDownloadRule rule = m_rules.take(name);
|
||||||
for(it = begin(); it != end(); it++) {
|
// Update feedRules hashtable
|
||||||
l << it->toHash();
|
foreach(const QString &feed_url, rule.rssFeeds()) {
|
||||||
|
m_feedRules[feed_url].removeOne(rule.name());
|
||||||
}
|
}
|
||||||
return l;
|
// Save rules
|
||||||
|
saveRulesToStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssDownloadRuleList::loadRulesFromVariantList(const QVariantList &l)
|
void RssDownloadRuleList::updateRule(const RssDownloadRule &rule)
|
||||||
{
|
{
|
||||||
QVariantList::const_iterator it;
|
if(!m_rules.contains(rule.name())) return;
|
||||||
for(it=l.begin(); it !=l.end(); it++) {
|
removeRule(rule.name());
|
||||||
RssDownloadRule rule = RssDownloadRule::fromNewFormat(it->toHash());
|
saveRule(rule);
|
||||||
if(!rule.name().isEmpty())
|
// Save rules
|
||||||
append(rule);
|
saveRulesToStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RssDownloadRuleList::renameRule(const QString &old_name, const QString &new_name)
|
||||||
|
{
|
||||||
|
if(!m_rules.contains(old_name)) return;
|
||||||
|
RssDownloadRule rule = m_rules.take(old_name);
|
||||||
|
rule.setName(new_name);
|
||||||
|
m_rules.insert(new_name, rule);
|
||||||
|
// Update feedRules hashtable
|
||||||
|
foreach(const QString &feed_url, rule.rssFeeds()) {
|
||||||
|
m_feedRules[feed_url].replace(m_feedRules[feed_url].indexOf(old_name), new_name);
|
||||||
|
}
|
||||||
|
// Save rules
|
||||||
|
saveRulesToStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
RssDownloadRule RssDownloadRuleList::getRule(const QString &name) const
|
||||||
|
{
|
||||||
|
return m_rules.value(name);
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,12 @@
|
|||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QVariantList>
|
#include <QVariantHash>
|
||||||
|
|
||||||
#include "rssdownloadrule.h"
|
#include "rssdownloadrule.h"
|
||||||
|
|
||||||
// This class is not thread-safe (not required)
|
// This class is not thread-safe (not required)
|
||||||
class RssDownloadRuleList : public QList<RssDownloadRule>
|
class RssDownloadRuleList
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(RssDownloadRuleList)
|
Q_DISABLE_COPY(RssDownloadRuleList)
|
||||||
|
|
||||||
@ -49,19 +49,26 @@ private:
|
|||||||
public:
|
public:
|
||||||
static RssDownloadRuleList* instance();
|
static RssDownloadRuleList* instance();
|
||||||
static void drop();
|
static void drop();
|
||||||
const RssDownloadRule* findMatchingRule(const QString &feed_url, const QString &article_title) const;
|
RssDownloadRule findMatchingRule(const QString &feed_url, const QString &article_title) const;
|
||||||
inline QList<RssDownloadRule*> feedRules(const QString &feed_url) const { return m_feedRules.value(feed_url); }
|
// Operators
|
||||||
QVariantList toVariantList() const;
|
void saveRule(const RssDownloadRule &rule);
|
||||||
void append(const RssDownloadRule& rule);
|
void removeRule(const QString &name);
|
||||||
void saveRulesToStorage();
|
void updateRule(const RssDownloadRule &rule);
|
||||||
|
void renameRule(const QString &old_name, const QString &new_name);
|
||||||
|
RssDownloadRule getRule(const QString &name) const;
|
||||||
|
inline QStringList ruleNames() const { return m_rules.keys(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadRulesFromStorage();
|
void loadRulesFromStorage();
|
||||||
void importRulesInOldFormat(const QHash<QString, QVariant> &rules); // Before v2.5.0
|
void importRulesInOldFormat(const QHash<QString, QVariant> &rules); // Before v2.5.0
|
||||||
void loadRulesFromVariantList(const QVariantList& l);
|
void loadRulesFromVariantHash(const QVariantHash& l);
|
||||||
|
QVariantHash toVariantHash() const;
|
||||||
|
void saveRulesToStorage();
|
||||||
|
inline QStringList feedRules(const QString &feed_url) const { return m_feedRules[feed_url]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString, QList<RssDownloadRule*> > m_feedRules;
|
QHash<QString, RssDownloadRule> m_rules;
|
||||||
|
QHash<QString, QStringList> m_feedRules;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -299,24 +299,13 @@ short RssFeed::readDoc(QIODevice* device) {
|
|||||||
else
|
else
|
||||||
torrent_url = item->getLink();
|
torrent_url = item->getLink();
|
||||||
// Check if the item should be automatically downloaded
|
// Check if the item should be automatically downloaded
|
||||||
RssFilter * matching_filter = RssFilters::getFeedFilters(url).matches(item->getTitle());
|
const RssDownloadRule matching_rule = RssDownloadRuleList::instance()->findMatchingRule(url, item->getTitle());
|
||||||
if(matching_filter != 0) {
|
if(matching_rule.isValid()) {
|
||||||
// Download the torrent
|
// Download the torrent
|
||||||
BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName()));
|
BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName()));
|
||||||
if(matching_filter->isValid()) {
|
BTSession->downloadUrlAndSkipDialog(torrent_url, matching_rule.savePath(), matching_rule.label());
|
||||||
QString save_path = matching_filter->getSavePath();
|
|
||||||
if(save_path.isEmpty())
|
|
||||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
|
||||||
else
|
|
||||||
BTSession->downloadUrlAndSkipDialog(torrent_url, save_path);
|
|
||||||
} else {
|
|
||||||
// All torrents are downloaded from this feed
|
|
||||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
|
||||||
}
|
|
||||||
// Item was downloaded, consider it as Read
|
// Item was downloaded, consider it as Read
|
||||||
item->setRead();
|
item->setRead();
|
||||||
// Clean up
|
|
||||||
delete matching_filter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "qbtsession.h"
|
#include "qbtsession.h"
|
||||||
#include "rssfeed.h"
|
#include "rssfeed.h"
|
||||||
#include "rssarticle.h"
|
#include "rssarticle.h"
|
||||||
|
#include "rssdownloadrulelist.h"
|
||||||
|
|
||||||
RssManager::RssManager(QBtSession *BTSession): RssFolder(0, this, BTSession, QString::null) {
|
RssManager::RssManager(QBtSession *BTSession): RssFolder(0, this, BTSession, QString::null) {
|
||||||
loadStreamList();
|
loadStreamList();
|
||||||
@ -43,6 +44,7 @@ RssManager::RssManager(QBtSession *BTSession): RssFolder(0, this, BTSession, QSt
|
|||||||
|
|
||||||
RssManager::~RssManager(){
|
RssManager::~RssManager(){
|
||||||
qDebug("Deleting RSSManager");
|
qDebug("Deleting RSSManager");
|
||||||
|
RssDownloadRuleList::drop();
|
||||||
saveStreamList();
|
saveStreamList();
|
||||||
qDebug("RSSManager deleted");
|
qDebug("RSSManager deleted");
|
||||||
}
|
}
|
||||||
@ -55,10 +57,9 @@ void RssManager::updateRefreshInterval(unsigned int val){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssManager::loadStreamList(){
|
void RssManager::loadStreamList() {
|
||||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
const QStringList streamsUrl = RssSettings::getRssFeedsUrls();
|
||||||
QStringList streamsUrl = settings.value("Rss/streamList").toStringList();
|
const QStringList aliases = RssSettings::getRssFeedsAliases();
|
||||||
QStringList aliases = settings.value("Rss/streamAlias").toStringList();
|
|
||||||
if(streamsUrl.size() != aliases.size()){
|
if(streamsUrl.size() != aliases.size()){
|
||||||
std::cerr << "Corrupted Rss list, not loading it\n";
|
std::cerr << "Corrupted Rss list, not loading it\n";
|
||||||
return;
|
return;
|
||||||
@ -117,12 +118,8 @@ void RssManager::saveStreamList(){
|
|||||||
streamsUrl << stream_path;
|
streamsUrl << stream_path;
|
||||||
aliases << stream->getName();
|
aliases << stream->getName();
|
||||||
}
|
}
|
||||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
RssSettings::setRssFeedsUrls(streamsUrl);
|
||||||
settings.beginGroup("Rss");
|
RssSettings::setRssFeedsAliases(aliases);
|
||||||
// FIXME: Empty folder are not saved
|
|
||||||
settings.setValue("streamList", streamsUrl);
|
|
||||||
settings.setValue("streamAlias", aliases);
|
|
||||||
settings.endGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RssManager::insertSortElem(QList<RssArticle*> &list, RssArticle *item) {
|
void RssManager::insertSortElem(QList<RssArticle*> &list, RssArticle *item) {
|
||||||
|
@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
static bool isRssDownloadingEnabled() {
|
static bool isRssDownloadingEnabled() {
|
||||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
return settings.value("Preferences/RSS/RssDownloading", false).toBool();
|
return settings.value("Preferences/RSS/RssDownloading", true).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setRssDownloadingEnabled(bool b) {
|
static void setRssDownloadingEnabled(bool b) {
|
||||||
@ -76,6 +76,25 @@ public:
|
|||||||
settings.setValue("Preferences/RSS/RssDownloading", b);
|
settings.setValue("Preferences/RSS/RssDownloading", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QStringList getRssFeedsUrls() {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
return settings.value("Rss/streamList").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setRssFeedsUrls(const QStringList &rssFeeds) {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
settings.setValue("Rss/streamList", rssFeeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList getRssFeedsAliases() {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
return settings.value("Rss/streamAlias").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setRssFeedsAliases(const QStringList &rssAliases) {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
settings.setValue("Rss/streamAlias", rssAliases);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RSSSETTINGS_H
|
#endif // RSSSETTINGS_H
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
#include "transferlistdelegate.h"
|
#include "transferlistdelegate.h"
|
||||||
#include "transferlistwidget.h"
|
#include "transferlistwidget.h"
|
||||||
|
#include "preferences.h"
|
||||||
#include "qinisettings.h"
|
#include "qinisettings.h"
|
||||||
|
|
||||||
class LabelFiltersList: public QListWidget {
|
class LabelFiltersList: public QListWidget {
|
||||||
@ -280,17 +281,10 @@ public:
|
|||||||
settings.setValue("customLabels", QVariant(customLabels.keys()));
|
settings.setValue("customLabels", QVariant(customLabels.keys()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveCustomLabels() const {
|
|
||||||
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
|
||||||
settings.beginGroup(QString::fromUtf8("TransferListFilters"));
|
|
||||||
settings.setValue("customLabels", QVariant(customLabels.keys()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadSettings() {
|
void loadSettings() {
|
||||||
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
settings.beginGroup(QString::fromUtf8("TransferListFilters"));
|
statusFilters->setCurrentRow(settings.value("TransferListFilters/selectedFilterIndex", 0).toInt());
|
||||||
statusFilters->setCurrentRow(settings.value("selectedFilterIndex", 0).toInt());
|
const QStringList label_list = Preferences::getTorrentLabels();
|
||||||
QStringList label_list = settings.value("customLabels", QStringList()).toStringList();
|
|
||||||
foreach(const QString &label, label_list) {
|
foreach(const QString &label, label_list) {
|
||||||
customLabels.insert(label, 0);
|
customLabels.insert(label, 0);
|
||||||
qDebug("Creating label QListWidgetItem: %s", qPrintable(label));
|
qDebug("Creating label QListWidgetItem: %s", qPrintable(label));
|
||||||
@ -328,7 +322,7 @@ protected slots:
|
|||||||
newLabel->setData(Qt::DecorationRole, QIcon(":/Icons/oxygen/folder.png"));
|
newLabel->setData(Qt::DecorationRole, QIcon(":/Icons/oxygen/folder.png"));
|
||||||
labelFilters->addItem(newLabel);
|
labelFilters->addItem(newLabel);
|
||||||
customLabels.insert(label, 0);
|
customLabels.insert(label, 0);
|
||||||
saveCustomLabels();
|
Preferences::addTorrentLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLabelMenu(QPoint) {
|
void showLabelMenu(QPoint) {
|
||||||
@ -395,7 +389,7 @@ protected slots:
|
|||||||
// Un display filter
|
// Un display filter
|
||||||
delete labelFilters->takeItem(row);
|
delete labelFilters->takeItem(row);
|
||||||
// Save custom labels to remember it was deleted
|
// Save custom labels to remember it was deleted
|
||||||
saveCustomLabels();
|
Preferences::removeTorrentLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyLabelFilter(int row) {
|
void applyLabelFilter(int row) {
|
||||||
|
Loading…
Reference in New Issue
Block a user