qBittorrent
RSS::Feed Class Referencefinal

#include <rss_feed.h>

Inheritance diagram for RSS::Feed:
Collaboration diagram for RSS::Feed:

Signals

void iconLoaded (Feed *feed=nullptr)
 
void titleChanged (Feed *feed=nullptr)
 
void stateChanged (Feed *feed=nullptr)
 
- Signals inherited from RSS::Item
void pathChanged (Item *item=nullptr)
 
void unreadCountChanged (Item *item=nullptr)
 
void aboutToBeDestroyed (Item *item=nullptr)
 
void newArticle (Article *article)
 
void articleRead (Article *article)
 
void articleAboutToBeRemoved (Article *article)
 

Public Member Functions

QList< Article * > articles () const override
 
int unreadCount () const override
 
void markAsRead () override
 
void refresh () override
 
QUuid uid () const
 
QString url () const
 
QString title () const
 
QString lastBuildDate () const
 
bool hasError () const
 
bool isLoading () const
 
ArticlearticleByGUID (const QString &guid) const
 
QString iconPath () const
 
QJsonValue toJsonValue (bool withData=false) const override
 
- Public Member Functions inherited from RSS::Item
QString path () const
 
QString name () const
 

Private Slots

void handleSessionProcessingEnabledChanged (bool enabled)
 
void handleMaxArticlesPerFeedChanged (int n)
 
void handleIconDownloadFinished (const Net::DownloadResult &result)
 
void handleDownloadFinished (const Net::DownloadResult &result)
 
void handleParsingFinished (const Private::ParsingResult &result)
 
void handleArticleRead (Article *article)
 

Private Member Functions

 Feed (const QUuid &uid, const QString &url, const QString &path, Session *session)
 
 ~Feed () override
 
void timerEvent (QTimerEvent *event) override
 
void cleanup () override
 
void load ()
 
void loadArticles (const QByteArray &data)
 
void loadArticlesLegacy ()
 
void store ()
 
void storeDeferred ()
 
bool addArticle (Article *article)
 
void removeOldestArticle ()
 
void increaseUnreadCount ()
 
void decreaseUnreadCount ()
 
void downloadIcon ()
 
int updateArticles (const QList< QVariantHash > &loadedArticles)
 

Private Attributes

Sessionm_session
 
Private::Parserm_parser
 
const QUuid m_uid
 
const QString m_url
 
QString m_title
 
QString m_lastBuildDate
 
bool m_hasError = false
 
bool m_isLoading = false
 
QHash< QString, Article * > m_articles
 
QList< Article * > m_articlesByDate
 
int m_unreadCount = 0
 
QString m_iconPath
 
QString m_dataFileName
 
QBasicTimer m_savingTimer
 
bool m_dirty = false
 
Net::DownloadHandlerm_downloadHandler = nullptr
 

Friends

class Session
 

Additional Inherited Members

- Static Public Member Functions inherited from RSS::Item
static bool isValidPath (const QString &path)
 
static QString joinPath (const QString &path1, const QString &path2)
 
static QStringList expandPath (const QString &path)
 
static QString parentPath (const QString &path)
 
static QString relativeName (const QString &path)
 
- Static Public Attributes inherited from RSS::Item
static const QChar PathSeparator
 
- Protected Member Functions inherited from RSS::Item
 Item (const QString &path)
 
 ~Item () override
 

Detailed Description

Definition at line 59 of file rss_feed.h.

Constructor & Destructor Documentation

◆ Feed()

Feed::Feed ( const QUuid &  uid,
const QString &  url,
const QString &  path,
Session session 
)
private

Definition at line 64 of file rss_feed.cpp.

