From 405b06319df2360cee9e4ae4b08453e435aa3925 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 14 Oct 2015 12:31:44 +0300 Subject: [PATCH 1/5] Move base RSS code to Core. --- src/base/base.pri | 16 ++++++++++++++++ src/{gui => base}/rss/rssarticle.cpp | 2 +- src/{gui => base}/rss/rssarticle.h | 0 src/{gui => base}/rss/rssdownloadrule.cpp | 4 ++-- src/{gui => base}/rss/rssdownloadrule.h | 0 src/{gui => base}/rss/rssdownloadrulelist.cpp | 2 +- src/{gui => base}/rss/rssdownloadrulelist.h | 0 src/{gui => base}/rss/rssfeed.cpp | 19 ++++++++++--------- src/{gui => base}/rss/rssfeed.h | 2 +- src/{gui => base}/rss/rssfile.cpp | 2 +- src/{gui => base}/rss/rssfile.h | 3 +-- src/{gui => base}/rss/rssfolder.cpp | 10 +++++----- src/{gui => base}/rss/rssfolder.h | 3 ++- src/{gui => base}/rss/rssmanager.cpp | 3 ++- src/{gui => base}/rss/rssmanager.h | 0 src/{gui => base}/rss/rssparser.cpp | 12 ++++-------- src/{gui => base}/rss/rssparser.h | 3 ++- src/gui/rss/automatedrssdownloader.cpp | 6 +++--- src/gui/rss/automatedrssdownloader.h | 3 ++- src/gui/rss/feedlistwidget.cpp | 6 +++--- src/gui/rss/feedlistwidget.h | 6 +++--- src/gui/rss/rss.pri | 16 ---------------- src/gui/rss/rss_imp.cpp | 14 +++++++------- src/gui/rss/rss_imp.h | 4 ++-- 24 files changed, 68 insertions(+), 68 deletions(-) rename src/{gui => base}/rss/rssarticle.cpp (100%) rename src/{gui => base}/rss/rssarticle.h (100%) rename src/{gui => base}/rss/rssdownloadrule.cpp (100%) rename src/{gui => base}/rss/rssdownloadrule.h (100%) rename src/{gui => base}/rss/rssdownloadrulelist.cpp (100%) rename src/{gui => base}/rss/rssdownloadrulelist.h (100%) rename src/{gui => base}/rss/rssfeed.cpp (99%) rename src/{gui => base}/rss/rssfeed.h (99%) rename src/{gui => base}/rss/rssfile.cpp (100%) rename src/{gui => base}/rss/rssfile.h (98%) rename src/{gui => base}/rss/rssfolder.cpp (98%) rename src/{gui => base}/rss/rssfolder.h (98%) rename src/{gui => base}/rss/rssmanager.cpp (99%) rename src/{gui => base}/rss/rssmanager.h (100%) rename src/{gui => base}/rss/rssparser.cpp (98%) rename src/{gui => base}/rss/rssparser.h (99%) diff --git a/src/base/base.pri b/src/base/base.pri index 3ce02d04d..ccea8efe1 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -36,6 +36,14 @@ HEADERS += \ $$PWD/bittorrent/private/bandwidthscheduler.h \ $$PWD/bittorrent/private/filterparserthread.h \ $$PWD/bittorrent/private/statistics.h \ + $$PWD/rss/rssmanager.h \ + $$PWD/rss/rssfeed.h \ + $$PWD/rss/rssfolder.h \ + $$PWD/rss/rssfile.h \ + $$PWD/rss/rssarticle.h \ + $$PWD/rss/rssdownloadrule.h \ + $$PWD/rss/rssdownloadrulelist.h \ + $$PWD/rss/rssparser.h \ $$PWD/utils/fs.h \ $$PWD/utils/gzip.h \ $$PWD/utils/misc.h \ @@ -79,6 +87,14 @@ SOURCES += \ $$PWD/bittorrent/private/bandwidthscheduler.cpp \ $$PWD/bittorrent/private/filterparserthread.cpp \ $$PWD/bittorrent/private/statistics.cpp \ + $$PWD/rss/rssmanager.cpp \ + $$PWD/rss/rssfeed.cpp \ + $$PWD/rss/rssfolder.cpp \ + $$PWD/rss/rssarticle.cpp \ + $$PWD/rss/rssdownloadrule.cpp \ + $$PWD/rss/rssdownloadrulelist.cpp \ + $$PWD/rss/rssfile.cpp \ + $$PWD/rss/rssparser.cpp \ $$PWD/utils/fs.cpp \ $$PWD/utils/gzip.cpp \ $$PWD/utils/misc.cpp \ diff --git a/src/gui/rss/rssarticle.cpp b/src/base/rss/rssarticle.cpp similarity index 100% rename from src/gui/rss/rssarticle.cpp rename to src/base/rss/rssarticle.cpp index fbc7218bf..34ab247eb 100644 --- a/src/gui/rss/rssarticle.cpp +++ b/src/base/rss/rssarticle.cpp @@ -32,8 +32,8 @@ #include #include -#include "rssarticle.h" #include "rssfeed.h" +#include "rssarticle.h" // public constructor RssArticle::RssArticle(RssFeed* parent, const QString& guid): diff --git a/src/gui/rss/rssarticle.h b/src/base/rss/rssarticle.h similarity index 100% rename from src/gui/rss/rssarticle.h rename to src/base/rss/rssarticle.h diff --git a/src/gui/rss/rssdownloadrule.cpp b/src/base/rss/rssdownloadrule.cpp similarity index 100% rename from src/gui/rss/rssdownloadrule.cpp rename to src/base/rss/rssdownloadrule.cpp index cd37a3a56..132166e06 100644 --- a/src/gui/rss/rssdownloadrule.cpp +++ b/src/base/rss/rssdownloadrule.cpp @@ -32,11 +32,11 @@ #include #include -#include "rssdownloadrule.h" #include "base/preferences.h" +#include "base/utils/fs.h" #include "rssfeed.h" #include "rssarticle.h" -#include "base/utils/fs.h" +#include "rssdownloadrule.h" RssDownloadRule::RssDownloadRule(): m_enabled(false), m_useRegex(false), m_apstate(USE_GLOBAL) { diff --git a/src/gui/rss/rssdownloadrule.h b/src/base/rss/rssdownloadrule.h similarity index 100% rename from src/gui/rss/rssdownloadrule.h rename to src/base/rss/rssdownloadrule.h diff --git a/src/gui/rss/rssdownloadrulelist.cpp b/src/base/rss/rssdownloadrulelist.cpp similarity index 100% rename from src/gui/rss/rssdownloadrulelist.cpp rename to src/base/rss/rssdownloadrulelist.cpp index 12f6a9710..c1d450769 100644 --- a/src/gui/rss/rssdownloadrulelist.cpp +++ b/src/base/rss/rssdownloadrulelist.cpp @@ -32,9 +32,9 @@ #include #include -#include "rssdownloadrulelist.h" #include "base/preferences.h" #include "base/qinisettings.h" +#include "rssdownloadrulelist.h" RssDownloadRuleList::RssDownloadRuleList() { diff --git a/src/gui/rss/rssdownloadrulelist.h b/src/base/rss/rssdownloadrulelist.h similarity index 100% rename from src/gui/rss/rssdownloadrulelist.h rename to src/base/rss/rssdownloadrulelist.h diff --git a/src/gui/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp similarity index 99% rename from src/gui/rss/rssfeed.cpp rename to src/base/rss/rssfeed.cpp index 7659ecae1..72bcb8993 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -29,21 +29,22 @@ */ #include -#include "rssfeed.h" -#include "rssmanager.h" + #include "base/bittorrent/session.h" #include "base/bittorrent/magneturi.h" -#include "rssfolder.h" #include "base/preferences.h" #include "base/qinisettings.h" -#include "rssarticle.h" -#include "rssparser.h" #include "base/utils/misc.h" -#include "rssdownloadrulelist.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" #include "base/utils/fs.h" #include "base/logger.h" +#include "rssdownloadrulelist.h" +#include "rssarticle.h" +#include "rssparser.h" +#include "rssfolder.h" +#include "rssmanager.h" +#include "rssfeed.h" bool rssArticleDateRecentThan(const RssArticlePtr& left, const RssArticlePtr& right) { @@ -227,12 +228,12 @@ QString RssFeed::url() const return m_url; } -QIcon RssFeed::icon() const +QString RssFeed::iconPath() const { if (m_inErrorState) - return QIcon(":/icons/oxygen/unavailable.png"); + return QLatin1String(":/icons/oxygen/unavailable.png"); - return QIcon(m_icon); + return m_icon; } bool RssFeed::hasCustomIcon() const diff --git a/src/gui/rss/rssfeed.h b/src/base/rss/rssfeed.h similarity index 99% rename from src/gui/rss/rssfeed.h rename to src/base/rss/rssfeed.h index 81e53a5b2..84e95119f 100644 --- a/src/gui/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -66,7 +66,7 @@ public: virtual void rename(const QString &alias); virtual QString displayName() const; QString url() const; - virtual QIcon icon() const; + virtual QString iconPath() const; bool hasCustomIcon() const; void setIconPath(const QString &pathHierarchy); RssArticlePtr getItem(const QString &guid) const; diff --git a/src/gui/rss/rssfile.cpp b/src/base/rss/rssfile.cpp similarity index 100% rename from src/gui/rss/rssfile.cpp rename to src/base/rss/rssfile.cpp index f41912484..ac090e0ee 100644 --- a/src/gui/rss/rssfile.cpp +++ b/src/base/rss/rssfile.cpp @@ -28,8 +28,8 @@ * Contact: chris@qbittorrent.org, arnaud@qbittorrent.org */ -#include "rssfile.h" #include "rssfolder.h" +#include "rssfile.h" QStringList RssFile::pathHierarchy() const { QStringList path; diff --git a/src/gui/rss/rssfile.h b/src/base/rss/rssfile.h similarity index 98% rename from src/gui/rss/rssfile.h rename to src/base/rss/rssfile.h index 3d8d95f4e..9b08d00f9 100644 --- a/src/gui/rss/rssfile.h +++ b/src/base/rss/rssfile.h @@ -31,7 +31,6 @@ #ifndef RSSFILE_H #define RSSFILE_H -#include #include #include #include @@ -55,7 +54,7 @@ public: virtual uint unreadCount() const = 0; virtual QString displayName() const = 0; virtual QString id() const = 0; - virtual QIcon icon() const = 0; + virtual QString iconPath() const = 0; virtual void rename(const QString &new_name) = 0; virtual void markAsRead() = 0; virtual RssFolder* parent() const = 0; diff --git a/src/gui/rss/rssfolder.cpp b/src/base/rss/rssfolder.cpp similarity index 98% rename from src/gui/rss/rssfolder.cpp rename to src/base/rss/rssfolder.cpp index 12b16e6df..a76357623 100644 --- a/src/gui/rss/rssfolder.cpp +++ b/src/base/rss/rssfolder.cpp @@ -30,12 +30,12 @@ #include -#include "guiiconprovider.h" -#include "rssfolder.h" -#include "rssarticle.h" +#include "base/iconprovider.h" #include "base/bittorrent/session.h" #include "rssmanager.h" #include "rssfeed.h" +#include "rssarticle.h" +#include "rssfolder.h" RssFolder::RssFolder(RssFolder *parent, const QString &name): m_parent(parent), m_name(name) { } @@ -231,9 +231,9 @@ QString RssFolder::id() const return m_name; } -QIcon RssFolder::icon() const +QString RssFolder::iconPath() const { - return GuiIconProvider::instance()->getIcon("inode-directory"); + return IconProvider::instance()->getIconPath("inode-directory"); } bool RssFolder::hasChild(const QString &childId) { diff --git a/src/gui/rss/rssfolder.h b/src/base/rss/rssfolder.h similarity index 98% rename from src/gui/rss/rssfolder.h rename to src/base/rss/rssfolder.h index b3210fbac..85e8c7ca0 100644 --- a/src/gui/rss/rssfolder.h +++ b/src/base/rss/rssfolder.h @@ -33,6 +33,7 @@ #include #include + #include "rssfile.h" class RssFolder; @@ -61,7 +62,7 @@ public: QHash getAllFeedsAsHash() const; virtual QString displayName() const; virtual QString id() const; - virtual QIcon icon() const; + virtual QString iconPath() const; bool hasChild(const QString &childId); virtual RssArticleList articleListByDateDesc() const; virtual RssArticleList unreadArticleListByDateDesc() const; diff --git a/src/gui/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp similarity index 99% rename from src/gui/rss/rssmanager.cpp rename to src/base/rss/rssmanager.cpp index 338c82d17..f00457189 100644 --- a/src/gui/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -29,13 +29,14 @@ */ #include -#include "rssmanager.h" + #include "base/preferences.h" #include "base/bittorrent/session.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" #include "rssparser.h" +#include "rssmanager.h" static const int MSECS_PER_MIN = 60000; diff --git a/src/gui/rss/rssmanager.h b/src/base/rss/rssmanager.h similarity index 100% rename from src/gui/rss/rssmanager.h rename to src/base/rss/rssmanager.h diff --git a/src/gui/rss/rssparser.cpp b/src/base/rss/rssparser.cpp similarity index 98% rename from src/gui/rss/rssparser.cpp rename to src/base/rss/rssparser.cpp index e09023cea..5929684e4 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/base/rss/rssparser.cpp @@ -28,15 +28,14 @@ * Contact : chris@qbittorrent.org */ -#include "rssparser.h" -#include "base/utils/fs.h" - #include #include #include #include #include -#include + +#include "base/utils/fs.h" +#include "rssparser.h" struct ParsingJob { QString feedUrl; @@ -343,10 +342,7 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, if (xml.isStartElement()) { if (xml.name() == "title") { - // Workaround for CDATA (QString cannot parse html escapes on it's own) - QTextDocument doc; - doc.setHtml(xml.readElementText()); - article["title"] = doc.toPlainText().trimmed(); + article["title"] = xml.readElementText().trimmed(); } else if (xml.name() == "link") { QString link = ( xml.attributes().isEmpty() ? diff --git a/src/gui/rss/rssparser.h b/src/base/rss/rssparser.h similarity index 99% rename from src/gui/rss/rssparser.h rename to src/base/rss/rssparser.h index bb3b2466f..23608127b 100644 --- a/src/gui/rss/rssparser.h +++ b/src/base/rss/rssparser.h @@ -31,12 +31,13 @@ #ifndef RSSPARSER_H #define RSSPARSER_H -#include "rssarticle.h" #include #include #include #include +#include "rssarticle.h" + struct ParsingJob; class RssParser : public QThread diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index bf5684a57..f087c9bec 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -36,10 +36,10 @@ #include "automatedrssdownloader.h" #include "ui_automatedrssdownloader.h" -#include "rssdownloadrulelist.h" +#include "base/rss/rssdownloadrulelist.h" #include "base/preferences.h" -#include "rssmanager.h" -#include "rssfeed.h" +#include "base/rss/rssmanager.h" +#include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" #include "base/utils/fs.h" diff --git a/src/gui/rss/automatedrssdownloader.h b/src/gui/rss/automatedrssdownloader.h index b3e409a88..ad7338ed5 100644 --- a/src/gui/rss/automatedrssdownloader.h +++ b/src/gui/rss/automatedrssdownloader.h @@ -35,7 +35,8 @@ #include #include #include -#include "rssdownloadrule.h" + +#include "base/rss/rssdownloadrule.h" QT_BEGIN_NAMESPACE namespace Ui { diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index f86ac9864..9c30274f0 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -28,10 +28,10 @@ * Contact: chris@qbittorrent.org, arnaud@qbittorrent.org */ -#include "feedlistwidget.h" -#include "rssmanager.h" -#include "rssfeed.h" +#include "core/rss/rssmanager.h" +#include "core/rss/rssfeed.h" #include "guiiconprovider.h" +#include "feedlistwidget.h" FeedListWidget::FeedListWidget(QWidget *parent, const RssManagerPtr& rssmanager): QTreeWidget(parent), m_rssManager(rssmanager) { setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/src/gui/rss/feedlistwidget.h b/src/gui/rss/feedlistwidget.h index 89f99eb98..8e69df244 100644 --- a/src/gui/rss/feedlistwidget.h +++ b/src/gui/rss/feedlistwidget.h @@ -39,9 +39,9 @@ #include #include -#include "rssfile.h" -#include "rssfeed.h" -#include "rssmanager.h" +#include "base/rss/rssfile.h" +#include "base/rss/rssfeed.h" +#include "base/rss/rssmanager.h" class FeedListWidget: public QTreeWidget { Q_OBJECT diff --git a/src/gui/rss/rss.pri b/src/gui/rss/rss.pri index e4d91389b..01df487de 100644 --- a/src/gui/rss/rss.pri +++ b/src/gui/rss/rss.pri @@ -3,31 +3,15 @@ INCLUDEPATH += $$PWD HEADERS += $$PWD/rss_imp.h \ $$PWD/rsssettingsdlg.h \ $$PWD/feedlistwidget.h \ - $$PWD/rssmanager.h \ - $$PWD/rssfeed.h \ - $$PWD/rssfolder.h \ - $$PWD/rssfile.h \ - $$PWD/rssarticle.h \ $$PWD/automatedrssdownloader.h \ - $$PWD/rssdownloadrule.h \ - $$PWD/rssdownloadrulelist.h \ $$PWD/cookiesdlg.h \ - $$PWD/rssparser.h \ $$PWD/htmlbrowser.h SOURCES += $$PWD/rss_imp.cpp \ $$PWD/rsssettingsdlg.cpp \ $$PWD/feedlistwidget.cpp \ - $$PWD/rssmanager.cpp \ - $$PWD/rssfeed.cpp \ - $$PWD/rssfolder.cpp \ - $$PWD/rssarticle.cpp \ $$PWD/automatedrssdownloader.cpp \ - $$PWD/rssdownloadrule.cpp \ - $$PWD/rssdownloadrulelist.cpp \ $$PWD/cookiesdlg.cpp \ - $$PWD/rssfile.cpp \ - $$PWD/rssparser.cpp \ $$PWD/htmlbrowser.cpp FORMS += $$PWD/rss.ui \ diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 2cda0096c..c5aa670bf 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -44,11 +44,11 @@ #include "cookiesdlg.h" #include "base/preferences.h" #include "rsssettingsdlg.h" -#include "rssmanager.h" -#include "rssfolder.h" -#include "rssarticle.h" -#include "rssparser.h" -#include "rssfeed.h" +#include "base/rss/rssmanager.h" +#include "base/rss/rssfolder.h" +#include "base/rss/rssarticle.h" +#include "base/rss/rssparser.h" +#include "base/rss/rssfeed.h" #include "automatedrssdownloader.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" @@ -478,7 +478,7 @@ QTreeWidgetItem* RSSImp::createFolderListItem(const RssFilePtr& rssFile) Q_ASSERT(rssFile); QTreeWidgetItem* item = new QTreeWidgetItem; item->setData(0, Qt::DisplayRole, QVariant(rssFile->displayName() + QString::fromUtf8(" (") + QString::number(rssFile->unreadCount()) + QString(")"))); - item->setData(0, Qt::DecorationRole, rssFile->icon()); + item->setData(0, Qt::DecorationRole, QIcon(rssFile->iconPath())); return item; } @@ -680,7 +680,7 @@ void RSSImp::updateFeedInfos(const QString& url, const QString& display_name, ui RssFeedPtr stream = qSharedPointerCast(m_feedList->getRSSItem(item)); item->setText(0, display_name + QString::fromUtf8(" (") + QString::number(nbUnread) + QString(")")); if (!stream->isLoading()) - item->setData(0, Qt::DecorationRole, QVariant(stream->icon())); + item->setData(0, Qt::DecorationRole, QIcon(stream->iconPath())); // Update parent if (item->parent()) updateItemInfos(item->parent()); diff --git a/src/gui/rss/rss_imp.h b/src/gui/rss/rss_imp.h index 5754effd6..966de0006 100644 --- a/src/gui/rss/rss_imp.h +++ b/src/gui/rss/rss_imp.h @@ -35,9 +35,9 @@ #include #include +#include "base/rss/rssfolder.h" +#include "base/rss/rssmanager.h" #include "ui_rss.h" -#include "rssfolder.h" -#include "rssmanager.h" class FeedListWidget; From 67758cb0925ada8089f3629b0e7ecb6886b1f3a3 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 14 Oct 2015 12:49:29 +0300 Subject: [PATCH 2/5] Fix coding style (Issue #2192). --- src/base/rss/rssarticle.cpp | 149 ++--- src/base/rss/rssarticle.h | 71 +-- src/base/rss/rssdownloadrule.cpp | 375 +++++++----- src/base/rss/rssdownloadrule.h | 101 ++-- src/base/rss/rssdownloadrulelist.cpp | 187 +++--- src/base/rss/rssdownloadrulelist.h | 43 +- src/base/rss/rssfeed.cpp | 530 +++++++++-------- src/base/rss/rssfeed.h | 114 ++-- src/base/rss/rssfile.cpp | 20 +- src/base/rss/rssfile.h | 43 +- src/base/rss/rssfolder.cpp | 367 ++++++------ src/base/rss/rssfolder.h | 73 +-- src/base/rss/rssmanager.cpp | 171 +++--- src/base/rss/rssmanager.h | 48 +- src/base/rss/rssparser.cpp | 844 ++++++++++++++------------- src/base/rss/rssparser.h | 47 +- src/gui/rss/feedlistwidget.cpp | 4 +- 17 files changed, 1710 insertions(+), 1477 deletions(-) diff --git a/src/base/rss/rssarticle.cpp b/src/base/rss/rssarticle.cpp index 34ab247eb..585da24c7 100644 --- a/src/base/rss/rssarticle.cpp +++ b/src/base/rss/rssarticle.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,93 +37,107 @@ #include "rssarticle.h" // public constructor -RssArticle::RssArticle(RssFeed* parent, const QString& guid): - m_parent(parent), m_guid(guid), m_read(false) {} - -bool RssArticle::hasAttachment() const { - return !m_torrentUrl.isEmpty(); +RssArticle::RssArticle(RssFeed *parent, const QString &guid) + : m_parent(parent) + , m_guid(guid) + , m_read(false) +{ } -QVariantHash RssArticle::toHash() const { - QVariantHash item; - item["title"] = m_title; - item["id"] = m_guid; - item["torrent_url"] = m_torrentUrl; - item["news_link"] = m_link; - item["description"] = m_description; - item["date"] = m_date; - item["author"] = m_author; - item["read"] = m_read; - return item; +bool RssArticle::hasAttachment() const +{ + return !m_torrentUrl.isEmpty(); } -RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& h) { - const QString guid = h.value("id").toString(); - if (guid.isEmpty()) - return RssArticlePtr(); - - RssArticlePtr art(new RssArticle(parent, guid)); - art->m_title = h.value("title", "").toString(); - art->m_torrentUrl = h.value("torrent_url", "").toString(); - art->m_link = h.value("news_link", "").toString(); - art->m_description = h.value("description").toString(); - art->m_date = h.value("date").toDateTime(); - art->m_author = h.value("author").toString(); - art->m_read = h.value("read", false).toBool(); - - return art; +QVariantHash RssArticle::toHash() const +{ + QVariantHash item; + item["title"] = m_title; + item["id"] = m_guid; + item["torrent_url"] = m_torrentUrl; + item["news_link"] = m_link; + item["description"] = m_description; + item["date"] = m_date; + item["author"] = m_author; + item["read"] = m_read; + return item; } -RssFeed* RssArticle::parent() const { - return m_parent; +RssArticlePtr RssArticle::fromHash(RssFeed *parent, const QVariantHash &h) +{ + const QString guid = h.value("id").toString(); + if (guid.isEmpty()) + return RssArticlePtr(); + + RssArticlePtr art(new RssArticle(parent, guid)); + art->m_title = h.value("title", "").toString(); + art->m_torrentUrl = h.value("torrent_url", "").toString(); + art->m_link = h.value("news_link", "").toString(); + art->m_description = h.value("description").toString(); + art->m_date = h.value("date").toDateTime(); + art->m_author = h.value("author").toString(); + art->m_read = h.value("read", false).toBool(); + + return art; } -const QString& RssArticle::author() const { - return m_author; +RssFeed *RssArticle::parent() const +{ + return m_parent; } -const QString& RssArticle::torrentUrl() const { - return m_torrentUrl; +const QString &RssArticle::author() const +{ + return m_author; } -const QString& RssArticle::link() const { - return m_link; +const QString &RssArticle::torrentUrl() const +{ + return m_torrentUrl; +} + +const QString &RssArticle::link() const +{ + return m_link; } QString RssArticle::description() const { - return m_description.isNull() ? "" : m_description; + return m_description.isNull() ? "" : m_description; } -const QDateTime& RssArticle::date() const { - return m_date; -} - -bool RssArticle::isRead() const { - return m_read; -} - -void RssArticle::markAsRead() { - if (m_read) - return; - - m_read = true; - m_parent->decrementUnreadCount(); - m_parent->markAsDirty(); - emit articleWasRead(); -} - -const QString& RssArticle::guid() const +const QDateTime &RssArticle::date() const { - return m_guid; + return m_date; } -const QString& RssArticle::title() const +bool RssArticle::isRead() const { - return m_title; + return m_read; } -void RssArticle::handleTorrentDownloadSuccess(const QString &url) { - if (url == m_torrentUrl) - markAsRead(); +void RssArticle::markAsRead() +{ + if (m_read) return; + + m_read = true; + m_parent->decrementUnreadCount(); + m_parent->markAsDirty(); + emit articleWasRead(); +} + +const QString &RssArticle::guid() const +{ + return m_guid; +} + +const QString &RssArticle::title() const +{ + return m_title; +} + +void RssArticle::handleTorrentDownloadSuccess(const QString &url) +{ + if (url == m_torrentUrl) + markAsRead(); } diff --git a/src/base/rss/rssarticle.h b/src/base/rss/rssarticle.h index 7deeaf560..72bdac20c 100644 --- a/src/base/rss/rssarticle.h +++ b/src/base/rss/rssarticle.h @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,47 +43,47 @@ class RssArticle; typedef QSharedPointer RssArticlePtr; // Item of a rss stream, single information -class RssArticle : public QObject { - Q_OBJECT +class RssArticle: public QObject +{ + Q_OBJECT public: - RssArticle(RssFeed* parent, const QString& guid); - // Accessors - bool hasAttachment() const; - const QString& guid() const; - RssFeed* parent() const; - const QString& title() const; - const QString& author() const; - const QString& torrentUrl() const; - const QString& link() const; - QString description() const; - const QDateTime& date() const; - bool isRead() const; - // Setters - void markAsRead(); - // Serialization - QVariantHash toHash() const; + RssArticle(RssFeed *parent, const QString &guid); + + // Accessors + bool hasAttachment() const; + const QString &guid() const; + RssFeed *parent() const; + const QString &title() const; + const QString &author() const; + const QString &torrentUrl() const; + const QString &link() const; + QString description() const; + const QDateTime &date() const; + bool isRead() const; + // Setters + void markAsRead(); + + // Serialization + QVariantHash toHash() const; + static RssArticlePtr fromHash(RssFeed *parent, const QVariantHash &hash); signals: - void articleWasRead(); + void articleWasRead(); public slots: - void handleTorrentDownloadSuccess(const QString& url); - - friend RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& hash); + void handleTorrentDownloadSuccess(const QString &url); private: - RssFeed* m_parent; - QString m_guid; - QString m_title; - QString m_torrentUrl; - QString m_link; - QString m_description; - QDateTime m_date; - QString m_author; - bool m_read; + RssFeed *m_parent; + QString m_guid; + QString m_title; + QString m_torrentUrl; + QString m_link; + QString m_description; + QDateTime m_date; + QString m_author; + bool m_read; }; -RssArticlePtr hashToRssArticle(RssFeed* parent, const QVariantHash& hash); - #endif // RSSARTICLE_H diff --git a/src/base/rss/rssdownloadrule.cpp b/src/base/rss/rssdownloadrule.cpp index 132166e06..25d4d4cd1 100644 --- a/src/base/rss/rssdownloadrule.cpp +++ b/src/base/rss/rssdownloadrule.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -38,163 +38,272 @@ #include "rssarticle.h" #include "rssdownloadrule.h" -RssDownloadRule::RssDownloadRule(): m_enabled(false), m_useRegex(false), m_apstate(USE_GLOBAL) +RssDownloadRule::RssDownloadRule() + : m_enabled(false) + , m_useRegex(false) + , m_apstate(USE_GLOBAL) { } -bool RssDownloadRule::matches(const QString &article_title) const +bool RssDownloadRule::matches(const QString &articleTitle) const { - foreach (const QString& token, m_mustContain) { - if (!token.isEmpty()) { - QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard); - if (reg.indexIn(article_title) < 0) + foreach (const QString &token, m_mustContain) { + if (!token.isEmpty()) { + QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard); + if (reg.indexIn(articleTitle) < 0) + return false; + } + } + qDebug("Checking not matching tokens"); + // Checking not matching + foreach (const QString &token, m_mustNotContain) { + if (!token.isEmpty()) { + QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard); + if (reg.indexIn(articleTitle) > -1) + return false; + } + } + if (!m_episodeFilter.isEmpty()) { + qDebug("Checking episode filter"); + QRegExp f("(^\\d{1,4})x(.*;$)"); + int pos = f.indexIn(m_episodeFilter); + if (pos < 0) + return false; + + QString s = f.cap(1); + QStringList eps = f.cap(2).split(";"); + QString expStr; + expStr += "s0?" + s + "[ -_\\.]?" + "e0?"; + + foreach (const QString &ep, eps) { + if (ep.isEmpty()) + continue; + + if (ep.indexOf('-') != -1) { // Range detected + QString partialPattern = "s0?" + s + "[ -_\\.]?" + "e(0?\\d{1,4})"; + QRegExp reg(partialPattern, Qt::CaseInsensitive); + + if (ep.endsWith('-')) { // Infinite range + int epOurs = ep.left(ep.size() - 1).toInt(); + + // Extract partial match from article and compare as digits + pos = reg.indexIn(articleTitle); + if (pos != -1) { + int epTheirs = reg.cap(1).toInt(); + if (epTheirs >= epOurs) + return true; + } + } + else { // Normal range + QStringList range = ep.split('-'); + Q_ASSERT(range.size() == 2); + if (range.first().toInt() > range.last().toInt()) + continue; // Ignore this subrule completely + + int epOursFirst = range.first().toInt(); + int epOursLast = range.last().toInt(); + + // Extract partial match from article and compare as digits + pos = reg.indexIn(articleTitle); + if (pos != -1) { + int epTheirs = reg.cap(1).toInt(); + if (epOursFirst <= epTheirs && epOursLast >= epTheirs) + return true; + } + } + } + else { // Single number + QRegExp reg(expStr + ep + "\\D", Qt::CaseInsensitive); + if (reg.indexIn(articleTitle) != -1) + return true; + } + } return false; } - } - qDebug("Checking not matching tokens"); - // Checking not matching - foreach (const QString& token, m_mustNotContain) { - if (!token.isEmpty()) { - QRegExp reg(token, Qt::CaseInsensitive, m_useRegex ? QRegExp::RegExp : QRegExp::Wildcard); - if (reg.indexIn(article_title) > -1) - return false; - } - } - if (!m_episodeFilter.isEmpty()) { - qDebug("Checking episode filter"); - QRegExp f("(^\\d{1,4})x(.*;$)"); - int pos = f.indexIn(m_episodeFilter); - if (pos < 0) - return false; - - QString s = f.cap(1); - QStringList eps = f.cap(2).split(";"); - QString expStr; - expStr += "s0?" + s + "[ -_\\.]?" + "e0?"; - - foreach (const QString& ep, eps) { - if (ep.isEmpty()) - continue; - - if (ep.indexOf('-') != -1) { // Range detected - QString partialPattern = "s0?" + s + "[ -_\\.]?" + "e(0?\\d{1,4})"; - QRegExp reg(partialPattern, Qt::CaseInsensitive); - - if (ep.endsWith('-')) { // Infinite range - int epOurs = ep.left(ep.size() - 1).toInt(); - - // Extract partial match from article and compare as digits - pos = reg.indexIn(article_title); - if (pos != -1) { - int epTheirs = reg.cap(1).toInt(); - if (epTheirs >= epOurs) - return true; - } - } - else { // Normal range - QStringList range = ep.split('-'); - Q_ASSERT(range.size() == 2); - if (range.first().toInt() > range.last().toInt()) - continue; // Ignore this subrule completely - - int epOursFirst = range.first().toInt(); - int epOursLast = range.last().toInt(); - - // Extract partial match from article and compare as digits - pos = reg.indexIn(article_title); - if (pos != -1) { - int epTheirs = reg.cap(1).toInt(); - if (epOursFirst <= epTheirs && epOursLast >= epTheirs) - return true; - } - } - } - else { // Single number - QRegExp reg(expStr + ep + "\\D", Qt::CaseInsensitive); - if (reg.indexIn(article_title) != -1) - return true; - } - } - return false; - } - return true; + return true; } void RssDownloadRule::setMustContain(const QString &tokens) { - if (m_useRegex) - m_mustContain = QStringList() << tokens; - else - m_mustContain = tokens.split(" "); + if (m_useRegex) + m_mustContain = QStringList() << tokens; + else + m_mustContain = tokens.split(" "); } void RssDownloadRule::setMustNotContain(const QString &tokens) { - if (m_useRegex) - m_mustNotContain = QStringList() << tokens; - else - m_mustNotContain = tokens.split("|"); + if (m_useRegex) + m_mustNotContain = QStringList() << tokens; + else + m_mustNotContain = tokens.split("|"); } -RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &rule_hash) +QStringList RssDownloadRule::rssFeeds() const { - RssDownloadRulePtr rule(new RssDownloadRule); - rule->setName(rule_hash.value("name").toString()); - rule->setUseRegex(rule_hash.value("use_regex", false).toBool()); - rule->setMustContain(rule_hash.value("must_contain").toString()); - rule->setMustNotContain(rule_hash.value("must_not_contain").toString()); - rule->setEpisodeFilter(rule_hash.value("episode_filter").toString()); - rule->setRssFeeds(rule_hash.value("affected_feeds").toStringList()); - rule->setEnabled(rule_hash.value("enabled", false).toBool()); - rule->setSavePath(rule_hash.value("save_path").toString()); - rule->setLabel(rule_hash.value("label_assigned").toString()); - rule->setAddPaused(AddPausedState(rule_hash.value("add_paused").toUInt())); - rule->setLastMatch(rule_hash.value("last_match").toDateTime()); - rule->setIgnoreDays(rule_hash.value("ignore_days").toInt()); - return rule; + return m_rssFeeds; +} + +void RssDownloadRule::setRssFeeds(const QStringList &rssFeeds) +{ + m_rssFeeds = rssFeeds; +} + +QString RssDownloadRule::name() const +{ + return m_name; +} + +void RssDownloadRule::setName(const QString &name) +{ + m_name = name; +} + +QString RssDownloadRule::savePath() const +{ + return m_savePath; +} + +RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &ruleHash) +{ + RssDownloadRulePtr rule(new RssDownloadRule); + rule->setName(ruleHash.value("name").toString()); + rule->setUseRegex(ruleHash.value("use_regex", false).toBool()); + rule->setMustContain(ruleHash.value("must_contain").toString()); + rule->setMustNotContain(ruleHash.value("must_not_contain").toString()); + rule->setEpisodeFilter(ruleHash.value("episode_filter").toString()); + rule->setRssFeeds(ruleHash.value("affected_feeds").toStringList()); + rule->setEnabled(ruleHash.value("enabled", false).toBool()); + rule->setSavePath(ruleHash.value("save_path").toString()); + rule->setLabel(ruleHash.value("label_assigned").toString()); + rule->setAddPaused(AddPausedState(ruleHash.value("add_paused").toUInt())); + rule->setLastMatch(ruleHash.value("last_match").toDateTime()); + rule->setIgnoreDays(ruleHash.value("ignore_days").toInt()); + return rule; } QVariantHash RssDownloadRule::toVariantHash() const { - QVariantHash hash; - hash["name"] = m_name; - hash["must_contain"] = m_mustContain.join(" "); - hash["must_not_contain"] = m_mustNotContain.join("|"); - hash["save_path"] = m_savePath; - hash["affected_feeds"] = m_rssFeeds; - hash["enabled"] = m_enabled; - hash["label_assigned"] = m_label; - hash["use_regex"] = m_useRegex; - hash["add_paused"] = m_apstate; - hash["episode_filter"] = m_episodeFilter; - hash["last_match"] = m_lastMatch; - hash["ignore_days"] = m_ignoreDays; - return hash; + QVariantHash hash; + hash["name"] = m_name; + hash["must_contain"] = m_mustContain.join(" "); + hash["must_not_contain"] = m_mustNotContain.join("|"); + hash["save_path"] = m_savePath; + hash["affected_feeds"] = m_rssFeeds; + hash["enabled"] = m_enabled; + hash["label_assigned"] = m_label; + hash["use_regex"] = m_useRegex; + hash["add_paused"] = m_apstate; + hash["episode_filter"] = m_episodeFilter; + hash["last_match"] = m_lastMatch; + hash["ignore_days"] = m_ignoreDays; + return hash; } -bool RssDownloadRule::operator==(const RssDownloadRule &other) const { - return m_name == other.name(); -} - -void RssDownloadRule::setSavePath(const QString &save_path) +bool RssDownloadRule::operator==(const RssDownloadRule &other) const { - if (!save_path.isEmpty() && QDir(save_path) != QDir(Preferences::instance()->getSavePath())) - m_savePath = Utils::Fs::fromNativePath(save_path); - else - m_savePath = QString(); + return m_name == other.name(); } -QStringList RssDownloadRule::findMatchingArticles(const RssFeedPtr& feed) const +void RssDownloadRule::setSavePath(const QString &savePath) { - QStringList ret; - const RssArticleHash& feed_articles = feed->articleHash(); - - RssArticleHash::ConstIterator artIt = feed_articles.begin(); - RssArticleHash::ConstIterator artItend = feed_articles.end(); - for ( ; artIt != artItend ; ++artIt) { - const QString title = artIt.value()->title(); - if (matches(title)) - ret << title; - } - return ret; + if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath()))) + m_savePath = Utils::Fs::fromNativePath(savePath); + else + m_savePath = QString(); +} + +RssDownloadRule::AddPausedState RssDownloadRule::addPaused() const +{ + return m_apstate; +} + +void RssDownloadRule::setAddPaused(const RssDownloadRule::AddPausedState &aps) +{ + m_apstate = aps; +} + +QString RssDownloadRule::label() const +{ + return m_label; +} + +void RssDownloadRule::setLabel(const QString &label) +{ + m_label = label; +} + +bool RssDownloadRule::isEnabled() const +{ + return m_enabled; +} + +void RssDownloadRule::setEnabled(bool enable) +{ + m_enabled = enable; +} + +void RssDownloadRule::setLastMatch(const QDateTime &d) +{ + m_lastMatch = d; +} + +QDateTime RssDownloadRule::lastMatch() const +{ + return m_lastMatch; +} + +void RssDownloadRule::setIgnoreDays(int d) +{ + m_ignoreDays = d; +} + +int RssDownloadRule::ignoreDays() const +{ + return m_ignoreDays; +} + +QString RssDownloadRule::mustContain() const +{ + return m_mustContain.join(" "); +} + +QString RssDownloadRule::mustNotContain() const +{ + return m_mustNotContain.join("|"); +} + +bool RssDownloadRule::useRegex() const +{ + return m_useRegex; +} + +void RssDownloadRule::setUseRegex(bool enabled) +{ + m_useRegex = enabled; +} + +QString RssDownloadRule::episodeFilter() const +{ + return m_episodeFilter; +} + +void RssDownloadRule::setEpisodeFilter(const QString &e) +{ + m_episodeFilter = e; +} + +QStringList RssDownloadRule::findMatchingArticles(const RssFeedPtr &feed) const +{ + QStringList ret; + const RssArticleHash &feedArticles = feed->articleHash(); + + RssArticleHash::ConstIterator artIt = feedArticles.begin(); + RssArticleHash::ConstIterator artItend = feedArticles.end(); + for ( ; artIt != artItend ; ++artIt) { + const QString title = artIt.value()->title(); + if (matches(title)) + ret << title; + } + return ret; } diff --git a/src/base/rss/rssdownloadrule.h b/src/base/rss/rssdownloadrule.h index c76bd3187..0bfc0616f 100644 --- a/src/base/rss/rssdownloadrule.h +++ b/src/base/rss/rssdownloadrule.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -44,59 +44,60 @@ typedef QSharedPointer RssDownloadRulePtr; class RssDownloadRule { - public: - enum AddPausedState { - USE_GLOBAL = 0, - ALWAYS_PAUSED, - NEVER_PAUSED - }; + enum AddPausedState + { + USE_GLOBAL = 0, + ALWAYS_PAUSED, + NEVER_PAUSED + }; - explicit RssDownloadRule(); - static RssDownloadRulePtr fromVariantHash(const QVariantHash &rule_hash); - QVariantHash toVariantHash() const; - bool matches(const QString &article_title) const; - void setMustContain(const QString &tokens); - void setMustNotContain(const QString &tokens); - inline QStringList rssFeeds() const { return m_rssFeeds; } - inline void setRssFeeds(const QStringList& rss_feeds) { m_rssFeeds = rss_feeds; } - inline QString name() const { return m_name; } - inline void setName(const QString &name) { m_name = name; } - inline QString savePath() const { return m_savePath; } - void setSavePath(const QString &save_path); - inline AddPausedState addPaused() const { return m_apstate; } - inline void setAddPaused(const AddPausedState &aps) { m_apstate = aps; } - inline QString label() const { return m_label; } - inline void setLabel(const QString &_label) { m_label = _label; } - inline bool isEnabled() const { return m_enabled; } - inline void setEnabled(bool enable) { m_enabled = enable; } - inline void setLastMatch(const QDateTime& d) { m_lastMatch = d; } - inline QDateTime lastMatch() const { return m_lastMatch; } - inline void setIgnoreDays(int d) { m_ignoreDays = d; } - inline int ignoreDays() const { return m_ignoreDays; } - inline QString mustContain() const { return m_mustContain.join(" "); } - inline QString mustNotContain() const { return m_mustNotContain.join("|"); } - inline bool useRegex() const { return m_useRegex; } - inline void setUseRegex(bool enabled) { m_useRegex = enabled; } - inline QString episodeFilter() const { return m_episodeFilter; } - inline void setEpisodeFilter(const QString& e) { m_episodeFilter = e; } - QStringList findMatchingArticles(const RssFeedPtr& feed) const; - // Operators - bool operator==(const RssDownloadRule &other) const; + RssDownloadRule(); + + static RssDownloadRulePtr fromVariantHash(const QVariantHash &ruleHash); + QVariantHash toVariantHash() const; + bool matches(const QString &articleTitle) const; + void setMustContain(const QString &tokens); + void setMustNotContain(const QString &tokens); + QStringList rssFeeds() const; + void setRssFeeds(const QStringList &rssFeeds); + QString name() const; + void setName(const QString &name); + QString savePath() const; + void setSavePath(const QString &savePath); + AddPausedState addPaused() const; + void setAddPaused(const AddPausedState &aps); + QString label() const; + void setLabel(const QString &label); + bool isEnabled() const; + void setEnabled(bool enable); + void setLastMatch(const QDateTime &d); + QDateTime lastMatch() const; + void setIgnoreDays(int d); + int ignoreDays() const; + QString mustContain() const; + QString mustNotContain() const; + bool useRegex() const; + void setUseRegex(bool enabled); + QString episodeFilter() const; + void setEpisodeFilter(const QString &e); + QStringList findMatchingArticles(const RssFeedPtr &feed) const; + // Operators + bool operator==(const RssDownloadRule &other) const; private: - QString m_name; - QStringList m_mustContain; - QStringList m_mustNotContain; - QString m_episodeFilter; - QString m_savePath; - QString m_label; - bool m_enabled; - QStringList m_rssFeeds; - bool m_useRegex; - AddPausedState m_apstate; - QDateTime m_lastMatch; - int m_ignoreDays; + QString m_name; + QStringList m_mustContain; + QStringList m_mustNotContain; + QString m_episodeFilter; + QString m_savePath; + QString m_label; + bool m_enabled; + QStringList m_rssFeeds; + bool m_useRegex; + AddPausedState m_apstate; + QDateTime m_lastMatch; + int m_ignoreDays; }; #endif // RSSDOWNLOADRULE_H diff --git a/src/base/rss/rssdownloadrulelist.cpp b/src/base/rss/rssdownloadrulelist.cpp index c1d450769..4f3e71bc4 100644 --- a/src/base/rss/rssdownloadrulelist.cpp +++ b/src/base/rss/rssdownloadrulelist.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -38,135 +38,146 @@ RssDownloadRuleList::RssDownloadRuleList() { - loadRulesFromStorage(); + loadRulesFromStorage(); } -RssDownloadRulePtr RssDownloadRuleList::findMatchingRule(const QString &feed_url, const QString &article_title) const +RssDownloadRulePtr RssDownloadRuleList::findMatchingRule(const QString &feedUrl, const QString &articleTitle) const { - Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - QStringList rule_names = m_feedRules.value(feed_url); - foreach (const QString &rule_name, rule_names) { - RssDownloadRulePtr rule = m_rules[rule_name]; - if (rule->isEnabled() && rule->matches(article_title)) return rule; - } - return RssDownloadRulePtr(); + Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); + QStringList ruleNames = m_feedRules.value(feedUrl); + foreach (const QString &rule_name, ruleNames) { + RssDownloadRulePtr rule = m_rules[rule_name]; + if (rule->isEnabled() && rule->matches(articleTitle)) return rule; + } + return RssDownloadRulePtr(); } -void RssDownloadRuleList::replace(RssDownloadRuleList *other) { - m_rules.clear(); - m_feedRules.clear(); - foreach (const QString& name, other->ruleNames()) { - saveRule(other->getRule(name)); - } +void RssDownloadRuleList::replace(RssDownloadRuleList *other) +{ + m_rules.clear(); + m_feedRules.clear(); + foreach (const QString &name, other->ruleNames()) { + saveRule(other->getRule(name)); + } } void RssDownloadRuleList::saveRulesToStorage() { - QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); - qBTRSS.setValue("download_rules", toVariantHash()); + QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); + qBTRSS.setValue("download_rules", toVariantHash()); } void RssDownloadRuleList::loadRulesFromStorage() { - QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); - loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash()); + QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); + loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash()); } QVariantHash RssDownloadRuleList::toVariantHash() const { - QVariantHash ret; - foreach (const RssDownloadRulePtr &rule, m_rules.values()) { - ret.insert(rule->name(), rule->toVariantHash()); - } - return ret; + QVariantHash ret; + foreach (const RssDownloadRulePtr &rule, m_rules.values()) { + ret.insert(rule->name(), rule->toVariantHash()); + } + return ret; } void RssDownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h) { - QVariantHash::ConstIterator it = h.begin(); - QVariantHash::ConstIterator itend = h.end(); - for ( ; it != itend; ++it) { - RssDownloadRulePtr rule = RssDownloadRule::fromVariantHash(it.value().toHash()); - if (rule && !rule->name().isEmpty()) - saveRule(rule); - } + QVariantHash::ConstIterator it = h.begin(); + QVariantHash::ConstIterator itend = h.end(); + for ( ; it != itend; ++it) { + RssDownloadRulePtr rule = RssDownloadRule::fromVariantHash(it.value().toHash()); + if (rule && !rule->name().isEmpty()) + saveRule(rule); + } } void RssDownloadRuleList::saveRule(const RssDownloadRulePtr &rule) { - qDebug() << Q_FUNC_INFO << rule->name(); - Q_ASSERT(rule); - if (m_rules.contains(rule->name())) { - qDebug("This is an update, removing old rule first"); - removeRule(rule->name()); - } - m_rules.insert(rule->name(), rule); - // Update feedRules hashtable - foreach (const QString &feed_url, rule->rssFeeds()) { - m_feedRules[feed_url].append(rule->name()); - } - qDebug() << Q_FUNC_INFO << "EXIT"; + qDebug() << Q_FUNC_INFO << rule->name(); + Q_ASSERT(rule); + if (m_rules.contains(rule->name())) { + qDebug("This is an update, removing old rule first"); + removeRule(rule->name()); + } + m_rules.insert(rule->name(), rule); + // Update feedRules hashtable + foreach (const QString &feedUrl, rule->rssFeeds()) { + m_feedRules[feedUrl].append(rule->name()); + } + qDebug() << Q_FUNC_INFO << "EXIT"; } void RssDownloadRuleList::removeRule(const QString &name) { - qDebug() << Q_FUNC_INFO << name; - if (!m_rules.contains(name)) return; - RssDownloadRulePtr rule = m_rules.take(name); - // Update feedRules hashtable - foreach (const QString &feed_url, rule->rssFeeds()) { - m_feedRules[feed_url].removeOne(rule->name()); - } + qDebug() << Q_FUNC_INFO << name; + if (!m_rules.contains(name)) return; + RssDownloadRulePtr rule = m_rules.take(name); + // Update feedRules hashtable + foreach (const QString &feedUrl, rule->rssFeeds()) { + m_feedRules[feedUrl].removeOne(rule->name()); + } } -void RssDownloadRuleList::renameRule(const QString &old_name, const QString &new_name) +void RssDownloadRuleList::renameRule(const QString &oldName, const QString &newName) { - if (!m_rules.contains(old_name)) return; - RssDownloadRulePtr rule = m_rules.take(old_name); - rule->setName(new_name); - m_rules.insert(new_name, rule); - // Update feedRules hashtable - foreach (const QString &feed_url, rule->rssFeeds()) { - m_feedRules[feed_url].replace(m_feedRules[feed_url].indexOf(old_name), new_name); - } + if (!m_rules.contains(oldName)) return; + + RssDownloadRulePtr rule = m_rules.take(oldName); + rule->setName(newName); + m_rules.insert(newName, rule); + // Update feedRules hashtable + foreach (const QString &feedUrl, rule->rssFeeds()) { + m_feedRules[feedUrl].replace(m_feedRules[feedUrl].indexOf(oldName), newName); + } } RssDownloadRulePtr RssDownloadRuleList::getRule(const QString &name) const { - return m_rules.value(name); + return m_rules.value(name); } -bool RssDownloadRuleList::serialize(const QString& path) +QStringList RssDownloadRuleList::ruleNames() const { - QFile f(path); - if (f.open(QIODevice::WriteOnly)) { - QDataStream out(&f); - out.setVersion(QDataStream::Qt_4_5); - out << toVariantHash(); - f.close(); - return true; - } else { + return m_rules.keys(); +} + +bool RssDownloadRuleList::isEmpty() const +{ + return m_rules.isEmpty(); +} + +bool RssDownloadRuleList::serialize(const QString &path) +{ + QFile f(path); + if (f.open(QIODevice::WriteOnly)) { + QDataStream out(&f); + out.setVersion(QDataStream::Qt_4_5); + out << toVariantHash(); + f.close(); + return true; + } + return false; - } } bool RssDownloadRuleList::unserialize(const QString &path) { - QFile f(path); - if (f.open(QIODevice::ReadOnly)) { - QDataStream in(&f); - in.setVersion(QDataStream::Qt_4_5); - QVariantHash tmp; - in >> tmp; - f.close(); - if (tmp.isEmpty()) - return false; - qDebug("Processing was successful!"); - loadRulesFromVariantHash(tmp); - return true; - } else { - qDebug("Error: could not open file at %s", qPrintable(path)); - return false; - } + QFile f(path); + if (f.open(QIODevice::ReadOnly)) { + QDataStream in(&f); + in.setVersion(QDataStream::Qt_4_5); + QVariantHash tmp; + in >> tmp; + f.close(); + if (tmp.isEmpty()) + return false; + qDebug("Processing was successful!"); + loadRulesFromVariantHash(tmp); + return true; + } else { + qDebug("Error: could not open file at %s", qPrintable(path)); + return false; + } } - diff --git a/src/base/rss/rssdownloadrulelist.h b/src/base/rss/rssdownloadrulelist.h index 4a20c4744..4afc8c3e4 100644 --- a/src/base/rss/rssdownloadrulelist.h +++ b/src/base/rss/rssdownloadrulelist.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -34,36 +34,37 @@ #include #include #include + #include "rssdownloadrule.h" class RssDownloadRuleList { - Q_DISABLE_COPY(RssDownloadRuleList) + Q_DISABLE_COPY(RssDownloadRuleList) public: - RssDownloadRuleList(); - RssDownloadRulePtr findMatchingRule(const QString &feed_url, const QString &article_title) const; - // Operators - void saveRule(const RssDownloadRulePtr &rule); - void removeRule(const QString &name); - void renameRule(const QString &old_name, const QString &new_name); - RssDownloadRulePtr getRule(const QString &name) const; - inline QStringList ruleNames() const { return m_rules.keys(); } - inline bool isEmpty() const { return m_rules.isEmpty(); } - void saveRulesToStorage(); - bool serialize(const QString& path); - bool unserialize(const QString& path); - void replace(RssDownloadRuleList* other); + RssDownloadRuleList(); + + RssDownloadRulePtr findMatchingRule(const QString &feedUrl, const QString &articleTitle) const; + // Operators + void saveRule(const RssDownloadRulePtr &rule); + void removeRule(const QString &name); + void renameRule(const QString &oldName, const QString &newName); + RssDownloadRulePtr getRule(const QString &name) const; + QStringList ruleNames() const; + bool isEmpty() const; + void saveRulesToStorage(); + bool serialize(const QString &path); + bool unserialize(const QString &path); + void replace(RssDownloadRuleList *other); private: - void loadRulesFromStorage(); - void loadRulesFromVariantHash(const QVariantHash& l); - QVariantHash toVariantHash() const; + void loadRulesFromStorage(); + void loadRulesFromVariantHash(const QVariantHash &l); + QVariantHash toVariantHash() const; private: - QHash m_rules; - QHash m_feedRules; - + QHash m_rules; + QHash m_feedRules; }; #endif // RSSDOWNLOADFILTERLIST_H diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index 72bcb8993..f52ccc1c4 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,15 +31,15 @@ #include -#include "base/bittorrent/session.h" -#include "base/bittorrent/magneturi.h" #include "base/preferences.h" #include "base/qinisettings.h" +#include "base/logger.h" +#include "base/bittorrent/session.h" +#include "base/bittorrent/magneturi.h" #include "base/utils/misc.h" +#include "base/utils/fs.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" -#include "base/utils/fs.h" -#include "base/logger.h" #include "rssdownloadrulelist.h" #include "rssarticle.h" #include "rssparser.h" @@ -46,404 +47,419 @@ #include "rssmanager.h" #include "rssfeed.h" -bool rssArticleDateRecentThan(const RssArticlePtr& left, const RssArticlePtr& right) +bool rssArticleDateRecentThan(const RssArticlePtr &left, const RssArticlePtr &right) { - return left->date() > right->date(); + return left->date() > right->date(); } -RssFeed::RssFeed(RssManager* manager, RssFolder* parent, const QString& url): - m_manager(manager), - m_parent(parent), - m_url (QUrl::fromEncoded(url.toUtf8()).toString()), - m_icon(":/icons/oxygen/application-rss+xml.png"), - m_unreadCount(0), - m_dirty(false), - m_inErrorState(false), - m_loading(false) +RssFeed::RssFeed(RssManager *manager, RssFolder *parent, const QString &url) + : m_manager(manager) + , m_parent(parent) + , m_url (QUrl::fromEncoded(url.toUtf8()).toString()) + , m_icon(":/icons/oxygen/application-rss+xml.png") + , m_unreadCount(0) + , m_dirty(false) + , m_inErrorState(false) + , m_loading(false) { - qDebug() << Q_FUNC_INFO << m_url; - // Listen for new RSS downloads - connect(manager->rssParser(), SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); - connect(manager->rssParser(), SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); - connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString))); + qDebug() << Q_FUNC_INFO << m_url; + // Listen for new RSS downloads + connect(manager->rssParser(), SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); + connect(manager->rssParser(), SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); + connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString))); - // Download the RSS Feed icon - Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_iconUrl = handler->url(); + // Download the RSS Feed icon + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); + m_iconUrl = handler->url(); - // Load old RSS articles - loadItemsFromDisk(); + // Load old RSS articles + loadItemsFromDisk(); } RssFeed::~RssFeed() { - if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) - Utils::Fs::forceRemove(m_icon); + if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) + Utils::Fs::forceRemove(m_icon); +} + +RssFolder *RssFeed::parent() const +{ + return m_parent; +} + +void RssFeed::setParent(RssFolder *parent) +{ + m_parent = parent; } void RssFeed::saveItemsToDisk() { - qDebug() << Q_FUNC_INFO << m_url; - if (!m_dirty) - return; - markAsDirty(false); + qDebug() << Q_FUNC_INFO << m_url; + if (!m_dirty) return; - QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); - QVariantList old_items; + markAsDirty(false); - RssArticleHash::ConstIterator it = m_articles.begin(); - RssArticleHash::ConstIterator itend = m_articles.end(); - for ( ; it != itend; ++it) { - old_items << it.value()->toHash(); - } - qDebug("Saving %d old items for feed %s", old_items.size(), qPrintable(displayName())); - QHash all_old_items = qBTRSS.value("old_items", QHash()).toHash(); - all_old_items[m_url] = old_items; - qBTRSS.setValue("old_items", all_old_items); + QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); + QVariantList oldItems; + + RssArticleHash::ConstIterator it = m_articles.begin(); + RssArticleHash::ConstIterator itend = m_articles.end(); + for ( ; it != itend; ++it) { + oldItems << it.value()->toHash(); + } + qDebug("Saving %d old items for feed %s", oldItems.size(), qPrintable(displayName())); + QHash allOldItems = qBTRSS.value("old_items", QHash()).toHash(); + allOldItems[m_url] = oldItems; + qBTRSS.setValue("old_items", allOldItems); } void RssFeed::loadItemsFromDisk() { - QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); - QHash all_old_items = qBTRSS.value("old_items", QHash()).toHash(); - const QVariantList old_items = all_old_items.value(m_url, QVariantList()).toList(); - qDebug("Loading %d old items for feed %s", old_items.size(), qPrintable(displayName())); + QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); + QHash allOldItems = qBTRSS.value("old_items", QHash()).toHash(); + const QVariantList oldItems = allOldItems.value(m_url, QVariantList()).toList(); + qDebug("Loading %d old items for feed %s", oldItems.size(), qPrintable(displayName())); - foreach (const QVariant& var_it, old_items) { - QVariantHash item = var_it.toHash(); - RssArticlePtr rss_item = hashToRssArticle(this, item); - if (rss_item) - addArticle(rss_item); - } + foreach (const QVariant &var_it, oldItems) { + QVariantHash item = var_it.toHash(); + RssArticlePtr rssItem = RssArticle::fromHash(this, item); + if (rssItem) + addArticle(rssItem); + } } -void RssFeed::addArticle(const RssArticlePtr& article) { - int max_articles = Preferences::instance()->getRSSMaxArticlesPerFeed(); +void RssFeed::addArticle(const RssArticlePtr &article) +{ + int maxArticles = Preferences::instance()->getRSSMaxArticlesPerFeed(); - if (!m_articles.contains(article->guid())) { - markAsDirty(); + if (!m_articles.contains(article->guid())) { + markAsDirty(); - // Update unreadCount - if (!article->isRead()) - ++m_unreadCount; - // Insert in hash table - m_articles[article->guid()] = article; - // Insertion sort - RssArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, rssArticleDateRecentThan); - m_articlesByDate.insert(lowerBound, article); - int lbIndex = m_articlesByDate.indexOf(article); - if (m_articlesByDate.size() > max_articles) { - RssArticlePtr oldestArticle = m_articlesByDate.takeLast(); - m_articles.remove(oldestArticle->guid()); - // Update unreadCount - if (!oldestArticle->isRead()) - --m_unreadCount; - } + // Update unreadCount + if (!article->isRead()) + ++m_unreadCount; + // Insert in hash table + m_articles[article->guid()] = article; + // Insertion sort + RssArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, rssArticleDateRecentThan); + m_articlesByDate.insert(lowerBound, article); + int lbIndex = m_articlesByDate.indexOf(article); + if (m_articlesByDate.size() > maxArticles) { + RssArticlePtr oldestArticle = m_articlesByDate.takeLast(); + m_articles.remove(oldestArticle->guid()); + // Update unreadCount + if (!oldestArticle->isRead()) + --m_unreadCount; + } - // Check if article was inserted at the end of the list and will break max_articles limit - if (Preferences::instance()->isRssDownloadingEnabled()) { - if (lbIndex < max_articles && !article->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), article); + // Check if article was inserted at the end of the list and will break max_articles limit + if (Preferences::instance()->isRssDownloadingEnabled()) { + if ((lbIndex < maxArticles) && !article->isRead()) + downloadArticleTorrentIfMatching(m_manager->downloadRules(), article); + } } - } - else { - // m_articles.contains(article->guid()) - // Try to download skipped articles - if (Preferences::instance()->isRssDownloadingEnabled()) { - RssArticlePtr skipped = m_articles.value(article->guid(), RssArticlePtr()); - if (skipped) { - if (!skipped->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), skipped); - } + else { + // m_articles.contains(article->guid()) + // Try to download skipped articles + if (Preferences::instance()->isRssDownloadingEnabled()) { + RssArticlePtr skipped = m_articles.value(article->guid(), RssArticlePtr()); + if (skipped) { + if (!skipped->isRead()) + downloadArticleTorrentIfMatching(m_manager->downloadRules(), skipped); + } + } } - } } bool RssFeed::refresh() { - if (m_loading) { - qWarning() << Q_FUNC_INFO << "Feed" << displayName() << "is already being refreshed, ignoring request"; - return false; - } - m_loading = true; - // Download the RSS again - Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url, true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_url = handler->url(); // sync URL encoding - return true; + if (m_loading) { + qWarning() << Q_FUNC_INFO << "Feed" << displayName() << "is already being refreshed, ignoring request"; + return false; + } + m_loading = true; + // Download the RSS again + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url, true); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); + m_url = handler->url(); // sync URL encoding + return true; +} + +QString RssFeed::id() const +{ + return m_url; } void RssFeed::removeAllSettings() { - qDebug() << "Removing all settings / history for feed: " << m_url; - QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); - QVariantHash feeds_w_downloader = qBTRSS.value("downloader_on", QVariantHash()).toHash(); - if (feeds_w_downloader.contains(m_url)) { - feeds_w_downloader.remove(m_url); - qBTRSS.setValue("downloader_on", feeds_w_downloader); - } - QVariantHash all_feeds_filters = qBTRSS.value("feed_filters", QVariantHash()).toHash(); - if (all_feeds_filters.contains(m_url)) { - all_feeds_filters.remove(m_url); - qBTRSS.setValue("feed_filters", all_feeds_filters); - } - QVariantHash all_old_items = qBTRSS.value("old_items", QVariantHash()).toHash(); - if (all_old_items.contains(m_url)) { - all_old_items.remove(m_url); - qBTRSS.setValue("old_items", all_old_items); - } + qDebug() << "Removing all settings / history for feed: " << m_url; + QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); + QVariantHash feedsWDownloader = qBTRSS.value("downloader_on", QVariantHash()).toHash(); + if (feedsWDownloader.contains(m_url)) { + feedsWDownloader.remove(m_url); + qBTRSS.setValue("downloader_on", feedsWDownloader); + } + QVariantHash allFeedsFilters = qBTRSS.value("feed_filters", QVariantHash()).toHash(); + if (allFeedsFilters.contains(m_url)) { + allFeedsFilters.remove(m_url); + qBTRSS.setValue("feed_filters", allFeedsFilters); + } + QVariantHash allOldItems = qBTRSS.value("old_items", QVariantHash()).toHash(); + if (allOldItems.contains(m_url)) { + allOldItems.remove(m_url); + qBTRSS.setValue("old_items", allOldItems); + } } bool RssFeed::isLoading() const { - return m_loading; + return m_loading; } QString RssFeed::title() const { - return m_title; + return m_title; } -void RssFeed::rename(const QString &new_name) +void RssFeed::rename(const QString &newName) { - qDebug() << "Renaming stream to" << new_name; - m_alias = new_name; + qDebug() << "Renaming stream to" << newName; + m_alias = newName; } // Return the alias if the stream has one, the url if it has no alias QString RssFeed::displayName() const { - if (!m_alias.isEmpty()) - return m_alias; - if (!m_title.isEmpty()) - return m_title; - return m_url; + if (!m_alias.isEmpty()) + return m_alias; + if (!m_title.isEmpty()) + return m_title; + return m_url; } QString RssFeed::url() const { - return m_url; + return m_url; } QString RssFeed::iconPath() const { - if (m_inErrorState) - return QLatin1String(":/icons/oxygen/unavailable.png"); + if (m_inErrorState) + return QLatin1String(":/icons/oxygen/unavailable.png"); - return m_icon; + return m_icon; } bool RssFeed::hasCustomIcon() const { - return !m_icon.startsWith(":/"); + return !m_icon.startsWith(":/"); } -void RssFeed::setIconPath(const QString& path) +void RssFeed::setIconPath(const QString &path) { - if (path.isEmpty() || !QFile::exists(path)) - return; - - m_icon = path; + if (!path.isEmpty() && QFile::exists(path)) + m_icon = path; } -RssArticlePtr RssFeed::getItem(const QString& guid) const +RssArticlePtr RssFeed::getItem(const QString &guid) const { - return m_articles.value(guid); + return m_articles.value(guid); } uint RssFeed::count() const { - return m_articles.size(); + return m_articles.size(); } void RssFeed::markAsRead() { - RssArticleHash::ConstIterator it = m_articles.begin(); - RssArticleHash::ConstIterator itend = m_articles.end(); - for ( ; it != itend; ++it) { - it.value()->markAsRead(); - } - m_unreadCount = 0; - m_manager->forwardFeedInfosChanged(m_url, displayName(), 0); + RssArticleHash::ConstIterator it = m_articles.begin(); + RssArticleHash::ConstIterator itend = m_articles.end(); + for ( ; it != itend; ++it) { + it.value()->markAsRead(); + } + m_unreadCount = 0; + m_manager->forwardFeedInfosChanged(m_url, displayName(), 0); } void RssFeed::markAsDirty(bool dirty) { - m_dirty = dirty; + m_dirty = dirty; } uint RssFeed::unreadCount() const { - return m_unreadCount; + return m_unreadCount; } RssArticleList RssFeed::articleListByDateDesc() const { - return m_articlesByDate; + return m_articlesByDate; +} + +const RssArticleHash &RssFeed::articleHash() const +{ + return m_articles; } RssArticleList RssFeed::unreadArticleListByDateDesc() const { - RssArticleList unread_news; + RssArticleList unreadNews; - RssArticleList::ConstIterator it = m_articlesByDate.begin(); - RssArticleList::ConstIterator itend = m_articlesByDate.end(); - for ( ; it != itend; ++it) { - if (!(*it)->isRead()) - unread_news << *it; - } - return unread_news; + RssArticleList::ConstIterator it = m_articlesByDate.begin(); + RssArticleList::ConstIterator itend = m_articlesByDate.end(); + for ( ; it != itend; ++it) { + if (!(*it)->isRead()) + unreadNews << *it; + } + return unreadNews; } // download the icon from the address QString RssFeed::iconUrl() const { - // XXX: This works for most sites but it is not perfect - return QString("http://") + QUrl(m_url).host() + QString("/favicon.ico"); + // XXX: This works for most sites but it is not perfect + return QString("http://") + QUrl(m_url).host() + QString("/favicon.ico"); } // read and store the downloaded rss' informations void RssFeed::handleFinishedDownload(const QString &url, const QString &filePath) { - if (url == m_url) { - qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; - // Parse the download RSS - m_manager->rssParser()->parseRssFile(m_url, filePath); - } else if (url == m_iconUrl) { - m_icon = filePath; - qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; - m_manager->forwardFeedIconChanged(m_url, m_icon); - } + if (url == m_url) { + qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; + // Parse the download RSS + m_manager->rssParser()->parseRssFile(m_url, filePath); + } + else if (url == m_iconUrl) { + m_icon = filePath; + qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; + m_manager->forwardFeedIconChanged(m_url, m_icon); + } } void RssFeed::handleDownloadFailure(const QString &url, const QString &error) { - if (url != m_url) return; + if (url != m_url) return; - m_inErrorState = true; - m_loading = false; - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); - qWarning() << "Failed to download RSS feed at" << url; - qWarning() << "Reason:" << error; + m_inErrorState = true; + m_loading = false; + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); + qWarning() << "Failed to download RSS feed at" << url; + qWarning() << "Reason:" << error; } -void RssFeed::handleFeedTitle(const QString& feedUrl, const QString& title) +void RssFeed::handleFeedTitle(const QString &feedUrl, const QString &title) { - if (feedUrl != m_url) - return; + if (feedUrl != m_url) return; + if (m_title == title) return; - if (m_title == title) - return; + m_title = title; - m_title = title; - - // Notify that we now have something better than a URL to display - if (m_alias.isEmpty()) - m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); + // Notify that we now have something better than a URL to display + if (m_alias.isEmpty()) + m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); } -void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const RssArticlePtr& article) +void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList *rules, const RssArticlePtr &article) { - Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - RssDownloadRulePtr matching_rule = rules->findMatchingRule(m_url, article->title()); - if (!matching_rule) - return; + Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); + RssDownloadRulePtr matchingRule = rules->findMatchingRule(m_url, article->title()); + if (!matchingRule) return; - if (matching_rule->ignoreDays() > 0) { - QDateTime lastMatch = matching_rule->lastMatch(); - if (lastMatch.isValid()) { - if (QDateTime::currentDateTime() < lastMatch.addDays(matching_rule->ignoreDays())) { - connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); + if (matchingRule->ignoreDays() > 0) { + QDateTime lastMatch = matchingRule->lastMatch(); + if (lastMatch.isValid()) { + if (QDateTime::currentDateTime() < lastMatch.addDays(matchingRule->ignoreDays())) { + connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); + article->markAsRead(); + return; + } + } + } + + matchingRule->setLastMatch(QDateTime::currentDateTime()); + rules->saveRulesToStorage(); + // Download the torrent + const QString &torrentUrl = article->torrentUrl(); + if (torrentUrl.isEmpty()) { + Logger::instance()->addMessage(tr("Automatic download of '%1' from '%2' RSS feed failed because it doesn't contain a torrent or a magnet link...").arg(article->title()).arg(displayName()), Log::WARNING); article->markAsRead(); return; - } } - } - matching_rule->setLastMatch(QDateTime::currentDateTime()); - rules->saveRulesToStorage(); - // Download the torrent - const QString& torrent_url = article->torrentUrl(); - if (torrent_url.isEmpty()) { - Logger::instance()->addMessage(tr("Automatic download of '%1' from '%2' RSS feed failed because it doesn't contain a torrent or a magnet link...").arg(article->title()).arg(displayName()), Log::WARNING); - article->markAsRead(); - return; - } - Logger::instance()->addMessage(tr("Automatically downloading '%1' torrent from '%2' RSS feed...").arg(article->title()).arg(displayName())); - connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); - if (BitTorrent::MagnetUri(torrent_url).isValid()) - article->markAsRead(); - else - connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); + Logger::instance()->addMessage(tr("Automatically downloading '%1' torrent from '%2' RSS feed...").arg(article->title()).arg(displayName())); + connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString &)), Qt::UniqueConnection); + if (BitTorrent::MagnetUri(torrent_url).isValid()) + article->markAsRead(); + else + connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); - BitTorrent::AddTorrentParams params; - params.savePath = matching_rule->savePath(); - params.label = matching_rule->label(); - if (matching_rule->addPaused() == RssDownloadRule::ALWAYS_PAUSED) - params.addPaused = TriStateBool::True; - else if (matching_rule->addPaused() == RssDownloadRule::NEVER_PAUSED) - params.addPaused = TriStateBool::False; - BitTorrent::Session::instance()->addTorrent(torrent_url, params); + BitTorrent::AddTorrentParams params; + params.savePath = matchingRule->savePath(); + params.label = matchingRule->label(); + if (matchingRule->addPaused() == RssDownloadRule::ALWAYS_PAUSED) + params.addPaused = TriStateBool::True; + else if (matchingRule->addPaused() == RssDownloadRule::NEVER_PAUSED) + params.addPaused = TriStateBool::False; + BitTorrent::Session::instance()->addTorrent(torrentUrl, params); } void RssFeed::recheckRssItemsForDownload() { - Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - RssDownloadRuleList* rules = m_manager->downloadRules(); - foreach (const RssArticlePtr& article, m_articlesByDate) { - if (!article->isRead()) - downloadArticleTorrentIfMatching(rules, article); - } + Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); + RssDownloadRuleList *rules = m_manager->downloadRules(); + foreach (const RssArticlePtr &article, m_articlesByDate) { + if (!article->isRead()) + downloadArticleTorrentIfMatching(rules, article); + } } -void RssFeed::handleNewArticle(const QString& feedUrl, const QVariantHash& articleData) +void RssFeed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleData) { - if (feedUrl != m_url) - return; + if (feedUrl != m_url) return; - RssArticlePtr article = hashToRssArticle(this, articleData); - if (article.isNull()) { - qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << feedUrl; - return; - } - Q_ASSERT(article); - addArticle(article); + RssArticlePtr article = RssArticle::fromHash(this, articleData); + if (article.isNull()) { + qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << feedUrl; + return; + } + Q_ASSERT(article); + addArticle(article); - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); - // FIXME: We should forward the information here but this would seriously decrease - // performance with current design. - //m_manager->forwardFeedContentChanged(m_url); + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); + // FIXME: We should forward the information here but this would seriously decrease + // performance with current design. + //m_manager->forwardFeedContentChanged(m_url); } -void RssFeed::handleFeedParsingFinished(const QString& feedUrl, const QString& error) +void RssFeed::handleFeedParsingFinished(const QString &feedUrl, const QString &error) { - if (feedUrl != m_url) - return; + if (feedUrl != m_url) return; - if (!error.isEmpty()) { - qWarning() << "Failed to parse RSS feed at" << feedUrl; - qWarning() << "Reason:" << error; - } + if (!error.isEmpty()) { + qWarning() << "Failed to parse RSS feed at" << feedUrl; + qWarning() << "Reason:" << error; + } - m_loading = false; - m_inErrorState = !error.isEmpty(); + m_loading = false; + m_inErrorState = !error.isEmpty(); - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); - // XXX: Would not be needed if we did this in handleNewArticle() instead - m_manager->forwardFeedContentChanged(m_url); + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); + // XXX: Would not be needed if we did this in handleNewArticle() instead + m_manager->forwardFeedContentChanged(m_url); - saveItemsToDisk(); + saveItemsToDisk(); } void RssFeed::handleArticleStateChanged() { - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); } void RssFeed::decrementUnreadCount() { - --m_unreadCount; + --m_unreadCount; } diff --git a/src/base/rss/rssfeed.h b/src/base/rss/rssfeed.h index 84e95119f..70fffa6b5 100644 --- a/src/base/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,6 +40,7 @@ #include "rssfile.h" +class RssFolder; class RssFeed; class RssManager; class RssDownloadRuleList; @@ -47,69 +49,69 @@ typedef QHash RssArticleHash; typedef QSharedPointer RssFeedPtr; typedef QList RssFeedList; -bool rssArticleDateRecentThan(const RssArticlePtr& left, const RssArticlePtr& right); +bool rssArticleDateRecentThan(const RssArticlePtr &left, const RssArticlePtr &right); -class RssFeed: public QObject, public RssFile { - Q_OBJECT +class RssFeed: public QObject, public RssFile +{ + Q_OBJECT public: - RssFeed(RssManager* manager, RssFolder* m_parent, const QString& url); - virtual ~RssFeed(); - virtual RssFolder* parent() const { return m_parent; } - virtual void setParent(RssFolder* parent) { m_parent = parent; } - bool refresh(); - virtual QString id() const { return m_url; } - virtual void removeAllSettings(); - virtual void saveItemsToDisk(); - bool isLoading() const; - QString title() const; - virtual void rename(const QString &alias); - virtual QString displayName() const; - QString url() const; - virtual QString iconPath() const; - bool hasCustomIcon() const; - void setIconPath(const QString &pathHierarchy); - RssArticlePtr getItem(const QString &guid) const; - uint count() const; - virtual void markAsRead(); - void markAsDirty(bool dirty = true); - virtual uint unreadCount() const; - virtual RssArticleList articleListByDateDesc() const; - const RssArticleHash& articleHash() const { return m_articles; } - virtual RssArticleList unreadArticleListByDateDesc() const; - void decrementUnreadCount(); - void recheckRssItemsForDownload(); + RssFeed(RssManager *manager, RssFolder *parent, const QString &url); + ~RssFeed(); + + RssFolder *parent() const; + void setParent(RssFolder *parent); + bool refresh(); + QString id() const; + void removeAllSettings(); + void saveItemsToDisk(); + bool isLoading() const; + QString title() const; + void rename(const QString &newName); + QString displayName() const; + QString url() const; + QString iconPath() const; + bool hasCustomIcon() const; + void setIconPath(const QString &pathHierarchy); + RssArticlePtr getItem(const QString &guid) const; + uint count() const; + void markAsRead(); + void markAsDirty(bool dirty = true); + uint unreadCount() const; + RssArticleList articleListByDateDesc() const; + const RssArticleHash &articleHash() const; + RssArticleList unreadArticleListByDateDesc() const; + void decrementUnreadCount(); + void recheckRssItemsForDownload(); private slots: - void handleFinishedDownload(const QString &url, const QString &filePath); - void handleDownloadFailure(const QString &url, const QString &error); - void handleFeedTitle(const QString& feedUrl, const QString& title); - void handleNewArticle(const QString& feedUrl, const QVariantHash& article); - void handleFeedParsingFinished(const QString& feedUrl, const QString& error); - void handleArticleStateChanged(); + void handleFinishedDownload(const QString &url, const QString &filePath); + void handleDownloadFailure(const QString &url, const QString &error); + void handleFeedTitle(const QString &feedUrl, const QString &title); + void handleNewArticle(const QString &feedUrl, const QVariantHash &article); + void handleFeedParsingFinished(const QString &feedUrl, const QString &error); + void handleArticleStateChanged(); private: - QString iconUrl() const; - void loadItemsFromDisk(); - void addArticle(const RssArticlePtr& article); - void downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const RssArticlePtr& article); + QString iconUrl() const; + void loadItemsFromDisk(); + void addArticle(const RssArticlePtr &article); + void downloadArticleTorrentIfMatching(RssDownloadRuleList *rules, const RssArticlePtr &article); private: - RssManager* m_manager; - RssArticleHash m_articles; - RssArticleList m_articlesByDate; // Articles sorted by date (more recent first) - RssFolder* m_parent; - QString m_title; - QString m_url; - QString m_alias; - QString m_icon; - QString m_iconUrl; - uint m_unreadCount; - bool m_dirty; - bool m_inErrorState; - bool m_loading; - + RssManager *m_manager; + RssArticleHash m_articles; + RssArticleList m_articlesByDate; // Articles sorted by date (more recent first) + RssFolder *m_parent; + QString m_title; + QString m_url; + QString m_alias; + QString m_icon; + QString m_iconUrl; + uint m_unreadCount; + bool m_dirty; + bool m_inErrorState; + bool m_loading; }; - #endif // RSSFEED_H diff --git a/src/base/rss/rssfile.cpp b/src/base/rss/rssfile.cpp index ac090e0ee..187dfd303 100644 --- a/src/base/rss/rssfile.cpp +++ b/src/base/rss/rssfile.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,10 +32,13 @@ #include "rssfolder.h" #include "rssfile.h" -QStringList RssFile::pathHierarchy() const { - QStringList path; - if (parent()) - path << parent()->pathHierarchy(); - path << id(); - return path; +RssFile::~RssFile() {} + +QStringList RssFile::pathHierarchy() const +{ + QStringList path; + if (parent()) + path << parent()->pathHierarchy(); + path << id(); + return path; } diff --git a/src/base/rss/rssfile.h b/src/base/rss/rssfile.h index 9b08d00f9..ff2f0d913 100644 --- a/src/base/rss/rssfile.h +++ b/src/base/rss/rssfile.h @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,28 +48,30 @@ typedef QList RssFileList; /** * Parent interface for RssFolder and RssFeed. */ -class RssFile { +class RssFile +{ public: - virtual ~RssFile() {} + virtual ~RssFile(); - virtual uint unreadCount() const = 0; - virtual QString displayName() const = 0; - virtual QString id() const = 0; - virtual QString iconPath() const = 0; - virtual void rename(const QString &new_name) = 0; - virtual void markAsRead() = 0; - virtual RssFolder* parent() const = 0; - virtual void setParent(RssFolder* parent) = 0; - virtual bool refresh() = 0; - virtual RssArticleList articleListByDateDesc() const = 0; - virtual RssArticleList unreadArticleListByDateDesc() const = 0; - virtual void removeAllSettings() = 0; - virtual void saveItemsToDisk() = 0; - virtual void recheckRssItemsForDownload() = 0; - QStringList pathHierarchy() const; + virtual uint unreadCount() const = 0; + virtual QString displayName() const = 0; + virtual QString id() const = 0; + virtual QString iconPath() const = 0; + virtual void rename(const QString &newName) = 0; + virtual void markAsRead() = 0; + virtual RssFolder *parent() const = 0; + virtual void setParent(RssFolder *parent) = 0; + virtual bool refresh() = 0; + virtual RssArticleList articleListByDateDesc() const = 0; + virtual RssArticleList unreadArticleListByDateDesc() const = 0; + virtual void removeAllSettings() = 0; + virtual void saveItemsToDisk() = 0; + virtual void recheckRssItemsForDownload() = 0; + + QStringList pathHierarchy() const; protected: - uint m_unreadCount; + uint m_unreadCount; }; #endif // RSSFILE_H diff --git a/src/base/rss/rssfolder.cpp b/src/base/rss/rssfolder.cpp index a76357623..ac5e2b84d 100644 --- a/src/base/rss/rssfolder.cpp +++ b/src/base/rss/rssfolder.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,226 +38,256 @@ #include "rssarticle.h" #include "rssfolder.h" -RssFolder::RssFolder(RssFolder *parent, const QString &name): m_parent(parent), m_name(name) { +RssFolder::RssFolder(RssFolder *parent, const QString &name) + : m_parent(parent) + , m_name(name) +{ } -RssFolder::~RssFolder() { +RssFolder::~RssFolder() {} + +RssFolder *RssFolder::parent() const +{ + return m_parent; } -unsigned int RssFolder::unreadCount() const { - uint nb_unread = 0; - - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - nb_unread += it.value()->unreadCount(); - } - return nb_unread; +void RssFolder::setParent(RssFolder *parent) +{ + m_parent = parent; } -void RssFolder::removeChild(const QString &childId) { - if (m_children.contains(childId)) { - RssFilePtr child = m_children.take(childId); - child->removeAllSettings(); - } +uint RssFolder::unreadCount() const +{ + uint nbUnread = 0; + + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) + nbUnread += it.value()->unreadCount(); + + return nbUnread; } -RssFolderPtr RssFolder::addFolder(const QString &name) { - RssFolderPtr subfolder; - if (!m_children.contains(name)) { - subfolder = RssFolderPtr(new RssFolder(this, name)); - m_children[name] = subfolder; - } else { - subfolder = qSharedPointerDynamicCast(m_children.value(name)); - } - return subfolder; +void RssFolder::removeChild(const QString &childId) +{ + if (m_children.contains(childId)) { + RssFilePtr child = m_children.take(childId); + child->removeAllSettings(); + } } -RssFeedPtr RssFolder::addStream(RssManager* manager, const QString &url) { - qDebug() << Q_FUNC_INFO << manager << url; - RssFeedPtr stream(new RssFeed(manager, this, url)); - Q_ASSERT(stream); - qDebug() << "Stream URL is " << stream->url(); - Q_ASSERT(!m_children.contains(stream->url())); - m_children[stream->url()] = stream; - stream->refresh(); - return stream; +RssFolderPtr RssFolder::addFolder(const QString &name) +{ + RssFolderPtr subfolder; + if (!m_children.contains(name)) { + subfolder = RssFolderPtr(new RssFolder(this, name)); + m_children[name] = subfolder; + } + else { + subfolder = qSharedPointerDynamicCast(m_children.value(name)); + } + return subfolder; +} + +RssFeedPtr RssFolder::addStream(RssManager *manager, const QString &url) +{ + qDebug() << Q_FUNC_INFO << manager << url; + RssFeedPtr stream(new RssFeed(manager, this, url)); + Q_ASSERT(stream); + qDebug() << "Stream URL is " << stream->url(); + Q_ASSERT(!m_children.contains(stream->url())); + m_children[stream->url()] = stream; + stream->refresh(); + return stream; } // Refresh All Children -bool RssFolder::refresh() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - bool refreshed = false; - for ( ; it != itend; ++it) { - if (it.value()->refresh()) - refreshed = true; - } - return refreshed; +bool RssFolder::refresh() +{ + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + bool refreshed = false; + for ( ; it != itend; ++it) { + if (it.value()->refresh()) + refreshed = true; + } + return refreshed; } -RssArticleList RssFolder::articleListByDateDesc() const { - RssArticleList news; +RssArticleList RssFolder::articleListByDateDesc() const +{ + RssArticleList news; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - int n = news.size(); - news << it.value()->articleListByDateDesc(); - std::inplace_merge(news.begin(), news.begin() + n, news.end(), rssArticleDateRecentThan); - } - return news; + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + int n = news.size(); + news << it.value()->articleListByDateDesc(); + std::inplace_merge(news.begin(), news.begin() + n, news.end(), rssArticleDateRecentThan); + } + return news; } -RssArticleList RssFolder::unreadArticleListByDateDesc() const { - RssArticleList unread_news; +RssArticleList RssFolder::unreadArticleListByDateDesc() const +{ + RssArticleList unreadNews; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - int n = unread_news.size(); - unread_news << it.value()->unreadArticleListByDateDesc(); - std::inplace_merge(unread_news.begin(), unread_news.begin() + n, unread_news.end(), rssArticleDateRecentThan); - } - return unread_news; + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + int n = unreadNews.size(); + unreadNews << it.value()->unreadArticleListByDateDesc(); + std::inplace_merge(unreadNews.begin(), unreadNews.begin() + n, unreadNews.end(), rssArticleDateRecentThan); + } + return unreadNews; } -RssFileList RssFolder::getContent() const { - return m_children.values(); +RssFileList RssFolder::getContent() const +{ + return m_children.values(); } -unsigned int RssFolder::getNbFeeds() const { - uint nbFeeds = 0; +uint RssFolder::getNbFeeds() const +{ + uint nbFeeds = 0; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) - nbFeeds += folder->getNbFeeds(); - else - ++nbFeeds; // Feed - } - return nbFeeds; + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) + nbFeeds += folder->getNbFeeds(); + else + ++nbFeeds; // Feed + } + return nbFeeds; } -QString RssFolder::displayName() const { - return m_name; +QString RssFolder::displayName() const +{ + return m_name; } -void RssFolder::rename(const QString &new_name) { - if (m_name == new_name) return; - Q_ASSERT(!m_parent->hasChild(new_name)); - if (!m_parent->hasChild(new_name)) { +void RssFolder::rename(const QString &newName) +{ + if (m_name == newName) return; + + Q_ASSERT(!m_parent->hasChild(newName)); + if (!m_parent->hasChild(newName)) { + // Update parent + m_parent->renameChildFolder(m_name, newName); + // Actually rename + m_name = newName; + } +} + +void RssFolder::markAsRead() +{ + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + it.value()->markAsRead(); + } +} + +RssFeedList RssFolder::getAllFeeds() const +{ + RssFeedList streams; + + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) + streams << feed; + else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) + streams << folder->getAllFeeds(); + } + return streams; +} + +QHash RssFolder::getAllFeedsAsHash() const +{ + QHash ret; + + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) { + if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) { + qDebug() << Q_FUNC_INFO << feed->url(); + ret[feed->url()] = feed; + } + else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) { + ret.unite(folder->getAllFeedsAsHash()); + } + } + return ret; +} + +void RssFolder::addFile(const RssFilePtr &item) +{ + if (RssFeedPtr feed = qSharedPointerDynamicCast(item)) { + Q_ASSERT(!m_children.contains(feed->url())); + m_children[feed->url()] = item; + qDebug("Added feed %s to folder ./%s", qPrintable(feed->url()), qPrintable(m_name)); + } + else if (RssFolderPtr folder = qSharedPointerDynamicCast(item)) { + Q_ASSERT(!m_children.contains(folder->displayName())); + m_children[folder->displayName()] = item; + qDebug("Added folder %s to folder ./%s", qPrintable(folder->displayName()), qPrintable(m_name)); + } // Update parent - m_parent->renameChildFolder(m_name, new_name); - // Actually rename - m_name = new_name; - } + item->setParent(this); } -void RssFolder::markAsRead() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - it.value()->markAsRead(); - } +void RssFolder::removeAllItems() +{ + m_children.clear(); } -RssFeedList RssFolder::getAllFeeds() const { - RssFeedList streams; - - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) { - streams << feed; - } else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) { - streams << folder->getAllFeeds(); - } - } - return streams; -} - -QHash RssFolder::getAllFeedsAsHash() const { - QHash ret; - - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) { - qDebug() << Q_FUNC_INFO << feed->url(); - ret[feed->url()] = feed; - } else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) { - ret.unite(folder->getAllFeedsAsHash()); - } - } - return ret; -} - -void RssFolder::addFile(const RssFilePtr& item) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(feed->url())); - m_children[feed->url()] = item; - qDebug("Added feed %s to folder ./%s", qPrintable(feed->url()), qPrintable(m_name)); - } else if (RssFolderPtr folder = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(folder->displayName())); - m_children[folder->displayName()] = item; - qDebug("Added folder %s to folder ./%s", qPrintable(folder->displayName()), qPrintable(m_name)); - } - // Update parent - item->setParent(this); -} - -void RssFolder::removeAllItems() { - m_children.clear(); -} - -void RssFolder::removeAllSettings() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - it.value()->removeAllSettings(); - } +void RssFolder::removeAllSettings() +{ + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) + it.value()->removeAllSettings(); } void RssFolder::saveItemsToDisk() { - foreach (const RssFilePtr& child, m_children.values()) { - child->saveItemsToDisk(); - } + foreach (const RssFilePtr &child, m_children.values()) + child->saveItemsToDisk(); } QString RssFolder::id() const { - return m_name; + return m_name; } QString RssFolder::iconPath() const { - return IconProvider::instance()->getIconPath("inode-directory"); + return IconProvider::instance()->getIconPath("inode-directory"); } -bool RssFolder::hasChild(const QString &childId) { - return m_children.contains(childId); -} - -void RssFolder::renameChildFolder(const QString &old_name, const QString &new_name) +bool RssFolder::hasChild(const QString &childId) { - Q_ASSERT(m_children.contains(old_name)); - RssFilePtr folder = m_children.take(old_name); - m_children[new_name] = folder; + return m_children.contains(childId); +} + +void RssFolder::renameChildFolder(const QString &oldName, const QString &newName) +{ + Q_ASSERT(m_children.contains(oldName)); + RssFilePtr folder = m_children.take(oldName); + m_children[newName] = folder; } RssFilePtr RssFolder::takeChild(const QString &childId) { - return m_children.take(childId); + return m_children.take(childId); } void RssFolder::recheckRssItemsForDownload() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); - for ( ; it != itend; ++it) { - it.value()->recheckRssItemsForDownload(); - } + RssFileHash::ConstIterator it = m_children.begin(); + RssFileHash::ConstIterator itend = m_children.end(); + for ( ; it != itend; ++it) + it.value()->recheckRssItemsForDownload(); } diff --git a/src/base/rss/rssfolder.h b/src/base/rss/rssfolder.h index 85e8c7ca0..137ee5e9c 100644 --- a/src/base/rss/rssfolder.h +++ b/src/base/rss/rssfolder.h @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,45 +46,47 @@ typedef QSharedPointer RssFeedPtr; typedef QSharedPointer RssFolderPtr; typedef QList RssFeedList; -class RssFolder: public QObject, public RssFile { - Q_OBJECT +class RssFolder: public QObject, public RssFile +{ + Q_OBJECT public: - RssFolder(RssFolder *parent = 0, const QString &name = QString()); - virtual ~RssFolder(); - virtual RssFolder* parent() const { return m_parent; } - virtual void setParent(RssFolder* parent) { m_parent = parent; } - virtual unsigned int unreadCount() const; - RssFeedPtr addStream(RssManager* manager, const QString &url); - RssFolderPtr addFolder(const QString &name); - unsigned int getNbFeeds() const; - RssFileList getContent() const; - RssFeedList getAllFeeds() const; - QHash getAllFeedsAsHash() const; - virtual QString displayName() const; - virtual QString id() const; - virtual QString iconPath() const; - bool hasChild(const QString &childId); - virtual RssArticleList articleListByDateDesc() const; - virtual RssArticleList unreadArticleListByDateDesc() const; - virtual void removeAllSettings(); - virtual void saveItemsToDisk(); - void removeAllItems(); - void renameChildFolder(const QString &old_name, const QString &new_name); - RssFilePtr takeChild(const QString &childId); - void recheckRssItemsForDownload(); + explicit RssFolder(RssFolder *parent = 0, const QString &name = QString()); + ~RssFolder(); + + RssFolder *parent() const; + void setParent(RssFolder *parent); + uint unreadCount() const; + RssFeedPtr addStream(RssManager *manager, const QString &url); + RssFolderPtr addFolder(const QString &name); + uint getNbFeeds() const; + RssFileList getContent() const; + RssFeedList getAllFeeds() const; + QHash getAllFeedsAsHash() const; + QString displayName() const; + QString id() const; + QString iconPath() const; + bool hasChild(const QString &childId); + RssArticleList articleListByDateDesc() const; + RssArticleList unreadArticleListByDateDesc() const; + void removeAllSettings(); + void saveItemsToDisk(); + void removeAllItems(); + void renameChildFolder(const QString &oldName, const QString &newName); + RssFilePtr takeChild(const QString &childId); + void recheckRssItemsForDownload(); public slots: - virtual bool refresh(); - void addFile(const RssFilePtr& item); - void removeChild(const QString &childId); - virtual void rename(const QString &new_name); - virtual void markAsRead(); + bool refresh(); + void addFile(const RssFilePtr &item); + void removeChild(const QString &childId); + void rename(const QString &newName); + void markAsRead(); private: - RssFolder *m_parent; - QString m_name; - RssFileHash m_children; + RssFolder *m_parent; + QString m_name; + RssFileHash m_children; }; #endif // RSSFOLDER_H diff --git a/src/base/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp index f00457189..248caabc8 100644 --- a/src/base/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,8 +31,8 @@ #include +#include "base/logger.h" #include "base/preferences.h" -#include "base/bittorrent/session.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" @@ -40,122 +41,124 @@ static const int MSECS_PER_MIN = 60000; -RssManager::RssManager(): - m_downloadRules(new RssDownloadRuleList), - m_rssParser(new RssParser(this)) +RssManager::RssManager() + : m_downloadRules(new RssDownloadRuleList) + , m_rssParser(new RssParser(this)) { - connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); - m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); - m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN); + connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); + m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); + m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN); } RssManager::~RssManager() { - qDebug("Deleting RSSManager..."); - delete m_downloadRules; - delete m_rssParser; - saveItemsToDisk(); - saveStreamList(); - qDebug("RSSManager deleted"); + qDebug("Deleting RSSManager..."); + delete m_downloadRules; + delete m_rssParser; + saveItemsToDisk(); + saveStreamList(); + qDebug("RSSManager deleted"); } -RssParser* RssManager::rssParser() const +RssParser *RssManager::rssParser() const { - return m_rssParser; + return m_rssParser; } void RssManager::updateRefreshInterval(uint val) { - if (m_refreshInterval != val) { - m_refreshInterval = val; - m_refreshTimer.start(m_refreshInterval*60000); - qDebug("New RSS refresh interval is now every %dmin", m_refreshInterval); - } + if (m_refreshInterval != val) { + m_refreshInterval = val; + m_refreshTimer.start(m_refreshInterval*60000); + qDebug("New RSS refresh interval is now every %dmin", m_refreshInterval); + } } void RssManager::loadStreamList() { - const Preferences* const pref = Preferences::instance(); - const QStringList streamsUrl = pref->getRssFeedsUrls(); - const QStringList aliases = pref->getRssFeedsAliases(); - if (streamsUrl.size() != aliases.size()) { - std::cerr << "Corrupted Rss list, not loading it\n"; - return; - } - uint i = 0; - qDebug() << Q_FUNC_INFO << streamsUrl; - foreach (QString s, streamsUrl) { - QStringList path = s.split("\\", QString::SkipEmptyParts); - if (path.empty()) continue; - const QString feed_url = path.takeLast(); - qDebug() << "Feed URL:" << feed_url; - // Create feed path (if it does not exists) - RssFolder* feed_parent = this; - foreach (const QString &folder_name, path) { - qDebug() << "Adding parent folder:" << folder_name; - feed_parent = feed_parent->addFolder(folder_name).data(); + const Preferences *const pref = Preferences::instance(); + const QStringList streamsUrl = pref->getRssFeedsUrls(); + const QStringList aliases = pref->getRssFeedsAliases(); + if (streamsUrl.size() != aliases.size()) { + Logger::instance()->addMessage("Corrupted RSS list, not loading it.", Log::WARNING); + return; } - // Create feed - qDebug() << "Adding feed to parent folder"; - RssFeedPtr stream = feed_parent->addStream(this, feed_url); - const QString& alias = aliases[i]; - if (!alias.isEmpty()) { - stream->rename(alias); + + uint i = 0; + qDebug() << Q_FUNC_INFO << streamsUrl; + foreach (QString s, streamsUrl) { + QStringList path = s.split("\\", QString::SkipEmptyParts); + if (path.empty()) continue; + + const QString feedUrl = path.takeLast(); + qDebug() << "Feed URL:" << feedUrl; + // Create feed path (if it does not exists) + RssFolder *feedParent = this; + foreach (const QString &folderName, path) { + qDebug() << "Adding parent folder:" << folderName; + feedParent = feedParent->addFolder(folderName).data(); + } + // Create feed + qDebug() << "Adding feed to parent folder"; + RssFeedPtr stream = feedParent->addStream(this, feedUrl); + const QString &alias = aliases[i]; + if (!alias.isEmpty()) + stream->rename(alias); + ++i; } - ++i; - } - qDebug("NB RSS streams loaded: %d", streamsUrl.size()); + qDebug("NB RSS streams loaded: %d", streamsUrl.size()); } -void RssManager::forwardFeedContentChanged(const QString& url) +void RssManager::forwardFeedContentChanged(const QString &url) { - emit feedContentChanged(url); + emit feedContentChanged(url); } -void RssManager::forwardFeedInfosChanged(const QString& url, const QString& displayName, uint unreadCount) +void RssManager::forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount) { - emit feedInfosChanged(url, displayName, unreadCount); + emit feedInfosChanged(url, displayName, unreadCount); } -void RssManager::forwardFeedIconChanged(const QString& url, const QString& iconPath) +void RssManager::forwardFeedIconChanged(const QString &url, const QString &iconPath) { - emit feedIconChanged(url, iconPath); + emit feedIconChanged(url, iconPath); } -void RssManager::moveFile(const RssFilePtr& file, const RssFolderPtr& destinationFolder) +void RssManager::moveFile(const RssFilePtr &file, const RssFolderPtr &destinationFolder) { - RssFolder* src_folder = file->parent(); - if (destinationFolder != src_folder) { - // Remove reference in old folder - src_folder->takeChild(file->id()); - // add to new Folder - destinationFolder->addFile(file); - } else { - qDebug("Nothing to move, same destination folder"); - } + RssFolder *srcFolder = file->parent(); + if (destinationFolder != srcFolder) { + // Remove reference in old folder + srcFolder->takeChild(file->id()); + // add to new Folder + destinationFolder->addFile(file); + } + else { + qDebug("Nothing to move, same destination folder"); + } } void RssManager::saveStreamList() const { - QStringList streamsUrl; - QStringList aliases; - RssFeedList streams = getAllFeeds(); - foreach (const RssFeedPtr& stream, streams) { - // This backslash has nothing to do with path handling - QString stream_path = stream->pathHierarchy().join("\\"); - if (stream_path.isNull()) - stream_path = ""; - qDebug("Saving stream path: %s", qPrintable(stream_path)); - streamsUrl << stream_path; - aliases << stream->displayName(); - } - Preferences* const pref = Preferences::instance(); - pref->setRssFeedsUrls(streamsUrl); - pref->setRssFeedsAliases(aliases); + QStringList streamsUrl; + QStringList aliases; + RssFeedList streams = getAllFeeds(); + foreach (const RssFeedPtr &stream, streams) { + // This backslash has nothing to do with path handling + QString streamPath = stream->pathHierarchy().join("\\"); + if (streamPath.isNull()) + streamPath = ""; + qDebug("Saving stream path: %s", qPrintable(streamPath)); + streamsUrl << streamPath; + aliases << stream->displayName(); + } + Preferences *const pref = Preferences::instance(); + pref->setRssFeedsUrls(streamsUrl); + pref->setRssFeedsAliases(aliases); } -RssDownloadRuleList* RssManager::downloadRules() const +RssDownloadRuleList *RssManager::downloadRules() const { - Q_ASSERT(m_downloadRules); - return m_downloadRules; + Q_ASSERT(m_downloadRules); + return m_downloadRules; } diff --git a/src/base/rss/rssmanager.h b/src/base/rss/rssmanager.h index 4a8252ccb..5fff13a65 100644 --- a/src/base/rss/rssmanager.h +++ b/src/base/rss/rssmanager.h @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 Christophe Dumez + * Copyright (C) 2010 Arnaud Demaiziere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,39 +39,40 @@ class RssDownloadRuleList; class RssParser; - class RssManager; + typedef QSharedPointer RssManagerPtr; -class RssManager: public RssFolder { - Q_OBJECT +class RssManager: public RssFolder +{ + Q_OBJECT public: - RssManager(); - virtual ~RssManager(); + RssManager(); + ~RssManager(); - RssParser* rssParser() const; - RssDownloadRuleList* downloadRules() const; + RssParser *rssParser() const; + RssDownloadRuleList *downloadRules() const; public slots: - void loadStreamList(); - void saveStreamList() const; - void forwardFeedContentChanged(const QString& url); - void forwardFeedInfosChanged(const QString& url, const QString& displayName, uint unreadCount); - void forwardFeedIconChanged(const QString& url, const QString& iconPath); - void moveFile(const RssFilePtr& file, const RssFolderPtr& destinationFolder); - void updateRefreshInterval(uint val); + void loadStreamList(); + void saveStreamList() const; + void forwardFeedContentChanged(const QString &url); + void forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); + void forwardFeedIconChanged(const QString &url, const QString &iconPath); + void moveFile(const RssFilePtr &file, const RssFolderPtr &destinationFolder); + void updateRefreshInterval(uint val); signals: - void feedContentChanged(const QString& url); - void feedInfosChanged(const QString& url, const QString& displayName, uint unreadCount); - void feedIconChanged(const QString& url, const QString& iconPath); + void feedContentChanged(const QString &url); + void feedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); + void feedIconChanged(const QString &url, const QString &iconPath); private: - QTimer m_refreshTimer; - uint m_refreshInterval; - RssDownloadRuleList* m_downloadRules; - RssParser* m_rssParser; + QTimer m_refreshTimer; + uint m_refreshInterval; + RssDownloadRuleList *m_downloadRules; + RssParser *m_rssParser; }; #endif // RSSMANAGER_H diff --git a/src/base/rss/rssparser.cpp b/src/base/rss/rssparser.cpp index 5929684e4..5f72d6c6a 100644 --- a/src/base/rss/rssparser.cpp +++ b/src/base/rss/rssparser.cpp @@ -37,475 +37,505 @@ #include "base/utils/fs.h" #include "rssparser.h" -struct ParsingJob { - QString feedUrl; - QString filePath; +struct ParsingJob +{ + QString feedUrl; + QString filePath; }; static const char shortDay[][4] = { - "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat", - "Sun" + "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat", + "Sun" }; + static const char longDay[][10] = { - "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", - "Sunday" + "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", + "Sunday" }; + static const char shortMonth[][4] = { - "Jan", "Feb", "Mar", "Apr", - "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec" + "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; -// Ported to Qt4 from KDElibs4 -QDateTime RssParser::parseDate(const QString &string) { - const QString str = string.trimmed(); - if (str.isEmpty()) - return QDateTime::currentDateTime(); +// Ported to Qt from KDElibs4 +QDateTime RssParser::parseDate(const QString &string) +{ + const QString str = string.trimmed(); + if (str.isEmpty()) + return QDateTime::currentDateTime(); - int nyear = 6; // indexes within string to values - int nmonth = 4; - int nday = 2; - int nwday = 1; - int nhour = 7; - int nmin = 8; - int nsec = 9; - // Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm" - QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$"); - QStringList parts; - if (!str.indexOf(rx)) { - // Check that if date has '-' separators, both separators are '-'. - parts = rx.capturedTexts(); - bool h1 = (parts[3] == QLatin1String("-")); - bool h2 = (parts[5] == QLatin1String("-")); - if (h1 != h2) - return QDateTime::currentDateTime(); - } else { - // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" - rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"); - if (str.indexOf(rx)) - return QDateTime::currentDateTime(); - nyear = 7; - nmonth = 2; - nday = 3; - nwday = 1; - nhour = 4; - nmin = 5; - nsec = 6; - parts = rx.capturedTexts(); - } - bool ok[4]; - const int day = parts[nday].toInt(&ok[0]); - int year = parts[nyear].toInt(&ok[1]); - const int hour = parts[nhour].toInt(&ok[2]); - const int minute = parts[nmin].toInt(&ok[3]); - if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) - return QDateTime::currentDateTime(); - int second = 0; - if (!parts[nsec].isEmpty()) { - second = parts[nsec].toInt(&ok[0]); - if (!ok[0]) - return QDateTime::currentDateTime(); - } - bool leapSecond = (second == 60); - if (leapSecond) - second = 59; // apparently a leap second - validate below, once time zone is known - int month = 0; - for ( ; month < 12 && parts[nmonth] != shortMonth[month]; ++month) ; - int dayOfWeek = -1; - if (!parts[nwday].isEmpty()) { - // Look up the weekday name - while (++dayOfWeek < 7 && shortDay[dayOfWeek] != parts[nwday]) ; - if (dayOfWeek >= 7) - for (dayOfWeek = 0; dayOfWeek < 7 && longDay[dayOfWeek] != parts[nwday]; ++dayOfWeek) ; - } - // if (month >= 12 || dayOfWeek >= 7 - // || (dayOfWeek < 0 && format == RFCDateDay)) - // return QDateTime; - int i = parts[nyear].size(); - if (i < 4) { - // It's an obsolete year specification with less than 4 digits - year += (i == 2 && year < 50) ? 2000: 1900; - } - - // Parse the UTC offset part - int offset = 0; // set default to '-0000' - bool negOffset = false; - if (parts.count() > 10) { - rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$"); - if (!parts[10].indexOf(rx)) { - // It's a UTC offset ±hhmm - parts = rx.capturedTexts(); - offset = parts[2].toInt(&ok[0]) * 3600; - int offsetMin = parts[3].toInt(&ok[1]); - if (!ok[0] || !ok[1] || offsetMin > 59) - return QDateTime(); - offset += offsetMin * 60; - negOffset = (parts[1] == QLatin1String("-")); - if (negOffset) - offset = -offset; - } else { - // Check for an obsolete time zone name - QByteArray zone = parts[10].toLatin1(); - if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') - negOffset = true; // military zone: RFC 2822 treats as '-0000' - else if (zone != "UT" && zone != "GMT") { // treated as '+0000' - offset = (zone == "EDT") ? -4*3600 - : (zone == "EST" || zone == "CDT") ? -5*3600 - : (zone == "CST" || zone == "MDT") ? -6*3600 - : (zone == "MST" || zone == "PDT") ? -7*3600 - : (zone == "PST") ? -8*3600 - : 0; - if (!offset) { - // Check for any other alphabetic time zone - bool nonalpha = false; - for (int i = 0, end = zone.size(); i < end && !nonalpha; ++i) - nonalpha = !isalpha(zone[i]); - if (nonalpha) - return QDateTime(); - // TODO: Attempt to recognize the time zone abbreviation? - negOffset = true; // unknown time zone: RFC 2822 treats as '-0000' - } - } + int nyear = 6; // indexes within string to values + int nmonth = 4; + int nday = 2; + int nwday = 1; + int nhour = 7; + int nmin = 8; + int nsec = 9; + // Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm" + QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$"); + QStringList parts; + if (!str.indexOf(rx)) { + // Check that if date has '-' separators, both separators are '-'. + parts = rx.capturedTexts(); + bool h1 = (parts[3] == QLatin1String("-")); + bool h2 = (parts[5] == QLatin1String("-")); + if (h1 != h2) + return QDateTime::currentDateTime(); + } + else { + // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" + rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"); + if (str.indexOf(rx)) + return QDateTime::currentDateTime(); + nyear = 7; + nmonth = 2; + nday = 3; + nwday = 1; + nhour = 4; + nmin = 5; + nsec = 6; + parts = rx.capturedTexts(); } - } - QDate qdate(year, month+1, day); // convert date, and check for out-of-range - if (!qdate.isValid()) - return QDateTime::currentDateTime(); - QTime qTime(hour, minute, second); - QDateTime result(qdate, qTime, Qt::UTC); - if (offset) - result = result.addSecs(-offset); - if (!result.isValid()) - return QDateTime::currentDateTime(); // invalid date/time + bool ok[4]; + const int day = parts[nday].toInt(&ok[0]); + int year = parts[nyear].toInt(&ok[1]); + const int hour = parts[nhour].toInt(&ok[2]); + const int minute = parts[nmin].toInt(&ok[3]); + if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) + return QDateTime::currentDateTime(); - if (leapSecond) { - // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. - // Convert the time to UTC and check that it is 00:00:00. - if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) - return QDateTime::currentDateTime(); // the time isn't the last second of the day - } - return result; + int second = 0; + if (!parts[nsec].isEmpty()) { + second = parts[nsec].toInt(&ok[0]); + if (!ok[0]) + return QDateTime::currentDateTime(); + } + + bool leapSecond = (second == 60); + if (leapSecond) + second = 59; // apparently a leap second - validate below, once time zone is known + int month = 0; + for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month); + int dayOfWeek = -1; + if (!parts[nwday].isEmpty()) { + // Look up the weekday name + while (++dayOfWeek < 7 && (shortDay[dayOfWeek] != parts[nwday])); + if (dayOfWeek >= 7) + for (dayOfWeek = 0; dayOfWeek < 7 && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek); + } + + // if (month >= 12 || dayOfWeek >= 7 + // || (dayOfWeek < 0 && format == RFCDateDay)) + // return QDateTime; + int i = parts[nyear].size(); + if (i < 4) { + // It's an obsolete year specification with less than 4 digits + year += (i == 2 && year < 50) ? 2000 : 1900; + } + + // Parse the UTC offset part + int offset = 0; // set default to '-0000' + bool negOffset = false; + if (parts.count() > 10) { + rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$"); + if (!parts[10].indexOf(rx)) { + // It's a UTC offset ±hhmm + parts = rx.capturedTexts(); + offset = parts[2].toInt(&ok[0]) * 3600; + int offsetMin = parts[3].toInt(&ok[1]); + if (!ok[0] || !ok[1] || offsetMin > 59) + return QDateTime(); + offset += offsetMin * 60; + negOffset = (parts[1] == QLatin1String("-")); + if (negOffset) + offset = -offset; + } + else { + // Check for an obsolete time zone name + QByteArray zone = parts[10].toLatin1(); + if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') { + negOffset = true; // military zone: RFC 2822 treats as '-0000' + } + else if (zone != "UT" && zone != "GMT") { // treated as '+0000' + offset = (zone == "EDT") + ? -4 * 3600 + : ((zone == "EST") || (zone == "CDT")) + ? -5 * 3600 + : ((zone == "CST") || (zone == "MDT")) + ? -6 * 3600 + : (zone == "MST" || zone == "PDT") + ? -7 * 3600 + : (zone == "PST") + ? -8 * 3600 + : 0; + if (!offset) { + // Check for any other alphabetic time zone + bool nonalpha = false; + for (int i = 0, end = zone.size(); (i < end) && !nonalpha; ++i) + nonalpha = !isalpha(zone[i]); + if (nonalpha) + return QDateTime(); + // TODO: Attempt to recognize the time zone abbreviation? + negOffset = true; // unknown time zone: RFC 2822 treats as '-0000' + } + } + } + } + + QDate qdate(year, month + 1, day); // convert date, and check for out-of-range + if (!qdate.isValid()) + return QDateTime::currentDateTime(); + + QTime qTime(hour, minute, second); + QDateTime result(qdate, qTime, Qt::UTC); + if (offset) + result = result.addSecs(-offset); + if (!result.isValid()) + return QDateTime::currentDateTime(); // invalid date/time + + if (leapSecond) { + // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. + // Convert the time to UTC and check that it is 00:00:00. + if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) + return QDateTime::currentDateTime(); // the time isn't the last second of the day + } + + return result; } -RssParser::RssParser(QObject *parent) : - QThread(parent), m_running(true) +RssParser::RssParser(QObject *parent) + : QThread(parent) + , m_running(true) { - start(); + start(); } RssParser::~RssParser() { - m_running = false; - m_waitCondition.wakeOne(); - wait(); + m_running = false; + m_waitCondition.wakeOne(); + wait(); } -void RssParser::parseRssFile(const QString& feedUrl, const QString& filePath) +void RssParser::parseRssFile(const QString &feedUrl, const QString &filePath) { - qDebug() << Q_FUNC_INFO << feedUrl << filePath; - m_mutex.lock(); - ParsingJob job = { feedUrl, Utils::Fs::fromNativePath(filePath) }; - m_queue.enqueue(job); - // Wake up thread. - if (m_queue.count() == 1) { - qDebug() << Q_FUNC_INFO << "Waking up thread"; - m_waitCondition.wakeOne(); - } - m_mutex.unlock(); + qDebug() << Q_FUNC_INFO << feedUrl << filePath; + m_mutex.lock(); + ParsingJob job = { feedUrl, Utils::Fs::fromNativePath(filePath) }; + m_queue.enqueue(job); + // Wake up thread. + if (m_queue.count() == 1) { + qDebug() << Q_FUNC_INFO << "Waking up thread"; + m_waitCondition.wakeOne(); + } + m_mutex.unlock(); } void RssParser::clearFeedData(const QString &feedUrl) { - m_mutex.lock(); - m_lastBuildDates.remove(feedUrl); - m_mutex.unlock(); + m_mutex.lock(); + m_lastBuildDates.remove(feedUrl); + m_mutex.unlock(); } void RssParser::run() { - while (m_running) { - m_mutex.lock(); - if (!m_queue.empty()) { - ParsingJob job = m_queue.dequeue(); - m_mutex.unlock(); - parseFeed(job); - } else { - qDebug() << Q_FUNC_INFO << "Thread is waiting."; - m_waitCondition.wait(&m_mutex); - qDebug() << Q_FUNC_INFO << "Thread woke up."; - m_mutex.unlock(); - } - } -} - -void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) -{ - QVariantHash article; - - while(!xml.atEnd()) { - xml.readNext(); - - if(xml.isEndElement() && xml.name() == "item") - break; - - if (xml.isStartElement()) { - if (xml.name() == "title") - article["title"] = xml.readElementText().trimmed(); - else if (xml.name() == "enclosure") { - if (xml.attributes().value("type") == "application/x-bittorrent") - article["torrent_url"] = xml.attributes().value("url").toString(); - } - else if (xml.name() == "link") { - QString link = xml.readElementText().trimmed(); - if (link.startsWith("magnet:", Qt::CaseInsensitive)) - article["torrent_url"] = link; // magnet link instead of a news URL - else - article["news_link"] = link; - } - else if (xml.name() == "description") - article["description"] = xml.readElementText().trimmed(); - else if (xml.name() == "pubDate") - article["date"] = parseDate(xml.readElementText().trimmed()); - else if (xml.name() == "author") - article["author"] = xml.readElementText().trimmed(); - else if (xml.name() == "guid") - article["id"] = xml.readElementText().trimmed(); - } - } - - if (!article.contains("torrent_url") && article.contains("news_link")) - article["torrent_url"] = article["news_link"]; - - if (!article.contains("id")) { - // Item does not have a guid, fall back to some other identifier - const QString link = article.value("news_link").toString(); - if (!link.isEmpty()) - article["id"] = link; - else { - const QString title = article.value("title").toString(); - if (!title.isEmpty()) - article["id"] = title; - else { - qWarning() << "Item has no guid, link or title, ignoring it..."; - return; - } - } - } - - emit newArticle(feedUrl, article); -} - -void RssParser::parseRSSChannel(QXmlStreamReader& xml, const QString& feedUrl) -{ - qDebug() << Q_FUNC_INFO << feedUrl; - Q_ASSERT(xml.isStartElement() && xml.name() == "channel"); - - while(!xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - if (xml.name() == "title") { - QString title = xml.readElementText(); - emit feedTitle(feedUrl, title); - } - else if (xml.name() == "lastBuildDate") { - QString lastBuildDate = xml.readElementText(); - if (!lastBuildDate.isEmpty()) { - QMutexLocker locker(&m_mutex); - if (m_lastBuildDates.value(feedUrl, "") == lastBuildDate) { - qDebug() << "The RSS feed has not changed since last time, aborting parsing."; - return; - } - m_lastBuildDates[feedUrl] = lastBuildDate; + while (m_running) { + m_mutex.lock(); + if (!m_queue.empty()) { + ParsingJob job = m_queue.dequeue(); + m_mutex.unlock(); + parseFeed(job); + } + else { + qDebug() << Q_FUNC_INFO << "Thread is waiting."; + m_waitCondition.wait(&m_mutex); + qDebug() << Q_FUNC_INFO << "Thread woke up."; + m_mutex.unlock(); } - } - else if (xml.name() == "item") { - parseRssArticle(xml, feedUrl); - } } - } } -void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, const QString& baseUrl) +void RssParser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) { - QVariantHash article; - bool double_content = false; + QVariantHash article; - while(!xml.atEnd()) { - xml.readNext(); - - if(xml.isEndElement() && xml.name() == "entry") - break; - - if (xml.isStartElement()) { - if (xml.name() == "title") { - article["title"] = xml.readElementText().trimmed(); - } - else if (xml.name() == "link") { - QString link = ( xml.attributes().isEmpty() ? - xml.readElementText().trimmed() : - xml.attributes().value("href").toString() ); - - if (link.startsWith("magnet:", Qt::CaseInsensitive)) - article["torrent_url"] = link; // magnet link instead of a news URL - else - // Atom feeds can have relative links, work around this and - // take the stress of figuring article full URI from UI - // Assemble full URI - article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link ); - - } - else if (xml.name() == "summary" || xml.name() == "content"){ - if(double_content) { // Duplicate content -> ignore - xml.readNext(); - - while(xml.name() != "summary" && xml.name() != "content") - xml.readNext(); - - continue; - } - - // Try to also parse broken articles, which don't use html '&' escapes - // Actually works great for non-broken content too - QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); - if (!feedText.isEmpty()) - article["description"] = feedText.trimmed(); - - double_content = true; - } - else if (xml.name() == "updated"){ - // ATOM uses standard compliant date, don't do fancy stuff - QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate); - article["date"] = ( articleDate.isValid() ? - articleDate : - QDateTime::currentDateTime() ); - } - else if (xml.name() == "author") { + while(!xml.atEnd()) { xml.readNext(); - while(xml.name() != "author") { - if(xml.name() == "name") - article["author"] = xml.readElementText().trimmed(); - xml.readNext(); + + if(xml.isEndElement() && xml.name() == "item") + break; + + if (xml.isStartElement()) { + if (xml.name() == "title") { + article["title"] = xml.readElementText().trimmed(); + } + else if (xml.name() == "enclosure") { + if (xml.attributes().value("type") == "application/x-bittorrent") + article["torrent_url"] = xml.attributes().value("url").toString(); + } + else if (xml.name() == "link") { + QString link = xml.readElementText().trimmed(); + if (link.startsWith("magnet:", Qt::CaseInsensitive)) + article["torrent_url"] = link; // magnet link instead of a news URL + else + article["news_link"] = link; + } + else if (xml.name() == "description") { + article["description"] = xml.readElementText().trimmed(); + } + else if (xml.name() == "pubDate") { + article["date"] = parseDate(xml.readElementText().trimmed()); + } + else if (xml.name() == "author") { + article["author"] = xml.readElementText().trimmed(); + } + else if (xml.name() == "guid") { + article["id"] = xml.readElementText().trimmed(); + } } - } - else if (xml.name() == "id") - article["id"] = xml.readElementText().trimmed(); } - } - if (!article.contains("torrent_url") && article.contains("news_link")) - article["torrent_url"] = article["news_link"]; + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; - if (!article.contains("id")) { - // Item does not have a guid, fall back to some other identifier - const QString link = article.value("news_link").toString(); - if (!link.isEmpty()) - article["id"] = link; - else { - const QString title = article.value("title").toString(); - if (!title.isEmpty()) - article["id"] = title; - else { - qWarning() << "Item has no guid, link or title, ignoring it..."; - return; - } + if (!article.contains("id")) { + // Item does not have a guid, fall back to some other identifier + const QString link = article.value("news_link").toString(); + if (!link.isEmpty()) { + article["id"] = link; + } + else { + const QString title = article.value("title").toString(); + if (!title.isEmpty()) { + article["id"] = title; + } + else { + qWarning() << "Item has no guid, link or title, ignoring it..."; + return; + } + } } - } - emit newArticle(feedUrl, article); + emit newArticle(feedUrl, article); } -void RssParser::parseAtomChannel(QXmlStreamReader& xml, const QString& feedUrl) +void RssParser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) { - qDebug() << Q_FUNC_INFO << feedUrl; - Q_ASSERT(xml.isStartElement() && xml.name() == "feed"); + qDebug() << Q_FUNC_INFO << feedUrl; + Q_ASSERT(xml.isStartElement() && xml.name() == "channel"); - QString baseURL = xml.attributes().value("xml:base").toString(); + while(!xml.atEnd()) { + xml.readNext(); - while(!xml.atEnd()) { - xml.readNext(); - - if (xml.isStartElement()) { - if (xml.name() == "title") { - QString title = xml.readElementText(); - emit feedTitle(feedUrl, title); - } - else if (xml.name() == "updated") { - QString lastBuildDate = xml.readElementText(); - if (!lastBuildDate.isEmpty()) { - QMutexLocker locker(&m_mutex); - if (m_lastBuildDates.value(feedUrl) == lastBuildDate) { - qDebug() << "The RSS feed has not changed since last time, aborting parsing."; - return; - } - m_lastBuildDates[feedUrl] = lastBuildDate; + if (xml.isStartElement()) { + if (xml.name() == "title") { + QString title = xml.readElementText(); + emit feedTitle(feedUrl, title); + } + else if (xml.name() == "lastBuildDate") { + QString lastBuildDate = xml.readElementText(); + if (!lastBuildDate.isEmpty()) { + QMutexLocker locker(&m_mutex); + if (m_lastBuildDates.value(feedUrl, "") == lastBuildDate) { + qDebug() << "The RSS feed has not changed since last time, aborting parsing."; + return; + } + m_lastBuildDates[feedUrl] = lastBuildDate; + } + } + else if (xml.name() == "item") { + parseRssArticle(xml, feedUrl); + } + } + } +} + +void RssParser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl) +{ + QVariantHash article; + bool doubleContent = false; + + while(!xml.atEnd()) { + xml.readNext(); + + if(xml.isEndElement() && (xml.name() == "entry")) + break; + + if (xml.isStartElement()) { + if (xml.name() == "title") { + article["title"] = xml.readElementText().trimmed(); + } + else if (xml.name() == "link") { + QString link = ( xml.attributes().isEmpty() ? + xml.readElementText().trimmed() : + xml.attributes().value("href").toString() ); + + if (link.startsWith("magnet:", Qt::CaseInsensitive)) + article["torrent_url"] = link; // magnet link instead of a news URL + else + // Atom feeds can have relative links, work around this and + // take the stress of figuring article full URI from UI + // Assemble full URI + article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link ); + + } + else if ((xml.name() == "summary") || (xml.name() == "content")){ + if (doubleContent) { // Duplicate content -> ignore + xml.readNext(); + + while ((xml.name() != "summary") && (xml.name() != "content")) + xml.readNext(); + + continue; + } + + // Try to also parse broken articles, which don't use html '&' escapes + // Actually works great for non-broken content too + QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); + if (!feedText.isEmpty()) + article["description"] = feedText.trimmed(); + + doubleContent = true; + } + else if (xml.name() == "updated") { + // ATOM uses standard compliant date, don't do fancy stuff + QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate); + article["date"] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime()); + } + else if (xml.name() == "author") { + xml.readNext(); + while(xml.name() != "author") { + if(xml.name() == "name") + article["author"] = xml.readElementText().trimmed(); + xml.readNext(); + } + } + else if (xml.name() == "id") { + article["id"] = xml.readElementText().trimmed(); + } + } + } + + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; + + if (!article.contains("id")) { + // Item does not have a guid, fall back to some other identifier + const QString link = article.value("news_link").toString(); + if (!link.isEmpty()) { + article["id"] = link; + } + else { + const QString title = article.value("title").toString(); + if (!title.isEmpty()) { + article["id"] = title; + } + else { + qWarning() << "Item has no guid, link or title, ignoring it..."; + return; + } + } + } + + emit newArticle(feedUrl, article); +} + +void RssParser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) +{ + qDebug() << Q_FUNC_INFO << feedUrl; + Q_ASSERT(xml.isStartElement() && xml.name() == "feed"); + + QString baseURL = xml.attributes().value("xml:base").toString(); + + while (!xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + if (xml.name() == "title") { + QString title = xml.readElementText(); + emit feedTitle(feedUrl, title); + } + else if (xml.name() == "updated") { + QString lastBuildDate = xml.readElementText(); + if (!lastBuildDate.isEmpty()) { + QMutexLocker locker(&m_mutex); + if (m_lastBuildDates.value(feedUrl) == lastBuildDate) { + qDebug() << "The RSS feed has not changed since last time, aborting parsing."; + return; + } + m_lastBuildDates[feedUrl] = lastBuildDate; + } + } + else if (xml.name() == "entry") { + parseAtomArticle(xml, feedUrl, baseURL); + } } - } - else if (xml.name() == "entry") { - parseAtomArticle(xml, feedUrl, baseURL); - } } - } } // read and create items from a rss document -void RssParser::parseFeed(const ParsingJob& job) +void RssParser::parseFeed(const ParsingJob &job) { - qDebug() << Q_FUNC_INFO << job.feedUrl << job.filePath; - QFile fileRss(job.filePath); - if (!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) { - reportFailure(job, tr("Failed to open downloaded RSS file.")); - return; - } - QXmlStreamReader xml(&fileRss); + qDebug() << Q_FUNC_INFO << job.feedUrl << job.filePath; + QFile fileRss(job.filePath); + if (!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) { + reportFailure(job, tr("Failed to open downloaded RSS file.")); + return; + } - bool found_channel = false; - while (xml.readNextStartElement()) { - if (xml.name() == "rss") { - // Find channels - while (xml.readNextStartElement()) { - if (xml.name() == "channel") { - parseRSSChannel(xml, job.feedUrl); - found_channel = true; - break; - } else { - qDebug() << "Skip rss item: " << xml.name(); - xml.skipCurrentElement(); + QXmlStreamReader xml(&fileRss); + bool foundChannel = false; + while (xml.readNextStartElement()) { + if (xml.name() == "rss") { + // Find channels + while (xml.readNextStartElement()) { + if (xml.name() == "channel") { + parseRSSChannel(xml, job.feedUrl); + foundChannel = true; + break; + } + else { + qDebug() << "Skip rss item: " << xml.name(); + xml.skipCurrentElement(); + } + } + break; + } + else if (xml.name() == "feed") { // Atom feed + parseAtomChannel(xml, job.feedUrl); + foundChannel = true; + break; + } + else { + qDebug() << "Skip root item: " << xml.name(); + xml.skipCurrentElement(); } - } - break; } - else if (xml.name() == "feed") { // Atom feed - parseAtomChannel(xml, job.feedUrl); - found_channel = true; - break; - } else { - qDebug() << "Skip root item: " << xml.name(); - xml.skipCurrentElement(); + + if (xml.hasError()) { + reportFailure(job, xml.errorString()); + return; } - } - if (xml.hasError()) { - reportFailure(job, xml.errorString()); - return; - } + if (!foundChannel) { + reportFailure(job, tr("Invalid RSS feed at '%1'.").arg(job.feedUrl)); + return; + } - if (!found_channel) { - reportFailure(job, tr("Invalid RSS feed at '%1'.").arg(job.feedUrl)); - return; - } - - // Clean up - fileRss.close(); - emit feedParsingFinished(job.feedUrl, QString()); - Utils::Fs::forceRemove(job.filePath); + // Clean up + fileRss.close(); + emit feedParsingFinished(job.feedUrl, QString()); + Utils::Fs::forceRemove(job.filePath); } -void RssParser::reportFailure(const ParsingJob& job, const QString& error) +void RssParser::reportFailure(const ParsingJob &job, const QString &error) { - emit feedParsingFinished(job.feedUrl, error); - Utils::Fs::forceRemove(job.filePath); + emit feedParsingFinished(job.feedUrl, error); + Utils::Fs::forceRemove(job.filePath); } diff --git a/src/base/rss/rssparser.h b/src/base/rss/rssparser.h index 23608127b..42b52d9bb 100644 --- a/src/base/rss/rssparser.h +++ b/src/base/rss/rssparser.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -40,39 +40,40 @@ struct ParsingJob; -class RssParser : public QThread +class RssParser: public QThread { - Q_OBJECT + Q_OBJECT public: - explicit RssParser(QObject *parent = 0); - virtual ~RssParser(); + explicit RssParser(QObject *parent = 0); + virtual ~RssParser(); signals: - void newArticle(const QString& feedUrl, const QVariantHash& rssArticle); - void feedTitle(const QString& feedUrl, const QString& title); - void feedParsingFinished(const QString& feedUrl, const QString& error); + void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); + void feedTitle(const QString &feedUrl, const QString &title); + void feedParsingFinished(const QString &feedUrl, const QString &error); public slots: - void parseRssFile(const QString& feedUrl, const QString& filePath); - void clearFeedData(const QString& feedUrl); + void parseRssFile(const QString &feedUrl, const QString &filePath); + void clearFeedData(const QString &feedUrl); protected: - virtual void run(); - static QDateTime parseDate(const QString& string); - void parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl); - void parseRSSChannel(QXmlStreamReader& xml, const QString& feedUrl); - void parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, const QString& baseUrl); - void parseAtomChannel(QXmlStreamReader& xml, const QString& feedUrl); - void parseFeed(const ParsingJob& job); - void reportFailure(const ParsingJob& job, const QString& error); + virtual void run(); private: - bool m_running; - QMutex m_mutex; - QQueue m_queue; - QWaitCondition m_waitCondition; - QHash m_lastBuildDates; // Optimization + static QDateTime parseDate(const QString &string); + void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); + void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); + void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseFeed(const ParsingJob &job); + void reportFailure(const ParsingJob &job, const QString &error); + + bool m_running; + QMutex m_mutex; + QQueue m_queue; + QWaitCondition m_waitCondition; + QHash m_lastBuildDates; // Optimization }; #endif // RSSPARSER_H diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index 9c30274f0..4bf2b2129 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -28,8 +28,8 @@ * Contact: chris@qbittorrent.org, arnaud@qbittorrent.org */ -#include "core/rss/rssmanager.h" -#include "core/rss/rssfeed.h" +#include "base/rss/rssmanager.h" +#include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "feedlistwidget.h" From 6f7ae728eb60d12e654b3ce32703460b3283ba63 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Thu, 15 Oct 2015 19:33:27 +0300 Subject: [PATCH 3/5] Move base RSS names to Rss namespace. --- src/base/rss/rssarticle.cpp | 36 ++++--- src/base/rss/rssarticle.h | 85 +++++++-------- src/base/rss/rssdownloadrule.cpp | 70 +++++++------ src/base/rss/rssdownloadrule.h | 123 +++++++++++----------- src/base/rss/rssdownloadrulelist.cpp | 44 ++++---- src/base/rss/rssdownloadrulelist.h | 51 ++++----- src/base/rss/rssfeed.cpp | 115 +++++++++++---------- src/base/rss/rssfeed.h | 137 ++++++++++++------------ src/base/rss/rssfile.cpp | 6 +- src/base/rss/rssfile.h | 69 +++++++------ src/base/rss/rssfolder.cpp | 138 +++++++++++++------------ src/base/rss/rssfolder.h | 95 ++++++++--------- src/base/rss/rssmanager.cpp | 38 +++---- src/base/rss/rssmanager.h | 65 ++++++------ src/base/rss/rssparser.cpp | 61 ++++++----- src/base/rss/rssparser.h | 63 +++++------ src/gui/rss/automatedrssdownloader.cpp | 30 +++--- src/gui/rss/automatedrssdownloader.h | 19 ++-- src/gui/rss/feedlistwidget.cpp | 36 +++---- src/gui/rss/feedlistwidget.h | 12 +-- src/gui/rss/rss_imp.cpp | 66 ++++++------ src/gui/rss/rss_imp.h | 8 +- 22 files changed, 708 insertions(+), 659 deletions(-) diff --git a/src/base/rss/rssarticle.cpp b/src/base/rss/rssarticle.cpp index 585da24c7..582575b64 100644 --- a/src/base/rss/rssarticle.cpp +++ b/src/base/rss/rssarticle.cpp @@ -36,20 +36,22 @@ #include "rssfeed.h" #include "rssarticle.h" +using namespace Rss; + // public constructor -RssArticle::RssArticle(RssFeed *parent, const QString &guid) +Article::Article(Feed *parent, const QString &guid) : m_parent(parent) , m_guid(guid) , m_read(false) { } -bool RssArticle::hasAttachment() const +bool Article::hasAttachment() const { return !m_torrentUrl.isEmpty(); } -QVariantHash RssArticle::toHash() const +QVariantHash Article::toHash() const { QVariantHash item; item["title"] = m_title; @@ -63,13 +65,13 @@ QVariantHash RssArticle::toHash() const return item; } -RssArticlePtr RssArticle::fromHash(RssFeed *parent, const QVariantHash &h) +ArticlePtr Article::fromHash(Feed *parent, const QVariantHash &h) { const QString guid = h.value("id").toString(); if (guid.isEmpty()) - return RssArticlePtr(); + return ArticlePtr(); - RssArticlePtr art(new RssArticle(parent, guid)); + ArticlePtr art(new Article(parent, guid)); art->m_title = h.value("title", "").toString(); art->m_torrentUrl = h.value("torrent_url", "").toString(); art->m_link = h.value("news_link", "").toString(); @@ -81,42 +83,42 @@ RssArticlePtr RssArticle::fromHash(RssFeed *parent, const QVariantHash &h) return art; } -RssFeed *RssArticle::parent() const +Feed *Article::parent() const { return m_parent; } -const QString &RssArticle::author() const +const QString &Article::author() const { return m_author; } -const QString &RssArticle::torrentUrl() const +const QString &Article::torrentUrl() const { return m_torrentUrl; } -const QString &RssArticle::link() const +const QString &Article::link() const { return m_link; } -QString RssArticle::description() const +QString Article::description() const { return m_description.isNull() ? "" : m_description; } -const QDateTime &RssArticle::date() const +const QDateTime &Article::date() const { return m_date; } -bool RssArticle::isRead() const +bool Article::isRead() const { return m_read; } -void RssArticle::markAsRead() +void Article::markAsRead() { if (m_read) return; @@ -126,17 +128,17 @@ void RssArticle::markAsRead() emit articleWasRead(); } -const QString &RssArticle::guid() const +const QString &Article::guid() const { return m_guid; } -const QString &RssArticle::title() const +const QString &Article::title() const { return m_title; } -void RssArticle::handleTorrentDownloadSuccess(const QString &url) +void Article::handleTorrentDownloadSuccess(const QString &url) { if (url == m_torrentUrl) markAsRead(); diff --git a/src/base/rss/rssarticle.h b/src/base/rss/rssarticle.h index 72bdac20c..88f8b4202 100644 --- a/src/base/rss/rssarticle.h +++ b/src/base/rss/rssarticle.h @@ -37,53 +37,56 @@ #include #include -class RssFeed; -class RssArticle; - -typedef QSharedPointer RssArticlePtr; - -// Item of a rss stream, single information -class RssArticle: public QObject +namespace Rss { - Q_OBJECT + class Feed; + class Article; -public: - RssArticle(RssFeed *parent, const QString &guid); + typedef QSharedPointer
ArticlePtr; - // Accessors - bool hasAttachment() const; - const QString &guid() const; - RssFeed *parent() const; - const QString &title() const; - const QString &author() const; - const QString &torrentUrl() const; - const QString &link() const; - QString description() const; - const QDateTime &date() const; - bool isRead() const; - // Setters - void markAsRead(); + // Item of a rss stream, single information + class Article: public QObject + { + Q_OBJECT - // Serialization - QVariantHash toHash() const; - static RssArticlePtr fromHash(RssFeed *parent, const QVariantHash &hash); + public: + Article(Feed *parent, const QString &guid); -signals: - void articleWasRead(); + // Accessors + bool hasAttachment() const; + const QString &guid() const; + Feed *parent() const; + const QString &title() const; + const QString &author() const; + const QString &torrentUrl() const; + const QString &link() const; + QString description() const; + const QDateTime &date() const; + bool isRead() const; + // Setters + void markAsRead(); -public slots: - void handleTorrentDownloadSuccess(const QString &url); + // Serialization + QVariantHash toHash() const; + static ArticlePtr fromHash(Feed *parent, const QVariantHash &hash); -private: - RssFeed *m_parent; - QString m_guid; - QString m_title; - QString m_torrentUrl; - QString m_link; - QString m_description; - QDateTime m_date; - QString m_author; - bool m_read; -}; + signals: + void articleWasRead(); + + public slots: + void handleTorrentDownloadSuccess(const QString &url); + + private: + Feed *m_parent; + QString m_guid; + QString m_title; + QString m_torrentUrl; + QString m_link; + QString m_description; + QDateTime m_date; + QString m_author; + bool m_read; + }; +} #endif // RSSARTICLE_H diff --git a/src/base/rss/rssdownloadrule.cpp b/src/base/rss/rssdownloadrule.cpp index 25d4d4cd1..a7ef0bb2d 100644 --- a/src/base/rss/rssdownloadrule.cpp +++ b/src/base/rss/rssdownloadrule.cpp @@ -38,14 +38,16 @@ #include "rssarticle.h" #include "rssdownloadrule.h" -RssDownloadRule::RssDownloadRule() +using namespace Rss; + +DownloadRule::DownloadRule() : m_enabled(false) , m_useRegex(false) , m_apstate(USE_GLOBAL) { } -bool RssDownloadRule::matches(const QString &articleTitle) const +bool DownloadRule::matches(const QString &articleTitle) const { foreach (const QString &token, m_mustContain) { if (!token.isEmpty()) { @@ -123,7 +125,7 @@ bool RssDownloadRule::matches(const QString &articleTitle) const return true; } -void RssDownloadRule::setMustContain(const QString &tokens) +void DownloadRule::setMustContain(const QString &tokens) { if (m_useRegex) m_mustContain = QStringList() << tokens; @@ -131,7 +133,7 @@ void RssDownloadRule::setMustContain(const QString &tokens) m_mustContain = tokens.split(" "); } -void RssDownloadRule::setMustNotContain(const QString &tokens) +void DownloadRule::setMustNotContain(const QString &tokens) { if (m_useRegex) m_mustNotContain = QStringList() << tokens; @@ -139,34 +141,34 @@ void RssDownloadRule::setMustNotContain(const QString &tokens) m_mustNotContain = tokens.split("|"); } -QStringList RssDownloadRule::rssFeeds() const +QStringList DownloadRule::rssFeeds() const { return m_rssFeeds; } -void RssDownloadRule::setRssFeeds(const QStringList &rssFeeds) +void DownloadRule::setRssFeeds(const QStringList &rssFeeds) { m_rssFeeds = rssFeeds; } -QString RssDownloadRule::name() const +QString DownloadRule::name() const { return m_name; } -void RssDownloadRule::setName(const QString &name) +void DownloadRule::setName(const QString &name) { m_name = name; } -QString RssDownloadRule::savePath() const +QString DownloadRule::savePath() const { return m_savePath; } -RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &ruleHash) +DownloadRulePtr DownloadRule::fromVariantHash(const QVariantHash &ruleHash) { - RssDownloadRulePtr rule(new RssDownloadRule); + DownloadRulePtr rule(new DownloadRule); rule->setName(ruleHash.value("name").toString()); rule->setUseRegex(ruleHash.value("use_regex", false).toBool()); rule->setMustContain(ruleHash.value("must_contain").toString()); @@ -182,7 +184,7 @@ RssDownloadRulePtr RssDownloadRule::fromVariantHash(const QVariantHash &ruleHash return rule; } -QVariantHash RssDownloadRule::toVariantHash() const +QVariantHash DownloadRule::toVariantHash() const { QVariantHash hash; hash["name"] = m_name; @@ -200,12 +202,12 @@ QVariantHash RssDownloadRule::toVariantHash() const return hash; } -bool RssDownloadRule::operator==(const RssDownloadRule &other) const +bool DownloadRule::operator==(const DownloadRule &other) const { return m_name == other.name(); } -void RssDownloadRule::setSavePath(const QString &savePath) +void DownloadRule::setSavePath(const QString &savePath) { if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath()))) m_savePath = Utils::Fs::fromNativePath(savePath); @@ -213,93 +215,93 @@ void RssDownloadRule::setSavePath(const QString &savePath) m_savePath = QString(); } -RssDownloadRule::AddPausedState RssDownloadRule::addPaused() const +DownloadRule::AddPausedState DownloadRule::addPaused() const { return m_apstate; } -void RssDownloadRule::setAddPaused(const RssDownloadRule::AddPausedState &aps) +void DownloadRule::setAddPaused(const DownloadRule::AddPausedState &aps) { m_apstate = aps; } -QString RssDownloadRule::label() const +QString DownloadRule::label() const { return m_label; } -void RssDownloadRule::setLabel(const QString &label) +void DownloadRule::setLabel(const QString &label) { m_label = label; } -bool RssDownloadRule::isEnabled() const +bool DownloadRule::isEnabled() const { return m_enabled; } -void RssDownloadRule::setEnabled(bool enable) +void DownloadRule::setEnabled(bool enable) { m_enabled = enable; } -void RssDownloadRule::setLastMatch(const QDateTime &d) +void DownloadRule::setLastMatch(const QDateTime &d) { m_lastMatch = d; } -QDateTime RssDownloadRule::lastMatch() const +QDateTime DownloadRule::lastMatch() const { return m_lastMatch; } -void RssDownloadRule::setIgnoreDays(int d) +void DownloadRule::setIgnoreDays(int d) { m_ignoreDays = d; } -int RssDownloadRule::ignoreDays() const +int DownloadRule::ignoreDays() const { return m_ignoreDays; } -QString RssDownloadRule::mustContain() const +QString DownloadRule::mustContain() const { return m_mustContain.join(" "); } -QString RssDownloadRule::mustNotContain() const +QString DownloadRule::mustNotContain() const { return m_mustNotContain.join("|"); } -bool RssDownloadRule::useRegex() const +bool DownloadRule::useRegex() const { return m_useRegex; } -void RssDownloadRule::setUseRegex(bool enabled) +void DownloadRule::setUseRegex(bool enabled) { m_useRegex = enabled; } -QString RssDownloadRule::episodeFilter() const +QString DownloadRule::episodeFilter() const { return m_episodeFilter; } -void RssDownloadRule::setEpisodeFilter(const QString &e) +void DownloadRule::setEpisodeFilter(const QString &e) { m_episodeFilter = e; } -QStringList RssDownloadRule::findMatchingArticles(const RssFeedPtr &feed) const +QStringList DownloadRule::findMatchingArticles(const FeedPtr &feed) const { QStringList ret; - const RssArticleHash &feedArticles = feed->articleHash(); + const ArticleHash &feedArticles = feed->articleHash(); - RssArticleHash::ConstIterator artIt = feedArticles.begin(); - RssArticleHash::ConstIterator artItend = feedArticles.end(); + ArticleHash::ConstIterator artIt = feedArticles.begin(); + ArticleHash::ConstIterator artItend = feedArticles.end(); for ( ; artIt != artItend ; ++artIt) { const QString title = artIt.value()->title(); if (matches(title)) diff --git a/src/base/rss/rssdownloadrule.h b/src/base/rss/rssdownloadrule.h index 0bfc0616f..b22c14594 100644 --- a/src/base/rss/rssdownloadrule.h +++ b/src/base/rss/rssdownloadrule.h @@ -36,68 +36,71 @@ #include #include -class RssFeed; -typedef QSharedPointer RssFeedPtr; - -class RssDownloadRule; -typedef QSharedPointer RssDownloadRulePtr; - -class RssDownloadRule +namespace Rss { -public: - enum AddPausedState + class Feed; + typedef QSharedPointer FeedPtr; + + class DownloadRule; + typedef QSharedPointer DownloadRulePtr; + + class DownloadRule { - USE_GLOBAL = 0, - ALWAYS_PAUSED, - NEVER_PAUSED + public: + enum AddPausedState + { + USE_GLOBAL = 0, + ALWAYS_PAUSED, + NEVER_PAUSED + }; + + DownloadRule(); + + static DownloadRulePtr fromVariantHash(const QVariantHash &ruleHash); + QVariantHash toVariantHash() const; + bool matches(const QString &articleTitle) const; + void setMustContain(const QString &tokens); + void setMustNotContain(const QString &tokens); + QStringList rssFeeds() const; + void setRssFeeds(const QStringList &rssFeeds); + QString name() const; + void setName(const QString &name); + QString savePath() const; + void setSavePath(const QString &savePath); + AddPausedState addPaused() const; + void setAddPaused(const AddPausedState &aps); + QString label() const; + void setLabel(const QString &label); + bool isEnabled() const; + void setEnabled(bool enable); + void setLastMatch(const QDateTime &d); + QDateTime lastMatch() const; + void setIgnoreDays(int d); + int ignoreDays() const; + QString mustContain() const; + QString mustNotContain() const; + bool useRegex() const; + void setUseRegex(bool enabled); + QString episodeFilter() const; + void setEpisodeFilter(const QString &e); + QStringList findMatchingArticles(const FeedPtr &feed) const; + // Operators + bool operator==(const DownloadRule &other) const; + + private: + QString m_name; + QStringList m_mustContain; + QStringList m_mustNotContain; + QString m_episodeFilter; + QString m_savePath; + QString m_label; + bool m_enabled; + QStringList m_rssFeeds; + bool m_useRegex; + AddPausedState m_apstate; + QDateTime m_lastMatch; + int m_ignoreDays; }; - - RssDownloadRule(); - - static RssDownloadRulePtr fromVariantHash(const QVariantHash &ruleHash); - QVariantHash toVariantHash() const; - bool matches(const QString &articleTitle) const; - void setMustContain(const QString &tokens); - void setMustNotContain(const QString &tokens); - QStringList rssFeeds() const; - void setRssFeeds(const QStringList &rssFeeds); - QString name() const; - void setName(const QString &name); - QString savePath() const; - void setSavePath(const QString &savePath); - AddPausedState addPaused() const; - void setAddPaused(const AddPausedState &aps); - QString label() const; - void setLabel(const QString &label); - bool isEnabled() const; - void setEnabled(bool enable); - void setLastMatch(const QDateTime &d); - QDateTime lastMatch() const; - void setIgnoreDays(int d); - int ignoreDays() const; - QString mustContain() const; - QString mustNotContain() const; - bool useRegex() const; - void setUseRegex(bool enabled); - QString episodeFilter() const; - void setEpisodeFilter(const QString &e); - QStringList findMatchingArticles(const RssFeedPtr &feed) const; - // Operators - bool operator==(const RssDownloadRule &other) const; - -private: - QString m_name; - QStringList m_mustContain; - QStringList m_mustNotContain; - QString m_episodeFilter; - QString m_savePath; - QString m_label; - bool m_enabled; - QStringList m_rssFeeds; - bool m_useRegex; - AddPausedState m_apstate; - QDateTime m_lastMatch; - int m_ignoreDays; -}; +} #endif // RSSDOWNLOADRULE_H diff --git a/src/base/rss/rssdownloadrulelist.cpp b/src/base/rss/rssdownloadrulelist.cpp index 4f3e71bc4..55d52b2d8 100644 --- a/src/base/rss/rssdownloadrulelist.cpp +++ b/src/base/rss/rssdownloadrulelist.cpp @@ -36,23 +36,25 @@ #include "base/qinisettings.h" #include "rssdownloadrulelist.h" -RssDownloadRuleList::RssDownloadRuleList() +using namespace Rss; + +DownloadRuleList::DownloadRuleList() { loadRulesFromStorage(); } -RssDownloadRulePtr RssDownloadRuleList::findMatchingRule(const QString &feedUrl, const QString &articleTitle) const +DownloadRulePtr DownloadRuleList::findMatchingRule(const QString &feedUrl, const QString &articleTitle) const { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); QStringList ruleNames = m_feedRules.value(feedUrl); foreach (const QString &rule_name, ruleNames) { - RssDownloadRulePtr rule = m_rules[rule_name]; + DownloadRulePtr rule = m_rules[rule_name]; if (rule->isEnabled() && rule->matches(articleTitle)) return rule; } - return RssDownloadRulePtr(); + return DownloadRulePtr(); } -void RssDownloadRuleList::replace(RssDownloadRuleList *other) +void DownloadRuleList::replace(DownloadRuleList *other) { m_rules.clear(); m_feedRules.clear(); @@ -61,39 +63,39 @@ void RssDownloadRuleList::replace(RssDownloadRuleList *other) } } -void RssDownloadRuleList::saveRulesToStorage() +void DownloadRuleList::saveRulesToStorage() { QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); qBTRSS.setValue("download_rules", toVariantHash()); } -void RssDownloadRuleList::loadRulesFromStorage() +void DownloadRuleList::loadRulesFromStorage() { QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); loadRulesFromVariantHash(qBTRSS.value("download_rules").toHash()); } -QVariantHash RssDownloadRuleList::toVariantHash() const +QVariantHash DownloadRuleList::toVariantHash() const { QVariantHash ret; - foreach (const RssDownloadRulePtr &rule, m_rules.values()) { + foreach (const DownloadRulePtr &rule, m_rules.values()) { ret.insert(rule->name(), rule->toVariantHash()); } return ret; } -void RssDownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h) +void DownloadRuleList::loadRulesFromVariantHash(const QVariantHash &h) { QVariantHash::ConstIterator it = h.begin(); QVariantHash::ConstIterator itend = h.end(); for ( ; it != itend; ++it) { - RssDownloadRulePtr rule = RssDownloadRule::fromVariantHash(it.value().toHash()); + DownloadRulePtr rule = DownloadRule::fromVariantHash(it.value().toHash()); if (rule && !rule->name().isEmpty()) saveRule(rule); } } -void RssDownloadRuleList::saveRule(const RssDownloadRulePtr &rule) +void DownloadRuleList::saveRule(const DownloadRulePtr &rule) { qDebug() << Q_FUNC_INFO << rule->name(); Q_ASSERT(rule); @@ -109,22 +111,22 @@ void RssDownloadRuleList::saveRule(const RssDownloadRulePtr &rule) qDebug() << Q_FUNC_INFO << "EXIT"; } -void RssDownloadRuleList::removeRule(const QString &name) +void DownloadRuleList::removeRule(const QString &name) { qDebug() << Q_FUNC_INFO << name; if (!m_rules.contains(name)) return; - RssDownloadRulePtr rule = m_rules.take(name); + DownloadRulePtr rule = m_rules.take(name); // Update feedRules hashtable foreach (const QString &feedUrl, rule->rssFeeds()) { m_feedRules[feedUrl].removeOne(rule->name()); } } -void RssDownloadRuleList::renameRule(const QString &oldName, const QString &newName) +void DownloadRuleList::renameRule(const QString &oldName, const QString &newName) { if (!m_rules.contains(oldName)) return; - RssDownloadRulePtr rule = m_rules.take(oldName); + DownloadRulePtr rule = m_rules.take(oldName); rule->setName(newName); m_rules.insert(newName, rule); // Update feedRules hashtable @@ -133,22 +135,22 @@ void RssDownloadRuleList::renameRule(const QString &oldName, const QString &newN } } -RssDownloadRulePtr RssDownloadRuleList::getRule(const QString &name) const +DownloadRulePtr DownloadRuleList::getRule(const QString &name) const { return m_rules.value(name); } -QStringList RssDownloadRuleList::ruleNames() const +QStringList DownloadRuleList::ruleNames() const { return m_rules.keys(); } -bool RssDownloadRuleList::isEmpty() const +bool DownloadRuleList::isEmpty() const { return m_rules.isEmpty(); } -bool RssDownloadRuleList::serialize(const QString &path) +bool DownloadRuleList::serialize(const QString &path) { QFile f(path); if (f.open(QIODevice::WriteOnly)) { @@ -162,7 +164,7 @@ bool RssDownloadRuleList::serialize(const QString &path) return false; } -bool RssDownloadRuleList::unserialize(const QString &path) +bool DownloadRuleList::unserialize(const QString &path) { QFile f(path); if (f.open(QIODevice::ReadOnly)) { diff --git a/src/base/rss/rssdownloadrulelist.h b/src/base/rss/rssdownloadrulelist.h index 4afc8c3e4..2dc8c36f6 100644 --- a/src/base/rss/rssdownloadrulelist.h +++ b/src/base/rss/rssdownloadrulelist.h @@ -37,34 +37,37 @@ #include "rssdownloadrule.h" -class RssDownloadRuleList +namespace Rss { - Q_DISABLE_COPY(RssDownloadRuleList) + class DownloadRuleList + { + Q_DISABLE_COPY(DownloadRuleList) -public: - RssDownloadRuleList(); + public: + DownloadRuleList(); - RssDownloadRulePtr findMatchingRule(const QString &feedUrl, const QString &articleTitle) const; - // Operators - void saveRule(const RssDownloadRulePtr &rule); - void removeRule(const QString &name); - void renameRule(const QString &oldName, const QString &newName); - RssDownloadRulePtr getRule(const QString &name) const; - QStringList ruleNames() const; - bool isEmpty() const; - void saveRulesToStorage(); - bool serialize(const QString &path); - bool unserialize(const QString &path); - void replace(RssDownloadRuleList *other); + DownloadRulePtr findMatchingRule(const QString &feedUrl, const QString &articleTitle) const; + // Operators + void saveRule(const DownloadRulePtr &rule); + void removeRule(const QString &name); + void renameRule(const QString &oldName, const QString &newName); + DownloadRulePtr getRule(const QString &name) const; + QStringList ruleNames() const; + bool isEmpty() const; + void saveRulesToStorage(); + bool serialize(const QString &path); + bool unserialize(const QString &path); + void replace(DownloadRuleList *other); -private: - void loadRulesFromStorage(); - void loadRulesFromVariantHash(const QVariantHash &l); - QVariantHash toVariantHash() const; + private: + void loadRulesFromStorage(); + void loadRulesFromVariantHash(const QVariantHash &l); + QVariantHash toVariantHash() const; -private: - QHash m_rules; - QHash m_feedRules; -}; + private: + QHash m_rules; + QHash m_feedRules; + }; +} #endif // RSSDOWNLOADFILTERLIST_H diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index f52ccc1c4..457851676 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -47,12 +47,17 @@ #include "rssmanager.h" #include "rssfeed.h" -bool rssArticleDateRecentThan(const RssArticlePtr &left, const RssArticlePtr &right) +namespace Rss { - return left->date() > right->date(); + bool articleDateRecentThan(const ArticlePtr &left, const ArticlePtr &right) + { + return left->date() > right->date(); + } } -RssFeed::RssFeed(RssManager *manager, RssFolder *parent, const QString &url) +using namespace Rss; + +Feed::Feed(Manager *manager, Folder *parent, const QString &url) : m_manager(manager) , m_parent(parent) , m_url (QUrl::fromEncoded(url.toUtf8()).toString()) @@ -78,23 +83,23 @@ RssFeed::RssFeed(RssManager *manager, RssFolder *parent, const QString &url) loadItemsFromDisk(); } -RssFeed::~RssFeed() +Feed::~Feed() { if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) Utils::Fs::forceRemove(m_icon); } -RssFolder *RssFeed::parent() const +Folder *Feed::parent() const { return m_parent; } -void RssFeed::setParent(RssFolder *parent) +void Feed::setParent(Folder *parent) { m_parent = parent; } -void RssFeed::saveItemsToDisk() +void Feed::saveItemsToDisk() { qDebug() << Q_FUNC_INFO << m_url; if (!m_dirty) return; @@ -104,8 +109,8 @@ void RssFeed::saveItemsToDisk() QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QVariantList oldItems; - RssArticleHash::ConstIterator it = m_articles.begin(); - RssArticleHash::ConstIterator itend = m_articles.end(); + ArticleHash::ConstIterator it = m_articles.begin(); + ArticleHash::ConstIterator itend = m_articles.end(); for ( ; it != itend; ++it) { oldItems << it.value()->toHash(); } @@ -115,7 +120,7 @@ void RssFeed::saveItemsToDisk() qBTRSS.setValue("old_items", allOldItems); } -void RssFeed::loadItemsFromDisk() +void Feed::loadItemsFromDisk() { QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QHash allOldItems = qBTRSS.value("old_items", QHash()).toHash(); @@ -124,13 +129,13 @@ void RssFeed::loadItemsFromDisk() foreach (const QVariant &var_it, oldItems) { QVariantHash item = var_it.toHash(); - RssArticlePtr rssItem = RssArticle::fromHash(this, item); + ArticlePtr rssItem = Article::fromHash(this, item); if (rssItem) addArticle(rssItem); } } -void RssFeed::addArticle(const RssArticlePtr &article) +void Feed::addArticle(const ArticlePtr &article) { int maxArticles = Preferences::instance()->getRSSMaxArticlesPerFeed(); @@ -143,11 +148,11 @@ void RssFeed::addArticle(const RssArticlePtr &article) // Insert in hash table m_articles[article->guid()] = article; // Insertion sort - RssArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, rssArticleDateRecentThan); + ArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, articleDateRecentThan); m_articlesByDate.insert(lowerBound, article); int lbIndex = m_articlesByDate.indexOf(article); if (m_articlesByDate.size() > maxArticles) { - RssArticlePtr oldestArticle = m_articlesByDate.takeLast(); + ArticlePtr oldestArticle = m_articlesByDate.takeLast(); m_articles.remove(oldestArticle->guid()); // Update unreadCount if (!oldestArticle->isRead()) @@ -164,7 +169,7 @@ void RssFeed::addArticle(const RssArticlePtr &article) // m_articles.contains(article->guid()) // Try to download skipped articles if (Preferences::instance()->isRssDownloadingEnabled()) { - RssArticlePtr skipped = m_articles.value(article->guid(), RssArticlePtr()); + ArticlePtr skipped = m_articles.value(article->guid(), ArticlePtr()); if (skipped) { if (!skipped->isRead()) downloadArticleTorrentIfMatching(m_manager->downloadRules(), skipped); @@ -173,7 +178,7 @@ void RssFeed::addArticle(const RssArticlePtr &article) } } -bool RssFeed::refresh() +bool Feed::refresh() { if (m_loading) { qWarning() << Q_FUNC_INFO << "Feed" << displayName() << "is already being refreshed, ignoring request"; @@ -188,12 +193,12 @@ bool RssFeed::refresh() return true; } -QString RssFeed::id() const +QString Feed::id() const { return m_url; } -void RssFeed::removeAllSettings() +void Feed::removeAllSettings() { qDebug() << "Removing all settings / history for feed: " << m_url; QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); @@ -214,24 +219,24 @@ void RssFeed::removeAllSettings() } } -bool RssFeed::isLoading() const +bool Feed::isLoading() const { return m_loading; } -QString RssFeed::title() const +QString Feed::title() const { return m_title; } -void RssFeed::rename(const QString &newName) +void Feed::rename(const QString &newName) { qDebug() << "Renaming stream to" << newName; m_alias = newName; } // Return the alias if the stream has one, the url if it has no alias -QString RssFeed::displayName() const +QString Feed::displayName() const { if (!m_alias.isEmpty()) return m_alias; @@ -240,12 +245,12 @@ QString RssFeed::displayName() const return m_url; } -QString RssFeed::url() const +QString Feed::url() const { return m_url; } -QString RssFeed::iconPath() const +QString Feed::iconPath() const { if (m_inErrorState) return QLatin1String(":/icons/oxygen/unavailable.png"); @@ -253,31 +258,31 @@ QString RssFeed::iconPath() const return m_icon; } -bool RssFeed::hasCustomIcon() const +bool Feed::hasCustomIcon() const { return !m_icon.startsWith(":/"); } -void RssFeed::setIconPath(const QString &path) +void Feed::setIconPath(const QString &path) { if (!path.isEmpty() && QFile::exists(path)) m_icon = path; } -RssArticlePtr RssFeed::getItem(const QString &guid) const +ArticlePtr Feed::getItem(const QString &guid) const { return m_articles.value(guid); } -uint RssFeed::count() const +uint Feed::count() const { return m_articles.size(); } -void RssFeed::markAsRead() +void Feed::markAsRead() { - RssArticleHash::ConstIterator it = m_articles.begin(); - RssArticleHash::ConstIterator itend = m_articles.end(); + ArticleHash::ConstIterator it = m_articles.begin(); + ArticleHash::ConstIterator itend = m_articles.end(); for ( ; it != itend; ++it) { it.value()->markAsRead(); } @@ -285,32 +290,32 @@ void RssFeed::markAsRead() m_manager->forwardFeedInfosChanged(m_url, displayName(), 0); } -void RssFeed::markAsDirty(bool dirty) +void Feed::markAsDirty(bool dirty) { m_dirty = dirty; } -uint RssFeed::unreadCount() const +uint Feed::unreadCount() const { return m_unreadCount; } -RssArticleList RssFeed::articleListByDateDesc() const +ArticleList Feed::articleListByDateDesc() const { return m_articlesByDate; } -const RssArticleHash &RssFeed::articleHash() const +const ArticleHash &Feed::articleHash() const { return m_articles; } -RssArticleList RssFeed::unreadArticleListByDateDesc() const +ArticleList Feed::unreadArticleListByDateDesc() const { - RssArticleList unreadNews; + ArticleList unreadNews; - RssArticleList::ConstIterator it = m_articlesByDate.begin(); - RssArticleList::ConstIterator itend = m_articlesByDate.end(); + ArticleList::ConstIterator it = m_articlesByDate.begin(); + ArticleList::ConstIterator itend = m_articlesByDate.end(); for ( ; it != itend; ++it) { if (!(*it)->isRead()) unreadNews << *it; @@ -319,14 +324,14 @@ RssArticleList RssFeed::unreadArticleListByDateDesc() const } // download the icon from the address -QString RssFeed::iconUrl() const +QString Feed::iconUrl() const { // XXX: This works for most sites but it is not perfect return QString("http://") + QUrl(m_url).host() + QString("/favicon.ico"); } // read and store the downloaded rss' informations -void RssFeed::handleFinishedDownload(const QString &url, const QString &filePath) +void Feed::handleFinishedDownload(const QString &url, const QString &filePath) { if (url == m_url) { qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; @@ -340,7 +345,7 @@ void RssFeed::handleFinishedDownload(const QString &url, const QString &filePath } } -void RssFeed::handleDownloadFailure(const QString &url, const QString &error) +void Feed::handleDownloadFailure(const QString &url, const QString &error) { if (url != m_url) return; @@ -351,7 +356,7 @@ void RssFeed::handleDownloadFailure(const QString &url, const QString &error) qWarning() << "Reason:" << error; } -void RssFeed::handleFeedTitle(const QString &feedUrl, const QString &title) +void Feed::handleFeedTitle(const QString &feedUrl, const QString &title) { if (feedUrl != m_url) return; if (m_title == title) return; @@ -363,10 +368,10 @@ void RssFeed::handleFeedTitle(const QString &feedUrl, const QString &title) m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); } -void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList *rules, const RssArticlePtr &article) +void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article) { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - RssDownloadRulePtr matchingRule = rules->findMatchingRule(m_url, article->title()); + DownloadRulePtr matchingRule = rules->findMatchingRule(m_url, article->title()); if (!matchingRule) return; if (matchingRule->ignoreDays() > 0) { @@ -400,28 +405,28 @@ void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList *rules, const BitTorrent::AddTorrentParams params; params.savePath = matchingRule->savePath(); params.label = matchingRule->label(); - if (matchingRule->addPaused() == RssDownloadRule::ALWAYS_PAUSED) + if (matchingRule->addPaused() == DownloadRule::ALWAYS_PAUSED) params.addPaused = TriStateBool::True; - else if (matchingRule->addPaused() == RssDownloadRule::NEVER_PAUSED) + else if (matchingRule->addPaused() == DownloadRule::NEVER_PAUSED) params.addPaused = TriStateBool::False; BitTorrent::Session::instance()->addTorrent(torrentUrl, params); } -void RssFeed::recheckRssItemsForDownload() +void Feed::recheckRssItemsForDownload() { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - RssDownloadRuleList *rules = m_manager->downloadRules(); - foreach (const RssArticlePtr &article, m_articlesByDate) { + DownloadRuleList *rules = m_manager->downloadRules(); + foreach (const ArticlePtr &article, m_articlesByDate) { if (!article->isRead()) downloadArticleTorrentIfMatching(rules, article); } } -void RssFeed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleData) +void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleData) { if (feedUrl != m_url) return; - RssArticlePtr article = RssArticle::fromHash(this, articleData); + ArticlePtr article = Article::fromHash(this, articleData); if (article.isNull()) { qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << feedUrl; return; @@ -435,7 +440,7 @@ void RssFeed::handleNewArticle(const QString &feedUrl, const QVariantHash &artic //m_manager->forwardFeedContentChanged(m_url); } -void RssFeed::handleFeedParsingFinished(const QString &feedUrl, const QString &error) +void Feed::handleFeedParsingFinished(const QString &feedUrl, const QString &error) { if (feedUrl != m_url) return; @@ -454,12 +459,12 @@ void RssFeed::handleFeedParsingFinished(const QString &feedUrl, const QString &e saveItemsToDisk(); } -void RssFeed::handleArticleStateChanged() +void Feed::handleArticleStateChanged() { m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); } -void RssFeed::decrementUnreadCount() +void Feed::decrementUnreadCount() { --m_unreadCount; } diff --git a/src/base/rss/rssfeed.h b/src/base/rss/rssfeed.h index 70fffa6b5..30fd45830 100644 --- a/src/base/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -40,78 +40,81 @@ #include "rssfile.h" -class RssFolder; -class RssFeed; -class RssManager; -class RssDownloadRuleList; - -typedef QHash RssArticleHash; -typedef QSharedPointer RssFeedPtr; -typedef QList RssFeedList; - -bool rssArticleDateRecentThan(const RssArticlePtr &left, const RssArticlePtr &right); - -class RssFeed: public QObject, public RssFile +namespace Rss { - Q_OBJECT + class Folder; + class Feed; + class Manager; + class DownloadRuleList; -public: - RssFeed(RssManager *manager, RssFolder *parent, const QString &url); - ~RssFeed(); + typedef QHash ArticleHash; + typedef QSharedPointer FeedPtr; + typedef QList FeedList; - RssFolder *parent() const; - void setParent(RssFolder *parent); - bool refresh(); - QString id() const; - void removeAllSettings(); - void saveItemsToDisk(); - bool isLoading() const; - QString title() const; - void rename(const QString &newName); - QString displayName() const; - QString url() const; - QString iconPath() const; - bool hasCustomIcon() const; - void setIconPath(const QString &pathHierarchy); - RssArticlePtr getItem(const QString &guid) const; - uint count() const; - void markAsRead(); - void markAsDirty(bool dirty = true); - uint unreadCount() const; - RssArticleList articleListByDateDesc() const; - const RssArticleHash &articleHash() const; - RssArticleList unreadArticleListByDateDesc() const; - void decrementUnreadCount(); - void recheckRssItemsForDownload(); + bool articleDateRecentThan(const ArticlePtr &left, const ArticlePtr &right); -private slots: - void handleFinishedDownload(const QString &url, const QString &filePath); - void handleDownloadFailure(const QString &url, const QString &error); - void handleFeedTitle(const QString &feedUrl, const QString &title); - void handleNewArticle(const QString &feedUrl, const QVariantHash &article); - void handleFeedParsingFinished(const QString &feedUrl, const QString &error); - void handleArticleStateChanged(); + class Feed: public QObject, public File + { + Q_OBJECT -private: - QString iconUrl() const; - void loadItemsFromDisk(); - void addArticle(const RssArticlePtr &article); - void downloadArticleTorrentIfMatching(RssDownloadRuleList *rules, const RssArticlePtr &article); + public: + Feed(Manager *manager, Folder *parent, const QString &url); + ~Feed(); -private: - RssManager *m_manager; - RssArticleHash m_articles; - RssArticleList m_articlesByDate; // Articles sorted by date (more recent first) - RssFolder *m_parent; - QString m_title; - QString m_url; - QString m_alias; - QString m_icon; - QString m_iconUrl; - uint m_unreadCount; - bool m_dirty; - bool m_inErrorState; - bool m_loading; -}; + Folder *parent() const; + void setParent(Folder *parent); + bool refresh(); + QString id() const; + void removeAllSettings(); + void saveItemsToDisk(); + bool isLoading() const; + QString title() const; + void rename(const QString &newName); + QString displayName() const; + QString url() const; + QString iconPath() const; + bool hasCustomIcon() const; + void setIconPath(const QString &pathHierarchy); + ArticlePtr getItem(const QString &guid) const; + uint count() const; + void markAsRead(); + void markAsDirty(bool dirty = true); + uint unreadCount() const; + ArticleList articleListByDateDesc() const; + const ArticleHash &articleHash() const; + ArticleList unreadArticleListByDateDesc() const; + void decrementUnreadCount(); + void recheckRssItemsForDownload(); + + private slots: + void handleFinishedDownload(const QString &url, const QString &filePath); + void handleDownloadFailure(const QString &url, const QString &error); + void handleFeedTitle(const QString &feedUrl, const QString &title); + void handleNewArticle(const QString &feedUrl, const QVariantHash &article); + void handleFeedParsingFinished(const QString &feedUrl, const QString &error); + void handleArticleStateChanged(); + + private: + QString iconUrl() const; + void loadItemsFromDisk(); + void addArticle(const ArticlePtr &article); + void downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article); + + private: + Manager *m_manager; + ArticleHash m_articles; + ArticleList m_articlesByDate; // Articles sorted by date (more recent first) + Folder *m_parent; + QString m_title; + QString m_url; + QString m_alias; + QString m_icon; + QString m_iconUrl; + uint m_unreadCount; + bool m_dirty; + bool m_inErrorState; + bool m_loading; + }; +} #endif // RSSFEED_H diff --git a/src/base/rss/rssfile.cpp b/src/base/rss/rssfile.cpp index 187dfd303..1f911fdda 100644 --- a/src/base/rss/rssfile.cpp +++ b/src/base/rss/rssfile.cpp @@ -32,9 +32,11 @@ #include "rssfolder.h" #include "rssfile.h" -RssFile::~RssFile() {} +using namespace Rss; -QStringList RssFile::pathHierarchy() const +File::~File() {} + +QStringList File::pathHierarchy() const { QStringList path; if (parent()) diff --git a/src/base/rss/rssfile.h b/src/base/rss/rssfile.h index ff2f0d913..0b5452624 100644 --- a/src/base/rss/rssfile.h +++ b/src/base/rss/rssfile.h @@ -36,42 +36,45 @@ #include #include -class RssFolder; -class RssFile; -class RssArticle; - -typedef QSharedPointer RssFilePtr; -typedef QSharedPointer RssArticlePtr; -typedef QList RssArticleList; -typedef QList RssFileList; - -/** - * Parent interface for RssFolder and RssFeed. - */ -class RssFile +namespace Rss { -public: - virtual ~RssFile(); + class Folder; + class File; + class Article; - virtual uint unreadCount() const = 0; - virtual QString displayName() const = 0; - virtual QString id() const = 0; - virtual QString iconPath() const = 0; - virtual void rename(const QString &newName) = 0; - virtual void markAsRead() = 0; - virtual RssFolder *parent() const = 0; - virtual void setParent(RssFolder *parent) = 0; - virtual bool refresh() = 0; - virtual RssArticleList articleListByDateDesc() const = 0; - virtual RssArticleList unreadArticleListByDateDesc() const = 0; - virtual void removeAllSettings() = 0; - virtual void saveItemsToDisk() = 0; - virtual void recheckRssItemsForDownload() = 0; + typedef QSharedPointer FilePtr; + typedef QSharedPointer
ArticlePtr; + typedef QList ArticleList; + typedef QList FileList; - QStringList pathHierarchy() const; + /** + * Parent interface for Rss::Folder and Rss::Feed. + */ + class File + { + public: + virtual ~File(); -protected: - uint m_unreadCount; -}; + virtual uint unreadCount() const = 0; + virtual QString displayName() const = 0; + virtual QString id() const = 0; + virtual QString iconPath() const = 0; + virtual void rename(const QString &newName) = 0; + virtual void markAsRead() = 0; + virtual Folder *parent() const = 0; + virtual void setParent(Folder *parent) = 0; + virtual bool refresh() = 0; + virtual ArticleList articleListByDateDesc() const = 0; + virtual ArticleList unreadArticleListByDateDesc() const = 0; + virtual void removeAllSettings() = 0; + virtual void saveItemsToDisk() = 0; + virtual void recheckRssItemsForDownload() = 0; + + QStringList pathHierarchy() const; + + protected: + uint m_unreadCount; + }; +} #endif // RSSFILE_H diff --git a/src/base/rss/rssfolder.cpp b/src/base/rss/rssfolder.cpp index ac5e2b84d..0a054c9b8 100644 --- a/src/base/rss/rssfolder.cpp +++ b/src/base/rss/rssfolder.cpp @@ -38,61 +38,63 @@ #include "rssarticle.h" #include "rssfolder.h" -RssFolder::RssFolder(RssFolder *parent, const QString &name) +using namespace Rss; + +Folder::Folder(Folder *parent, const QString &name) : m_parent(parent) , m_name(name) { } -RssFolder::~RssFolder() {} +Folder::~Folder() {} -RssFolder *RssFolder::parent() const +Folder *Folder::parent() const { return m_parent; } -void RssFolder::setParent(RssFolder *parent) +void Folder::setParent(Folder *parent) { m_parent = parent; } -uint RssFolder::unreadCount() const +uint Folder::unreadCount() const { uint nbUnread = 0; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) nbUnread += it.value()->unreadCount(); return nbUnread; } -void RssFolder::removeChild(const QString &childId) +void Folder::removeChild(const QString &childId) { if (m_children.contains(childId)) { - RssFilePtr child = m_children.take(childId); + FilePtr child = m_children.take(childId); child->removeAllSettings(); } } -RssFolderPtr RssFolder::addFolder(const QString &name) +FolderPtr Folder::addFolder(const QString &name) { - RssFolderPtr subfolder; + FolderPtr subfolder; if (!m_children.contains(name)) { - subfolder = RssFolderPtr(new RssFolder(this, name)); + subfolder = FolderPtr(new Folder(this, name)); m_children[name] = subfolder; } else { - subfolder = qSharedPointerDynamicCast(m_children.value(name)); + subfolder = qSharedPointerDynamicCast(m_children.value(name)); } return subfolder; } -RssFeedPtr RssFolder::addStream(RssManager *manager, const QString &url) +FeedPtr Folder::addStream(Manager *manager, const QString &url) { qDebug() << Q_FUNC_INFO << manager << url; - RssFeedPtr stream(new RssFeed(manager, this, url)); + FeedPtr stream(new Feed(manager, this, url)); Q_ASSERT(stream); qDebug() << "Stream URL is " << stream->url(); Q_ASSERT(!m_children.contains(stream->url())); @@ -102,10 +104,10 @@ RssFeedPtr RssFolder::addStream(RssManager *manager, const QString &url) } // Refresh All Children -bool RssFolder::refresh() +bool Folder::refresh() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); bool refreshed = false; for ( ; it != itend; ++it) { if (it.value()->refresh()) @@ -114,47 +116,47 @@ bool RssFolder::refresh() return refreshed; } -RssArticleList RssFolder::articleListByDateDesc() const +ArticleList Folder::articleListByDateDesc() const { - RssArticleList news; + ArticleList news; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { int n = news.size(); news << it.value()->articleListByDateDesc(); - std::inplace_merge(news.begin(), news.begin() + n, news.end(), rssArticleDateRecentThan); + std::inplace_merge(news.begin(), news.begin() + n, news.end(), articleDateRecentThan); } return news; } -RssArticleList RssFolder::unreadArticleListByDateDesc() const +ArticleList Folder::unreadArticleListByDateDesc() const { - RssArticleList unreadNews; + ArticleList unreadNews; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { int n = unreadNews.size(); unreadNews << it.value()->unreadArticleListByDateDesc(); - std::inplace_merge(unreadNews.begin(), unreadNews.begin() + n, unreadNews.end(), rssArticleDateRecentThan); + std::inplace_merge(unreadNews.begin(), unreadNews.begin() + n, unreadNews.end(), articleDateRecentThan); } return unreadNews; } -RssFileList RssFolder::getContent() const +FileList Folder::getContent() const { return m_children.values(); } -uint RssFolder::getNbFeeds() const +uint Folder::getNbFeeds() const { uint nbFeeds = 0; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { - if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) + if (FolderPtr folder = qSharedPointerDynamicCast(it.value())) nbFeeds += folder->getNbFeeds(); else ++nbFeeds; // Feed @@ -162,12 +164,12 @@ uint RssFolder::getNbFeeds() const return nbFeeds; } -QString RssFolder::displayName() const +QString Folder::displayName() const { return m_name; } -void RssFolder::rename(const QString &newName) +void Folder::rename(const QString &newName) { if (m_name == newName) return; @@ -180,56 +182,56 @@ void RssFolder::rename(const QString &newName) } } -void RssFolder::markAsRead() +void Folder::markAsRead() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { it.value()->markAsRead(); } } -RssFeedList RssFolder::getAllFeeds() const +FeedList Folder::getAllFeeds() const { - RssFeedList streams; + FeedList streams; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) + if (FeedPtr feed = qSharedPointerDynamicCast(it.value())) streams << feed; - else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) + else if (FolderPtr folder = qSharedPointerDynamicCast(it.value())) streams << folder->getAllFeeds(); } return streams; } -QHash RssFolder::getAllFeedsAsHash() const +QHash Folder::getAllFeedsAsHash() const { - QHash ret; + QHash ret; - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(it.value())) { + if (FeedPtr feed = qSharedPointerDynamicCast(it.value())) { qDebug() << Q_FUNC_INFO << feed->url(); ret[feed->url()] = feed; } - else if (RssFolderPtr folder = qSharedPointerDynamicCast(it.value())) { + else if (FolderPtr folder = qSharedPointerDynamicCast(it.value())) { ret.unite(folder->getAllFeedsAsHash()); } } return ret; } -void RssFolder::addFile(const RssFilePtr &item) +void Folder::addFile(const FilePtr &item) { - if (RssFeedPtr feed = qSharedPointerDynamicCast(item)) { + if (FeedPtr feed = qSharedPointerDynamicCast(item)) { Q_ASSERT(!m_children.contains(feed->url())); m_children[feed->url()] = item; qDebug("Added feed %s to folder ./%s", qPrintable(feed->url()), qPrintable(m_name)); } - else if (RssFolderPtr folder = qSharedPointerDynamicCast(item)) { + else if (FolderPtr folder = qSharedPointerDynamicCast(item)) { Q_ASSERT(!m_children.contains(folder->displayName())); m_children[folder->displayName()] = item; qDebug("Added folder %s to folder ./%s", qPrintable(folder->displayName()), qPrintable(m_name)); @@ -238,56 +240,56 @@ void RssFolder::addFile(const RssFilePtr &item) item->setParent(this); } -void RssFolder::removeAllItems() +void Folder::removeAllItems() { m_children.clear(); } -void RssFolder::removeAllSettings() +void Folder::removeAllSettings() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) it.value()->removeAllSettings(); } -void RssFolder::saveItemsToDisk() +void Folder::saveItemsToDisk() { - foreach (const RssFilePtr &child, m_children.values()) + foreach (const FilePtr &child, m_children.values()) child->saveItemsToDisk(); } -QString RssFolder::id() const +QString Folder::id() const { return m_name; } -QString RssFolder::iconPath() const +QString Folder::iconPath() const { return IconProvider::instance()->getIconPath("inode-directory"); } -bool RssFolder::hasChild(const QString &childId) +bool Folder::hasChild(const QString &childId) { return m_children.contains(childId); } -void RssFolder::renameChildFolder(const QString &oldName, const QString &newName) +void Folder::renameChildFolder(const QString &oldName, const QString &newName) { Q_ASSERT(m_children.contains(oldName)); - RssFilePtr folder = m_children.take(oldName); + FilePtr folder = m_children.take(oldName); m_children[newName] = folder; } -RssFilePtr RssFolder::takeChild(const QString &childId) +FilePtr Folder::takeChild(const QString &childId) { return m_children.take(childId); } -void RssFolder::recheckRssItemsForDownload() +void Folder::recheckRssItemsForDownload() { - RssFileHash::ConstIterator it = m_children.begin(); - RssFileHash::ConstIterator itend = m_children.end(); + FileHash::ConstIterator it = m_children.begin(); + FileHash::ConstIterator itend = m_children.end(); for ( ; it != itend; ++it) it.value()->recheckRssItemsForDownload(); } diff --git a/src/base/rss/rssfolder.h b/src/base/rss/rssfolder.h index 137ee5e9c..78ff21401 100644 --- a/src/base/rss/rssfolder.h +++ b/src/base/rss/rssfolder.h @@ -37,56 +37,59 @@ #include "rssfile.h" -class RssFolder; -class RssFeed; -class RssManager; - -typedef QHash RssFileHash; -typedef QSharedPointer RssFeedPtr; -typedef QSharedPointer RssFolderPtr; -typedef QList RssFeedList; - -class RssFolder: public QObject, public RssFile +namespace Rss { - Q_OBJECT + class Folder; + class Feed; + class Manager; -public: - explicit RssFolder(RssFolder *parent = 0, const QString &name = QString()); - ~RssFolder(); + typedef QHash FileHash; + typedef QSharedPointer FeedPtr; + typedef QSharedPointer FolderPtr; + typedef QList FeedList; - RssFolder *parent() const; - void setParent(RssFolder *parent); - uint unreadCount() const; - RssFeedPtr addStream(RssManager *manager, const QString &url); - RssFolderPtr addFolder(const QString &name); - uint getNbFeeds() const; - RssFileList getContent() const; - RssFeedList getAllFeeds() const; - QHash getAllFeedsAsHash() const; - QString displayName() const; - QString id() const; - QString iconPath() const; - bool hasChild(const QString &childId); - RssArticleList articleListByDateDesc() const; - RssArticleList unreadArticleListByDateDesc() const; - void removeAllSettings(); - void saveItemsToDisk(); - void removeAllItems(); - void renameChildFolder(const QString &oldName, const QString &newName); - RssFilePtr takeChild(const QString &childId); - void recheckRssItemsForDownload(); + class Folder: public QObject, public File + { + Q_OBJECT -public slots: - bool refresh(); - void addFile(const RssFilePtr &item); - void removeChild(const QString &childId); - void rename(const QString &newName); - void markAsRead(); + public: + explicit Folder(Folder *parent = 0, const QString &name = QString()); + ~Folder(); -private: - RssFolder *m_parent; - QString m_name; - RssFileHash m_children; -}; + Folder *parent() const; + void setParent(Folder *parent); + uint unreadCount() const; + FeedPtr addStream(Manager *manager, const QString &url); + FolderPtr addFolder(const QString &name); + uint getNbFeeds() const; + FileList getContent() const; + FeedList getAllFeeds() const; + QHash getAllFeedsAsHash() const; + QString displayName() const; + QString id() const; + QString iconPath() const; + bool hasChild(const QString &childId); + ArticleList articleListByDateDesc() const; + ArticleList unreadArticleListByDateDesc() const; + void removeAllSettings(); + void saveItemsToDisk(); + void removeAllItems(); + void renameChildFolder(const QString &oldName, const QString &newName); + FilePtr takeChild(const QString &childId); + void recheckRssItemsForDownload(); + + public slots: + bool refresh(); + void addFile(const FilePtr &item); + void removeChild(const QString &childId); + void rename(const QString &newName); + void markAsRead(); + + private: + Folder *m_parent; + QString m_name; + FileHash m_children; + }; +} #endif // RSSFOLDER_H diff --git a/src/base/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp index 248caabc8..17e665661 100644 --- a/src/base/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -41,16 +41,18 @@ static const int MSECS_PER_MIN = 60000; -RssManager::RssManager() - : m_downloadRules(new RssDownloadRuleList) - , m_rssParser(new RssParser(this)) +using namespace Rss; + +Manager::Manager() + : m_downloadRules(new DownloadRuleList) + , m_rssParser(new Parser(this)) { connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN); } -RssManager::~RssManager() +Manager::~Manager() { qDebug("Deleting RSSManager..."); delete m_downloadRules; @@ -60,12 +62,12 @@ RssManager::~RssManager() qDebug("RSSManager deleted"); } -RssParser *RssManager::rssParser() const +Parser *Manager::rssParser() const { return m_rssParser; } -void RssManager::updateRefreshInterval(uint val) +void Manager::updateRefreshInterval(uint val) { if (m_refreshInterval != val) { m_refreshInterval = val; @@ -74,7 +76,7 @@ void RssManager::updateRefreshInterval(uint val) } } -void RssManager::loadStreamList() +void Manager::loadStreamList() { const Preferences *const pref = Preferences::instance(); const QStringList streamsUrl = pref->getRssFeedsUrls(); @@ -93,14 +95,14 @@ void RssManager::loadStreamList() const QString feedUrl = path.takeLast(); qDebug() << "Feed URL:" << feedUrl; // Create feed path (if it does not exists) - RssFolder *feedParent = this; + Folder *feedParent = this; foreach (const QString &folderName, path) { qDebug() << "Adding parent folder:" << folderName; feedParent = feedParent->addFolder(folderName).data(); } // Create feed qDebug() << "Adding feed to parent folder"; - RssFeedPtr stream = feedParent->addStream(this, feedUrl); + FeedPtr stream = feedParent->addStream(this, feedUrl); const QString &alias = aliases[i]; if (!alias.isEmpty()) stream->rename(alias); @@ -109,24 +111,24 @@ void RssManager::loadStreamList() qDebug("NB RSS streams loaded: %d", streamsUrl.size()); } -void RssManager::forwardFeedContentChanged(const QString &url) +void Manager::forwardFeedContentChanged(const QString &url) { emit feedContentChanged(url); } -void RssManager::forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount) +void Manager::forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount) { emit feedInfosChanged(url, displayName, unreadCount); } -void RssManager::forwardFeedIconChanged(const QString &url, const QString &iconPath) +void Manager::forwardFeedIconChanged(const QString &url, const QString &iconPath) { emit feedIconChanged(url, iconPath); } -void RssManager::moveFile(const RssFilePtr &file, const RssFolderPtr &destinationFolder) +void Manager::moveFile(const FilePtr &file, const FolderPtr &destinationFolder) { - RssFolder *srcFolder = file->parent(); + Folder *srcFolder = file->parent(); if (destinationFolder != srcFolder) { // Remove reference in old folder srcFolder->takeChild(file->id()); @@ -138,12 +140,12 @@ void RssManager::moveFile(const RssFilePtr &file, const RssFolderPtr &destinatio } } -void RssManager::saveStreamList() const +void Manager::saveStreamList() const { QStringList streamsUrl; QStringList aliases; - RssFeedList streams = getAllFeeds(); - foreach (const RssFeedPtr &stream, streams) { + FeedList streams = getAllFeeds(); + foreach (const FeedPtr &stream, streams) { // This backslash has nothing to do with path handling QString streamPath = stream->pathHierarchy().join("\\"); if (streamPath.isNull()) @@ -157,7 +159,7 @@ void RssManager::saveStreamList() const pref->setRssFeedsAliases(aliases); } -RssDownloadRuleList *RssManager::downloadRules() const +DownloadRuleList *Manager::downloadRules() const { Q_ASSERT(m_downloadRules); return m_downloadRules; diff --git a/src/base/rss/rssmanager.h b/src/base/rss/rssmanager.h index 5fff13a65..515d45a64 100644 --- a/src/base/rss/rssmanager.h +++ b/src/base/rss/rssmanager.h @@ -37,42 +37,45 @@ #include "rssfolder.h" -class RssDownloadRuleList; -class RssParser; -class RssManager; - -typedef QSharedPointer RssManagerPtr; - -class RssManager: public RssFolder +namespace Rss { - Q_OBJECT + class DownloadRuleList; + class Parser; + class Manager; -public: - RssManager(); - ~RssManager(); + typedef QSharedPointer ManagerPtr; - RssParser *rssParser() const; - RssDownloadRuleList *downloadRules() const; + class Manager: public Folder + { + Q_OBJECT -public slots: - void loadStreamList(); - void saveStreamList() const; - void forwardFeedContentChanged(const QString &url); - void forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); - void forwardFeedIconChanged(const QString &url, const QString &iconPath); - void moveFile(const RssFilePtr &file, const RssFolderPtr &destinationFolder); - void updateRefreshInterval(uint val); + public: + Manager(); + ~Manager(); -signals: - void feedContentChanged(const QString &url); - void feedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); - void feedIconChanged(const QString &url, const QString &iconPath); + Parser *rssParser() const; + DownloadRuleList *downloadRules() const; -private: - QTimer m_refreshTimer; - uint m_refreshInterval; - RssDownloadRuleList *m_downloadRules; - RssParser *m_rssParser; -}; + public slots: + void loadStreamList(); + void saveStreamList() const; + void forwardFeedContentChanged(const QString &url); + void forwardFeedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); + void forwardFeedIconChanged(const QString &url, const QString &iconPath); + void moveFile(const FilePtr &file, const FolderPtr &destinationFolder); + void updateRefreshInterval(uint val); + + signals: + void feedContentChanged(const QString &url); + void feedInfosChanged(const QString &url, const QString &displayName, uint unreadCount); + void feedIconChanged(const QString &url, const QString &iconPath); + + private: + QTimer m_refreshTimer; + uint m_refreshInterval; + DownloadRuleList *m_downloadRules; + Parser *m_rssParser; + }; +} #endif // RSSMANAGER_H diff --git a/src/base/rss/rssparser.cpp b/src/base/rss/rssparser.cpp index 5f72d6c6a..18b138c41 100644 --- a/src/base/rss/rssparser.cpp +++ b/src/base/rss/rssparser.cpp @@ -37,11 +37,14 @@ #include "base/utils/fs.h" #include "rssparser.h" -struct ParsingJob +namespace Rss { - QString feedUrl; - QString filePath; -}; + struct ParsingJob + { + QString feedUrl; + QString filePath; + }; +} static const char shortDay[][4] = { "Mon", "Tue", "Wed", @@ -61,8 +64,24 @@ static const char shortMonth[][4] = { "Sep", "Oct", "Nov", "Dec" }; +using namespace Rss; + +Parser::Parser(QObject *parent) + : QThread(parent) + , m_running(true) +{ + start(); +} + +Parser::~Parser() +{ + m_running = false; + m_waitCondition.wakeOne(); + wait(); +} + // Ported to Qt from KDElibs4 -QDateTime RssParser::parseDate(const QString &string) +QDateTime Parser::parseDate(const QString &string) { const QString str = string.trimmed(); if (str.isEmpty()) @@ -208,21 +227,7 @@ QDateTime RssParser::parseDate(const QString &string) return result; } -RssParser::RssParser(QObject *parent) - : QThread(parent) - , m_running(true) -{ - start(); -} - -RssParser::~RssParser() -{ - m_running = false; - m_waitCondition.wakeOne(); - wait(); -} - -void RssParser::parseRssFile(const QString &feedUrl, const QString &filePath) +void Parser::parseRssFile(const QString &feedUrl, const QString &filePath) { qDebug() << Q_FUNC_INFO << feedUrl << filePath; m_mutex.lock(); @@ -236,14 +241,14 @@ void RssParser::parseRssFile(const QString &feedUrl, const QString &filePath) m_mutex.unlock(); } -void RssParser::clearFeedData(const QString &feedUrl) +void Parser::clearFeedData(const QString &feedUrl) { m_mutex.lock(); m_lastBuildDates.remove(feedUrl); m_mutex.unlock(); } -void RssParser::run() +void Parser::run() { while (m_running) { m_mutex.lock(); @@ -261,7 +266,7 @@ void RssParser::run() } } -void RssParser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) { QVariantHash article; @@ -325,7 +330,7 @@ void RssParser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) emit newArticle(feedUrl, article); } -void RssParser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) { qDebug() << Q_FUNC_INFO << feedUrl; Q_ASSERT(xml.isStartElement() && xml.name() == "channel"); @@ -356,7 +361,7 @@ void RssParser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) } } -void RssParser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl) +void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl) { QVariantHash article; bool doubleContent = false; @@ -446,7 +451,7 @@ void RssParser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, emit newArticle(feedUrl, article); } -void RssParser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) { qDebug() << Q_FUNC_INFO << feedUrl; Q_ASSERT(xml.isStartElement() && xml.name() == "feed"); @@ -480,7 +485,7 @@ void RssParser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) } // read and create items from a rss document -void RssParser::parseFeed(const ParsingJob &job) +void Parser::parseFeed(const ParsingJob &job) { qDebug() << Q_FUNC_INFO << job.feedUrl << job.filePath; QFile fileRss(job.filePath); @@ -534,7 +539,7 @@ void RssParser::parseFeed(const ParsingJob &job) Utils::Fs::forceRemove(job.filePath); } -void RssParser::reportFailure(const ParsingJob &job, const QString &error) +void Parser::reportFailure(const ParsingJob &job, const QString &error) { emit feedParsingFinished(job.feedUrl, error); Utils::Fs::forceRemove(job.filePath); diff --git a/src/base/rss/rssparser.h b/src/base/rss/rssparser.h index 42b52d9bb..0a02ca773 100644 --- a/src/base/rss/rssparser.h +++ b/src/base/rss/rssparser.h @@ -38,42 +38,45 @@ #include "rssarticle.h" -struct ParsingJob; - -class RssParser: public QThread +namespace Rss { - Q_OBJECT + struct ParsingJob; -public: - explicit RssParser(QObject *parent = 0); - virtual ~RssParser(); + class Parser: public QThread + { + Q_OBJECT -signals: - void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); - void feedTitle(const QString &feedUrl, const QString &title); - void feedParsingFinished(const QString &feedUrl, const QString &error); + public: + explicit Parser(QObject *parent = 0); + virtual ~Parser(); -public slots: - void parseRssFile(const QString &feedUrl, const QString &filePath); - void clearFeedData(const QString &feedUrl); + signals: + void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); + void feedTitle(const QString &feedUrl, const QString &title); + void feedParsingFinished(const QString &feedUrl, const QString &error); -protected: - virtual void run(); + public slots: + void parseRssFile(const QString &feedUrl, const QString &filePath); + void clearFeedData(const QString &feedUrl); -private: - static QDateTime parseDate(const QString &string); - void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); - void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); - void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseFeed(const ParsingJob &job); - void reportFailure(const ParsingJob &job, const QString &error); + protected: + virtual void run(); - bool m_running; - QMutex m_mutex; - QQueue m_queue; - QWaitCondition m_waitCondition; - QHash m_lastBuildDates; // Optimization -}; + private: + static QDateTime parseDate(const QString &string); + void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); + void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); + void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseFeed(const ParsingJob &job); + void reportFailure(const ParsingJob &job, const QString &error); + + bool m_running; + QMutex m_mutex; + QQueue m_queue; + QWaitCondition m_waitCondition; + QHash m_lastBuildDates; // Optimization + }; +} #endif // RSSPARSER_H diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index f087c9bec..98ef89088 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -45,7 +45,7 @@ #include "base/utils/fs.h" #include "base/utils/string.h" -AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : +AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : QDialog(parent), ui(new Ui::AutomatedRssDownloader), m_manager(manager), m_editedRule(0) @@ -68,7 +68,7 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& m ok = connect(ui->listRules, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayRulesListMenu(const QPoint&))); Q_ASSERT(ok); m_ruleList = manager.toStrongRef()->downloadRules(); - m_editableRuleList = new RssDownloadRuleList; // Read rule list from disk + m_editableRuleList = new Rss::DownloadRuleList; // Read rule list from disk m_episodeValidator = new QRegExpValidator( QRegExp("^(^[1-9]{1,1}\\d{0,3}x([1-9]{1,1}\\d{0,3}(-([1-9]{1,1}\\d{0,3})?)?;){1,}){1,1}", Qt::CaseInsensitive), @@ -199,7 +199,7 @@ void AutomatedRssDownloader::updateFeedList() const QString feed_url = item->data(Qt::UserRole).toString(); bool all_enabled = false; foreach (const QListWidgetItem *ruleItem, ui->listRules->selectedItems()) { - RssDownloadRulePtr rule = m_editableRuleList->getRule(ruleItem->text()); + Rss::DownloadRulePtr rule = m_editableRuleList->getRule(ruleItem->text()); if (!rule) continue; qDebug() << "Rule" << rule->name() << "affects" << rule->rssFeeds().size() << "feeds."; foreach (QString test, rule->rssFeeds()) { @@ -238,7 +238,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox() const QList selection = ui->listRules->selectedItems(); if (selection.count() == 1) { m_editedRule = selection.first(); - RssDownloadRulePtr rule = getCurrentRule(); + Rss::DownloadRulePtr rule = getCurrentRule(); if (rule) { ui->lineContains->setText(rule->mustContain()); ui->lineNotContains->setText(rule->mustNotContain()); @@ -300,12 +300,12 @@ void AutomatedRssDownloader::clearRuleDefinitionBox() updateMustNotLineValidity(); } -RssDownloadRulePtr AutomatedRssDownloader::getCurrentRule() const +Rss::DownloadRulePtr AutomatedRssDownloader::getCurrentRule() const { QListWidgetItem * current_item = ui->listRules->currentItem(); if (current_item) return m_editableRuleList->getRule(current_item->text()); - return RssDownloadRulePtr(); + return Rss::DownloadRulePtr(); } void AutomatedRssDownloader::initLabelCombobox() @@ -326,9 +326,9 @@ void AutomatedRssDownloader::saveEditedRule() qDebug() << "Probably removed the item, no need to save it"; return; } - RssDownloadRulePtr rule = m_editableRuleList->getRule(m_editedRule->text()); + Rss::DownloadRulePtr rule = m_editableRuleList->getRule(m_editedRule->text()); if (!rule) { - rule = RssDownloadRulePtr(new RssDownloadRule); + rule = Rss::DownloadRulePtr(new Rss::DownloadRule); rule->setName(m_editedRule->text()); } if (m_editedRule->checkState() == Qt::Unchecked) @@ -344,7 +344,7 @@ void AutomatedRssDownloader::saveEditedRule() else rule->setSavePath(""); rule->setLabel(ui->comboLabel->currentText()); - rule->setAddPaused(RssDownloadRule::AddPausedState(ui->comboAddPaused->currentIndex())); + rule->setAddPaused(Rss::DownloadRule::AddPausedState(ui->comboAddPaused->currentIndex())); // Save new label if (!rule->label().isEmpty()) Preferences::instance()->addTorrentLabelExternal(rule->label()); @@ -498,7 +498,7 @@ void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feed_it } const QString feed_url = feed_item->data(Qt::UserRole).toString(); foreach (QListWidgetItem* rule_item, ui->listRules->selectedItems()) { - RssDownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text()); + Rss::DownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text()); Q_ASSERT(rule); QStringList affected_feeds = rule->rssFeeds(); if (feed_item->checkState() == Qt::Checked) { @@ -521,19 +521,19 @@ void AutomatedRssDownloader::handleFeedCheckStateChange(QListWidgetItem *feed_it void AutomatedRssDownloader::updateMatchingArticles() { ui->treeMatchingArticles->clear(); - RssManagerPtr manager = m_manager.toStrongRef(); + Rss::ManagerPtr manager = m_manager.toStrongRef(); if (!manager) return; - const QHash all_feeds = manager->getAllFeedsAsHash(); + const QHash all_feeds = manager->getAllFeedsAsHash(); saveEditedRule(); foreach (const QListWidgetItem *rule_item, ui->listRules->selectedItems()) { - RssDownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text()); + Rss::DownloadRulePtr rule = m_editableRuleList->getRule(rule_item->text()); if (!rule) continue; foreach (const QString &feed_url, rule->rssFeeds()) { qDebug() << Q_FUNC_INFO << feed_url; if (!all_feeds.contains(feed_url)) continue; // Feed was removed - RssFeedPtr feed = all_feeds.value(feed_url); + Rss::FeedPtr feed = all_feeds.value(feed_url); Q_ASSERT(feed); if (!feed) continue; const QStringList matching_articles = rule->findMatchingArticles(feed); @@ -543,7 +543,7 @@ void AutomatedRssDownloader::updateMatchingArticles() } } -void AutomatedRssDownloader::addFeedArticlesToTree(const RssFeedPtr& feed, const QStringList &articles) +void AutomatedRssDownloader::addFeedArticlesToTree(const Rss::FeedPtr& feed, const QStringList &articles) { // Check if this feed is already in the tree QTreeWidgetItem *treeFeedItem = 0; diff --git a/src/gui/rss/automatedrssdownloader.h b/src/gui/rss/automatedrssdownloader.h index ad7338ed5..8bbda030c 100644 --- a/src/gui/rss/automatedrssdownloader.h +++ b/src/gui/rss/automatedrssdownloader.h @@ -44,8 +44,11 @@ class AutomatedRssDownloader; } QT_END_NAMESPACE -class RssDownloadRuleList; -class RssManager; +namespace Rss +{ + class DownloadRuleList; + class Manager; +} QT_BEGIN_NAMESPACE class QListWidgetItem; @@ -56,7 +59,7 @@ class AutomatedRssDownloader : public QDialog Q_OBJECT public: - explicit AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent = 0); + explicit AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent = 0); ~AutomatedRssDownloader(); bool isRssDownloaderEnabled() const; @@ -86,16 +89,16 @@ private slots: void onFinished(int result); private: - RssDownloadRulePtr getCurrentRule() const; + Rss::DownloadRulePtr getCurrentRule() const; void initLabelCombobox(); - void addFeedArticlesToTree(const RssFeedPtr& feed, const QStringList& articles); + void addFeedArticlesToTree(const Rss::FeedPtr& feed, const QStringList& articles); private: Ui::AutomatedRssDownloader *ui; - QWeakPointer m_manager; + QWeakPointer m_manager; QListWidgetItem* m_editedRule; - RssDownloadRuleList *m_ruleList; - RssDownloadRuleList *m_editableRuleList; + Rss::DownloadRuleList *m_ruleList; + Rss::DownloadRuleList *m_editableRuleList; QRegExpValidator *m_episodeValidator; QShortcut *editHotkey; QShortcut *deleteHotkey; diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index 4bf2b2129..ebdd325d6 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -33,7 +33,7 @@ #include "guiiconprovider.h" #include "feedlistwidget.h" -FeedListWidget::FeedListWidget(QWidget *parent, const RssManagerPtr& rssmanager): QTreeWidget(parent), m_rssManager(rssmanager) { +FeedListWidget::FeedListWidget(QWidget *parent, const Rss::ManagerPtr& rssmanager): QTreeWidget(parent), m_rssManager(rssmanager) { setContextMenuPolicy(Qt::CustomContextMenu); setDragDropMode(QAbstractItemView::InternalMove); setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -51,20 +51,20 @@ FeedListWidget::~FeedListWidget() { delete m_unreadStickyItem; } -void FeedListWidget::itemAdded(QTreeWidgetItem *item, const RssFilePtr& file) { +void FeedListWidget::itemAdded(QTreeWidgetItem *item, const Rss::FilePtr& file) { m_rssMapping[item] = file; - if (RssFeedPtr feed = qSharedPointerDynamicCast(file)) { + if (Rss::FeedPtr feed = qSharedPointerDynamicCast(file)) { m_feedsItems[feed->id()] = item; } } void FeedListWidget::itemAboutToBeRemoved(QTreeWidgetItem *item) { - RssFilePtr file = m_rssMapping.take(item); - if (RssFeedPtr feed = qSharedPointerDynamicCast(file)) { + Rss::FilePtr file = m_rssMapping.take(item); + if (Rss::FeedPtr feed = qSharedPointerDynamicCast(file)) { m_feedsItems.remove(feed->id()); - } if (RssFolderPtr folder = qSharedPointerDynamicCast(file)) { - RssFeedList feeds = folder->getAllFeeds(); - foreach (const RssFeedPtr& feed, feeds) { + } if (Rss::FolderPtr folder = qSharedPointerDynamicCast(file)) { + Rss::FeedList feeds = folder->getAllFeeds(); + foreach (const Rss::FeedPtr& feed, feeds) { m_feedsItems.remove(feed->id()); } } @@ -131,18 +131,18 @@ QList FeedListWidget::getAllFeedItems(QTreeWidgetItem* folder) return feeds; } -RssFilePtr FeedListWidget::getRSSItem(QTreeWidgetItem *item) const { - return m_rssMapping.value(item, RssFilePtr()); +Rss::FilePtr FeedListWidget::getRSSItem(QTreeWidgetItem *item) const { + return m_rssMapping.value(item, Rss::FilePtr()); } bool FeedListWidget::isFeed(QTreeWidgetItem *item) const { - return (qSharedPointerDynamicCast(m_rssMapping.value(item)) != NULL); + return (qSharedPointerDynamicCast(m_rssMapping.value(item)) != NULL); } bool FeedListWidget::isFolder(QTreeWidgetItem *item) const { - return (qSharedPointerDynamicCast(m_rssMapping.value(item)) != NULL); + return (qSharedPointerDynamicCast(m_rssMapping.value(item)) != NULL); } QString FeedListWidget::getItemID(QTreeWidgetItem *item) const { @@ -153,8 +153,8 @@ QTreeWidgetItem* FeedListWidget::getTreeItemFromUrl(const QString &url) const { return m_feedsItems.value(url, 0); } -RssFeedPtr FeedListWidget::getRSSItemFromUrl(const QString &url) const { - return qSharedPointerDynamicCast(getRSSItem(getTreeItemFromUrl(url))); +Rss::FeedPtr FeedListWidget::getRSSItemFromUrl(const QString &url) const { + return qSharedPointerDynamicCast(getRSSItem(getTreeItemFromUrl(url))); } QTreeWidgetItem* FeedListWidget::currentItem() const { @@ -197,9 +197,9 @@ void FeedListWidget::dropEvent(QDropEvent *event) { qDebug("dropEvent"); QList folders_altered; QTreeWidgetItem *dest_folder_item = itemAt(event->pos()); - RssFolderPtr dest_folder; + Rss::FolderPtr dest_folder; if (dest_folder_item) { - dest_folder = qSharedPointerCast(getRSSItem(dest_folder_item)); + dest_folder = qSharedPointerCast(getRSSItem(dest_folder_item)); folders_altered << dest_folder_item; } else { dest_folder = m_rssManager; @@ -207,7 +207,7 @@ void FeedListWidget::dropEvent(QDropEvent *event) { QList src_items = selectedItems(); // Check if there is not going to overwrite another file foreach (QTreeWidgetItem *src_item, src_items) { - RssFilePtr file = getRSSItem(src_item); + Rss::FilePtr file = getRSSItem(src_item); if (dest_folder->hasChild(file->id())) { QTreeWidget::dropEvent(event); return; @@ -219,7 +219,7 @@ void FeedListWidget::dropEvent(QDropEvent *event) { if (parent_folder && !folders_altered.contains(parent_folder)) folders_altered << parent_folder; // Actually move the file - RssFilePtr file = getRSSItem(src_item); + Rss::FilePtr file = getRSSItem(src_item); m_rssManager->moveFile(file, dest_folder); } QTreeWidget::dropEvent(event); diff --git a/src/gui/rss/feedlistwidget.h b/src/gui/rss/feedlistwidget.h index 8e69df244..edc108cc3 100644 --- a/src/gui/rss/feedlistwidget.h +++ b/src/gui/rss/feedlistwidget.h @@ -47,7 +47,7 @@ class FeedListWidget: public QTreeWidget { Q_OBJECT public: - FeedListWidget(QWidget *parent, const RssManagerPtr& rssManager); + FeedListWidget(QWidget *parent, const Rss::ManagerPtr& rssManager); ~FeedListWidget(); bool hasFeed(const QString &url) const; @@ -56,17 +56,17 @@ public: QStringList getItemPath(QTreeWidgetItem* item) const; QList getAllOpenFolders(QTreeWidgetItem *parent=0) const; QList getAllFeedItems(QTreeWidgetItem* folder); - RssFilePtr getRSSItem(QTreeWidgetItem *item) const; + Rss::FilePtr getRSSItem(QTreeWidgetItem *item) const; bool isFeed(QTreeWidgetItem *item) const; bool isFolder(QTreeWidgetItem *item) const; QString getItemID(QTreeWidgetItem *item) const; QTreeWidgetItem* getTreeItemFromUrl(const QString &url) const; - RssFeedPtr getRSSItemFromUrl(const QString &url) const; + Rss::FeedPtr getRSSItemFromUrl(const QString &url) const; QTreeWidgetItem* currentItem() const; QTreeWidgetItem* currentFeed() const; public slots: - void itemAdded(QTreeWidgetItem *item, const RssFilePtr& file); + void itemAdded(QTreeWidgetItem *item, const Rss::FilePtr& file); void itemAboutToBeRemoved(QTreeWidgetItem *item); signals: @@ -80,8 +80,8 @@ protected: void dropEvent(QDropEvent *event); private: - RssManagerPtr m_rssManager; - QHash m_rssMapping; + Rss::ManagerPtr m_rssManager; + QHash m_rssMapping; QHash m_feedsItems; QTreeWidgetItem* m_currentFeed; QTreeWidgetItem *m_unreadStickyItem; diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index c5aa670bf..a37dac89c 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -119,9 +119,9 @@ void RSSImp::displayItemsListMenu(const QPoint&) bool hasLink = false; foreach (const QListWidgetItem* item, selectedItems) { if (!item) continue; - RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + Rss::FeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); if (!feed) continue; - RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + Rss::ArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) continue; if (!article->torrentUrl().isEmpty()) @@ -160,10 +160,10 @@ void RSSImp::on_actionManage_cookies_triggered() void RSSImp::askNewFolder() { QTreeWidgetItem* parent_item = 0; - RssFolderPtr rss_parent; + Rss::FolderPtr rss_parent; if (m_feedList->selectedItems().size() > 0) { parent_item = m_feedList->selectedItems().at(0); - rss_parent = qSharedPointerDynamicCast(m_feedList->getRSSItem(parent_item)); + rss_parent = qSharedPointerDynamicCast(m_feedList->getRSSItem(parent_item)); Q_ASSERT(rss_parent); } else { @@ -174,7 +174,7 @@ void RSSImp::askNewFolder() if (!ok) return; - RssFolderPtr newFolder = rss_parent->addFolder(new_name); + Rss::FolderPtr newFolder = rss_parent->addFolder(new_name); QTreeWidgetItem* folderItem = createFolderListItem(newFolder); if (parent_item) parent_item->addChild(folderItem); @@ -203,9 +203,9 @@ void RSSImp::on_newFeedButton_clicked() if (!m_feedList->isFolder(parent_item)) parent_item = parent_item->parent(); } - RssFolderPtr rss_parent; + Rss::FolderPtr rss_parent; if (parent_item) - rss_parent = qSharedPointerCast(m_feedList->getRSSItem(parent_item)); + rss_parent = qSharedPointerCast(m_feedList->getRSSItem(parent_item)); else rss_parent = m_rssManager; // Ask for feed URL @@ -229,7 +229,7 @@ void RSSImp::on_newFeedButton_clicked() QMessageBox::Ok); return; } - RssFeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl); + Rss::FeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl); // Create TreeWidget item QTreeWidgetItem* item = createFolderListItem(stream); if (parent_item) @@ -260,7 +260,7 @@ void RSSImp::deleteSelectedItems() foreach (QTreeWidgetItem* item, selectedItems) { if (item == m_feedList->stickyUnreadItem()) continue; - RssFilePtr rss_item = m_feedList->getRSSItem(item); + Rss::FilePtr rss_item = m_feedList->getRSSItem(item); QTreeWidgetItem* parent = item->parent(); // Notify TreeWidget m_feedList->itemAboutToBeRemoved(item); @@ -273,7 +273,7 @@ void RSSImp::deleteSelectedItems() parent = parent->parent(); } // Clear feed data from RSS parser (possible caching). - RssFeed* rssFeed = dynamic_cast(rss_item.data()); + Rss::Feed* rssFeed = dynamic_cast(rss_item.data()); if (rssFeed) m_rssManager->rssParser()->clearFeedData(rssFeed->url()); } @@ -341,9 +341,9 @@ void RSSImp::downloadSelectedTorrents() return; foreach (QListWidgetItem* item, selected_items) { if (!item) continue; - RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + Rss::FeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); if (!feed) continue; - RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + Rss::ArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) continue; // Mark as read @@ -371,9 +371,9 @@ void RSSImp::openSelectedArticlesUrls() return; foreach (QListWidgetItem* item, selected_items) { if (!item) continue; - RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + Rss::FeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); if (!feed) continue; - RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + Rss::ArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) continue; // Mark as read @@ -399,7 +399,7 @@ void RSSImp::renameSelectedRssFile() QTreeWidgetItem* item = selectedItems.first(); if (item == m_feedList->stickyUnreadItem()) return; - RssFilePtr rss_item = m_feedList->getRSSItem(item); + Rss::FilePtr rss_item = m_feedList->getRSSItem(item); bool ok; QString newName; do { @@ -426,7 +426,7 @@ void RSSImp::refreshSelectedItems() { QList selectedItems = m_feedList->selectedItems(); foreach (QTreeWidgetItem* item, selectedItems) { - RssFilePtr file = m_feedList->getRSSItem(item); + Rss::FilePtr file = m_feedList->getRSSItem(item); // Update icons if (item == m_feedList->stickyUnreadItem()) { refreshAllFeeds(); @@ -436,10 +436,10 @@ void RSSImp::refreshSelectedItems() if (!file->refresh()) continue; // Update UI - if (qSharedPointerDynamicCast(file)) { + if (qSharedPointerDynamicCast(file)) { item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png"))); } - else if (qSharedPointerDynamicCast(file)) { + else if (qSharedPointerDynamicCast(file)) { // Update feeds in the folder foreach (QTreeWidgetItem *feed, m_feedList->getAllFeedItems(item)) feed->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png"))); @@ -463,7 +463,7 @@ void RSSImp::on_markReadButton_clicked() { QList selectedItems = m_feedList->selectedItems(); foreach (QTreeWidgetItem* item, selectedItems) { - RssFilePtr rss_item = m_feedList->getRSSItem(item); + Rss::FilePtr rss_item = m_feedList->getRSSItem(item); Q_ASSERT(rss_item); rss_item->markAsRead(); updateItemInfos(item); @@ -473,7 +473,7 @@ void RSSImp::on_markReadButton_clicked() populateArticleList(m_feedList->currentItem()); } -QTreeWidgetItem* RSSImp::createFolderListItem(const RssFilePtr& rssFile) +QTreeWidgetItem* RSSImp::createFolderListItem(const Rss::FilePtr& rssFile) { Q_ASSERT(rssFile); QTreeWidgetItem* item = new QTreeWidgetItem; @@ -483,14 +483,14 @@ QTreeWidgetItem* RSSImp::createFolderListItem(const RssFilePtr& rssFile) return item; } -void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const RssFolderPtr& rss_parent) +void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const Rss::FolderPtr& rss_parent) { - QList children; + QList children; if (parent) children = rss_parent->getContent(); else children = m_rssManager->getContent(); - foreach (const RssFilePtr& rssFile, children) { + foreach (const Rss::FilePtr& rssFile, children) { QTreeWidgetItem* item = createFolderListItem(rssFile); Q_ASSERT(item); if (parent) @@ -502,12 +502,12 @@ void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const RssFolderPtr& rss_pare m_feedList->itemAdded(item, rssFile); // Recursive call if this is a folder. - if (RssFolderPtr folder = qSharedPointerDynamicCast(rssFile)) + if (Rss::FolderPtr folder = qSharedPointerDynamicCast(rssFile)) fillFeedsList(item, folder); } } -QListWidgetItem* RSSImp::createArticleListItem(const RssArticlePtr& article) +QListWidgetItem* RSSImp::createArticleListItem(const Rss::ArticlePtr& article) { Q_ASSERT(article); QListWidgetItem* item = new QListWidgetItem; @@ -535,7 +535,7 @@ void RSSImp::populateArticleList(QTreeWidgetItem* item) return; } - RssFilePtr rss_item = m_feedList->getRSSItem(item); + Rss::FilePtr rss_item = m_feedList->getRSSItem(item); if (!rss_item) return; @@ -545,14 +545,14 @@ void RSSImp::populateArticleList(QTreeWidgetItem* item) listArticles->clear(); qDebug("Getting the list of news"); - RssArticleList articles; + Rss::ArticleList articles; if (rss_item == m_rssManager) articles = rss_item->unreadArticleListByDateDesc(); else articles = rss_item->articleListByDateDesc(); qDebug("Got the list of news"); - foreach (const RssArticlePtr& article, articles) { + foreach (const Rss::ArticlePtr& article, articles) { QListWidgetItem* articleItem = createArticleListItem(article); listArticles->addItem(articleItem); } @@ -569,9 +569,9 @@ void RSSImp::refreshTextBrowser() if (item == m_currentArticle) return; m_currentArticle = item; - RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); + Rss::FeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); if (!feed) return; - RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); + Rss::ArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) return; QString html; html += "
"; @@ -650,7 +650,7 @@ void RSSImp::updateItemsInfos(const QList& items) void RSSImp::updateItemInfos(QTreeWidgetItem *item) { - RssFilePtr rss_item = m_feedList->getRSSItem(item); + Rss::FilePtr rss_item = m_feedList->getRSSItem(item); if (!rss_item) return; @@ -677,7 +677,7 @@ void RSSImp::updateFeedInfos(const QString& url, const QString& display_name, ui { qDebug() << Q_FUNC_INFO << display_name; QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url); - RssFeedPtr stream = qSharedPointerCast(m_feedList->getRSSItem(item)); + Rss::FeedPtr stream = qSharedPointerCast(m_feedList->getRSSItem(item)); item->setText(0, display_name + QString::fromUtf8(" (") + QString::number(nbUnread) + QString(")")); if (!stream->isLoading()) item->setData(0, Qt::DecorationRole, QIcon(stream->iconPath())); @@ -707,7 +707,7 @@ void RSSImp::updateRefreshInterval(uint val) RSSImp::RSSImp(QWidget *parent): QWidget(parent), - m_rssManager(new RssManager) + m_rssManager(new Rss::Manager) { setupUi(this); // Icons diff --git a/src/gui/rss/rss_imp.h b/src/gui/rss/rss_imp.h index 966de0006..5395ef9f8 100644 --- a/src/gui/rss/rss_imp.h +++ b/src/gui/rss/rss_imp.h @@ -78,7 +78,7 @@ private slots: void updateItemInfos(QTreeWidgetItem *item); void openSelectedArticlesUrls(); void downloadSelectedTorrents(); - void fillFeedsList(QTreeWidgetItem *parent = 0, const RssFolderPtr& rss_parent = RssFolderPtr()); + void fillFeedsList(QTreeWidgetItem *parent = 0, const Rss::FolderPtr& rss_parent = Rss::FolderPtr()); void saveSlidersPosition(); void restoreSlidersPosition(); void askNewFolder(); @@ -89,11 +89,11 @@ private slots: void on_rssDownloaderBtn_clicked(); private: - static QListWidgetItem* createArticleListItem(const RssArticlePtr& article); - static QTreeWidgetItem* createFolderListItem(const RssFilePtr& rssFile); + static QListWidgetItem* createArticleListItem(const Rss::ArticlePtr& article); + static QTreeWidgetItem* createFolderListItem(const Rss::FilePtr& rssFile); private: - RssManagerPtr m_rssManager; + Rss::ManagerPtr m_rssManager; FeedListWidget *m_feedList; QListWidgetItem* m_currentArticle; QShortcut *editHotkey; From 28ed981082e2ef548da85f27b7f71cd4aa7f9c54 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 17 Oct 2015 18:59:04 +0300 Subject: [PATCH 4/5] Redesign RSS base classes. --- src/base/base.pri | 4 +- src/base/rss/{ => private}/rssparser.cpp | 34 ++++---- src/base/rss/{ => private}/rssparser.h | 66 ++++++++------- src/base/rss/rssarticle.cpp | 10 +-- src/base/rss/rssarticle.h | 1 - src/base/rss/rssfeed.cpp | 101 +++++++++-------------- src/base/rss/rssfeed.h | 19 ++--- src/base/rss/rssfile.cpp | 9 +- src/base/rss/rssfile.h | 18 ++-- src/base/rss/rssfolder.cpp | 78 ++++------------- src/base/rss/rssfolder.h | 31 +++---- src/base/rss/rssmanager.cpp | 53 ++++++++---- src/base/rss/rssmanager.h | 28 +++++-- src/gui/rss/automatedrssdownloader.cpp | 3 +- src/gui/rss/feedlistwidget.cpp | 7 +- src/gui/rss/rss_imp.cpp | 36 ++++---- 16 files changed, 232 insertions(+), 266 deletions(-) rename src/base/rss/{ => private}/rssparser.cpp (96%) rename src/base/rss/{ => private}/rssparser.h (51%) diff --git a/src/base/base.pri b/src/base/base.pri index ccea8efe1..b38caa601 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -43,7 +43,7 @@ HEADERS += \ $$PWD/rss/rssarticle.h \ $$PWD/rss/rssdownloadrule.h \ $$PWD/rss/rssdownloadrulelist.h \ - $$PWD/rss/rssparser.h \ + $$PWD/rss/private/rssparser.h \ $$PWD/utils/fs.h \ $$PWD/utils/gzip.h \ $$PWD/utils/misc.h \ @@ -94,7 +94,7 @@ SOURCES += \ $$PWD/rss/rssdownloadrule.cpp \ $$PWD/rss/rssdownloadrulelist.cpp \ $$PWD/rss/rssfile.cpp \ - $$PWD/rss/rssparser.cpp \ + $$PWD/rss/private/rssparser.cpp \ $$PWD/utils/fs.cpp \ $$PWD/utils/gzip.cpp \ $$PWD/utils/misc.cpp \ diff --git a/src/base/rss/rssparser.cpp b/src/base/rss/private/rssparser.cpp similarity index 96% rename from src/base/rss/rssparser.cpp rename to src/base/rss/private/rssparser.cpp index 18b138c41..eecfe1af1 100644 --- a/src/base/rss/rssparser.cpp +++ b/src/base/rss/private/rssparser.cpp @@ -29,21 +29,26 @@ */ #include +#include #include #include #include #include +#include #include "base/utils/fs.h" #include "rssparser.h" namespace Rss { - struct ParsingJob + namespace Private { - QString feedUrl; - QString filePath; - }; + struct ParsingJob + { + QString feedUrl; + QByteArray feedData; + }; + } } static const char shortDay[][4] = { @@ -64,7 +69,7 @@ static const char shortMonth[][4] = { "Sep", "Oct", "Nov", "Dec" }; -using namespace Rss; +using namespace Rss::Private; Parser::Parser(QObject *parent) : QThread(parent) @@ -227,11 +232,11 @@ QDateTime Parser::parseDate(const QString &string) return result; } -void Parser::parseRssFile(const QString &feedUrl, const QString &filePath) +void Parser::parseFeedData(const QString &feedUrl, const QByteArray &feedData) { - qDebug() << Q_FUNC_INFO << feedUrl << filePath; + qDebug() << Q_FUNC_INFO << feedUrl; m_mutex.lock(); - ParsingJob job = { feedUrl, Utils::Fs::fromNativePath(filePath) }; + ParsingJob job = { feedUrl, feedData }; m_queue.enqueue(job); // Wake up thread. if (m_queue.count() == 1) { @@ -487,14 +492,9 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) // read and create items from a rss document void Parser::parseFeed(const ParsingJob &job) { - qDebug() << Q_FUNC_INFO << job.feedUrl << job.filePath; - QFile fileRss(job.filePath); - if (!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) { - reportFailure(job, tr("Failed to open downloaded RSS file.")); - return; - } + qDebug() << Q_FUNC_INFO << job.feedUrl; - QXmlStreamReader xml(&fileRss); + QXmlStreamReader xml(job.feedData); bool foundChannel = false; while (xml.readNextStartElement()) { if (xml.name() == "rss") { @@ -533,14 +533,10 @@ void Parser::parseFeed(const ParsingJob &job) return; } - // Clean up - fileRss.close(); emit feedParsingFinished(job.feedUrl, QString()); - Utils::Fs::forceRemove(job.filePath); } void Parser::reportFailure(const ParsingJob &job, const QString &error) { emit feedParsingFinished(job.feedUrl, error); - Utils::Fs::forceRemove(job.filePath); } diff --git a/src/base/rss/rssparser.h b/src/base/rss/private/rssparser.h similarity index 51% rename from src/base/rss/rssparser.h rename to src/base/rss/private/rssparser.h index 0a02ca773..89893feaa 100644 --- a/src/base/rss/rssparser.h +++ b/src/base/rss/private/rssparser.h @@ -31,52 +31,56 @@ #ifndef RSSPARSER_H #define RSSPARSER_H +#include #include #include #include +#include #include -#include "rssarticle.h" +class QXmlStreamReader; namespace Rss { - struct ParsingJob; - - class Parser: public QThread + namespace Private { - Q_OBJECT + struct ParsingJob; - public: - explicit Parser(QObject *parent = 0); - virtual ~Parser(); + class Parser: public QThread + { + Q_OBJECT - signals: - void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); - void feedTitle(const QString &feedUrl, const QString &title); - void feedParsingFinished(const QString &feedUrl, const QString &error); + public: + explicit Parser(QObject *parent = 0); + ~Parser(); - public slots: - void parseRssFile(const QString &feedUrl, const QString &filePath); - void clearFeedData(const QString &feedUrl); + void parseFeedData(const QString &feedUrl, const QByteArray &feedData); + void clearFeedData(const QString &feedUrl); - protected: - virtual void run(); + signals: + void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); + void feedTitle(const QString &feedUrl, const QString &title); + void feedParsingFinished(const QString &feedUrl, const QString &error); - private: - static QDateTime parseDate(const QString &string); - void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); - void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); - void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseFeed(const ParsingJob &job); - void reportFailure(const ParsingJob &job, const QString &error); + private: + void run() override; - bool m_running; - QMutex m_mutex; - QQueue m_queue; - QWaitCondition m_waitCondition; - QHash m_lastBuildDates; // Optimization - }; + static QDateTime parseDate(const QString &string); + + void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); + void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); + void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); + void parseFeed(const ParsingJob &job); + void reportFailure(const ParsingJob &job, const QString &error); + + bool m_running; + QMutex m_mutex; + QQueue m_queue; + QWaitCondition m_waitCondition; + QHash m_lastBuildDates; // Optimization + }; + } } #endif // RSSPARSER_H diff --git a/src/base/rss/rssarticle.cpp b/src/base/rss/rssarticle.cpp index 582575b64..1357d09c9 100644 --- a/src/base/rss/rssarticle.cpp +++ b/src/base/rss/rssarticle.cpp @@ -120,12 +120,10 @@ bool Article::isRead() const void Article::markAsRead() { - if (m_read) return; - - m_read = true; - m_parent->decrementUnreadCount(); - m_parent->markAsDirty(); - emit articleWasRead(); + if (!m_read) { + m_read = true; + emit articleWasRead(); + } } const QString &Article::guid() const diff --git a/src/base/rss/rssarticle.h b/src/base/rss/rssarticle.h index 88f8b4202..e81a10f6a 100644 --- a/src/base/rss/rssarticle.h +++ b/src/base/rss/rssarticle.h @@ -32,7 +32,6 @@ #ifndef RSSARTICLE_H #define RSSARTICLE_H -#include #include #include #include diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index 457851676..76acfaf4b 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -40,9 +40,9 @@ #include "base/utils/fs.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" +#include "private/rssparser.h" #include "rssdownloadrulelist.h" #include "rssarticle.h" -#include "rssparser.h" #include "rssfolder.h" #include "rssmanager.h" #include "rssfeed.h" @@ -57,9 +57,8 @@ namespace Rss using namespace Rss; -Feed::Feed(Manager *manager, Folder *parent, const QString &url) +Feed::Feed(const QString &url, Manager *manager) : m_manager(manager) - , m_parent(parent) , m_url (QUrl::fromEncoded(url.toUtf8()).toString()) , m_icon(":/icons/oxygen/application-rss+xml.png") , m_unreadCount(0) @@ -69,34 +68,26 @@ Feed::Feed(Manager *manager, Folder *parent, const QString &url) { qDebug() << Q_FUNC_INFO << m_url; // Listen for new RSS downloads - connect(manager->rssParser(), SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); - connect(manager->rssParser(), SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); - connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString))); + Private::Parser *const parser = m_manager->rssParser(); + connect(parser, SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); + connect(parser, SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); + connect(parser, SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleParsingFinished(QString,QString))); // Download the RSS Feed icon Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_iconUrl = handler->url(); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleIconDownloadFinished(QString, QString))); // Load old RSS articles loadItemsFromDisk(); + + refresh(); } Feed::~Feed() { if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) Utils::Fs::forceRemove(m_icon); -} - -Folder *Feed::parent() const -{ - return m_parent; -} - -void Feed::setParent(Folder *parent) -{ - m_parent = parent; + m_manager->rssParser()->clearFeedData(m_url); } void Feed::saveItemsToDisk() @@ -104,7 +95,7 @@ void Feed::saveItemsToDisk() qDebug() << Q_FUNC_INFO << m_url; if (!m_dirty) return; - markAsDirty(false); + m_dirty = false; QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QVariantList oldItems; @@ -140,13 +131,15 @@ void Feed::addArticle(const ArticlePtr &article) int maxArticles = Preferences::instance()->getRSSMaxArticlesPerFeed(); if (!m_articles.contains(article->guid())) { - markAsDirty(); + m_dirty = true; // Update unreadCount if (!article->isRead()) ++m_unreadCount; // Insert in hash table m_articles[article->guid()] = article; + if (!article->isRead()) // Optimization + connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleRead()), Qt::UniqueConnection); // Insertion sort ArticleList::Iterator lowerBound = qLowerBound(m_articlesByDate.begin(), m_articlesByDate.end(), article, articleDateRecentThan); m_articlesByDate.insert(lowerBound, article); @@ -162,7 +155,7 @@ void Feed::addArticle(const ArticlePtr &article) // Check if article was inserted at the end of the list and will break max_articles limit if (Preferences::instance()->isRssDownloadingEnabled()) { if ((lbIndex < maxArticles) && !article->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), article); + downloadArticleTorrentIfMatching(article); } } else { @@ -172,7 +165,7 @@ void Feed::addArticle(const ArticlePtr &article) ArticlePtr skipped = m_articles.value(article->guid(), ArticlePtr()); if (skipped) { if (!skipped->isRead()) - downloadArticleTorrentIfMatching(m_manager->downloadRules(), skipped); + downloadArticleTorrentIfMatching(skipped); } } } @@ -186,10 +179,9 @@ bool Feed::refresh() } m_loading = true; // Download the RSS again - Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url, true); - connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); - connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); - m_url = handler->url(); // sync URL encoding + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url); + connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), this, SLOT(handleRssDownloadFinished(QString, QByteArray))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleRssDownloadFailed(QString, QString))); return true; } @@ -290,11 +282,6 @@ void Feed::markAsRead() m_manager->forwardFeedInfosChanged(m_url, displayName(), 0); } -void Feed::markAsDirty(bool dirty) -{ - m_dirty = dirty; -} - uint Feed::unreadCount() const { return m_unreadCount; @@ -327,28 +314,27 @@ ArticleList Feed::unreadArticleListByDateDesc() const QString Feed::iconUrl() const { // XXX: This works for most sites but it is not perfect - return QString("http://") + QUrl(m_url).host() + QString("/favicon.ico"); + return QString("http://%1/favicon.ico").arg(QUrl(m_url).host()); } -// read and store the downloaded rss' informations -void Feed::handleFinishedDownload(const QString &url, const QString &filePath) +void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath) { - if (url == m_url) { - qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; - // Parse the download RSS - m_manager->rssParser()->parseRssFile(m_url, filePath); - } - else if (url == m_iconUrl) { - m_icon = filePath; - qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; - m_manager->forwardFeedIconChanged(m_url, m_icon); - } + Q_UNUSED(url); + + m_icon = filePath; + qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; + m_manager->forwardFeedIconChanged(m_url, m_icon); } -void Feed::handleDownloadFailure(const QString &url, const QString &error) +void Feed::handleRssDownloadFinished(const QString &url, const QByteArray &data) { - if (url != m_url) return; + qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; + // Parse the download RSS + m_manager->rssParser()->parseFeedData(m_url, data); +} +void Feed::handleRssDownloadFailed(const QString &url, const QString &error) +{ m_inErrorState = true; m_loading = false; m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); @@ -368,9 +354,10 @@ void Feed::handleFeedTitle(const QString &feedUrl, const QString &title) m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); } -void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article) +void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article) { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); + DownloadRuleList *rules = m_manager->downloadRules(); DownloadRulePtr matchingRule = rules->findMatchingRule(m_url, article->title()); if (!matchingRule) return; @@ -378,7 +365,6 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic QDateTime lastMatch = matchingRule->lastMatch(); if (lastMatch.isValid()) { if (QDateTime::currentDateTime() < lastMatch.addDays(matchingRule->ignoreDays())) { - connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); article->markAsRead(); return; } @@ -396,8 +382,7 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic } Logger::instance()->addMessage(tr("Automatically downloading '%1' torrent from '%2' RSS feed...").arg(article->title()).arg(displayName())); - connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString &)), Qt::UniqueConnection); - if (BitTorrent::MagnetUri(torrent_url).isValid()) + if (BitTorrent::MagnetUri(torrentUrl).isValid()) article->markAsRead(); else connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); @@ -415,10 +400,9 @@ void Feed::downloadArticleTorrentIfMatching(DownloadRuleList *rules, const Artic void Feed::recheckRssItemsForDownload() { Q_ASSERT(Preferences::instance()->isRssDownloadingEnabled()); - DownloadRuleList *rules = m_manager->downloadRules(); foreach (const ArticlePtr &article, m_articlesByDate) { if (!article->isRead()) - downloadArticleTorrentIfMatching(rules, article); + downloadArticleTorrentIfMatching(article); } } @@ -440,7 +424,7 @@ void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleD //m_manager->forwardFeedContentChanged(m_url); } -void Feed::handleFeedParsingFinished(const QString &feedUrl, const QString &error) +void Feed::handleParsingFinished(const QString &feedUrl, const QString &error) { if (feedUrl != m_url) return; @@ -459,12 +443,9 @@ void Feed::handleFeedParsingFinished(const QString &feedUrl, const QString &erro saveItemsToDisk(); } -void Feed::handleArticleStateChanged() -{ - m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); -} - -void Feed::decrementUnreadCount() +void Feed::handleArticleRead() { --m_unreadCount; + m_dirty = true; + m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); } diff --git a/src/base/rss/rssfeed.h b/src/base/rss/rssfeed.h index 30fd45830..5198234bd 100644 --- a/src/base/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -58,11 +58,9 @@ namespace Rss Q_OBJECT public: - Feed(Manager *manager, Folder *parent, const QString &url); + Feed(const QString &url, Manager *manager); ~Feed(); - Folder *parent() const; - void setParent(Folder *parent); bool refresh(); QString id() const; void removeAllSettings(); @@ -78,38 +76,35 @@ namespace Rss ArticlePtr getItem(const QString &guid) const; uint count() const; void markAsRead(); - void markAsDirty(bool dirty = true); uint unreadCount() const; ArticleList articleListByDateDesc() const; const ArticleHash &articleHash() const; ArticleList unreadArticleListByDateDesc() const; - void decrementUnreadCount(); void recheckRssItemsForDownload(); private slots: - void handleFinishedDownload(const QString &url, const QString &filePath); - void handleDownloadFailure(const QString &url, const QString &error); + void handleIconDownloadFinished(const QString &url, const QString &filePath); + void handleRssDownloadFinished(const QString &url, const QByteArray &data); + void handleRssDownloadFailed(const QString &url, const QString &error); void handleFeedTitle(const QString &feedUrl, const QString &title); void handleNewArticle(const QString &feedUrl, const QVariantHash &article); - void handleFeedParsingFinished(const QString &feedUrl, const QString &error); - void handleArticleStateChanged(); + void handleParsingFinished(const QString &feedUrl, const QString &error); + void handleArticleRead(); private: QString iconUrl() const; void loadItemsFromDisk(); void addArticle(const ArticlePtr &article); - void downloadArticleTorrentIfMatching(DownloadRuleList *rules, const ArticlePtr &article); + void downloadArticleTorrentIfMatching(const ArticlePtr &article); private: Manager *m_manager; ArticleHash m_articles; ArticleList m_articlesByDate; // Articles sorted by date (more recent first) - Folder *m_parent; QString m_title; QString m_url; QString m_alias; QString m_icon; - QString m_iconUrl; uint m_unreadCount; bool m_dirty; bool m_inErrorState; diff --git a/src/base/rss/rssfile.cpp b/src/base/rss/rssfile.cpp index 1f911fdda..394bd56f0 100644 --- a/src/base/rss/rssfile.cpp +++ b/src/base/rss/rssfile.cpp @@ -36,11 +36,16 @@ using namespace Rss; File::~File() {} +Folder *File::parentFolder() const +{ + return m_parent; +} + QStringList File::pathHierarchy() const { QStringList path; - if (parent()) - path << parent()->pathHierarchy(); + if (m_parent) + path << m_parent->pathHierarchy(); path << id(); return path; } diff --git a/src/base/rss/rssfile.h b/src/base/rss/rssfile.h index 0b5452624..285da93f6 100644 --- a/src/base/rss/rssfile.h +++ b/src/base/rss/rssfile.h @@ -55,25 +55,27 @@ namespace Rss public: virtual ~File(); - virtual uint unreadCount() const = 0; - virtual QString displayName() const = 0; virtual QString id() const = 0; + virtual QString displayName() const = 0; + virtual uint unreadCount() const = 0; virtual QString iconPath() const = 0; - virtual void rename(const QString &newName) = 0; - virtual void markAsRead() = 0; - virtual Folder *parent() const = 0; - virtual void setParent(Folder *parent) = 0; - virtual bool refresh() = 0; virtual ArticleList articleListByDateDesc() const = 0; virtual ArticleList unreadArticleListByDateDesc() const = 0; + + virtual void rename(const QString &newName) = 0; + virtual void markAsRead() = 0; + virtual bool refresh() = 0; virtual void removeAllSettings() = 0; virtual void saveItemsToDisk() = 0; virtual void recheckRssItemsForDownload() = 0; + Folder *parentFolder() const; QStringList pathHierarchy() const; protected: - uint m_unreadCount; + friend class Folder; + + Folder *m_parent = nullptr; }; } diff --git a/src/base/rss/rssfolder.cpp b/src/base/rss/rssfolder.cpp index 0a054c9b8..e17afecb9 100644 --- a/src/base/rss/rssfolder.cpp +++ b/src/base/rss/rssfolder.cpp @@ -40,24 +40,11 @@ using namespace Rss; -Folder::Folder(Folder *parent, const QString &name) - : m_parent(parent) - , m_name(name) +Folder::Folder(const QString &name) + : m_name(name) { } -Folder::~Folder() {} - -Folder *Folder::parent() const -{ - return m_parent; -} - -void Folder::setParent(Folder *parent) -{ - m_parent = parent; -} - uint Folder::unreadCount() const { uint nbUnread = 0; @@ -78,31 +65,6 @@ void Folder::removeChild(const QString &childId) } } -FolderPtr Folder::addFolder(const QString &name) -{ - FolderPtr subfolder; - if (!m_children.contains(name)) { - subfolder = FolderPtr(new Folder(this, name)); - m_children[name] = subfolder; - } - else { - subfolder = qSharedPointerDynamicCast(m_children.value(name)); - } - return subfolder; -} - -FeedPtr Folder::addStream(Manager *manager, const QString &url) -{ - qDebug() << Q_FUNC_INFO << manager << url; - FeedPtr stream(new Feed(manager, this, url)); - Q_ASSERT(stream); - qDebug() << "Stream URL is " << stream->url(); - Q_ASSERT(!m_children.contains(stream->url())); - m_children[stream->url()] = stream; - stream->refresh(); - return stream; -} - // Refresh All Children bool Folder::refresh() { @@ -176,7 +138,8 @@ void Folder::rename(const QString &newName) Q_ASSERT(!m_parent->hasChild(newName)); if (!m_parent->hasChild(newName)) { // Update parent - m_parent->renameChildFolder(m_name, newName); + FilePtr folder = m_parent->m_children.take(m_name); + m_parent->m_children[newName] = folder; // Actually rename m_name = newName; } @@ -224,20 +187,17 @@ QHash Folder::getAllFeedsAsHash() const return ret; } -void Folder::addFile(const FilePtr &item) +bool Folder::addFile(const FilePtr &item) { - if (FeedPtr feed = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(feed->url())); - m_children[feed->url()] = item; - qDebug("Added feed %s to folder ./%s", qPrintable(feed->url()), qPrintable(m_name)); + Q_ASSERT(!m_children.contains(item->id())); + if (!m_children.contains(item->id())) { + m_children[item->id()] = item; + // Update parent + item->m_parent = this; + return true; } - else if (FolderPtr folder = qSharedPointerDynamicCast(item)) { - Q_ASSERT(!m_children.contains(folder->displayName())); - m_children[folder->displayName()] = item; - qDebug("Added folder %s to folder ./%s", qPrintable(folder->displayName()), qPrintable(m_name)); - } - // Update parent - item->setParent(this); + + return false; } void Folder::removeAllItems() @@ -245,6 +205,11 @@ void Folder::removeAllItems() m_children.clear(); } +FilePtr Folder::child(const QString &childId) +{ + return m_children.value(childId); +} + void Folder::removeAllSettings() { FileHash::ConstIterator it = m_children.begin(); @@ -274,13 +239,6 @@ bool Folder::hasChild(const QString &childId) return m_children.contains(childId); } -void Folder::renameChildFolder(const QString &oldName, const QString &newName) -{ - Q_ASSERT(m_children.contains(oldName)); - FilePtr folder = m_children.take(oldName); - m_children[newName] = folder; -} - FilePtr Folder::takeChild(const QString &childId) { return m_children.take(childId); diff --git a/src/base/rss/rssfolder.h b/src/base/rss/rssfolder.h index 78ff21401..7adefb423 100644 --- a/src/base/rss/rssfolder.h +++ b/src/base/rss/rssfolder.h @@ -48,19 +48,12 @@ namespace Rss typedef QSharedPointer FolderPtr; typedef QList FeedList; - class Folder: public QObject, public File + class Folder: public File { - Q_OBJECT - public: - explicit Folder(Folder *parent = 0, const QString &name = QString()); - ~Folder(); + explicit Folder(const QString &name = QString()); - Folder *parent() const; - void setParent(Folder *parent); uint unreadCount() const; - FeedPtr addStream(Manager *manager, const QString &url); - FolderPtr addFolder(const QString &name); uint getNbFeeds() const; FileList getContent() const; FeedList getAllFeeds() const; @@ -71,22 +64,20 @@ namespace Rss bool hasChild(const QString &childId); ArticleList articleListByDateDesc() const; ArticleList unreadArticleListByDateDesc() const; - void removeAllSettings(); - void saveItemsToDisk(); - void removeAllItems(); - void renameChildFolder(const QString &oldName, const QString &newName); - FilePtr takeChild(const QString &childId); - void recheckRssItemsForDownload(); - public slots: - bool refresh(); - void addFile(const FilePtr &item); - void removeChild(const QString &childId); void rename(const QString &newName); void markAsRead(); + bool refresh(); + void removeAllSettings(); + void saveItemsToDisk(); + void recheckRssItemsForDownload(); + void removeAllItems(); + FilePtr child(const QString &childId); + FilePtr takeChild(const QString &childId); + bool addFile(const FilePtr &item); + void removeChild(const QString &childId); private: - Folder *m_parent; QString m_name; FileHash m_children; }; diff --git a/src/base/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp index 17e665661..a7bd087d1 100644 --- a/src/base/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -33,19 +33,23 @@ #include "base/logger.h" #include "base/preferences.h" +#include "private/rssparser.h" +#include "rssfolder.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" -#include "rssparser.h" #include "rssmanager.h" static const int MSECS_PER_MIN = 60000; using namespace Rss; +using namespace Rss::Private; -Manager::Manager() - : m_downloadRules(new DownloadRuleList) +Manager::Manager(QObject *parent) + : QObject(parent) + , m_downloadRules(new DownloadRuleList) , m_rssParser(new Parser(this)) + , m_rootFolder(new Folder) { connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); @@ -57,16 +61,12 @@ Manager::~Manager() qDebug("Deleting RSSManager..."); delete m_downloadRules; delete m_rssParser; - saveItemsToDisk(); + m_rootFolder->saveItemsToDisk(); saveStreamList(); + m_rootFolder.clear(); qDebug("RSSManager deleted"); } -Parser *Manager::rssParser() const -{ - return m_rssParser; -} - void Manager::updateRefreshInterval(uint val) { if (m_refreshInterval != val) { @@ -95,14 +95,22 @@ void Manager::loadStreamList() const QString feedUrl = path.takeLast(); qDebug() << "Feed URL:" << feedUrl; // Create feed path (if it does not exists) - Folder *feedParent = this; + FolderPtr feedParent = m_rootFolder; foreach (const QString &folderName, path) { - qDebug() << "Adding parent folder:" << folderName; - feedParent = feedParent->addFolder(folderName).data(); + if (!feedParent->hasChild(folderName)) { + qDebug() << "Adding parent folder:" << folderName; + FolderPtr folder(new Folder(folderName)); + feedParent->addFile(folder); + feedParent = folder; + } + else { + feedParent = qSharedPointerDynamicCast(feedParent->child(folderName)); + } } // Create feed qDebug() << "Adding feed to parent folder"; - FeedPtr stream = feedParent->addStream(this, feedUrl); + FeedPtr stream(new Feed(feedUrl, this)); + feedParent->addFile(stream); const QString &alias = aliases[i]; if (!alias.isEmpty()) stream->rename(alias); @@ -128,7 +136,7 @@ void Manager::forwardFeedIconChanged(const QString &url, const QString &iconPath void Manager::moveFile(const FilePtr &file, const FolderPtr &destinationFolder) { - Folder *srcFolder = file->parent(); + Folder *srcFolder = file->parentFolder(); if (destinationFolder != srcFolder) { // Remove reference in old folder srcFolder->takeChild(file->id()); @@ -144,7 +152,7 @@ void Manager::saveStreamList() const { QStringList streamsUrl; QStringList aliases; - FeedList streams = getAllFeeds(); + FeedList streams = m_rootFolder->getAllFeeds(); foreach (const FeedPtr &stream, streams) { // This backslash has nothing to do with path handling QString streamPath = stream->pathHierarchy().join("\\"); @@ -164,3 +172,18 @@ DownloadRuleList *Manager::downloadRules() const Q_ASSERT(m_downloadRules); return m_downloadRules; } + +FolderPtr Manager::rootFolder() const +{ + return m_rootFolder; +} + +Parser *Manager::rssParser() const +{ + return m_rssParser; +} + +void Manager::refresh() +{ + m_rootFolder->refresh(); +} diff --git a/src/base/rss/rssmanager.h b/src/base/rss/rssmanager.h index 515d45a64..2321ce5a2 100644 --- a/src/base/rss/rssmanager.h +++ b/src/base/rss/rssmanager.h @@ -32,31 +32,44 @@ #ifndef RSSMANAGER_H #define RSSMANAGER_H +#include #include #include -#include "rssfolder.h" - namespace Rss { class DownloadRuleList; - class Parser; + class File; + class Folder; + class Feed; class Manager; + typedef QSharedPointer FilePtr; + typedef QSharedPointer FolderPtr; + typedef QSharedPointer FeedPtr; + + namespace Private + { + class Parser; + } + typedef QSharedPointer ManagerPtr; - class Manager: public Folder + class Manager: public QObject { Q_OBJECT public: - Manager(); + explicit Manager(QObject *parent = 0); ~Manager(); - Parser *rssParser() const; DownloadRuleList *downloadRules() const; + FolderPtr rootFolder() const; + + Private::Parser *rssParser() const; public slots: + void refresh(); void loadStreamList(); void saveStreamList() const; void forwardFeedContentChanged(const QString &url); @@ -74,7 +87,8 @@ namespace Rss QTimer m_refreshTimer; uint m_refreshInterval; DownloadRuleList *m_downloadRules; - Parser *m_rssParser; + Private::Parser *m_rssParser; + FolderPtr m_rootFolder; }; } diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 98ef89088..5b5493763 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -39,6 +39,7 @@ #include "base/rss/rssdownloadrulelist.h" #include "base/preferences.h" #include "base/rss/rssmanager.h" +#include "base/rss/rssfolder.h" #include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" @@ -524,7 +525,7 @@ void AutomatedRssDownloader::updateMatchingArticles() Rss::ManagerPtr manager = m_manager.toStrongRef(); if (!manager) return; - const QHash all_feeds = manager->getAllFeedsAsHash(); + const QHash all_feeds = manager->rootFolder()->getAllFeedsAsHash(); saveEditedRule(); foreach (const QListWidgetItem *rule_item, ui->listRules->selectedItems()) { diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index ebdd325d6..6f00ffcbd 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -29,6 +29,7 @@ */ #include "base/rss/rssmanager.h" +#include "base/rss/rssfolder.h" #include "base/rss/rssfeed.h" #include "guiiconprovider.h" #include "feedlistwidget.h" @@ -40,9 +41,9 @@ FeedListWidget::FeedListWidget(QWidget *parent, const Rss::ManagerPtr& rssmanage setColumnCount(1); headerItem()->setText(0, tr("RSS feeds")); m_unreadStickyItem = new QTreeWidgetItem(this); - m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->unreadCount())+ QString(")")); + m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->rootFolder()->unreadCount()) + QString(")")); m_unreadStickyItem->setData(0,Qt::DecorationRole, GuiIconProvider::instance()->getIcon("mail-folder-inbox")); - itemAdded(m_unreadStickyItem, rssmanager); + itemAdded(m_unreadStickyItem, rssmanager->rootFolder()); connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(updateCurrentFeed(QTreeWidgetItem*))); setCurrentItem(m_unreadStickyItem); } @@ -202,7 +203,7 @@ void FeedListWidget::dropEvent(QDropEvent *event) { dest_folder = qSharedPointerCast(getRSSItem(dest_folder_item)); folders_altered << dest_folder_item; } else { - dest_folder = m_rssManager; + dest_folder = m_rssManager->rootFolder(); } QList src_items = selectedItems(); // Check if there is not going to overwrite another file diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index a37dac89c..853204f1b 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -47,7 +47,6 @@ #include "base/rss/rssmanager.h" #include "base/rss/rssfolder.h" #include "base/rss/rssarticle.h" -#include "base/rss/rssparser.h" #include "base/rss/rssfeed.h" #include "automatedrssdownloader.h" #include "guiiconprovider.h" @@ -79,7 +78,7 @@ void RSSImp::displayRSSListMenu(const QPoint& pos) myRSSListMenu.addAction(actionMark_items_read); myRSSListMenu.addSeparator(); if (selectedItems.size() == 1) { - if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager) { + if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager->rootFolder()) { myRSSListMenu.addAction(actionRename); myRSSListMenu.addAction(actionDelete); myRSSListMenu.addSeparator(); @@ -167,14 +166,15 @@ void RSSImp::askNewFolder() Q_ASSERT(rss_parent); } else { - rss_parent = m_rssManager; + rss_parent = m_rssManager->rootFolder(); } bool ok; QString new_name = AutoExpandableDialog::getText(this, tr("Please choose a folder name"), tr("Folder name:"), QLineEdit::Normal, tr("New folder"), &ok); - if (!ok) + if (!ok || rss_parent->hasChild(new_name)) return; - Rss::FolderPtr newFolder = rss_parent->addFolder(new_name); + Rss::FolderPtr newFolder(new Rss::Folder(new_name)); + rss_parent->addFile(newFolder); QTreeWidgetItem* folderItem = createFolderListItem(newFolder); if (parent_item) parent_item->addChild(folderItem); @@ -207,7 +207,7 @@ void RSSImp::on_newFeedButton_clicked() if (parent_item) rss_parent = qSharedPointerCast(m_feedList->getRSSItem(parent_item)); else - rss_parent = m_rssManager; + rss_parent = m_rssManager->rootFolder(); // Ask for feed URL bool ok; QString clip_txt = qApp->clipboard()->text(); @@ -229,7 +229,9 @@ void RSSImp::on_newFeedButton_clicked() QMessageBox::Ok); return; } - Rss::FeedPtr stream = rss_parent->addStream(m_rssManager.data(), newUrl); + + Rss::FeedPtr stream(new Rss::Feed(newUrl, m_rssManager.data())); + rss_parent->addFile(stream); // Create TreeWidget item QTreeWidgetItem* item = createFolderListItem(stream); if (parent_item) @@ -265,17 +267,13 @@ void RSSImp::deleteSelectedItems() // Notify TreeWidget m_feedList->itemAboutToBeRemoved(item); // Actually delete the item - rss_item->parent()->removeChild(rss_item->id()); + rss_item->parentFolder()->removeChild(rss_item->id()); delete item; // Update parents count - while (parent && parent != m_feedList->invisibleRootItem()) { - updateItemInfos (parent); + while (parent && (parent != m_feedList->invisibleRootItem())) { + updateItemInfos(parent); parent = parent->parent(); } - // Clear feed data from RSS parser (possible caching). - Rss::Feed* rssFeed = dynamic_cast(rss_item.data()); - if (rssFeed) - m_rssManager->rssParser()->clearFeedData(rssFeed->url()); } m_rssManager->saveStreamList(); // Update Unread items @@ -406,7 +404,7 @@ void RSSImp::renameSelectedRssFile() newName = AutoExpandableDialog::getText(this, tr("Please choose a new name for this RSS feed"), tr("New feed name:"), QLineEdit::Normal, m_feedList->getRSSItem(item)->displayName(), &ok); // Check if name is already taken if (ok) { - if (rss_item->parent()->hasChild(newName)) { + if (rss_item->parentFolder()->hasChild(newName)) { QMessageBox::warning(0, tr("Name already in use"), tr("This name is already used by another item, please choose another one.")); ok = false; } @@ -489,7 +487,7 @@ void RSSImp::fillFeedsList(QTreeWidgetItem* parent, const Rss::FolderPtr& rss_pa if (parent) children = rss_parent->getContent(); else - children = m_rssManager->getContent(); + children = m_rssManager->rootFolder()->getContent(); foreach (const Rss::FilePtr& rssFile, children) { QTreeWidgetItem* item = createFolderListItem(rssFile); Q_ASSERT(item); @@ -546,7 +544,7 @@ void RSSImp::populateArticleList(QTreeWidgetItem* item) qDebug("Getting the list of news"); Rss::ArticleList articles; - if (rss_item == m_rssManager) + if (rss_item == m_rssManager->rootFolder()) articles = rss_item->unreadArticleListByDateDesc(); else articles = rss_item->articleListByDateDesc(); @@ -655,7 +653,7 @@ void RSSImp::updateItemInfos(QTreeWidgetItem *item) return; QString name; - if (rss_item == m_rssManager) { + if (rss_item == m_rssManager->rootFolder()) { name = tr("Unread"); emit updateRSSCount(rss_item->unreadCount()); } @@ -799,7 +797,7 @@ void RSSImp::on_rssDownloaderBtn_clicked() AutomatedRssDownloader dlg(m_rssManager, this); dlg.exec(); if (dlg.isRssDownloaderEnabled()) { - m_rssManager->recheckRssItemsForDownload(); + m_rssManager->rootFolder()->recheckRssItemsForDownload(); refreshAllFeeds(); } } From 66620810442085e4282b772f8892190c9dd03d38 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 24 Oct 2015 11:13:35 +0300 Subject: [PATCH 5/5] Improve RSS parsing logic. --- src/base/rss/private/rssparser.cpp | 495 ++++++++++++----------------- src/base/rss/private/rssparser.h | 49 +-- src/base/rss/rssfeed.cpp | 38 ++- src/base/rss/rssfeed.h | 13 +- src/base/rss/rssmanager.cpp | 11 +- src/base/rss/rssmanager.h | 11 +- 6 files changed, 263 insertions(+), 354 deletions(-) diff --git a/src/base/rss/private/rssparser.cpp b/src/base/rss/private/rssparser.cpp index eecfe1af1..8dc1b0849 100644 --- a/src/base/rss/private/rssparser.cpp +++ b/src/base/rss/private/rssparser.cpp @@ -1,6 +1,7 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2012 Christophe Dumez + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,248 +31,226 @@ #include #include -#include #include #include #include #include -#include "base/utils/fs.h" #include "rssparser.h" -namespace Rss +namespace { - namespace Private + const char shortDay[][4] = { + "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat", + "Sun" + }; + + const char longDay[][10] = { + "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", + "Sunday" + }; + + const char shortMonth[][4] = { + "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" + }; + + // Ported to Qt from KDElibs4 + QDateTime parseDate(const QString &string) { - struct ParsingJob - { - QString feedUrl; - QByteArray feedData; - }; - } -} - -static const char shortDay[][4] = { - "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat", - "Sun" -}; - -static const char longDay[][10] = { - "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", - "Sunday" -}; - -static const char shortMonth[][4] = { - "Jan", "Feb", "Mar", "Apr", - "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec" -}; - -using namespace Rss::Private; - -Parser::Parser(QObject *parent) - : QThread(parent) - , m_running(true) -{ - start(); -} - -Parser::~Parser() -{ - m_running = false; - m_waitCondition.wakeOne(); - wait(); -} - -// Ported to Qt from KDElibs4 -QDateTime Parser::parseDate(const QString &string) -{ - const QString str = string.trimmed(); - if (str.isEmpty()) - return QDateTime::currentDateTime(); - - int nyear = 6; // indexes within string to values - int nmonth = 4; - int nday = 2; - int nwday = 1; - int nhour = 7; - int nmin = 8; - int nsec = 9; - // Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm" - QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$"); - QStringList parts; - if (!str.indexOf(rx)) { - // Check that if date has '-' separators, both separators are '-'. - parts = rx.capturedTexts(); - bool h1 = (parts[3] == QLatin1String("-")); - bool h2 = (parts[5] == QLatin1String("-")); - if (h1 != h2) + const QString str = string.trimmed(); + if (str.isEmpty()) return QDateTime::currentDateTime(); - } - else { - // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" - rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"); - if (str.indexOf(rx)) - return QDateTime::currentDateTime(); - nyear = 7; - nmonth = 2; - nday = 3; - nwday = 1; - nhour = 4; - nmin = 5; - nsec = 6; - parts = rx.capturedTexts(); - } - bool ok[4]; - const int day = parts[nday].toInt(&ok[0]); - int year = parts[nyear].toInt(&ok[1]); - const int hour = parts[nhour].toInt(&ok[2]); - const int minute = parts[nmin].toInt(&ok[3]); - if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) - return QDateTime::currentDateTime(); - - int second = 0; - if (!parts[nsec].isEmpty()) { - second = parts[nsec].toInt(&ok[0]); - if (!ok[0]) - return QDateTime::currentDateTime(); - } - - bool leapSecond = (second == 60); - if (leapSecond) - second = 59; // apparently a leap second - validate below, once time zone is known - int month = 0; - for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month); - int dayOfWeek = -1; - if (!parts[nwday].isEmpty()) { - // Look up the weekday name - while (++dayOfWeek < 7 && (shortDay[dayOfWeek] != parts[nwday])); - if (dayOfWeek >= 7) - for (dayOfWeek = 0; dayOfWeek < 7 && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek); - } - - // if (month >= 12 || dayOfWeek >= 7 - // || (dayOfWeek < 0 && format == RFCDateDay)) - // return QDateTime; - int i = parts[nyear].size(); - if (i < 4) { - // It's an obsolete year specification with less than 4 digits - year += (i == 2 && year < 50) ? 2000 : 1900; - } - - // Parse the UTC offset part - int offset = 0; // set default to '-0000' - bool negOffset = false; - if (parts.count() > 10) { - rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$"); - if (!parts[10].indexOf(rx)) { - // It's a UTC offset ±hhmm + int nyear = 6; // indexes within string to values + int nmonth = 4; + int nday = 2; + int nwday = 1; + int nhour = 7; + int nmin = 8; + int nsec = 9; + // Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm" + QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$"); + QStringList parts; + if (!str.indexOf(rx)) { + // Check that if date has '-' separators, both separators are '-'. parts = rx.capturedTexts(); - offset = parts[2].toInt(&ok[0]) * 3600; - int offsetMin = parts[3].toInt(&ok[1]); - if (!ok[0] || !ok[1] || offsetMin > 59) - return QDateTime(); - offset += offsetMin * 60; - negOffset = (parts[1] == QLatin1String("-")); - if (negOffset) - offset = -offset; + bool h1 = (parts[3] == QLatin1String("-")); + bool h2 = (parts[5] == QLatin1String("-")); + if (h1 != h2) + return QDateTime::currentDateTime(); } else { - // Check for an obsolete time zone name - QByteArray zone = parts[10].toLatin1(); - if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') { - negOffset = true; // military zone: RFC 2822 treats as '-0000' + // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" + rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"); + if (str.indexOf(rx)) + return QDateTime::currentDateTime(); + nyear = 7; + nmonth = 2; + nday = 3; + nwday = 1; + nhour = 4; + nmin = 5; + nsec = 6; + parts = rx.capturedTexts(); + } + + bool ok[4]; + const int day = parts[nday].toInt(&ok[0]); + int year = parts[nyear].toInt(&ok[1]); + const int hour = parts[nhour].toInt(&ok[2]); + const int minute = parts[nmin].toInt(&ok[3]); + if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) + return QDateTime::currentDateTime(); + + int second = 0; + if (!parts[nsec].isEmpty()) { + second = parts[nsec].toInt(&ok[0]); + if (!ok[0]) + return QDateTime::currentDateTime(); + } + + bool leapSecond = (second == 60); + if (leapSecond) + second = 59; // apparently a leap second - validate below, once time zone is known + int month = 0; + for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month); + int dayOfWeek = -1; + if (!parts[nwday].isEmpty()) { + // Look up the weekday name + while (++dayOfWeek < 7 && (shortDay[dayOfWeek] != parts[nwday])); + if (dayOfWeek >= 7) + for (dayOfWeek = 0; dayOfWeek < 7 && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek); + } + + // if (month >= 12 || dayOfWeek >= 7 + // || (dayOfWeek < 0 && format == RFCDateDay)) + // return QDateTime; + int i = parts[nyear].size(); + if (i < 4) { + // It's an obsolete year specification with less than 4 digits + year += (i == 2 && year < 50) ? 2000 : 1900; + } + + // Parse the UTC offset part + int offset = 0; // set default to '-0000' + bool negOffset = false; + if (parts.count() > 10) { + rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$"); + if (!parts[10].indexOf(rx)) { + // It's a UTC offset ±hhmm + parts = rx.capturedTexts(); + offset = parts[2].toInt(&ok[0]) * 3600; + int offsetMin = parts[3].toInt(&ok[1]); + if (!ok[0] || !ok[1] || offsetMin > 59) + return QDateTime(); + offset += offsetMin * 60; + negOffset = (parts[1] == QLatin1String("-")); + if (negOffset) + offset = -offset; } - else if (zone != "UT" && zone != "GMT") { // treated as '+0000' - offset = (zone == "EDT") - ? -4 * 3600 - : ((zone == "EST") || (zone == "CDT")) - ? -5 * 3600 - : ((zone == "CST") || (zone == "MDT")) - ? -6 * 3600 - : (zone == "MST" || zone == "PDT") - ? -7 * 3600 - : (zone == "PST") - ? -8 * 3600 - : 0; - if (!offset) { - // Check for any other alphabetic time zone - bool nonalpha = false; - for (int i = 0, end = zone.size(); (i < end) && !nonalpha; ++i) - nonalpha = !isalpha(zone[i]); - if (nonalpha) - return QDateTime(); - // TODO: Attempt to recognize the time zone abbreviation? - negOffset = true; // unknown time zone: RFC 2822 treats as '-0000' + else { + // Check for an obsolete time zone name + QByteArray zone = parts[10].toLatin1(); + if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') { + negOffset = true; // military zone: RFC 2822 treats as '-0000' + } + else if (zone != "UT" && zone != "GMT") { // treated as '+0000' + offset = (zone == "EDT") + ? -4 * 3600 + : ((zone == "EST") || (zone == "CDT")) + ? -5 * 3600 + : ((zone == "CST") || (zone == "MDT")) + ? -6 * 3600 + : (zone == "MST" || zone == "PDT") + ? -7 * 3600 + : (zone == "PST") + ? -8 * 3600 + : 0; + if (!offset) { + // Check for any other alphabetic time zone + bool nonalpha = false; + for (int i = 0, end = zone.size(); (i < end) && !nonalpha; ++i) + nonalpha = !isalpha(zone[i]); + if (nonalpha) + return QDateTime(); + // TODO: Attempt to recognize the time zone abbreviation? + negOffset = true; // unknown time zone: RFC 2822 treats as '-0000' + } } } } + + QDate qdate(year, month + 1, day); // convert date, and check for out-of-range + if (!qdate.isValid()) + return QDateTime::currentDateTime(); + + QTime qTime(hour, minute, second); + QDateTime result(qdate, qTime, Qt::UTC); + if (offset) + result = result.addSecs(-offset); + if (!result.isValid()) + return QDateTime::currentDateTime(); // invalid date/time + + if (leapSecond) { + // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. + // Convert the time to UTC and check that it is 00:00:00. + if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) + return QDateTime::currentDateTime(); // the time isn't the last second of the day + } + + return result; } - - QDate qdate(year, month + 1, day); // convert date, and check for out-of-range - if (!qdate.isValid()) - return QDateTime::currentDateTime(); - - QTime qTime(hour, minute, second); - QDateTime result(qdate, qTime, Qt::UTC); - if (offset) - result = result.addSecs(-offset); - if (!result.isValid()) - return QDateTime::currentDateTime(); // invalid date/time - - if (leapSecond) { - // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. - // Convert the time to UTC and check that it is 00:00:00. - if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) - return QDateTime::currentDateTime(); // the time isn't the last second of the day - } - - return result; } -void Parser::parseFeedData(const QString &feedUrl, const QByteArray &feedData) -{ - qDebug() << Q_FUNC_INFO << feedUrl; - m_mutex.lock(); - ParsingJob job = { feedUrl, feedData }; - m_queue.enqueue(job); - // Wake up thread. - if (m_queue.count() == 1) { - qDebug() << Q_FUNC_INFO << "Waking up thread"; - m_waitCondition.wakeOne(); - } - m_mutex.unlock(); -} +using namespace Rss::Private; -void Parser::clearFeedData(const QString &feedUrl) +// read and create items from a rss document +void Parser::parse(const QByteArray &feedData) { - m_mutex.lock(); - m_lastBuildDates.remove(feedUrl); - m_mutex.unlock(); -} + qDebug() << Q_FUNC_INFO; -void Parser::run() -{ - while (m_running) { - m_mutex.lock(); - if (!m_queue.empty()) { - ParsingJob job = m_queue.dequeue(); - m_mutex.unlock(); - parseFeed(job); + QXmlStreamReader xml(feedData); + bool foundChannel = false; + while (xml.readNextStartElement()) { + if (xml.name() == "rss") { + // Find channels + while (xml.readNextStartElement()) { + if (xml.name() == "channel") { + parseRSSChannel(xml); + foundChannel = true; + break; + } + else { + qDebug() << "Skip rss item: " << xml.name(); + xml.skipCurrentElement(); + } + } + break; + } + else if (xml.name() == "feed") { // Atom feed + parseAtomChannel(xml); + foundChannel = true; + break; } else { - qDebug() << Q_FUNC_INFO << "Thread is waiting."; - m_waitCondition.wait(&m_mutex); - qDebug() << Q_FUNC_INFO << "Thread woke up."; - m_mutex.unlock(); + qDebug() << "Skip root item: " << xml.name(); + xml.skipCurrentElement(); } } + + if (xml.hasError()) + emit finished(xml.errorString()); + else if (!foundChannel) + emit finished(tr("Invalid RSS feed.")); + else + emit finished(QString()); } -void Parser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseRssArticle(QXmlStreamReader &xml) { QVariantHash article; @@ -332,12 +311,12 @@ void Parser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl) } } - emit newArticle(feedUrl, article); + emit newArticle(article); } -void Parser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseRSSChannel(QXmlStreamReader &xml) { - qDebug() << Q_FUNC_INFO << feedUrl; + qDebug() << Q_FUNC_INFO; Q_ASSERT(xml.isStartElement() && xml.name() == "channel"); while(!xml.atEnd()) { @@ -346,27 +325,26 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl) if (xml.isStartElement()) { if (xml.name() == "title") { QString title = xml.readElementText(); - emit feedTitle(feedUrl, title); + emit feedTitle(title); } else if (xml.name() == "lastBuildDate") { QString lastBuildDate = xml.readElementText(); if (!lastBuildDate.isEmpty()) { - QMutexLocker locker(&m_mutex); - if (m_lastBuildDates.value(feedUrl, "") == lastBuildDate) { + if (m_lastBuildDate == lastBuildDate) { qDebug() << "The RSS feed has not changed since last time, aborting parsing."; return; } - m_lastBuildDates[feedUrl] = lastBuildDate; + m_lastBuildDate = lastBuildDate; } } else if (xml.name() == "item") { - parseRssArticle(xml, feedUrl); + parseRssArticle(xml); } } } } -void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl) +void Parser::parseAtomArticle(QXmlStreamReader &xml) { QVariantHash article; bool doubleContent = false; @@ -392,7 +370,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, con // Atom feeds can have relative links, work around this and // take the stress of figuring article full URI from UI // Assemble full URI - article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link ); + article["news_link"] = ( m_baseUrl.isEmpty() ? link : m_baseUrl + link ); } else if ((xml.name() == "summary") || (xml.name() == "content")){ @@ -453,15 +431,15 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, con } } - emit newArticle(feedUrl, article); + emit newArticle(article); } -void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) +void Parser::parseAtomChannel(QXmlStreamReader &xml) { - qDebug() << Q_FUNC_INFO << feedUrl; + qDebug() << Q_FUNC_INFO; Q_ASSERT(xml.isStartElement() && xml.name() == "feed"); - QString baseURL = xml.attributes().value("xml:base").toString(); + m_baseUrl = xml.attributes().value("xml:base").toString(); while (!xml.atEnd()) { xml.readNext(); @@ -469,74 +447,21 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl) if (xml.isStartElement()) { if (xml.name() == "title") { QString title = xml.readElementText(); - emit feedTitle(feedUrl, title); + emit feedTitle(title); } else if (xml.name() == "updated") { QString lastBuildDate = xml.readElementText(); if (!lastBuildDate.isEmpty()) { - QMutexLocker locker(&m_mutex); - if (m_lastBuildDates.value(feedUrl) == lastBuildDate) { + if (m_lastBuildDate == lastBuildDate) { qDebug() << "The RSS feed has not changed since last time, aborting parsing."; return; } - m_lastBuildDates[feedUrl] = lastBuildDate; + m_lastBuildDate = lastBuildDate; } } else if (xml.name() == "entry") { - parseAtomArticle(xml, feedUrl, baseURL); + parseAtomArticle(xml); } } } } - -// read and create items from a rss document -void Parser::parseFeed(const ParsingJob &job) -{ - qDebug() << Q_FUNC_INFO << job.feedUrl; - - QXmlStreamReader xml(job.feedData); - bool foundChannel = false; - while (xml.readNextStartElement()) { - if (xml.name() == "rss") { - // Find channels - while (xml.readNextStartElement()) { - if (xml.name() == "channel") { - parseRSSChannel(xml, job.feedUrl); - foundChannel = true; - break; - } - else { - qDebug() << "Skip rss item: " << xml.name(); - xml.skipCurrentElement(); - } - } - break; - } - else if (xml.name() == "feed") { // Atom feed - parseAtomChannel(xml, job.feedUrl); - foundChannel = true; - break; - } - else { - qDebug() << "Skip root item: " << xml.name(); - xml.skipCurrentElement(); - } - } - - if (xml.hasError()) { - reportFailure(job, xml.errorString()); - return; - } - - if (!foundChannel) { - reportFailure(job, tr("Invalid RSS feed at '%1'.").arg(job.feedUrl)); - return; - } - - emit feedParsingFinished(job.feedUrl, QString()); -} - -void Parser::reportFailure(const ParsingJob &job, const QString &error) -{ - emit feedParsingFinished(job.feedUrl, error); -} diff --git a/src/base/rss/private/rssparser.h b/src/base/rss/private/rssparser.h index 89893feaa..afbf1df03 100644 --- a/src/base/rss/private/rssparser.h +++ b/src/base/rss/private/rssparser.h @@ -1,6 +1,7 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2012 Christophe Dumez + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,12 +32,9 @@ #ifndef RSSPARSER_H #define RSSPARSER_H -#include -#include -#include -#include +#include +#include #include -#include class QXmlStreamReader; @@ -44,41 +42,26 @@ namespace Rss { namespace Private { - struct ParsingJob; - - class Parser: public QThread + class Parser: public QObject { Q_OBJECT - public: - explicit Parser(QObject *parent = 0); - ~Parser(); - - void parseFeedData(const QString &feedUrl, const QByteArray &feedData); - void clearFeedData(const QString &feedUrl); + public slots: + void parse(const QByteArray &feedData); signals: - void newArticle(const QString &feedUrl, const QVariantHash &rssArticle); - void feedTitle(const QString &feedUrl, const QString &title); - void feedParsingFinished(const QString &feedUrl, const QString &error); + void newArticle(const QVariantHash &rssArticle); + void feedTitle(const QString &title); + void finished(const QString &error); private: - void run() override; + void parseRssArticle(QXmlStreamReader &xml); + void parseRSSChannel(QXmlStreamReader &xml); + void parseAtomArticle(QXmlStreamReader &xml); + void parseAtomChannel(QXmlStreamReader &xml); - static QDateTime parseDate(const QString &string); - - void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl); - void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl); - void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl); - void parseFeed(const ParsingJob &job); - void reportFailure(const ParsingJob &job, const QString &error); - - bool m_running; - QMutex m_mutex; - QQueue m_queue; - QWaitCondition m_waitCondition; - QHash m_lastBuildDates; // Optimization + QString m_lastBuildDate; // Optimization + QString m_baseUrl; }; } } diff --git a/src/base/rss/rssfeed.cpp b/src/base/rss/rssfeed.cpp index 76acfaf4b..066964045 100644 --- a/src/base/rss/rssfeed.cpp +++ b/src/base/rss/rssfeed.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere * @@ -67,11 +68,13 @@ Feed::Feed(const QString &url, Manager *manager) , m_loading(false) { qDebug() << Q_FUNC_INFO << m_url; + m_parser = new Private::Parser; + m_parser->moveToThread(m_manager->workingThread()); + connect(this, SIGNAL(destroyed()), m_parser, SLOT(deleteLater())); // Listen for new RSS downloads - Private::Parser *const parser = m_manager->rssParser(); - connect(parser, SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); - connect(parser, SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); - connect(parser, SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleParsingFinished(QString,QString))); + connect(m_parser, SIGNAL(feedTitle(QString)), SLOT(handleFeedTitle(QString))); + connect(m_parser, SIGNAL(newArticle(QVariantHash)), SLOT(handleNewArticle(QVariantHash))); + connect(m_parser, SIGNAL(finished(QString)), SLOT(handleParsingFinished(QString))); // Download the RSS Feed icon Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true); @@ -87,7 +90,6 @@ Feed::~Feed() { if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) Utils::Fs::forceRemove(m_icon); - m_manager->rssParser()->clearFeedData(m_url); } void Feed::saveItemsToDisk() @@ -320,7 +322,6 @@ QString Feed::iconUrl() const void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath) { Q_UNUSED(url); - m_icon = filePath; qDebug() << Q_FUNC_INFO << "icon path:" << m_icon; m_manager->forwardFeedIconChanged(m_url, m_icon); @@ -328,30 +329,31 @@ void Feed::handleIconDownloadFinished(const QString &url, const QString &filePat void Feed::handleRssDownloadFinished(const QString &url, const QByteArray &data) { - qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; + Q_UNUSED(url); + qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << m_url; // Parse the download RSS - m_manager->rssParser()->parseFeedData(m_url, data); + QMetaObject::invokeMethod(m_parser, "parse", Qt::QueuedConnection, Q_ARG(QByteArray, data)); } void Feed::handleRssDownloadFailed(const QString &url, const QString &error) { + Q_UNUSED(url); m_inErrorState = true; m_loading = false; m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); - qWarning() << "Failed to download RSS feed at" << url; + qWarning() << "Failed to download RSS feed at" << m_url; qWarning() << "Reason:" << error; } -void Feed::handleFeedTitle(const QString &feedUrl, const QString &title) +void Feed::handleFeedTitle(const QString &title) { - if (feedUrl != m_url) return; if (m_title == title) return; m_title = title; // Notify that we now have something better than a URL to display if (m_alias.isEmpty()) - m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount); + m_manager->forwardFeedInfosChanged(m_url, title, m_unreadCount); } void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article) @@ -406,13 +408,11 @@ void Feed::recheckRssItemsForDownload() } } -void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleData) +void Feed::handleNewArticle(const QVariantHash &articleData) { - if (feedUrl != m_url) return; - ArticlePtr article = Article::fromHash(this, articleData); if (article.isNull()) { - qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << feedUrl; + qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << m_url; return; } Q_ASSERT(article); @@ -424,12 +424,10 @@ void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleD //m_manager->forwardFeedContentChanged(m_url); } -void Feed::handleParsingFinished(const QString &feedUrl, const QString &error) +void Feed::handleParsingFinished(const QString &error) { - if (feedUrl != m_url) return; - if (!error.isEmpty()) { - qWarning() << "Failed to parse RSS feed at" << feedUrl; + qWarning() << "Failed to parse RSS feed at" << m_url; qWarning() << "Reason:" << error; } diff --git a/src/base/rss/rssfeed.h b/src/base/rss/rssfeed.h index 5198234bd..b457a04b4 100644 --- a/src/base/rss/rssfeed.h +++ b/src/base/rss/rssfeed.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere * @@ -51,6 +52,11 @@ namespace Rss typedef QSharedPointer FeedPtr; typedef QList FeedList; + namespace Private + { + class Parser; + } + bool articleDateRecentThan(const ArticlePtr &left, const ArticlePtr &right); class Feed: public QObject, public File @@ -86,9 +92,9 @@ namespace Rss void handleIconDownloadFinished(const QString &url, const QString &filePath); void handleRssDownloadFinished(const QString &url, const QByteArray &data); void handleRssDownloadFailed(const QString &url, const QString &error); - void handleFeedTitle(const QString &feedUrl, const QString &title); - void handleNewArticle(const QString &feedUrl, const QVariantHash &article); - void handleParsingFinished(const QString &feedUrl, const QString &error); + void handleFeedTitle(const QString &title); + void handleNewArticle(const QVariantHash &article); + void handleParsingFinished(const QString &error); void handleArticleRead(); private: @@ -99,6 +105,7 @@ namespace Rss private: Manager *m_manager; + Private::Parser *m_parser; ArticleHash m_articles; ArticleList m_articlesByDate; // Articles sorted by date (more recent first) QString m_title; diff --git a/src/base/rss/rssmanager.cpp b/src/base/rss/rssmanager.cpp index a7bd087d1..8fc936d6e 100644 --- a/src/base/rss/rssmanager.cpp +++ b/src/base/rss/rssmanager.cpp @@ -33,7 +33,6 @@ #include "base/logger.h" #include "base/preferences.h" -#include "private/rssparser.h" #include "rssfolder.h" #include "rssfeed.h" #include "rssarticle.h" @@ -48,9 +47,10 @@ using namespace Rss::Private; Manager::Manager(QObject *parent) : QObject(parent) , m_downloadRules(new DownloadRuleList) - , m_rssParser(new Parser(this)) , m_rootFolder(new Folder) + , m_workingThread(new QThread(this)) { + m_workingThread->start(); connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); m_refreshInterval = Preferences::instance()->getRSSRefreshInterval(); m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN); @@ -59,8 +59,9 @@ Manager::Manager(QObject *parent) Manager::~Manager() { qDebug("Deleting RSSManager..."); + m_workingThread->quit(); + m_workingThread->wait(); delete m_downloadRules; - delete m_rssParser; m_rootFolder->saveItemsToDisk(); saveStreamList(); m_rootFolder.clear(); @@ -178,9 +179,9 @@ FolderPtr Manager::rootFolder() const return m_rootFolder; } -Parser *Manager::rssParser() const +QThread *Manager::workingThread() const { - return m_rssParser; + return m_workingThread; } void Manager::refresh() diff --git a/src/base/rss/rssmanager.h b/src/base/rss/rssmanager.h index 2321ce5a2..3c7b81738 100644 --- a/src/base/rss/rssmanager.h +++ b/src/base/rss/rssmanager.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace Rss { @@ -48,11 +49,6 @@ namespace Rss typedef QSharedPointer FolderPtr; typedef QSharedPointer FeedPtr; - namespace Private - { - class Parser; - } - typedef QSharedPointer ManagerPtr; class Manager: public QObject @@ -65,8 +61,7 @@ namespace Rss DownloadRuleList *downloadRules() const; FolderPtr rootFolder() const; - - Private::Parser *rssParser() const; + QThread *workingThread() const; public slots: void refresh(); @@ -87,8 +82,8 @@ namespace Rss QTimer m_refreshTimer; uint m_refreshInterval; DownloadRuleList *m_downloadRules; - Private::Parser *m_rssParser; FolderPtr m_rootFolder; + QThread *m_workingThread; }; }