mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-11 07:18:08 +00:00
parent
7d97905716
commit
a2121f2483
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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())
|
||||||
|
@ -37,10 +37,8 @@
|
|||||||
|
|
||||||
class QXmlStreamReader;
|
class QXmlStreamReader;
|
||||||
|
|
||||||
namespace RSS
|
namespace RSS::Private
|
||||||
{
|
{
|
||||||
namespace Private
|
|
||||||
{
|
|
||||||
struct ParsingResult
|
struct ParsingResult
|
||||||
{
|
{
|
||||||
QString error;
|
QString error;
|
||||||
@ -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);
|
||||||
@ -73,7 +70,6 @@ namespace RSS
|
|||||||
ParsingResult m_result;
|
ParsingResult m_result;
|
||||||
QSet<QString> m_articleIDs;
|
QSet<QString> m_articleIDs;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(RSS::Private::ParsingResult)
|
Q_DECLARE_METATYPE(RSS::Private::ParsingResult)
|
||||||
|
Loading…
Reference in New Issue
Block a user