65  : Item(path)
66  , m_session(session)
67  , m_uid(uid)
68  , m_url(url)
69 {
70  const auto uidHex = QString::fromLatin1(m_uid.toRfc4122().toHex());
71  m_dataFileName = uidHex + QLatin1String(".json");
72 
73  // Move to new file naming scheme (since v4.1.2)
74  const QString legacyFilename
75  {Utils::Fs::toValidFileSystemName(m_url, false, QLatin1String("_"))
76  + QLatin1String(".json")};
77  const QDir storageDir {m_session->dataFileStorage()->storageDir()};
78  if (!QFile::exists(storageDir.absoluteFilePath(m_dataFileName)))
79  QFile::rename(storageDir.absoluteFilePath(legacyFilename), storageDir.absoluteFilePath(m_dataFileName));
80 
81  m_iconPath = Utils::Fs::toUniformPath(storageDir.absoluteFilePath(uidHex + QLatin1String(".ico")));
82 
83  m_parser = new Private::Parser(m_lastBuildDate);
84  m_parser->moveToThread(m_session->workingThread());
85  connect(this, &Feed::destroyed, m_parser, &Private::Parser::deleteLater);
87 
89 
91  downloadIcon();
92  else
94 
96 
97  load();
98 }
QDir storageDir() const
void registerSequentialService(const ServiceID &serviceID)
static DownloadManager * instance()
QString m_dataFileName
Definition: rss_feed.h:126
QString m_lastBuildDate
Definition: rss_feed.h:119
void downloadIcon()
Definition: rss_feed.cpp:424
void load()
Definition: rss_feed.cpp:263
Private::Parser * m_parser
Definition: rss_feed.h:115
QUuid uid() const
Definition: rss_feed.cpp:149
void handleParsingFinished(const Private::ParsingResult &result)
Definition: rss_feed.cpp:227
const QString m_url
Definition: rss_feed.h:117
void handleMaxArticlesPerFeedChanged(int n)
Definition: rss_feed.cpp:184
const QUuid m_uid
Definition: rss_feed.h:116
void handleSessionProcessingEnabledChanged(bool enabled)
Definition: rss_feed.cpp:530
QString url() const
Definition: rss_feed.cpp:154
QString m_iconPath
Definition: rss_feed.h:125
Session * m_session
Definition: rss_feed.h:114
Item(const QString &path)
Definition: rss_item.cpp:41
QString path() const
Definition: rss_item.cpp:57
void finished(const RSS::Private::ParsingResult &result)
bool isProcessingEnabled() const
QThread * workingThread() const
AsyncFileStorage * dataFileStorage() const
void maxArticlesPerFeedChanged(int n)
void processingStateChanged(bool enabled)
QString toValidFileSystemName(const QString &name, bool allowSeparators=false, const QString &pad=QLatin1String(" "))
Definition: fs.cpp:237
QString toUniformPath(const QString &path)
Definition: fs.cpp:69
static ServiceID fromURL(const QUrl &url)

References RSS::Session::dataFileStorage(), downloadIcon(), RSS::Private::Parser::finished(), Net::ServiceID::fromURL(), handleMaxArticlesPerFeedChanged(), handleParsingFinished(), handleSessionProcessingEnabledChanged(), Net::DownloadManager::instance(), RSS::Session::isProcessingEnabled(), load(), m_dataFileName, m_iconPath, m_lastBuildDate, m_parser, m_session, m_uid, m_url, RSS::Session::maxArticlesPerFeedChanged(), RSS::Session::processingStateChanged(), Net::DownloadManager::registerSequentialService(), AsyncFileStorage::storageDir(), Utils::Fs::toUniformPath(), Utils::Fs::toValidFileSystemName(), and RSS::Session::workingThread().

Here is the call graph for this function:

◆ ~Feed()

Feed::~Feed ( )
overrideprivate

Definition at line 100 of file rss_feed.cpp.

101 {
102  emit aboutToBeDestroyed(this);
103 }
void aboutToBeDestroyed(Item *item=nullptr)

References RSS::Item::aboutToBeDestroyed().

Member Function Documentation

◆ addArticle()

bool Feed::addArticle ( Article article)
private

Definition at line 367 of file rss_feed.cpp.

368 {
369  Q_ASSERT(article);
370  Q_ASSERT(!m_articles.contains(article->guid()));
371 
372  // Insertion sort
373  const int maxArticles = m_session->maxArticlesPerFeed();
374  const auto lowerBound = std::lower_bound(m_articlesByDate.begin(), m_articlesByDate.end()
375  , article->date(), Article::articleDateRecentThan);
376  if ((lowerBound - m_articlesByDate.begin()) >= maxArticles)
377  return false; // we reach max articles
378 
379  m_articles[article->guid()] = article;
380  m_articlesByDate.insert(lowerBound, article);
381  if (!article->isRead())
382  {
384  connect(article, &Article::read, this, &Feed::handleArticleRead);
385  }
386 
387  m_dirty = true;
388  emit newArticle(article);
389 
390  if (m_articlesByDate.size() > maxArticles)
392 
393  return true;
394 }
QString guid() const
Definition: rss_article.cpp:82
void read(Article *article=nullptr)
static bool articleDateRecentThan(const Article *article, const QDateTime &date)
QDateTime date() const
Definition: rss_article.cpp:87
bool isRead() const
QHash< QString, Article * > m_articles
Definition: rss_feed.h:122
void removeOldestArticle()
Definition: rss_feed.cpp:396
QList< Article * > m_articlesByDate
Definition: rss_feed.h:123
void increaseUnreadCount()
Definition: rss_feed.cpp:410
bool m_dirty
Definition: rss_feed.h:128
void handleArticleRead(Article *article)
Definition: rss_feed.cpp:540
void newArticle(Article *article)
int maxArticlesPerFeed() const

