mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-10 23:07:59 +00:00
parent
7d97905716
commit
a2121f2483
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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())
|
||||
|
@ -37,9 +37,7 @@
|
||||
|
||||
class QXmlStreamReader;
|
||||
|
||||
namespace RSS
|
||||
{
|
||||
namespace Private
|
||||
namespace RSS::Private
|
||||
{
|
||||
struct ParsingResult
|
||||
{
|
||||
@ -49,7 +47,7 @@ namespace RSS
|
||||
QList<QVariantHash> articles;
|
||||
};
|
||||
|
||||
class Parser : public QObject
|
||||
class Parser final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Parser)
|
||||
@ -62,7 +60,6 @@ namespace RSS
|
||||
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);
|
||||
@ -74,6 +71,5 @@ namespace RSS
|
||||
QSet<QString> m_articleIDs;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(RSS::Private::ParsingResult)
|
||||
|
Loading…
Reference in New Issue
Block a user