1
0
mirror of https://github.com/d47081/qBittorrent.git synced 2025-01-10 23:07:59 +00:00

Improve RSS subsystem initialization

PR #17227.
This commit is contained in:
Vladimir Golovnev 2022-06-18 09:56:49 +03:00 committed by GitHub
parent 7d97905716
commit a2121f2483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 94 additions and 96 deletions

View File

@ -42,6 +42,8 @@
#include "base/utils/io.h"
#include "rss_article.h"
const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QVector<QVariantHash>>();
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
{
QFile file {dataFileName.data()};
@ -122,5 +124,10 @@ QVector<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArra
result.push_back(varHash);
}
std::sort(result.begin(), result.end(), [](const QVariantHash &left, const QVariantHash &right)
{
return (left.value(Article::KeyDate).toDateTime() > right.value(Article::KeyDate).toDateTime());
});
return result;
}

View File

@ -30,7 +30,6 @@
#include "rss_article.h"
#include <QJsonObject>
#include <QVariant>
#include "base/global.h"
@ -38,19 +37,6 @@
using namespace RSS;
namespace
{
QVariantHash articleDataFromJSON(const QJsonObject &jsonObj)
{
auto varHash = jsonObj.toVariantHash();
// JSON object store DateTime as string so we need to convert it
varHash[Article::KeyDate] =
QDateTime::fromString(jsonObj.value(Article::KeyDate).toString(), Qt::RFC2822Date);
return varHash;
}
}
const QString Article::KeyId = u"id"_qs;
const QString Article::KeyDate = u"date"_qs;
const QString Article::KeyTitle = u"title"_qs;
@ -75,11 +61,6 @@ Article::Article(Feed *feed, const QVariantHash &varHash)
{
}
Article::Article(Feed *feed, const QJsonObject &jsonObj)
: Article(feed, articleDataFromJSON(jsonObj))
{
}
QString Article::guid() const
{
return m_guid;
@ -135,15 +116,6 @@ void Article::markAsRead()
}
}
QJsonObject Article::toJsonObject() const
{
auto jsonObj = QJsonObject::fromVariantHash(m_data);
// JSON object doesn't support DateTime so we need to convert it
jsonObj[KeyDate] = m_date.toString(Qt::RFC2822Date);
return jsonObj;
}
bool Article::articleDateRecentThan(const Article *article, const QDateTime &date)
{
return article->date() > date;

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2017-2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
*
@ -39,7 +39,7 @@ namespace RSS
{
class Feed;
class Article : public QObject
class Article final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Article)
@ -47,7 +47,6 @@ namespace RSS
friend class Feed;
Article(Feed *feed, const QVariantHash &varHash);
Article(Feed *feed, const QJsonObject &jsonObj);
public:
static const QString KeyId;
@ -72,8 +71,6 @@ namespace RSS
void markAsRead();
QJsonObject toJsonObject() const;
static bool articleDateRecentThan(const Article *article, const QDateTime &date);
signals:

View File

@ -491,7 +491,12 @@ void AutoDownloader::resetProcessingQueue()
void AutoDownloader::startProcessing()
{
resetProcessingQueue();
connect(Session::instance()->rootFolder(), &Folder::newArticle, this, &AutoDownloader::handleNewArticle);
const RSS::Folder *rootFolder = Session::instance()->rootFolder();
for (const Article *article : asConst(rootFolder->articles()))
handleNewArticle(article);
connect(rootFolder, &Folder::newArticle, this, &AutoDownloader::handleNewArticle);
}
void AutoDownloader::setProcessingEnabled(const bool enabled)

View File

@ -222,7 +222,10 @@ void Feed::handleDownloadFinished(const Net::DownloadResult &result)
LogMsg(tr("RSS feed at '%1' is successfully downloaded. Starting to parse it.")
.arg(result.url));
// Parse the download RSS
m_parser->parse(result.data);
QMetaObject::invokeMethod(m_parser, [this, data = result.data]()
{
m_parser->parse(data);
});
}
else
{
@ -306,18 +309,18 @@ void Feed::storeDeferred()
m_savingTimer.start(5 * 1000, this);
}
bool Feed::addArticle(Article *article)
bool Feed::addArticle(const QVariantHash &articleData)
{
Q_ASSERT(article);
Q_ASSERT(!m_articles.contains(article->guid()));
Q_ASSERT(!m_articles.contains(articleData.value(Article::KeyId).toString()));
// Insertion sort
const int maxArticles = m_session->maxArticlesPerFeed();
const auto lowerBound = std::lower_bound(m_articlesByDate.begin(), m_articlesByDate.end()
, article->date(), Article::articleDateRecentThan);
, articleData.value(Article::KeyDate).toDateTime(), Article::articleDateRecentThan);
if ((lowerBound - m_articlesByDate.begin()) >= maxArticles)
return false; // we reach max articles
auto *article = new Article(this, articleData);
m_articles[article->guid()] = article;
m_articlesByDate.insert(lowerBound, article);
if (!article->isRead())
@ -434,7 +437,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
{
if (a.second)
{
addArticle(new Article {this, *a.second});
addArticle(*a.second);
++newArticlesCount;
}
});
@ -462,7 +465,12 @@ QJsonValue Feed::toJsonValue(const bool withData) const
QJsonArray jsonArr;
for (Article *article : asConst(m_articles))
jsonArr << article->toJsonObject();
{
auto articleObj = QJsonObject::fromVariantHash(article->data());
// JSON object doesn't support DateTime so we need to convert it
articleObj[Article::KeyDate] = article->date().toString(Qt::RFC2822Date);
jsonArr.append(articleObj);
}
jsonObj.insert(KEY_ARTICLES, jsonArr);
}
@ -489,19 +497,40 @@ void Feed::handleArticleRead(Article *article)
storeDeferred();
}
void Feed::handleArticleLoadFinished(const QVector<QVariantHash> &articles)
void Feed::handleArticleLoadFinished(QVector<QVariantHash> articles)
{
for (const QVariantHash &data : articles)
Q_ASSERT(m_articles.isEmpty());
Q_ASSERT(m_unreadCount == 0);
const int maxArticles = m_session->maxArticlesPerFeed();
if (articles.size() > maxArticles)
articles.resize(maxArticles);
m_articles.reserve(articles.size());
m_articlesByDate.reserve(articles.size());
for (const QVariantHash &articleData : articles)
{
try
const auto articleID = articleData.value(Article::KeyId).toString();
// TODO: use [[unlikely]] in C++20
if (Q_UNLIKELY(m_articles.contains(articleID)))
continue;
auto *article = new Article(this, articleData);
m_articles[articleID] = article;
m_articlesByDate.append(article);
if (!article->isRead())
{
auto *article = new Article(this, data);
if (!addArticle(article))
delete article;
++m_unreadCount;
connect(article, &Article::read, this, &Feed::handleArticleRead);
}
catch (const RuntimeError &) {}
emit newArticle(article);
}
if (m_unreadCount > 0)
emit unreadCountChanged(this);
m_isInitialized = true;
emit stateChanged(this);

View File

@ -99,7 +99,7 @@ namespace RSS
void handleDownloadFinished(const Net::DownloadResult &result);
void handleParsingFinished(const Private::ParsingResult &result);
void handleArticleRead(Article *article);
void handleArticleLoadFinished(const QVector<QVariantHash> &articles);
void handleArticleLoadFinished(QVector<QVariantHash> articles);
private:
void timerEvent(QTimerEvent *event) override;
@ -107,7 +107,7 @@ namespace RSS
void load();
void store();
void storeDeferred();
bool addArticle(Article *article);
bool addArticle(const QVariantHash &articleData);
void removeOldestArticle();
void increaseUnreadCount();
void decreaseUnreadCount();

View File

@ -539,23 +539,15 @@ namespace
}
}
using namespace RSS::Private;
const int PARSINGRESULT_TYPEID = qRegisterMetaType<RSS::Private::ParsingResult>();
const int ParsingResultTypeId = qRegisterMetaType<ParsingResult>();
Parser::Parser(const QString lastBuildDate)
RSS::Private::Parser::Parser(const QString lastBuildDate)
{
m_result.lastBuildDate = lastBuildDate;
}
void Parser::parse(const QByteArray &feedData)
{
QMetaObject::invokeMethod(this, [this, feedData]() { parse_impl(feedData); }
, Qt::QueuedConnection);
}
// read and create items from a rss document
void Parser::parse_impl(const QByteArray &feedData)
void RSS::Private::Parser::parse(const QByteArray &feedData)
{
QXmlStreamReader xml(feedData);
XmlStreamEntityResolver resolver;
@ -608,7 +600,7 @@ void Parser::parse_impl(const QByteArray &feedData)
m_articleIDs.clear();
}
void Parser::parseRssArticle(QXmlStreamReader &xml)
void RSS::Private::Parser::parseRssArticle(QXmlStreamReader &xml)
{
QVariantHash article;
QString altTorrentUrl;
@ -671,7 +663,7 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
addArticle(article);
}
void Parser::parseRSSChannel(QXmlStreamReader &xml)
void RSS::Private::Parser::parseRSSChannel(QXmlStreamReader &xml)
{
while (!xml.atEnd())
{
@ -704,7 +696,7 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml)
}
}
void Parser::parseAtomArticle(QXmlStreamReader &xml)
void RSS::Private::Parser::parseAtomArticle(QXmlStreamReader &xml)
{
QVariantHash article;
bool doubleContent = false;
@ -785,7 +777,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
addArticle(article);
}
void Parser::parseAtomChannel(QXmlStreamReader &xml)
void RSS::Private::Parser::parseAtomChannel(QXmlStreamReader &xml)
{
m_baseUrl = xml.attributes().value(u"xml:base"_qs).toString();
@ -820,7 +812,7 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
}
}
void Parser::addArticle(QVariantHash article)
void RSS::Private::Parser::addArticle(QVariantHash article)
{
QVariant &torrentURL = article[Article::KeyTorrentURL];
if (torrentURL.toString().isEmpty())

View File

@ -37,43 +37,39 @@
class QXmlStreamReader;
namespace RSS
namespace RSS::Private
{
namespace Private
struct ParsingResult
{
struct ParsingResult
{
QString error;
QString lastBuildDate;
QString title;
QList<QVariantHash> articles;
};
QString error;
QString lastBuildDate;
QString title;
QList<QVariantHash> articles;
};
class Parser : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Parser)
class Parser final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Parser)
public:
explicit Parser(QString lastBuildDate);
void parse(const QByteArray &feedData);
public:
explicit Parser(QString lastBuildDate);
void parse(const QByteArray &feedData);
signals:
void finished(const RSS::Private::ParsingResult &result);
signals:
void finished(const RSS::Private::ParsingResult &result);
private:
Q_INVOKABLE void parse_impl(const QByteArray &feedData);
void parseRssArticle(QXmlStreamReader &xml);
void parseRSSChannel(QXmlStreamReader &xml);
void parseAtomArticle(QXmlStreamReader &xml);
void parseAtomChannel(QXmlStreamReader &xml);
void addArticle(QVariantHash article);
private:
void parseRssArticle(QXmlStreamReader &xml);
void parseRSSChannel(QXmlStreamReader &xml);
void parseAtomArticle(QXmlStreamReader &xml);
void parseAtomChannel(QXmlStreamReader &xml);
void addArticle(QVariantHash article);
QString m_baseUrl;
ParsingResult m_result;
QSet<QString> m_articleIDs;
};
}
QString m_baseUrl;
ParsingResult m_result;
QSet<QString> m_articleIDs;
};
}
Q_DECLARE_METATYPE(RSS::Private::ParsingResult)