References RSS::Article::articleDateRecentThan(), RSS::Article::date(), RSS::Article::guid(), handleArticleRead(), increaseUnreadCount(), RSS::Article::isRead(), m_articles, m_articlesByDate, m_dirty, m_session, RSS::Session::maxArticlesPerFeed(), RSS::Item::newArticle(), RSS::Article::read(), and removeOldestArticle().

Referenced by loadArticles(), and loadArticlesLegacy().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ articleByGUID()

Article * Feed::articleByGUID ( const QString &  guid) const

Definition at line 179 of file rss_feed.cpp.

180 {
181  return m_articles.value(guid);
182 }

References m_articles.

Referenced by RSSController::markAsReadAction(), and updateArticles().

Here is the caller graph for this function:

◆ articles()

QList< Article * > Feed::articles ( ) const
overridevirtual

Implements RSS::Item.

Definition at line 105 of file rss_feed.cpp.

106 {
107  return m_articlesByDate;
108 }

References m_articlesByDate.

Referenced by RSSController::matchingArticlesAction(), and updateArticles().

Here is the caller graph for this function:

◆ cleanup()

void Feed::cleanup ( )
overrideprivatevirtual

Implements RSS::Item.

Definition at line 550 of file rss_feed.cpp.

551 {
554 }
bool forceRemove(const QString &filePath)
Definition: fs.cpp:173

References RSS::Session::dataFileStorage(), Utils::Fs::forceRemove(), m_dataFileName, m_iconPath, m_session, and AsyncFileStorage::storageDir().

Here is the call graph for this function:

◆ decreaseUnreadCount()

void Feed::decreaseUnreadCount ( )
private

Definition at line 416 of file rss_feed.cpp.

417 {
418  Q_ASSERT(m_unreadCount > 0);
419 
420  --m_unreadCount;
421  emit unreadCountChanged(this);
422 }
int m_unreadCount
Definition: rss_feed.h:124
void unreadCountChanged(Item *item=nullptr)

References m_unreadCount, and RSS::Item::unreadCountChanged().

Referenced by handleArticleRead(), and removeOldestArticle().

Here is the caller graph for this function:

◆ downloadIcon()

void Feed::downloadIcon ( )
private

Definition at line 424 of file rss_feed.cpp.

425 {
426  // Download the RSS Feed icon
427  // XXX: This works for most sites but it is not perfect
428  const QUrl url(m_url);
429  const auto iconUrl = QString::fromLatin1("%1://%2/favicon.ico").arg(url.scheme(), url.host());
431  Net::DownloadRequest(iconUrl).saveToFile(true).destFileName(m_iconPath)
433 }
DownloadHandler * download(const DownloadRequest &downloadRequest)
void handleIconDownloadFinished(const Net::DownloadResult &result)
Definition: rss_feed.cpp:191
nonstd::expected< void, QString > saveToFile(const QString &path, const QByteArray &data)
Definition: io.cpp:69

References Net::DownloadManager::download(), handleIconDownloadFinished(), Net::DownloadManager::instance(), m_iconPath, m_url, Utils::IO::saveToFile(), and url().

Referenced by Feed(), handleSessionProcessingEnabledChanged(), and refresh().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleArticleRead

void Feed::handleArticleRead ( Article article)
privateslot

Definition at line 540 of file rss_feed.cpp.

541 {
542  article->disconnect(this);
544  emit articleRead(article);
545  // will be stored deferred
546  m_dirty = true;
547  storeDeferred();
548 }
void decreaseUnreadCount()
Definition: rss_feed.cpp:416
void storeDeferred()
Definition: rss_feed.cpp:361
void articleRead(Article *article)

References RSS::Item::articleRead(), decreaseUnreadCount(), m_dirty, and storeDeferred().

Referenced by addArticle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleDownloadFinished

void Feed::handleDownloadFinished ( const Net::DownloadResult result)
privateslot

Definition at line 204 of file rss_feed.cpp.

205 {
206  m_downloadHandler = nullptr; // will be deleted by DownloadManager later
207 
208  if (result.status == Net::DownloadStatus::Success)
209  {
210  LogMsg(tr("RSS feed at '%1' is successfully downloaded. Starting to parse it.")
211  .arg(result.url));
212  // Parse the download RSS
213  m_parser->parse(result.data);
214  }
215  else
216  {
217  m_isLoading = false;
218  m_hasError = true;
219 
220  LogMsg(tr("Failed to download RSS feed at '%1'. Reason: %2")
221  .arg(result.url, result.errorString), Log::WARNING);
222 
223  emit stateChanged(this);
224  }
225 }
bool m_hasError
Definition: rss_feed.h:120
Net::DownloadHandler * m_downloadHandler
Definition: rss_feed.h:129
void stateChanged(Feed *feed=nullptr)
bool m_isLoading
Definition: rss_feed.h:121
void parse(const QByteArray &feedData)
Definition: rss_parser.cpp:550
void LogMsg(const QString &message, const Log::MsgType &type)
Definition: logger.cpp:125
@ WARNING
Definition: logger.h:47
DownloadStatus status

