1
0
mirror of https://github.com/d47081/qBittorrent.git synced 2025-01-11 07:18:08 +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 "base/utils/io.h"
#include "rss_article.h" #include "rss_article.h"
const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QVector<QVariantHash>>();
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url) void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
{ {
QFile file {dataFileName.data()}; QFile file {dataFileName.data()};
@ -122,5 +124,10 @@ QVector<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArra
result.push_back(varHash); 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; return result;
} }

View File

@ -30,7 +30,6 @@
#include "rss_article.h" #include "rss_article.h"
#include <QJsonObject>
#include <QVariant> #include <QVariant>
#include "base/global.h" #include "base/global.h"
@ -38,19 +37,6 @@
using namespace RSS; 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::KeyId = u"id"_qs;
const QString Article::KeyDate = u"date"_qs; const QString Article::KeyDate = u"date"_qs;
const QString Article::KeyTitle = u"title"_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 QString Article::guid() const
{ {
return m_guid; 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) bool Article::articleDateRecentThan(const Article *article, const QDateTime &date)
{ {
return article->date() > date; return article->date() > date;

View File

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

View File

@ -491,7 +491,12 @@ void AutoDownloader::resetProcessingQueue()
void AutoDownloader::startProcessing() void AutoDownloader::startProcessing()
{ {
resetProcessingQueue(); 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) 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.") LogMsg(tr("RSS feed at '%1' is successfully downloaded. Starting to parse it.")
.arg(result.url)); .arg(result.url));
// Parse the download RSS // Parse the download RSS
m_parser->parse(result.data); QMetaObject::invokeMethod(m_parser, [this, data = result.data]()
{
m_parser->parse(data);
});
} }
else else
{ {
@ -306,18 +309,18 @@ void Feed::storeDeferred()
m_savingTimer.start(5 * 1000, this); 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(articleData.value(Article::KeyId).toString()));
Q_ASSERT(!m_articles.contains(article->guid()));
// Insertion sort // Insertion sort
const int maxArticles = m_session->maxArticlesPerFeed(); const int maxArticles = m_session->maxArticlesPerFeed();
const auto lowerBound = std::lower_bound(m_articlesByDate.begin(), m_articlesByDate.end() 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) if ((lowerBound - m_articlesByDate.begin()) >= maxArticles)
return false; // we reach max articles return false; // we reach max articles
auto *article = new Article(this, articleData);
m_articles[article->guid()] = article; m_articles[article->guid()] = article;
m_articlesByDate.insert(lowerBound, article); m_articlesByDate.insert(lowerBound, article);
if (!article->isRead()) if (!article->isRead())
@ -434,7 +437,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
{ {
if (a.second) if (a.second)
{ {
addArticle(new Article {this, *a.second}); addArticle(*a.second);
++newArticlesCount; ++newArticlesCount;
} }
}); });
@ -462,7 +465,12 @@ QJsonValue Feed::toJsonValue(const bool withData) const
QJsonArray jsonArr; QJsonArray jsonArr;
for (Article *article : asConst(m_articles)) 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); jsonObj.insert(KEY_ARTICLES, jsonArr);
} }
@ -489,19 +497,40 @@ void Feed::handleArticleRead(Article *article)
storeDeferred(); 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); ++m_unreadCount;
if (!addArticle(article)) connect(article, &Article::read, this, &Feed::handleArticleRead);
delete article;
} }
catch (const RuntimeError &) {}
emit newArticle(article);
} }
if (m_unreadCount > 0)
emit unreadCountChanged(this);
m_isInitialized = true; m_isInitialized = true;
emit stateChanged(this); emit stateChanged(this);

View File

@ -99,7 +99,7 @@ namespace RSS
void handleDownloadFinished(const Net::DownloadResult &result); void handleDownloadFinished(const Net::DownloadResult &result);
void handleParsingFinished(const Private::ParsingResult &result); void handleParsingFinished(const Private::ParsingResult &result);
void handleArticleRead(Article *article); void handleArticleRead(Article *article);
void handleArticleLoadFinished(const QVector<QVariantHash> &articles); void handleArticleLoadFinished(QVector<QVariantHash> articles);
private: private:
void timerEvent(QTimerEvent *event) override; void timerEvent(QTimerEvent *event) override;
@ -107,7 +107,7 @@ namespace RSS
void load(); void load();
void store(); void store();
void storeDeferred(); void storeDeferred();
bool addArticle(Article *article); bool addArticle(const QVariantHash &articleData);
void removeOldestArticle(); void removeOldestArticle();
void increaseUnreadCount(); void increaseUnreadCount();
void decreaseUnreadCount(); 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>(); RSS::Private::Parser::Parser(const QString lastBuildDate)
Parser::Parser(const QString lastBuildDate)
{ {
m_result.lastBuildDate = 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 // 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); QXmlStreamReader xml(feedData);
XmlStreamEntityResolver resolver; XmlStreamEntityResolver resolver;
@ -608,7 +600,7 @@ void Parser::parse_impl(const QByteArray &feedData)
m_articleIDs.clear(); m_articleIDs.clear();
} }
void Parser::parseRssArticle(QXmlStreamReader &xml) void RSS::Private::Parser::parseRssArticle(QXmlStreamReader &xml)
{ {
QVariantHash article; QVariantHash article;
QString altTorrentUrl; QString altTorrentUrl;
@ -671,7 +663,7 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
addArticle(article); addArticle(article);
} }
void Parser::parseRSSChannel(QXmlStreamReader &xml) void RSS::Private::Parser::parseRSSChannel(QXmlStreamReader &xml)
{ {
while (!xml.atEnd()) 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; QVariantHash article;
bool doubleContent = false; bool doubleContent = false;
@ -785,7 +777,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
addArticle(article); addArticle(article);
} }
void Parser::parseAtomChannel(QXmlStreamReader &xml) void RSS::Private::Parser::parseAtomChannel(QXmlStreamReader &xml)
{ {
m_baseUrl = xml.attributes().value(u"xml:base"_qs).toString(); 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]; QVariant &torrentURL = article[Article::KeyTorrentURL];
if (torrentURL.toString().isEmpty()) if (torrentURL.toString().isEmpty())

View File

@ -37,9 +37,7 @@
class QXmlStreamReader; class QXmlStreamReader;
namespace RSS namespace RSS::Private
{
namespace Private
{ {
struct ParsingResult struct ParsingResult
{ {
@ -49,7 +47,7 @@ namespace RSS
QList<QVariantHash> articles; QList<QVariantHash> articles;
}; };
class Parser : public QObject class Parser final : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(Parser) Q_DISABLE_COPY_MOVE(Parser)
@ -62,7 +60,6 @@ namespace RSS
void finished(const RSS::Private::ParsingResult &result); void finished(const RSS::Private::ParsingResult &result);
private: private:
Q_INVOKABLE void parse_impl(const QByteArray &feedData);
void parseRssArticle(QXmlStreamReader &xml); void parseRssArticle(QXmlStreamReader &xml);
void parseRSSChannel(QXmlStreamReader &xml); void parseRSSChannel(QXmlStreamReader &xml);
void parseAtomArticle(QXmlStreamReader &xml); void parseAtomArticle(QXmlStreamReader &xml);
@ -74,6 +71,5 @@ namespace RSS
QSet<QString> m_articleIDs; QSet<QString> m_articleIDs;
}; };
} }
}
Q_DECLARE_METATYPE(RSS::Private::ParsingResult) Q_DECLARE_METATYPE(RSS::Private::ParsingResult)