References Net::DownloadResult::data, Net::DownloadResult::errorString, LogMsg(), m_downloadHandler, m_hasError, m_isLoading, m_parser, RSS::Private::Parser::parse(), stateChanged(), Net::DownloadResult::status, Net::Success, Net::DownloadResult::url, and Log::WARNING.

Referenced by refresh().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleIconDownloadFinished

void Feed::handleIconDownloadFinished ( const Net::DownloadResult result)
privateslot

Definition at line 191 of file rss_feed.cpp.

192 {
193  if (result.status == Net::DownloadStatus::Success)
194  {
195  emit iconLoaded(this);
196  }
197 }
void iconLoaded(Feed *feed=nullptr)

References iconLoaded(), Net::DownloadResult::status, and Net::Success.

Referenced by downloadIcon().

Here is the caller graph for this function:

◆ handleMaxArticlesPerFeedChanged

void Feed::handleMaxArticlesPerFeedChanged ( int  n)
privateslot

Definition at line 184 of file rss_feed.cpp.

185 {
186  while (m_articlesByDate.size() > n)
188  // We don't need store articles here
189 }

References m_articlesByDate, and removeOldestArticle().

Referenced by Feed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleParsingFinished

void Feed::handleParsingFinished ( const Private::ParsingResult result)
privateslot

Definition at line 227 of file rss_feed.cpp.

228 {
229  m_hasError = !result.error.isEmpty();
230 
231  if (!result.title.isEmpty() && (title() != result.title))
232  {
233  m_title = result.title;
234  m_dirty = true;
235  emit titleChanged(this);
236  }
237 
238  if (!result.lastBuildDate.isEmpty())
239  {
240  m_lastBuildDate = result.lastBuildDate;
241  m_dirty = true;
242  }
243 
244  // For some reason, the RSS feed may contain malformed XML data and it may not be
245  // successfully parsed by the XML parser. We are still trying to load as many articles
246  // as possible until we encounter corrupted data. So we can have some articles here
247  // even in case of parsing error.
248  const int newArticlesCount = updateArticles(result.articles);
249  store();
250 
251  if (m_hasError)
252  {
253  LogMsg(tr("Failed to parse RSS feed at '%1'. Reason: %2").arg(m_url, result.error)
254  , Log::WARNING);
255  }
256  LogMsg(tr("RSS feed at '%1' updated. Added %2 new articles.")
257  .arg(url(), QString::number(newArticlesCount)));
258 
259  m_isLoading = false;
260  emit stateChanged(this);
261 }
QString title() const
Definition: rss_feed.cpp:159
void titleChanged(Feed *feed=nullptr)
int updateArticles(const QList< QVariantHash > &loadedArticles)
Definition: rss_feed.cpp:435
void store()
Definition: rss_feed.cpp:347
QString m_title
Definition: rss_feed.h:118

References RSS::Private::ParsingResult::articles, RSS::Private::ParsingResult::error, RSS::Private::ParsingResult::lastBuildDate, LogMsg(), m_dirty, m_hasError, m_isLoading, m_lastBuildDate, m_title, m_url, stateChanged(), store(), title(), RSS::Private::ParsingResult::title, titleChanged(), updateArticles(), url(), and Log::WARNING.

Referenced by Feed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleSessionProcessingEnabledChanged

void Feed::handleSessionProcessingEnabledChanged ( bool  enabled)
privateslot

Definition at line 530 of file rss_feed.cpp.

531 {
532  if (enabled)
533  {
534  downloadIcon();
537  }
538 }

References downloadIcon(), m_session, and RSS::Session::processingStateChanged().

Referenced by Feed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hasError()

bool Feed::hasError ( ) const

Definition at line 199 of file rss_feed.cpp.

200 {
201  return m_hasError;
202 }

References m_hasError.

Referenced by FeedListWidget::handleFeedIconLoaded(), anonymous_namespace{feedlistwidget.cpp}::rssFeedIcon(), and toJsonValue().

Here is the caller graph for this function:

◆ iconLoaded

void RSS::Feed::iconLoaded ( Feed feed = nullptr)
signal

Referenced by RSS::Session::addItem(), and handleIconDownloadFinished().

Here is the caller graph for this function:

◆ iconPath()

QString Feed::iconPath ( ) const

Definition at line 503 of file rss_feed.cpp.

504 {
505  return m_iconPath;
506 }

References m_iconPath.

Referenced by anonymous_namespace{feedlistwidget.cpp}::rssFeedIcon().

Here is the caller graph for this function:

◆ increaseUnreadCount()

void Feed::increaseUnreadCount ( )
private

Definition at line 410 of file rss_feed.cpp.

411 {
412  ++m_unreadCount;
413  emit unreadCountChanged(this);
414 }

References m_unreadCount, and RSS::Item::unreadCountChanged().

Referenced by addArticle().

Here is the caller graph for this function:

◆ isLoading()

bool Feed::isLoading ( ) const

Definition at line 164 of file rss_feed.cpp.

165 {
166  return m_isLoading;
167 }

References m_isLoading.

Referenced by FeedListWidget::handleFeedIconLoaded(), anonymous_namespace{feedlistwidget.cpp}::rssFeedIcon(), and toJsonValue().

Here is the caller graph for this function:

◆ lastBuildDate()

QString Feed::lastBuildDate ( ) const

Definition at line 169 of file rss_feed.cpp.

170 {
171  return m_lastBuildDate;
172 }

References m_lastBuildDate.

Referenced by toJsonValue().

Here is the caller graph for this function:

◆ load()

void Feed::load ( )
private

Definition at line 263 of file rss_feed.cpp.

264 {
265  QFile file(m_session->dataFileStorage()->storageDir().absoluteFilePath(m_dataFileName));
266 
267  if (!file.exists())
268  {
270  m_dirty = true;
271  store(); // convert to new format
272  }
273  else if (file.open(QFile::ReadOnly))
274  {
275  loadArticles(file.readAll());
276  file.close();
277  }
278  else
279  {
280  LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
281  .arg(m_dataFileName, file.errorString())
282  , Log::WARNING);
283  }
284 }
void loadArticles(const QByteArray &data)
Definition: rss_feed.cpp:286
void loadArticlesLegacy()
Definition: rss_feed.cpp:325
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts") set_source_files_properties($
Definition: CMakeLists.txt:5

References RSS::Session::dataFileStorage(), file(), loadArticles(), loadArticlesLegacy(), LogMsg(), m_dataFileName, m_dirty, m_session, AsyncFileStorage::storageDir(), store(), and Log::WARNING.

Referenced by Feed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ loadArticles()

void Feed::loadArticles ( const QByteArray &  data)
private

Definition at line 286 of file rss_feed.cpp.

287 {
288  QJsonParseError jsonError;
289  const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
290  if (jsonError.error != QJsonParseError::NoError)
291  {
292  LogMsg(tr("Couldn't parse RSS Session data. Error: %1").arg(jsonError.errorString())
293  , Log::WARNING);
294  return;
295  }
296 
297  if (!jsonDoc.isArray())
298  {
299  LogMsg(tr("Couldn't load RSS Session data. Invalid data format."), Log::WARNING);
300  return;
301  }
302 
303  const QJsonArray jsonArr = jsonDoc.array();
304  int i = -1;
305  for (const QJsonValue &jsonVal : jsonArr)
306  {
307  ++i;
308  if (!jsonVal.isObject())
309  {
310  LogMsg(tr("Couldn't load RSS article '%1#%2'. Invalid data format.").arg(m_url).arg(i)
311  , Log::WARNING);
312  continue;
313  }
314 
315  try
316  {
317  auto article = new Article(this, jsonVal.toObject());
318  if (!addArticle(article))
319  delete article;
320  }
321  catch (const RuntimeError &) {}
322  }
323 }
Definition: rss_article.h:43
bool addArticle(Article *article)
Definition: rss_feed.cpp:367

References addArticle(), LogMsg(), m_url, and Log::WARNING.

Referenced by load().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ loadArticlesLegacy()

void Feed::loadArticlesLegacy ( )
private

Definition at line 325 of file rss_feed.cpp.

326 {
327  const SettingsPtr qBTRSSFeeds = Profile::instance()->applicationSettings(QStringLiteral("qBittorrent-rss-feeds"));
328  const QVariantHash allOldItems = qBTRSSFeeds->value("old_items").toHash();
329 
330  for (const QVariant &var : asConst(allOldItems.value(m_url).toList()))
331  {
332  auto hash = var.toHash();
333  // update legacy keys
334  hash[Article::KeyLink] = hash.take(QLatin1String("news_link"));
335  hash[Article::KeyTorrentURL] = hash.take(QLatin1String("torrent_url"));
336  hash[Article::KeyIsRead] = hash.take(QLatin1String("read"));
337  try
338  {
339  auto article = new Article(this, hash);
340  if (!addArticle(article))
341  delete article;
342  }
343  catch (const RuntimeError &) {}
344  }
345 }
SettingsPtr applicationSettings(const QString &name) const
Definition: profile.cpp:109
static const Profile * instance()
Definition: profile.cpp:67
static const QString KeyIsRead
Definition: rss_article.h:60
static const QString KeyLink
Definition: rss_article.h:59
static const QString KeyTorrentURL
Definition: rss_article.h:58
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
std::unique_ptr< QSettings > SettingsPtr
Definition: profile.h:44

References addArticle(), Profile::applicationSettings(), asConst(), Profile::instance(), RSS::Article::KeyIsRead, RSS::Article::KeyLink, RSS::Article::KeyTorrentURL, and m_url.

Referenced by load().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ markAsRead()

void Feed::markAsRead ( )
overridevirtual

Implements RSS::Item.

Definition at line 110 of file rss_feed.cpp.

111 {
112  const int oldUnreadCount = m_unreadCount;
113  for (Article *article : asConst(m_articles))
114  {
115  if (!article->isRead())
116  {
117  article->disconnect(this);
118  article->markAsRead();
119  --m_unreadCount;
120  emit articleRead(article);
121  }
122  }
123 
124  if (m_unreadCount != oldUnreadCount)
125  {
126  m_dirty = true;
127  store();
128  emit unreadCountChanged(this);
129  }
130 }

References RSS::Item::articleRead(), asConst(), m_articles, m_dirty, m_unreadCount, store(), and RSS::Item::unreadCountChanged().

Here is the call graph for this function:

◆ refresh()

void Feed::refresh ( )
overridevirtual

Implements RSS::Item.

Definition at line 132 of file rss_feed.cpp.

133 {
134  if (m_downloadHandler)
136 
137  // NOTE: Should we allow manually refreshing for disabled session?
138 
141 
142  if (!QFile::exists(m_iconPath))
143  downloadIcon();
144 
145  m_isLoading = true;
146  emit stateChanged(this);
147 }
virtual void cancel()=0
void finished(const DownloadResult &result)
void handleDownloadFinished(const Net::DownloadResult &result)
Definition: rss_feed.cpp:204

References Net::DownloadHandler::cancel(), Net::DownloadManager::download(), downloadIcon(), Net::DownloadHandler::finished(), handleDownloadFinished(), Net::DownloadManager::instance(), m_downloadHandler, m_iconPath, m_isLoading, m_url, and stateChanged().

Referenced by RSS::Session::addFeed().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ removeOldestArticle()

void Feed::removeOldestArticle ( )
private

Definition at line 396 of file rss_feed.cpp.

397 {
398  auto oldestArticle = m_articlesByDate.last();
399  emit articleAboutToBeRemoved(oldestArticle);
400 
401  m_articles.remove(oldestArticle->guid());
402  m_articlesByDate.removeLast();
403  const bool isRead = oldestArticle->isRead();
404  delete oldestArticle;
405 
406  if (!isRead)
408 }
void articleAboutToBeRemoved(Article *article)

References RSS::Item::articleAboutToBeRemoved(), decreaseUnreadCount(), m_articles, and m_articlesByDate.

Referenced by addArticle(), and handleMaxArticlesPerFeedChanged().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ stateChanged

void RSS::Feed::stateChanged ( Feed feed = nullptr)
signal

Referenced by RSS::Session::addItem(), handleDownloadFinished(), handleParsingFinished(), and refresh().

Here is the caller graph for this function:

◆ store()

void Feed::store ( )
private

Definition at line 347 of file rss_feed.cpp.

348 {
349  if (!m_dirty) return;
350 
351  m_dirty = false;
352  m_savingTimer.stop();
353 
354  QJsonArray jsonArr;
355  for (Article *article :asConst(m_articles))
356  jsonArr << article->toJsonObject();
357 
358  m_session->dataFileStorage()->store(m_dataFileName, QJsonDocument(jsonArr).toJson());
359 }
void store(const QString &fileName, const QByteArray &data)
QBasicTimer m_savingTimer
Definition: rss_feed.h:127

References asConst(), RSS::Session::dataFileStorage(), m_articles, m_dataFileName, m_dirty, m_savingTimer, m_session, and AsyncFileStorage::store().

Referenced by handleParsingFinished(), load(), markAsRead(), and timerEvent().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ storeDeferred()

void Feed::storeDeferred ( )
private

Definition at line 361 of file rss_feed.cpp.

362 {
363  if (!m_savingTimer.isActive())
364  m_savingTimer.start(5 * 1000, this);
365 }

References m_savingTimer.

Referenced by handleArticleRead().

Here is the caller graph for this function:

◆ timerEvent()

void Feed::timerEvent ( QTimerEvent *  event)
overrideprivate

Definition at line 556 of file rss_feed.cpp.

557 {
558  Q_UNUSED(event);
559  store();
560 }

References store().

Here is the call graph for this function:

◆ title()

QString Feed::title ( ) const

Definition at line 159 of file rss_feed.cpp.

160 {
161  return m_title;
162 }

References m_title.

Referenced by RSS::Session::handleFeedTitleChanged(), handleParsingFinished(), RSSController::matchingArticlesAction(), toJsonValue(), and AutomatedRssDownloader::updateMatchingArticles().

Here is the caller graph for this function:

◆ titleChanged

void RSS::Feed::titleChanged ( Feed feed = nullptr)
signal

Referenced by RSS::Session::addItem(), and handleParsingFinished().

Here is the caller graph for this function:

◆ toJsonValue()

QJsonValue Feed::toJsonValue ( bool  withData = false) const
overridevirtual

Implements RSS::Item.

Definition at line 508 of file rss_feed.cpp.

509 {
510  QJsonObject jsonObj;
511  jsonObj.insert(KEY_UID, uid().toString());
512  jsonObj.insert(KEY_URL, url());
513 
514  if (withData)
515  {
516  jsonObj.insert(KEY_TITLE, title());
517  jsonObj.insert(KEY_LASTBUILDDATE, lastBuildDate());
518  jsonObj.insert(KEY_ISLOADING, isLoading());
519  jsonObj.insert(KEY_HASERROR, hasError());
520 
521  QJsonArray jsonArr;
522  for (Article *article : asConst(m_articles))
523  jsonArr << article->toJsonObject();
524  jsonObj.insert(KEY_ARTICLES, jsonArr);
525  }
526 
527  return jsonObj;
528 }
QString lastBuildDate() const
Definition: rss_feed.cpp:169
bool isLoading() const
Definition: rss_feed.cpp:164
bool hasError() const
Definition: rss_feed.cpp:199
QString toString(const lt::socket_type_t socketType)
Definition: session.cpp:183
const QString KEY_LASTBUILDDATE(QStringLiteral("lastBuildDate"))
const QString KEY_URL(QStringLiteral("url"))
const QString KEY_UID(QStringLiteral("uid"))
const QString KEY_TITLE(QStringLiteral("title"))
const QString KEY_ISLOADING(QStringLiteral("isLoading"))
const QString KEY_ARTICLES(QStringLiteral("articles"))
const QString KEY_HASERROR(QStringLiteral("hasError"))

References asConst(), hasError(), isLoading(), KEY_ARTICLES(), KEY_HASERROR(), KEY_ISLOADING(), KEY_LASTBUILDDATE(), KEY_TITLE(), KEY_UID(), KEY_URL(), lastBuildDate(), m_articles, title(), anonymous_namespace{session.cpp}::toString(), uid(), and url().

Here is the call graph for this function:

◆ uid()

QUuid Feed::uid ( ) const

Definition at line 149 of file rss_feed.cpp.

150 {
151  return m_uid;
152 }

References m_uid.

Referenced by toJsonValue().

Here is the caller graph for this function:

◆ unreadCount()

int Feed::unreadCount ( ) const
overridevirtual

Implements RSS::Item.

Definition at line 174 of file rss_feed.cpp.

175 {
176  return m_unreadCount;
177 }

References m_unreadCount.

◆ updateArticles()

int Feed::updateArticles ( const QList< QVariantHash > &  loadedArticles)
private

Definition at line 435 of file rss_feed.cpp.

436 {
437  if (loadedArticles.empty())
438  return 0;
439 
440  QDateTime dummyPubDate {QDateTime::currentDateTime()};
441  QVector<QVariantHash> newArticles;
442  newArticles.reserve(loadedArticles.size());
443  for (QVariantHash article : loadedArticles)
444  {
445  // If article has no publication date we use feed update time as a fallback.
446  // To prevent processing of "out-of-limit" articles we must not assign dates
447  // that are earlier than the dates of existing articles.
448  const Article *existingArticle = articleByGUID(article[Article::KeyId].toString());
449  if (existingArticle)
450  {
451  dummyPubDate = existingArticle->date().addMSecs(-1);
452  continue;
453  }
454 
455  QVariant &articleDate = article[Article::KeyDate];
456  if (!articleDate.toDateTime().isValid())
457  articleDate = dummyPubDate;
458 
459  newArticles.append(article);
460  }
461 
462  if (newArticles.empty())
463  return 0;
464 
465  using ArticleSortAdaptor = std::pair<QDateTime, const QVariantHash *>;
466  std::vector<ArticleSortAdaptor> sortData;
467  const QList<Article *> existingArticles = articles();
468  sortData.reserve(existingArticles.size() + newArticles.size());
469  std::transform(existingArticles.begin(), existingArticles.end(), std::back_inserter(sortData)
470  , [](const Article *article)
471  {
472  return std::make_pair(article->date(), nullptr);
473  });
474  std::transform(newArticles.begin(), newArticles.end(), std::back_inserter(sortData)
475  , [](const QVariantHash &article)
476  {
477  return std::make_pair(article[Article::KeyDate].toDateTime(), &article);
478  });
479 
480  // Sort article list in reverse chronological order
481  std::sort(sortData.begin(), sortData.end()
482  , [](const ArticleSortAdaptor &a1, const ArticleSortAdaptor &a2)
483  {
484  return (a1.first > a2.first);
485  });
486 
487  if (sortData.size() > static_cast<uint>(m_session->maxArticlesPerFeed()))
488  sortData.resize(m_session->maxArticlesPerFeed());
489 
490  int newArticlesCount = 0;
491  std::for_each(sortData.crbegin(), sortData.crend(), [this, &newArticlesCount](const ArticleSortAdaptor &a)
492  {
493  if (a.second)
494  {
495  addArticle(new Article {this, *a.second});
496  ++newArticlesCount;
497  }
498  });
499 
500  return newArticlesCount;
501 }
static const QString KeyDate
Definition: rss_article.h:54
static const QString KeyId
Definition: rss_article.h:53
Article * articleByGUID(const QString &guid) const
Definition: rss_feed.cpp:179
QList< Article * > articles() const override
Definition: rss_feed.cpp:105

References articleByGUID(), articles(), RSS::Article::date(), RSS::Article::KeyDate, RSS::Article::KeyId, m_session, RSS::Session::maxArticlesPerFeed(), and anonymous_namespace{session.cpp}::toString().

Referenced by handleParsingFinished().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ url()

QString Feed::url ( ) const

Definition at line 154 of file rss_feed.cpp.

155 {
156  return m_url;
157 }

References m_url.

Referenced by AutomatedRssDownloader::addFeedArticlesToTree(), RSS::AutoDownloader::addJobForArticle(), downloadIcon(), RSS::Session::handleFeedTitleChanged(), handleParsingFinished(), and toJsonValue().

Here is the caller graph for this function:

Friends And Related Function Documentation

◆ Session

friend class Session
friend

Definition at line 64 of file rss_feed.h.

Member Data Documentation

◆ m_articles

QHash<QString, Article *> RSS::Feed::m_articles
private

◆ m_articlesByDate

QList<Article *> RSS::Feed::m_articlesByDate
private

◆ m_dataFileName

QString RSS::Feed::m_dataFileName
private

Definition at line 126 of file rss_feed.h.

Referenced by cleanup(), Feed(), load(), and store().

◆ m_dirty

bool RSS::Feed::m_dirty = false
private

◆ m_downloadHandler

Net::DownloadHandler* RSS::Feed::m_downloadHandler = nullptr
private

Definition at line 129 of file rss_feed.h.

Referenced by handleDownloadFinished(), and refresh().

◆ m_hasError

bool RSS::Feed::m_hasError = false
private

Definition at line 120 of file rss_feed.h.

Referenced by handleDownloadFinished(), handleParsingFinished(), and hasError().

◆ m_iconPath

QString RSS::Feed::m_iconPath
private

Definition at line 125 of file rss_feed.h.

Referenced by cleanup(), downloadIcon(), Feed(), iconPath(), and refresh().

◆ m_isLoading

bool RSS::Feed::m_isLoading = false
private

Definition at line 121 of file rss_feed.h.

Referenced by handleDownloadFinished(), handleParsingFinished(), isLoading(), and refresh().

◆ m_lastBuildDate

QString RSS::Feed::m_lastBuildDate
private

Definition at line 119 of file rss_feed.h.

Referenced by Feed(), handleParsingFinished(), and lastBuildDate().

◆ m_parser

Private::Parser* RSS::Feed::m_parser
private

Definition at line 115 of file rss_feed.h.

Referenced by Feed(), and handleDownloadFinished().

◆ m_savingTimer

QBasicTimer RSS::Feed::m_savingTimer
private

Definition at line 127 of file rss_feed.h.

Referenced by store(), and storeDeferred().

◆ m_session

Session* RSS::Feed::m_session
private

◆ m_title

QString RSS::Feed::m_title
private

Definition at line 118 of file rss_feed.h.

Referenced by handleParsingFinished(), and title().

◆ m_uid

const QUuid RSS::Feed::m_uid
private

Definition at line 116 of file rss_feed.h.

Referenced by Feed(), and uid().

◆ m_unreadCount

int RSS::Feed::m_unreadCount = 0
private

Definition at line 124 of file rss_feed.h.

Referenced by decreaseUnreadCount(), increaseUnreadCount(), markAsRead(), and unreadCount().

◆ m_url

const QString RSS::Feed::m_url
private

The documentation for this class was generated from the following files: