mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-22 01:06:03 +03:00
Big restructuring of the RSS code
Dropped Qt 4.4 support
This commit is contained in:
parent
f7cb6a7da5
commit
a27d2dcac2
21 changed files with 1611 additions and 1344 deletions
2
INSTALL
2
INSTALL
|
@ -10,7 +10,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4
|
|||
will install and execute qBittorrent hopefully without any problems.
|
||||
|
||||
Dependencies:
|
||||
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
||||
- Qt >= 4.5.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
||||
|
||||
- pkg-config executable
|
||||
|
||||
|
|
8
configure
vendored
8
configure
vendored
|
@ -325,7 +325,7 @@ cat >$1/modules.cpp <<EOT
|
|||
#line 1 "qt4.qcm"
|
||||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: Qt >= 4.4
|
||||
name: Qt >= 4.5
|
||||
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
|
@ -333,14 +333,14 @@ class qc_qt4 : public ConfObj
|
|||
{
|
||||
public:
|
||||
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.4"; }
|
||||
QString shortname() const { return "Qt 4.4"; }
|
||||
QString name() const { return "Qt >= 4.5"; }
|
||||
QString shortname() const { return "Qt 4.5"; }
|
||||
bool exec()
|
||||
{
|
||||
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||
conf->addDefine("DISABLE_GUI");
|
||||
}
|
||||
return(QT_VERSION >= 0x040400);
|
||||
return(QT_VERSION >= 0x040500);
|
||||
}
|
||||
};
|
||||
#line 1 "pkg-config.qcm"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: Qt >= 4.4
|
||||
name: Qt >= 4.5
|
||||
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
|
@ -8,13 +8,13 @@ class qc_qt4 : public ConfObj
|
|||
{
|
||||
public:
|
||||
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.4"; }
|
||||
QString shortname() const { return "Qt 4.4"; }
|
||||
QString name() const { return "Qt >= 4.5"; }
|
||||
QString shortname() const { return "Qt 4.5"; }
|
||||
bool exec()
|
||||
{
|
||||
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||
conf->addDefine("DISABLE_GUI");
|
||||
}
|
||||
return(QT_VERSION >= 0x040400);
|
||||
return(QT_VERSION >= 0x040500);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -41,18 +41,12 @@
|
|||
#include <QFile>
|
||||
#include <QDataStream>
|
||||
#include <QFileDialog>
|
||||
#include <QHash>
|
||||
|
||||
#include "qbtsession.h"
|
||||
#include "ui_feeddownloader.h"
|
||||
#include "qinisettings.h"
|
||||
|
||||
#if QT_VERSION >= 0x040500
|
||||
#include <QHash>
|
||||
#else
|
||||
#include <QMap>
|
||||
#define QHash QMap
|
||||
#define toHash toMap
|
||||
#endif
|
||||
|
||||
class FeedFilter: public QHash<QString, QVariant> {
|
||||
private:
|
||||
|
@ -508,7 +502,4 @@ signals:
|
|||
|
||||
};
|
||||
|
||||
#undef QHash
|
||||
#undef toHash
|
||||
|
||||
#endif // FEEDDOWNLOADER_H
|
||||
|
|
|
@ -28,15 +28,17 @@
|
|||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#
|
||||
#include "feedlistwidget.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfeed.h"
|
||||
|
||||
FeedListWidget::FeedListWidget(QWidget *parent, RssManager *rssmanager): QTreeWidget(parent), rssmanager(rssmanager) {
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setDragDropMode(QAbstractItemView::InternalMove);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setColumnCount(1);
|
||||
QTreeWidgetItem *___qtreewidgetitem = headerItem();
|
||||
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
|
||||
headerItem()->setText(0, tr("RSS feeds"));
|
||||
unread_item = new QTreeWidgetItem(this);
|
||||
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
|
||||
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
|
||||
|
@ -52,18 +54,18 @@ FeedListWidget::~FeedListWidget() {
|
|||
|
||||
void FeedListWidget::itemAdded(QTreeWidgetItem *item, RssFile* file) {
|
||||
mapping[item] = file;
|
||||
if(file->getType() == RssFile::STREAM) {
|
||||
if(file->getType() == RssFile::FEED) {
|
||||
feeds_items[file->getID()] = item;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedListWidget::itemAboutToBeRemoved(QTreeWidgetItem *item) {
|
||||
RssFile* file = mapping.take(item);
|
||||
if(file->getType() == RssFile::STREAM) {
|
||||
if(file->getType() == RssFile::FEED) {
|
||||
feeds_items.remove(file->getID());
|
||||
} else {
|
||||
QList<RssStream*> feeds = ((RssFolder*)file)->getAllFeeds();
|
||||
foreach(RssStream* feed, feeds) {
|
||||
QList<RssFeed*> feeds = ((RssFolder*)file)->getAllFeeds();
|
||||
foreach(RssFeed* feed, feeds) {
|
||||
feeds_items.remove(feed->getID());
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ QList<QTreeWidgetItem*> FeedListWidget::getAllFeedItems(QTreeWidgetItem* folder)
|
|||
int nbChildren = folder->childCount();
|
||||
for(int i=0; i<nbChildren; ++i) {
|
||||
QTreeWidgetItem *item = folder->child(i);
|
||||
if(getItemType(item) == RssFile::STREAM) {
|
||||
if(getItemType(item) == RssFile::FEED) {
|
||||
feeds << item;
|
||||
} else {
|
||||
feeds << getAllFeedItems(item);
|
||||
|
@ -146,8 +148,8 @@ QTreeWidgetItem* FeedListWidget::getTreeItemFromUrl(QString url) const{
|
|||
return feeds_items.value(url, 0);
|
||||
}
|
||||
|
||||
RssStream* FeedListWidget::getRSSItemFromUrl(QString url) const {
|
||||
return (RssStream*)getRSSItem(getTreeItemFromUrl(url));
|
||||
RssFeed* FeedListWidget::getRSSItemFromUrl(QString url) const {
|
||||
return (RssFeed*)getRSSItem(getTreeItemFromUrl(url));
|
||||
}
|
||||
|
||||
QTreeWidgetItem* FeedListWidget::currentItem() const {
|
||||
|
@ -161,7 +163,7 @@ QTreeWidgetItem* FeedListWidget::currentFeed() const {
|
|||
void FeedListWidget::updateCurrentFeed(QTreeWidgetItem* new_item) {
|
||||
if(!new_item) return;
|
||||
if(!mapping.contains(new_item)) return;
|
||||
if((getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
|
||||
if((getItemType(new_item) == RssFile::FEED) || new_item == unread_item)
|
||||
current_feed = new_item;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,11 @@
|
|||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QUrl>
|
||||
#include "rss.h"
|
||||
|
||||
#include "rssfile.h"
|
||||
|
||||
class RssManager;
|
||||
class RssFeed;
|
||||
|
||||
class FeedListWidget: public QTreeWidget {
|
||||
Q_OBJECT
|
||||
|
@ -59,7 +63,7 @@ public:
|
|||
RssFile::FileType getItemType(QTreeWidgetItem *item) const;
|
||||
QString getItemID(QTreeWidgetItem *item) const;
|
||||
QTreeWidgetItem* getTreeItemFromUrl(QString url) const;
|
||||
RssStream* getRSSItemFromUrl(QString url) const;
|
||||
RssFeed* getRSSItemFromUrl(QString url) const;
|
||||
QTreeWidgetItem* currentItem() const;
|
||||
QTreeWidgetItem* currentFeed() const;
|
||||
|
||||
|
|
716
src/rss/rss.cpp
716
src/rss/rss.cpp
|
@ -1,716 +0,0 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "rss.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#if QT_VERSION < 0x040500
|
||||
#include <QMap>
|
||||
#define QHash QMap
|
||||
#define toHash toMap
|
||||
#else
|
||||
#include <QHash>
|
||||
#endif
|
||||
|
||||
/** RssFolder **/
|
||||
|
||||
RssFolder::RssFolder(RssFolder *parent, RssManager *rssmanager, Bittorrent *BTSession, QString name): parent(parent), rssmanager(rssmanager), BTSession(BTSession), name(name) {
|
||||
downloader = new downloadThread(this);
|
||||
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processFinishedDownload(QString, QString)));
|
||||
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
||||
}
|
||||
|
||||
RssFolder::~RssFolder() {
|
||||
qDebug("Deleting a RSS folder, removing elements");
|
||||
qDeleteAll(this->values());
|
||||
qDebug("Deleting downloader thread");
|
||||
delete downloader;
|
||||
qDebug("Downloader thread removed");
|
||||
}
|
||||
|
||||
unsigned int RssFolder::getNbUnRead() const {
|
||||
unsigned int nb_unread = 0;
|
||||
foreach(RssFile *file, this->values()) {
|
||||
nb_unread += file->getNbUnRead();
|
||||
}
|
||||
return nb_unread;
|
||||
}
|
||||
|
||||
RssFile::FileType RssFolder::getType() const {
|
||||
return RssFile::FOLDER;
|
||||
}
|
||||
|
||||
void RssFolder::refreshAll(){
|
||||
qDebug("Refreshing all rss feeds");
|
||||
const QList<RssFile*> items = this->values();
|
||||
for(int i=0; i<items.size(); ++i) {
|
||||
//foreach(RssFile *item, *this){
|
||||
RssFile *item = items.at(i);
|
||||
if(item->getType() == RssFile::STREAM) {
|
||||
RssStream* stream = (RssStream*) item;
|
||||
QString url = stream->getUrl();
|
||||
if(stream->isLoading()) return;
|
||||
stream->setLoading(true);
|
||||
downloader->downloadUrl(url);
|
||||
if(!stream->hasCustomIcon()){
|
||||
downloader->downloadUrl(stream->getIconUrl());
|
||||
}
|
||||
} else {
|
||||
RssFolder *folder = (RssFolder*)item;
|
||||
folder->refreshAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RssFolder::removeFile(QString ID) {
|
||||
if(this->contains(ID)) {
|
||||
RssFile* child = this->take(ID);
|
||||
child->removeAllSettings();
|
||||
child->removeAllItems();
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
RssFolder* RssFolder::addFolder(QString name) {
|
||||
RssFolder *subfolder;
|
||||
if(!this->contains(name)) {
|
||||
subfolder = new RssFolder(this, rssmanager, BTSession, name);
|
||||
(*this)[name] = subfolder;
|
||||
} else {
|
||||
subfolder = (RssFolder*)this->value(name);
|
||||
}
|
||||
return subfolder;
|
||||
}
|
||||
|
||||
RssStream* RssFolder::addStream(QString url) {
|
||||
RssStream* stream = new RssStream(this, rssmanager, BTSession, url);
|
||||
Q_ASSERT(!this->contains(stream->getUrl()));
|
||||
(*this)[stream->getUrl()] = stream;
|
||||
refreshStream(stream->getUrl());
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Refresh All Children
|
||||
void RssFolder::refresh() {
|
||||
foreach(RssFile *child, this->values()) {
|
||||
// Little optimization child->refresh() would work too
|
||||
if(child->getType() == RssFile::STREAM)
|
||||
refreshStream(child->getID());
|
||||
else
|
||||
child->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssItem*> RssFolder::getNewsList() const {
|
||||
QList<RssItem*> news;
|
||||
foreach(RssFile *child, this->values()) {
|
||||
news << child->getNewsList();
|
||||
}
|
||||
return news;
|
||||
}
|
||||
|
||||
QList<RssItem*> RssFolder::getUnreadNewsList() const {
|
||||
QList<RssItem*> unread_news;
|
||||
foreach(RssFile *child, this->values()) {
|
||||
unread_news << child->getUnreadNewsList();
|
||||
}
|
||||
return unread_news;
|
||||
}
|
||||
|
||||
void RssFolder::refreshStream(QString url) {
|
||||
qDebug("Refreshing feed: %s", url.toLocal8Bit().data());
|
||||
Q_ASSERT(this->contains(url));
|
||||
RssStream *stream = (RssStream*)this->value(url);
|
||||
if(stream->isLoading()) {
|
||||
qDebug("Stream %s is already being loaded...", stream->getUrl().toLocal8Bit().data());
|
||||
return;
|
||||
}
|
||||
stream->setLoading(true);
|
||||
qDebug("stream %s : loaded=true", stream->getUrl().toLocal8Bit().data());
|
||||
downloader->downloadUrl(url);
|
||||
if(!stream->hasCustomIcon()){
|
||||
downloader->downloadUrl(stream->getIconUrl());
|
||||
}else{
|
||||
qDebug("No need to download this feed's icon, it was already downloaded");
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssFile*> RssFolder::getContent() const {
|
||||
return this->values();
|
||||
}
|
||||
|
||||
unsigned int RssFolder::getNbFeeds() const {
|
||||
unsigned int nbFeeds = 0;
|
||||
foreach(RssFile* item, this->values()) {
|
||||
if(item->getType() == RssFile::FOLDER)
|
||||
nbFeeds += ((RssFolder*)item)->getNbFeeds();
|
||||
else
|
||||
nbFeeds += 1;
|
||||
}
|
||||
return nbFeeds;
|
||||
}
|
||||
|
||||
void RssFolder::processFinishedDownload(QString url, QString path) {
|
||||
if(url.endsWith("favicon.ico")){
|
||||
// Icon downloaded
|
||||
QImage fileIcon;
|
||||
if(fileIcon.load(path)) {
|
||||
QList<RssStream*> res = findFeedsWithIcon(url);
|
||||
RssStream* stream;
|
||||
foreach(stream, res){
|
||||
stream->setIconPath(path);
|
||||
if(!stream->isLoading())
|
||||
rssmanager->forwardFeedIconChanged(stream->getUrl(), stream->getIconPath());
|
||||
}
|
||||
}else{
|
||||
qDebug("Unsupported icon format at %s", (const char*)url.toLocal8Bit());
|
||||
}
|
||||
return;
|
||||
}
|
||||
RssStream *stream = (RssStream*)this->value(url, 0);
|
||||
if(!stream){
|
||||
qDebug("This rss stream was deleted in the meantime, nothing to update");
|
||||
return;
|
||||
}
|
||||
stream->processDownloadedFile(path);
|
||||
stream->setLoading(false);
|
||||
qDebug("stream %s : loaded=false", stream->getUrl().toLocal8Bit().data());
|
||||
// If the feed has no alias, then we use the title as Alias
|
||||
// this is more user friendly
|
||||
if(stream->getName().isEmpty()){
|
||||
if(!stream->getTitle().isEmpty())
|
||||
stream->rename(stream->getTitle());
|
||||
}
|
||||
rssmanager->forwardFeedInfosChanged(url, stream->getName(), stream->getNbUnRead());
|
||||
}
|
||||
|
||||
void RssFolder::handleDownloadFailure(QString url, QString reason) {
|
||||
if(url.endsWith("favicon.ico")){
|
||||
// Icon download failure
|
||||
qDebug("Could not download icon at %s, reason: %s", (const char*)url.toLocal8Bit(), (const char*)reason.toLocal8Bit());
|
||||
return;
|
||||
}
|
||||
RssStream *stream = (RssStream*)this->value(url, 0);
|
||||
if(!stream){
|
||||
qDebug("This rss stream was deleted in the meantime, nothing to update");
|
||||
return;
|
||||
}
|
||||
stream->setLoading(false);
|
||||
qDebug("Could not download Rss at %s, reason: %s", (const char*)url.toLocal8Bit(), (const char*)reason.toLocal8Bit());
|
||||
stream->setDownloadFailed();
|
||||
rssmanager->forwardFeedInfosChanged(url, stream->getName(), stream->getNbUnRead());
|
||||
}
|
||||
|
||||
QList<RssStream*> RssFolder::findFeedsWithIcon(QString icon_url) const {
|
||||
QList<RssStream*> res;
|
||||
RssFile* item;
|
||||
foreach(item, this->values()){
|
||||
if(item->getType() == RssFile::STREAM && ((RssStream*)item)->getIconUrl() == icon_url)
|
||||
res << (RssStream*)item;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QString RssFolder::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
void RssFolder::rename(QString new_name) {
|
||||
Q_ASSERT(!parent->contains(new_name));
|
||||
if(!parent->contains(new_name)) {
|
||||
// Update parent
|
||||
(*parent)[new_name] = parent->take(name);
|
||||
// Actually rename
|
||||
name = new_name;
|
||||
}
|
||||
}
|
||||
|
||||
void RssFolder::markAllAsRead() {
|
||||
foreach(RssFile *item, this->values()) {
|
||||
item->markAllAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssStream*> RssFolder::getAllFeeds() const {
|
||||
QList<RssStream*> streams;
|
||||
foreach(RssFile *item, this->values()) {
|
||||
if(item->getType() == RssFile::STREAM) {
|
||||
streams << ((RssStream*)item);
|
||||
} else {
|
||||
foreach(RssStream* stream, ((RssFolder*)item)->getAllFeeds()) {
|
||||
streams << stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
return streams;
|
||||
}
|
||||
|
||||
void RssFolder::addFile(RssFile * item) {
|
||||
if(item->getType() == RssFile::STREAM) {
|
||||
Q_ASSERT(!this->contains(((RssStream*)item)->getUrl()));
|
||||
(*this)[((RssStream*)item)->getUrl()] = item;
|
||||
qDebug("Added feed %s to folder ./%s", ((RssStream*)item)->getUrl().toLocal8Bit().data(), name.toLocal8Bit().data());
|
||||
} else {
|
||||
Q_ASSERT(!this->contains(((RssFolder*)item)->getName()));
|
||||
(*this)[((RssFolder*)item)->getName()] = item;
|
||||
qDebug("Added folder %s to folder ./%s", ((RssFolder*)item)->getName().toLocal8Bit().data(), name.toLocal8Bit().data());
|
||||
}
|
||||
// Update parent
|
||||
item->setParent(this);
|
||||
}
|
||||
|
||||
/** RssManager **/
|
||||
|
||||
RssManager::RssManager(Bittorrent *BTSession): RssFolder(0, this, BTSession, QString::null) {
|
||||
loadStreamList();
|
||||
connect(&newsRefresher, SIGNAL(timeout()), this, SLOT(refreshAll()));
|
||||
refreshInterval = Preferences::getRSSRefreshInterval();
|
||||
newsRefresher.start(refreshInterval*60000);
|
||||
}
|
||||
|
||||
RssManager::~RssManager(){
|
||||
qDebug("Deleting RSSManager");
|
||||
saveStreamList();
|
||||
qDebug("RSSManager deleted");
|
||||
}
|
||||
|
||||
void RssManager::updateRefreshInterval(unsigned int val){
|
||||
if(refreshInterval != val) {
|
||||
refreshInterval = val;
|
||||
newsRefresher.start(refreshInterval*60000);
|
||||
qDebug("New RSS refresh interval is now every %dmin", refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void RssManager::loadStreamList(){
|
||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||
QStringList streamsUrl = settings.value("Rss/streamList").toStringList();
|
||||
QStringList aliases = settings.value("Rss/streamAlias").toStringList();
|
||||
if(streamsUrl.size() != aliases.size()){
|
||||
std::cerr << "Corrupted Rss list, not loading it\n";
|
||||
return;
|
||||
}
|
||||
unsigned int i = 0;
|
||||
foreach(QString s, streamsUrl){
|
||||
QStringList path = s.split("\\");
|
||||
if(path.empty()) continue;
|
||||
QString feed_url = path.takeLast();
|
||||
// Create feed path (if it does not exists)
|
||||
RssFolder * feed_parent = this;
|
||||
foreach(QString folder_name, path) {
|
||||
feed_parent = feed_parent->addFolder(folder_name);
|
||||
}
|
||||
// Create feed
|
||||
RssStream *stream = feed_parent->addStream(feed_url);
|
||||
QString alias = aliases.at(i);
|
||||
if(!alias.isEmpty()) {
|
||||
stream->rename(alias);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
qDebug("NB RSS streams loaded: %d", streamsUrl.size());
|
||||
}
|
||||
|
||||
void RssManager::forwardFeedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread) {
|
||||
emit feedInfosChanged(url, aliasOrUrl, nbUnread);
|
||||
}
|
||||
|
||||
void RssManager::forwardFeedIconChanged(QString url, QString icon_path) {
|
||||
emit feedIconChanged(url, icon_path);
|
||||
}
|
||||
|
||||
void RssManager::moveFile(RssFile* file, RssFolder* dest_folder) {
|
||||
RssFolder* src_folder = file->getParent();
|
||||
if(dest_folder != src_folder) {
|
||||
// Copy to new Folder
|
||||
dest_folder->addFile(file);
|
||||
// Remove reference in old folder
|
||||
src_folder->remove(file->getID());
|
||||
} else {
|
||||
qDebug("Nothing to move, same destination folder");
|
||||
}
|
||||
}
|
||||
|
||||
void RssManager::saveStreamList(){
|
||||
QStringList streamsUrl;
|
||||
QStringList aliases;
|
||||
const QList<RssStream*> streams = getAllFeeds();
|
||||
foreach(const RssStream *stream, streams) {
|
||||
QString stream_path = stream->getPath().join("\\");
|
||||
if(stream_path.isNull()) {
|
||||
stream_path = "";
|
||||
}
|
||||
qDebug("Saving stream path: %s", qPrintable(stream_path));
|
||||
streamsUrl << stream_path;
|
||||
aliases << stream->getName();
|
||||
}
|
||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||
settings.beginGroup("Rss");
|
||||
// FIXME: Empty folder are not saved
|
||||
settings.setValue("streamList", streamsUrl);
|
||||
settings.setValue("streamAlias", aliases);
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
/** RssStream **/
|
||||
|
||||
RssStream::RssStream(RssFolder* parent, RssManager *rssmanager, Bittorrent *BTSession, QString _url): parent(parent), rssmanager(rssmanager), BTSession(BTSession), alias(""), iconPath(":/Icons/rss16.png"), refreshed(false), downloadFailure(false), currently_loading(false) {
|
||||
qDebug("RSSStream constructed");
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
url = QUrl(_url).toString();
|
||||
QHash<QString, QVariant> all_old_items = qBTRSS.value("old_items", QHash<QString, QVariant>()).toHash();
|
||||
QVariantList old_items = all_old_items.value(url, QVariantList()).toList();
|
||||
qDebug("Loading %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
|
||||
foreach(const QVariant &var_it, old_items) {
|
||||
QHash<QString, QVariant> item = var_it.toHash();
|
||||
RssItem *rss_item = RssItem::fromHash(this, item);
|
||||
if(rss_item->isValid()) {
|
||||
(*this)[rss_item->getId()] = rss_item;
|
||||
} else {
|
||||
delete rss_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RssStream::~RssStream(){
|
||||
qDebug("Deleting a RSS stream: %s", getName().toLocal8Bit().data());
|
||||
if(refreshed) {
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
QVariantList old_items;
|
||||
foreach(RssItem *item, this->values()) {
|
||||
old_items << item->toHash();
|
||||
}
|
||||
qDebug("Saving %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
|
||||
QHash<QString, QVariant> all_old_items = qBTRSS.value("old_items", QHash<QString, QVariant>()).toHash();
|
||||
all_old_items[url] = old_items;
|
||||
qBTRSS.setValue("old_items", all_old_items);
|
||||
}
|
||||
qDebug("Removing all item from feed");
|
||||
removeAllItems();
|
||||
qDebug("All items were removed");
|
||||
if(QFile::exists(filePath))
|
||||
misc::safeRemove(filePath);
|
||||
if(QFile::exists(iconPath) && !iconPath.startsWith(":/"))
|
||||
misc::safeRemove(iconPath);
|
||||
}
|
||||
|
||||
RssFile::FileType RssStream::getType() const {
|
||||
return RssFile::STREAM;
|
||||
}
|
||||
|
||||
void RssStream::refresh() {
|
||||
parent->refreshStream(url);
|
||||
}
|
||||
|
||||
// delete all the items saved
|
||||
void RssStream::removeAllItems() {
|
||||
qDeleteAll(this->values());
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void RssStream::removeAllSettings() {
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
||||
if(feeds_w_downloader.contains(url)) {
|
||||
feeds_w_downloader.remove(url);
|
||||
qBTRSS.setValue("downloader_on", feeds_w_downloader);
|
||||
}
|
||||
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
||||
if(all_feeds_filters.contains(url)) {
|
||||
all_feeds_filters.remove(url);
|
||||
qBTRSS.setValue("feed_filters", all_feeds_filters);
|
||||
}
|
||||
}
|
||||
|
||||
bool RssStream::itemAlreadyExists(QString name) {
|
||||
return this->contains(name);
|
||||
}
|
||||
|
||||
void RssStream::setLoading(bool val) {
|
||||
currently_loading = val;
|
||||
}
|
||||
|
||||
bool RssStream::isLoading() {
|
||||
return currently_loading;
|
||||
}
|
||||
|
||||
QString RssStream::getTitle() const{
|
||||
return title;
|
||||
}
|
||||
|
||||
void RssStream::rename(QString new_name){
|
||||
qDebug("Renaming stream to %s", new_name.toLocal8Bit().data());
|
||||
alias = new_name;
|
||||
}
|
||||
|
||||
// Return the alias if the stream has one, the url if it has no alias
|
||||
QString RssStream::getName() const{
|
||||
if(!alias.isEmpty()) {
|
||||
//qDebug("getName() returned alias: %s", (const char*)alias.toLocal8Bit());
|
||||
return alias;
|
||||
}
|
||||
if(!title.isEmpty()) {
|
||||
//qDebug("getName() returned title: %s", (const char*)title.toLocal8Bit());
|
||||
return title;
|
||||
}
|
||||
//qDebug("getName() returned url: %s", (const char*)url.toLocal8Bit());
|
||||
return url;
|
||||
}
|
||||
|
||||
QString RssStream::getLink() const{
|
||||
return link;
|
||||
}
|
||||
|
||||
QString RssStream::getUrl() const{
|
||||
return url;
|
||||
}
|
||||
|
||||
QString RssStream::getDescription() const{
|
||||
return description;
|
||||
}
|
||||
|
||||
QString RssStream::getImage() const{
|
||||
return image;
|
||||
}
|
||||
|
||||
QString RssStream::getFilePath() const{
|
||||
return filePath;
|
||||
}
|
||||
|
||||
QString RssStream::getIconPath() const{
|
||||
if(downloadFailure)
|
||||
return ":/Icons/oxygen/unavailable.png";
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
bool RssStream::hasCustomIcon() const{
|
||||
return !iconPath.startsWith(":/");
|
||||
}
|
||||
|
||||
void RssStream::setIconPath(QString path) {
|
||||
iconPath = path;
|
||||
}
|
||||
|
||||
RssItem* RssStream::getItem(QString id) const{
|
||||
return this->value(id);
|
||||
}
|
||||
|
||||
unsigned int RssStream::getNbNews() const{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void RssStream::markAllAsRead() {
|
||||
foreach(RssItem *item, this->values()){
|
||||
item->setRead();
|
||||
}
|
||||
rssmanager->forwardFeedInfosChanged(url, getName(), 0);
|
||||
}
|
||||
|
||||
unsigned int RssStream::getNbUnRead() const{
|
||||
unsigned int nbUnread=0;
|
||||
foreach(RssItem *item, this->values()) {
|
||||
if(!item->isRead())
|
||||
++nbUnread;
|
||||
}
|
||||
return nbUnread;
|
||||
}
|
||||
|
||||
QList<RssItem*> RssStream::getNewsList() const{
|
||||
return this->values();
|
||||
}
|
||||
|
||||
QList<RssItem*> RssStream::getUnreadNewsList() const {
|
||||
QList<RssItem*> unread_news;
|
||||
foreach(RssItem *item, this->values()) {
|
||||
if(!item->isRead())
|
||||
unread_news << item;
|
||||
}
|
||||
return unread_news;
|
||||
}
|
||||
|
||||
// download the icon from the adress
|
||||
QString RssStream::getIconUrl() {
|
||||
QUrl siteUrl(url);
|
||||
return QString::fromUtf8("http://")+siteUrl.host()+QString::fromUtf8("/favicon.ico");
|
||||
}
|
||||
|
||||
// read and create items from a rss document
|
||||
short RssStream::readDoc(QIODevice* device) {
|
||||
qDebug("Parsing RSS file...");
|
||||
QXmlStreamReader xml(device);
|
||||
// is it a rss file ?
|
||||
if (xml.atEnd()) {
|
||||
qDebug("ERROR: Could not parse RSS file");
|
||||
return -1;
|
||||
}
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
if(xml.isStartElement()) {
|
||||
if(xml.name() != "rss") {
|
||||
qDebug("ERROR: this is not a rss file, root tag is <%s>", qPrintable(xml.name().toString()));
|
||||
return -1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Read channels
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement())
|
||||
break;
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
//qDebug("xml.name() == %s", qPrintable(xml.name().toString()));
|
||||
if(xml.name() == "channel") {
|
||||
qDebug("in channel");
|
||||
|
||||
// Parse channel content
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement() && xml.name() == "channel") {
|
||||
break;
|
||||
}
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
//qDebug("xml.name() == %s", qPrintable(xml.name().toString()));
|
||||
if(xml.name() == "title") {
|
||||
title = xml.readElementText();
|
||||
if(alias == getUrl())
|
||||
rename(title);
|
||||
}
|
||||
else if(xml.name() == "link") {
|
||||
link = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "description") {
|
||||
description = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "image") {
|
||||
image = xml.attributes().value("url").toString();
|
||||
}
|
||||
else if(xml.name() == "item") {
|
||||
RssItem * item = new RssItem(this, xml);
|
||||
if(item->isValid() && !itemAlreadyExists(item->getId())) {
|
||||
this->insert(item->getId(), item);
|
||||
} else {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resizeList();
|
||||
|
||||
// RSS Feed Downloader
|
||||
foreach(RssItem* item, values()) {
|
||||
if(item->isRead()) continue;
|
||||
QString torrent_url;
|
||||
if(item->has_attachment())
|
||||
torrent_url = item->getTorrentUrl();
|
||||
else
|
||||
torrent_url = item->getLink();
|
||||
// Check if the item should be automatically downloaded
|
||||
FeedFilter * matching_filter = FeedFilters::getFeedFilters(url).matches(item->getTitle());
|
||||
if(matching_filter != 0) {
|
||||
// Download the torrent
|
||||
BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName()));
|
||||
if(matching_filter->isValid()) {
|
||||
QString save_path = matching_filter->getSavePath();
|
||||
if(save_path.isEmpty())
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
||||
else
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url, save_path);
|
||||
} else {
|
||||
// All torrents are downloaded from this feed
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
||||
}
|
||||
// Item was downloaded, consider it as Read
|
||||
item->setRead();
|
||||
// Clean up
|
||||
delete matching_filter;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RssStream::resizeList() {
|
||||
unsigned int max_articles = Preferences::getRSSMaxArticlesPerFeed();
|
||||
unsigned int nb_articles = this->size();
|
||||
if(nb_articles > max_articles) {
|
||||
QList<RssItem*> listItem = RssManager::sortNewsList(this->values());
|
||||
int excess = nb_articles - max_articles;
|
||||
for(int i=0; i<excess; ++i){
|
||||
RssItem *lastItem = listItem.takeLast();
|
||||
delete this->take(lastItem->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// existing and opening test after download
|
||||
short RssStream::openRss(){
|
||||
qDebug("openRss() called");
|
||||
QFile fileRss(filePath);
|
||||
if(!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qDebug("openRss error: open failed, no file or locked, %s", (const char*)filePath.toLocal8Bit());
|
||||
if(QFile::exists(filePath)) {
|
||||
fileRss.remove();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// start reading the xml
|
||||
short return_lecture = readDoc(&fileRss);
|
||||
fileRss.close();
|
||||
if(QFile::exists(filePath)) {
|
||||
fileRss.remove();
|
||||
}
|
||||
return return_lecture;
|
||||
}
|
||||
|
||||
// read and store the downloaded rss' informations
|
||||
void RssStream::processDownloadedFile(QString file_path) {
|
||||
filePath = file_path;
|
||||
downloadFailure = false;
|
||||
if(openRss() >= 0) {
|
||||
refreshed = true;
|
||||
} else {
|
||||
qDebug("OpenRss: Feed update Failed");
|
||||
}
|
||||
}
|
||||
|
||||
void RssStream::setDownloadFailed(){
|
||||
downloadFailure = true;
|
||||
}
|
||||
|
553
src/rss/rss.h
553
src/rss/rss.h
|
@ -1,553 +0,0 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSS_H
|
||||
#define RSS_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QTemporaryFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QTime>
|
||||
#include <QUrl>
|
||||
#include <QTimer>
|
||||
#include <QImage>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
#include "misc.h"
|
||||
#include "feeddownloader.h"
|
||||
#include "qbtsession.h"
|
||||
#include "downloadthread.h"
|
||||
|
||||
#if QT_VERSION >= 0x040500
|
||||
#include <QHash>
|
||||
#else
|
||||
#include <QMap>
|
||||
#define QHash QMap
|
||||
#define toHash toMap
|
||||
#endif
|
||||
|
||||
class RssManager;
|
||||
class RssFile; // Folder or Stream
|
||||
class RssFolder;
|
||||
class RssStream;
|
||||
class RssItem;
|
||||
|
||||
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"
|
||||
};
|
||||
static const char longMonth[][10] = {
|
||||
"January", "February", "March",
|
||||
"April", "May", "June",
|
||||
"July", "August", "September",
|
||||
"October", "November", "December"
|
||||
};
|
||||
|
||||
class RssFile: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum FileType {STREAM, FOLDER};
|
||||
|
||||
RssFile(): QObject() {}
|
||||
virtual ~RssFile() {}
|
||||
|
||||
virtual unsigned int getNbUnRead() const = 0;
|
||||
virtual FileType getType() const = 0;
|
||||
virtual QString getName() const = 0;
|
||||
virtual QString getID() const = 0;
|
||||
virtual void removeAllItems() = 0;
|
||||
virtual void rename(QString new_name) = 0;
|
||||
virtual void markAllAsRead() = 0;
|
||||
virtual RssFolder* getParent() const = 0;
|
||||
virtual void setParent(RssFolder*) = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual void removeAllSettings() = 0;
|
||||
virtual QList<RssItem*> getNewsList() const = 0;
|
||||
virtual QList<RssItem*> getUnreadNewsList() const = 0;
|
||||
QStringList getPath() const {
|
||||
QStringList path;
|
||||
if(getParent()) {
|
||||
path = ((RssFile*)getParent())->getPath();
|
||||
path.append(getID());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
// Item of a rss stream, single information
|
||||
class RssItem: public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
RssStream* parent;
|
||||
QString id;
|
||||
QString title;
|
||||
QString torrent_url;
|
||||
QString news_link;
|
||||
QString description;
|
||||
QDateTime date;
|
||||
QString author;
|
||||
|
||||
|
||||
bool is_valid;
|
||||
bool read;
|
||||
|
||||
protected:
|
||||
// Ported to Qt4 from KDElibs4
|
||||
QDateTime parseDate(const QString &string) {
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QDate qdate(year, month+1, day); // convert date, and check for out-of-range
|
||||
if (!qdate.isValid())
|
||||
return QDateTime::currentDateTime();
|
||||
QDateTime result(qdate, QTime(hour, minute, second));
|
||||
if (!result.isValid()
|
||||
|| (dayOfWeek >= 0 && result.date().dayOfWeek() != dayOfWeek+1))
|
||||
return QDateTime::currentDateTime(); // invalid date/time, or weekday doesn't correspond with date
|
||||
if (!offset) {
|
||||
result.setTimeSpec(Qt::UTC);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public:
|
||||
// public constructor
|
||||
RssItem(RssStream* parent, QXmlStreamReader& xml): parent(parent), read(false) {
|
||||
is_valid = false;
|
||||
torrent_url = QString::null;
|
||||
news_link = QString::null;
|
||||
title = QString::null;
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement() && xml.name() == "item")
|
||||
break;
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
if(xml.name() == "title") {
|
||||
title = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "enclosure") {
|
||||
if(xml.attributes().value("type") == "application/x-bittorrent") {
|
||||
torrent_url = xml.attributes().value("url").toString();
|
||||
}
|
||||
}
|
||||
else if(xml.name() == "link") {
|
||||
news_link = xml.readElementText();
|
||||
if(id.isEmpty())
|
||||
id = news_link;
|
||||
}
|
||||
else if(xml.name() == "description") {
|
||||
description = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "pubDate") {
|
||||
date = parseDate(xml.readElementText());
|
||||
}
|
||||
else if(xml.name() == "author") {
|
||||
author = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "guid") {
|
||||
id = xml.readElementText();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!id.isEmpty())
|
||||
is_valid = true;
|
||||
}
|
||||
|
||||
RssItem(RssStream* parent, QString _id, QString _title, QString _torrent_url, QString _news_link, QString _description, QDateTime _date, QString _author, bool _read):
|
||||
parent(parent), id(_id), title(_title), torrent_url(_torrent_url), news_link(_news_link), description(_description), date(_date), author(_author), read(_read){
|
||||
if(id.isEmpty())
|
||||
id = news_link;
|
||||
if(!id.isEmpty()) {
|
||||
is_valid = true;
|
||||
} else {
|
||||
std::cerr << "ERROR: an invalid RSS item was saved" << std::endl;
|
||||
is_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
~RssItem(){
|
||||
}
|
||||
|
||||
bool has_attachment() const {
|
||||
return !torrent_url.isEmpty();
|
||||
}
|
||||
|
||||
QString getId() const { return id; }
|
||||
|
||||
QHash<QString, QVariant> toHash() const {
|
||||
QHash<QString, QVariant> item;
|
||||
item["title"] = title;
|
||||
item["id"] = id;
|
||||
item["torrent_url"] = torrent_url;
|
||||
item["news_link"] = news_link;
|
||||
item["description"] = description;
|
||||
item["date"] = date;
|
||||
item["author"] = author;
|
||||
item["read"] = read;
|
||||
return item;
|
||||
}
|
||||
|
||||
static RssItem* fromHash(RssStream* parent, const QHash<QString, QVariant> &h) {
|
||||
return new RssItem(parent, h.value("id", "").toString(), h["title"].toString(), h["torrent_url"].toString(), h["news_link"].toString(),
|
||||
h["description"].toString(), h["date"].toDateTime(), h["author"].toString(), h["read"].toBool());
|
||||
}
|
||||
|
||||
RssStream* getParent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
QString getTitle() const{
|
||||
return title;
|
||||
}
|
||||
|
||||
QString getAuthor() const {
|
||||
return author;
|
||||
}
|
||||
|
||||
QString getTorrentUrl() const{
|
||||
return torrent_url;
|
||||
}
|
||||
|
||||
QString getLink() const {
|
||||
return news_link;
|
||||
}
|
||||
|
||||
QString getDescription() const{
|
||||
if(description.isEmpty())
|
||||
return tr("No description available");
|
||||
return description;
|
||||
}
|
||||
|
||||
QDateTime getDate() const {
|
||||
return date;
|
||||
}
|
||||
|
||||
bool isRead() const{
|
||||
return read;
|
||||
}
|
||||
|
||||
void setRead(){
|
||||
read = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Rss stream, loaded form an xml file
|
||||
class RssStream: public RssFile, public QHash<QString, RssItem*> {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
RssFolder *parent;
|
||||
RssManager *rssmanager;
|
||||
Bittorrent *BTSession;
|
||||
QString title;
|
||||
QString link;
|
||||
QString description;
|
||||
QString image;
|
||||
QString url;
|
||||
QString alias;
|
||||
QString filePath;
|
||||
QString iconPath;
|
||||
bool read;
|
||||
bool refreshed;
|
||||
bool downloadFailure;
|
||||
bool currently_loading;
|
||||
|
||||
public slots:
|
||||
void processDownloadedFile(QString file_path);
|
||||
void setDownloadFailed();
|
||||
|
||||
public:
|
||||
RssStream(RssFolder* parent, RssManager *rssmanager, Bittorrent *BTSession, QString _url);
|
||||
~RssStream();
|
||||
RssFolder* getParent() const { return parent; }
|
||||
void setParent(RssFolder* _parent) { parent = _parent; }
|
||||
FileType getType() const;
|
||||
void refresh();
|
||||
QString getID() const { return url; }
|
||||
void removeAllItems();
|
||||
void removeAllSettings();
|
||||
bool itemAlreadyExists(QString hash);
|
||||
void setLoading(bool val);
|
||||
bool isLoading();
|
||||
QString getTitle() const;
|
||||
void rename(QString _alias);
|
||||
QString getName() const;
|
||||
QString getLink() const;
|
||||
QString getUrl() const;
|
||||
QString getDescription() const;
|
||||
QString getImage() const;
|
||||
QString getFilePath() const;
|
||||
QString getIconPath() const;
|
||||
bool hasCustomIcon() const;
|
||||
void setIconPath(QString path);
|
||||
RssItem* getItem(QString name) const;
|
||||
unsigned int getNbNews() const;
|
||||
void markAllAsRead();
|
||||
unsigned int getNbUnRead() const;
|
||||
QList<RssItem*> getNewsList() const;
|
||||
QList<RssItem*> getUnreadNewsList() const;
|
||||
QString getIconUrl();
|
||||
|
||||
private:
|
||||
short readDoc(QIODevice* device);
|
||||
void resizeList();
|
||||
short openRss();
|
||||
};
|
||||
|
||||
class RssFolder: public RssFile, public QHash<QString, RssFile*> {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
RssFolder *parent;
|
||||
RssManager *rssmanager;
|
||||
downloadThread *downloader;
|
||||
Bittorrent *BTSession;
|
||||
QString name;
|
||||
|
||||
public:
|
||||
RssFolder(RssFolder *parent, RssManager *rssmanager, Bittorrent *BTSession, QString name);
|
||||
~RssFolder();
|
||||
RssFolder* getParent() const { return parent; }
|
||||
void setParent(RssFolder* _parent) { parent = _parent; }
|
||||
unsigned int getNbUnRead() const;
|
||||
FileType getType() const;
|
||||
RssStream* addStream(QString url);
|
||||
RssFolder* addFolder(QString name);
|
||||
QList<RssStream*> findFeedsWithIcon(QString icon_url) const;
|
||||
unsigned int getNbFeeds() const;
|
||||
QList<RssFile*> getContent() const;
|
||||
QList<RssStream*> getAllFeeds() const;
|
||||
QString getName() const;
|
||||
QString getID() const { return name; }
|
||||
bool hasChild(QString ID) { return this->contains(ID); }
|
||||
QList<RssItem*> getNewsList() const;
|
||||
QList<RssItem*> getUnreadNewsList() const;
|
||||
void removeAllSettings() {
|
||||
foreach(RssFile* child, values()) {
|
||||
child->removeAllSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllItems() {
|
||||
foreach(RssFile* child, values()) {
|
||||
child->removeAllItems();
|
||||
}
|
||||
qDeleteAll(values());
|
||||
clear();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void refreshAll();
|
||||
void addFile(RssFile * item);
|
||||
void removeFile(QString ID);
|
||||
void refresh();
|
||||
void refreshStream(QString url);
|
||||
void processFinishedDownload(QString url, QString path);
|
||||
void handleDownloadFailure(QString url, QString reason);
|
||||
void rename(QString new_name);
|
||||
void markAllAsRead();
|
||||
};
|
||||
|
||||
class RssManager: public RssFolder{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QTimer newsRefresher;
|
||||
unsigned int refreshInterval;
|
||||
Bittorrent *BTSession;
|
||||
|
||||
signals:
|
||||
void feedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread);
|
||||
void feedIconChanged(QString url, QString icon_path);
|
||||
|
||||
public slots:
|
||||
void loadStreamList();
|
||||
void saveStreamList();
|
||||
void forwardFeedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread);
|
||||
void forwardFeedIconChanged(QString url, QString icon_path);
|
||||
void moveFile(RssFile* file, RssFolder* dest_folder);
|
||||
void updateRefreshInterval(unsigned int val);
|
||||
|
||||
public:
|
||||
RssManager(Bittorrent *BTSession);
|
||||
~RssManager();
|
||||
static void insertSortElem(QList<RssItem*> &list, RssItem *item) {
|
||||
int i = 0;
|
||||
while(i < list.size() && item->getDate() < list.at(i)->getDate()) {
|
||||
++i;
|
||||
}
|
||||
list.insert(i, item);
|
||||
}
|
||||
|
||||
static QList<RssItem*> sortNewsList(const QList<RssItem*>& news_list) {
|
||||
QList<RssItem*> new_list;
|
||||
foreach(RssItem *item, news_list) {
|
||||
insertSortElem(new_list, item);
|
||||
}
|
||||
return new_list;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,19 +2,25 @@ INCLUDEPATH += $$PWD
|
|||
|
||||
!contains(DEFINES, DISABLE_GUI) {
|
||||
|
||||
HEADERS += $$PWD/rss.h \
|
||||
$$PWD/rss_imp.h \
|
||||
HEADERS += $$PWD/rss_imp.h \
|
||||
$$PWD/rsssettings.h \
|
||||
$$PWD/feeddownloader.h \
|
||||
$$PWD/feedlistwidget.h \
|
||||
$$PWD/rssmanager.h \
|
||||
$$PWD/rssfeed.h \
|
||||
$$PWD/rssfolder.h \
|
||||
$$PWD/rssfile.h \
|
||||
$$PWD/rssarticle.h
|
||||
|
||||
SOURCES += $$PWD/rss.cpp \
|
||||
$$PWD/rss_imp.cpp \
|
||||
SOURCES += $$PWD/rss_imp.cpp \
|
||||
$$PWD/rsssettings.cpp \
|
||||
$$PWD/feedlistwidget.cpp
|
||||
$$PWD/feedlistwidget.cpp \
|
||||
$$PWD/rssmanager.cpp \
|
||||
$$PWD/rssfeed.cpp \
|
||||
$$PWD/rssfolder.cpp \
|
||||
$$PWD/rssarticle.cpp
|
||||
|
||||
FORMS += $$PWD/ui/rss.ui \
|
||||
$$PWD/ui/feeddownloader.ui \
|
||||
$$PWD/ui/rsssettings.ui
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
#include "cookiesdlg.h"
|
||||
#include "preferences.h"
|
||||
#include "rsssettings.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfolder.h"
|
||||
#include "rssarticle.h"
|
||||
#include "rssfeed.h"
|
||||
|
||||
enum NewsCols { NEWS_ICON, NEWS_TITLE_COL, NEWS_URL_COL, NEWS_ID };
|
||||
|
||||
|
@ -72,7 +76,7 @@ void RSSImp::displayRSSListMenu(const QPoint& pos){
|
|||
}
|
||||
}
|
||||
myRSSListMenu.addAction(actionNew_subscription);
|
||||
if(listStreams->getItemType(selectedItems.first()) == RssFile::STREAM) {
|
||||
if(listStreams->getItemType(selectedItems.first()) == RssFile::FEED) {
|
||||
myRSSListMenu.addSeparator();
|
||||
myRSSListMenu.addAction(actionCopy_feed_URL);
|
||||
if(selectedItems.size() == 1) {
|
||||
|
@ -198,7 +202,7 @@ void RSSImp::on_newFeedButton_clicked() {
|
|||
QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
RssStream *stream = rss_parent->addStream(newUrl);
|
||||
RssFeed *stream = rss_parent->addStream(newUrl);
|
||||
// Create TreeWidget item
|
||||
QTreeWidgetItem* item;
|
||||
if(parent_item)
|
||||
|
@ -305,7 +309,7 @@ void RSSImp::on_updateAllButton_clicked() {
|
|||
void RSSImp::downloadTorrent() {
|
||||
QList<QTreeWidgetItem *> selected_items = listNews->selectedItems();
|
||||
foreach(const QTreeWidgetItem* item, selected_items) {
|
||||
RssItem* article = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL))->getItem(item->text(NEWS_ID));
|
||||
RssArticle* article = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL))->getItem(item->text(NEWS_ID));
|
||||
if(article->has_attachment()) {
|
||||
BTSession->downloadFromUrl(article->getTorrentUrl());
|
||||
} else {
|
||||
|
@ -318,7 +322,7 @@ void RSSImp::downloadTorrent() {
|
|||
void RSSImp::openNewsUrl() {
|
||||
QList<QTreeWidgetItem *> selected_items = listNews->selectedItems();
|
||||
foreach(const QTreeWidgetItem* item, selected_items) {
|
||||
RssItem* news = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL))->getItem(item->text(NEWS_ID));
|
||||
RssArticle* news = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL))->getItem(item->text(NEWS_ID));
|
||||
QString link = news->getLink();
|
||||
if(!link.isEmpty())
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
|
@ -364,7 +368,7 @@ void RSSImp::refreshSelectedItems() {
|
|||
file->refresh();
|
||||
break;
|
||||
} else {
|
||||
if(file->getType() == RssFile::STREAM) {
|
||||
if(file->getType() == RssFile::FEED) {
|
||||
item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/loading.png")));
|
||||
} else {
|
||||
// Update feeds in the folder
|
||||
|
@ -383,7 +387,7 @@ void RSSImp::copySelectedFeedsURL() {
|
|||
QList<QTreeWidgetItem*> selectedItems = listStreams->selectedItems();
|
||||
QTreeWidgetItem* item;
|
||||
foreach(item, selectedItems){
|
||||
if(listStreams->getItemType(item) == RssFile::STREAM)
|
||||
if(listStreams->getItemType(item) == RssFile::FEED)
|
||||
URLs << listStreams->getItemID(item);
|
||||
}
|
||||
qApp->clipboard()->setText(URLs.join("\n"));
|
||||
|
@ -392,7 +396,7 @@ void RSSImp::copySelectedFeedsURL() {
|
|||
void RSSImp::showFeedDownloader() {
|
||||
QTreeWidgetItem* item = listStreams->selectedItems()[0];
|
||||
RssFile* rss_item = listStreams->getRSSItem(item);
|
||||
if(rss_item->getType() == RssFile::STREAM) {
|
||||
if(rss_item->getType() == RssFile::FEED) {
|
||||
FeedDownloaderDlg* feedDownloader = new FeedDownloaderDlg(this, listStreams->getItemID(item), rss_item->getName(), BTSession);
|
||||
connect(feedDownloader, SIGNAL(filteringEnabled()), this, SLOT(on_updateAllButton_clicked()));
|
||||
}
|
||||
|
@ -427,7 +431,7 @@ void RSSImp::fillFeedsList(QTreeWidgetItem *parent, RssFolder *rss_parent) {
|
|||
// Notify TreeWidget of item addition
|
||||
listStreams->itemAdded(item, rss_child);
|
||||
// Set Icon
|
||||
if(rss_child->getType() == RssFile::STREAM) {
|
||||
if(rss_child->getType() == RssFile::FEED) {
|
||||
item->setData(0,Qt::DecorationRole, QVariant(QIcon(QString::fromUtf8(":/Icons/loading.png"))));
|
||||
} else {
|
||||
item->setData(0,Qt::DecorationRole, QVariant(QIcon(QString::fromUtf8(":/Icons/oxygen/folder.png"))));
|
||||
|
@ -448,7 +452,7 @@ void RSSImp::refreshNewsList(QTreeWidgetItem* item) {
|
|||
if(!rss_item) return;
|
||||
|
||||
qDebug("Getting the list of news");
|
||||
QList<RssItem*> news;
|
||||
QList<RssArticle*> news;
|
||||
if(rss_item == rssmanager)
|
||||
news = RssManager::sortNewsList(rss_item->getUnreadNewsList());
|
||||
else if(rss_item)
|
||||
|
@ -458,7 +462,7 @@ void RSSImp::refreshNewsList(QTreeWidgetItem* item) {
|
|||
previous_news = 0;
|
||||
listNews->clear();
|
||||
qDebug("Got the list of news");
|
||||
foreach(RssItem* article, news){
|
||||
foreach(RssArticle* article, news){
|
||||
QTreeWidgetItem* it = new QTreeWidgetItem(listNews);
|
||||
it->setText(NEWS_TITLE_COL, article->getTitle());
|
||||
it->setText(NEWS_URL_COL, article->getParent()->getUrl());
|
||||
|
@ -489,8 +493,8 @@ void RSSImp::refreshTextBrowser() {
|
|||
}
|
||||
previous_news = item;
|
||||
}
|
||||
RssStream *stream = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL));
|
||||
RssItem* article = stream->getItem(item->text(NEWS_ID));
|
||||
RssFeed *stream = listStreams->getRSSItemFromUrl(item->text(NEWS_URL_COL));
|
||||
RssArticle* article = stream->getItem(item->text(NEWS_ID));
|
||||
QString html;
|
||||
html += "<div style='border: 2px solid red; margin-left: 5px; margin-right: 5px; margin-bottom: 5px;'>";
|
||||
html += "<div style='background-color: #678db2; font-weight: bold; color: #fff;'>"+article->getTitle() + "</div>";
|
||||
|
@ -557,7 +561,7 @@ void RSSImp::updateFeedIcon(QString url, QString icon_path){
|
|||
|
||||
void RSSImp::updateFeedInfos(QString url, QString aliasOrUrl, unsigned int nbUnread){
|
||||
QTreeWidgetItem *item = listStreams->getTreeItemFromUrl(url);
|
||||
RssStream *stream = (RssStream*)listStreams->getRSSItem(item);
|
||||
RssFeed *stream = (RssFeed*)listStreams->getRSSItem(item);
|
||||
item->setText(0, aliasOrUrl + QString::fromUtf8(" (") + QString::number(nbUnread, 10)+ QString(")"));
|
||||
if(!stream->isLoading())
|
||||
item->setData(0,Qt::DecorationRole, QVariant(QIcon(stream->getIconPath())));
|
||||
|
|
|
@ -35,11 +35,12 @@
|
|||
#include <QPointer>
|
||||
|
||||
#include "ui_rss.h"
|
||||
#include "rss.h"
|
||||
|
||||
class Bittorrent;
|
||||
class FeedListWidget;
|
||||
class QTreeWidgetItem;
|
||||
class RssFolder;
|
||||
class RssManager;
|
||||
|
||||
class RSSImp : public QWidget, public Ui::RSS{
|
||||
Q_OBJECT
|
||||
|
@ -88,9 +89,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
#if QT_VERSION < 0x040500
|
||||
#undef QHash
|
||||
#undef toHash
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
317
src/rss/rssarticle.cpp
Normal file
317
src/rss/rssarticle.cpp
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QVariant>
|
||||
#include <QStringList>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "rssarticle.h"
|
||||
|
||||
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"
|
||||
};
|
||||
static const char longMonth[][10] = {
|
||||
"January", "February", "March",
|
||||
"April", "May", "June",
|
||||
"July", "August", "September",
|
||||
"October", "November", "December"
|
||||
};
|
||||
|
||||
// Ported to Qt4 from KDElibs4
|
||||
QDateTime RssArticle::parseDate(const QString &string) {
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QDate qdate(year, month+1, day); // convert date, and check for out-of-range
|
||||
if (!qdate.isValid())
|
||||
return QDateTime::currentDateTime();
|
||||
QDateTime result(qdate, QTime(hour, minute, second));
|
||||
if (!result.isValid()
|
||||
|| (dayOfWeek >= 0 && result.date().dayOfWeek() != dayOfWeek+1))
|
||||
return QDateTime::currentDateTime(); // invalid date/time, or weekday doesn't correspond with date
|
||||
if (!offset) {
|
||||
result.setTimeSpec(Qt::UTC);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// public constructor
|
||||
RssArticle::RssArticle(RssFeed* parent, QXmlStreamReader& xml): parent(parent), read(false) {
|
||||
is_valid = false;
|
||||
torrent_url = QString::null;
|
||||
news_link = QString::null;
|
||||
title = QString::null;
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement() && xml.name() == "item")
|
||||
break;
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
if(xml.name() == "title") {
|
||||
title = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "enclosure") {
|
||||
if(xml.attributes().value("type") == "application/x-bittorrent") {
|
||||
torrent_url = xml.attributes().value("url").toString();
|
||||
}
|
||||
}
|
||||
else if(xml.name() == "link") {
|
||||
news_link = xml.readElementText();
|
||||
if(id.isEmpty())
|
||||
id = news_link;
|
||||
}
|
||||
else if(xml.name() == "description") {
|
||||
description = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "pubDate") {
|
||||
date = parseDate(xml.readElementText());
|
||||
}
|
||||
else if(xml.name() == "author") {
|
||||
author = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "guid") {
|
||||
id = xml.readElementText();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!id.isEmpty())
|
||||
is_valid = true;
|
||||
}
|
||||
|
||||
RssArticle::RssArticle(RssFeed* parent, QString _id, QString _title, QString _torrent_url, QString _news_link, QString _description, QDateTime _date, QString _author, bool _read):
|
||||
parent(parent), id(_id), title(_title), torrent_url(_torrent_url), news_link(_news_link), description(_description), date(_date), author(_author), read(_read){
|
||||
if(id.isEmpty())
|
||||
id = news_link;
|
||||
if(!id.isEmpty()) {
|
||||
is_valid = true;
|
||||
} else {
|
||||
std::cerr << "ERROR: an invalid RSS item was saved" << std::endl;
|
||||
is_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
RssArticle::~RssArticle(){
|
||||
}
|
||||
|
||||
bool RssArticle::has_attachment() const {
|
||||
return !torrent_url.isEmpty();
|
||||
}
|
||||
|
||||
QString RssArticle::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
QHash<QString, QVariant> RssArticle::toHash() const {
|
||||
QHash<QString, QVariant> item;
|
||||
item["title"] = title;
|
||||
item["id"] = id;
|
||||
item["torrent_url"] = torrent_url;
|
||||
item["news_link"] = news_link;
|
||||
item["description"] = description;
|
||||
item["date"] = date;
|
||||
item["author"] = author;
|
||||
item["read"] = read;
|
||||
return item;
|
||||
}
|
||||
|
||||
RssArticle* RssArticle::fromHash(RssFeed* parent, const QHash<QString, QVariant> &h) {
|
||||
return new RssArticle(parent, h.value("id", "").toString(), h["title"].toString(), h["torrent_url"].toString(), h["news_link"].toString(),
|
||||
h["description"].toString(), h["date"].toDateTime(), h["author"].toString(), h["read"].toBool());
|
||||
}
|
||||
|
||||
RssFeed* RssArticle::getParent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool RssArticle::isValid() const {
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
QString RssArticle::getTitle() const{
|
||||
return title;
|
||||
}
|
||||
|
||||
QString RssArticle::getAuthor() const {
|
||||
return author;
|
||||
}
|
||||
|
||||
QString RssArticle::getTorrentUrl() const{
|
||||
return torrent_url;
|
||||
}
|
||||
|
||||
QString RssArticle::getLink() const {
|
||||
return news_link;
|
||||
}
|
||||
|
||||
QString RssArticle::getDescription() const{
|
||||
if(description.isEmpty())
|
||||
return tr("No description available");
|
||||
return description;
|
||||
}
|
||||
|
||||
QDateTime RssArticle::getDate() const {
|
||||
return date;
|
||||
}
|
||||
|
||||
bool RssArticle::isRead() const{
|
||||
return read;
|
||||
}
|
||||
|
||||
void RssArticle::setRead(){
|
||||
read = true;
|
||||
}
|
82
src/rss/rssarticle.h
Normal file
82
src/rss/rssarticle.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSARTICLE_H
|
||||
#define RSSARTICLE_H
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
|
||||
class RssFeed;
|
||||
|
||||
// Item of a rss stream, single information
|
||||
class RssArticle: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RssArticle(RssFeed* parent, QXmlStreamReader& xml);
|
||||
RssArticle(RssFeed* parent, QString _id, QString _title,
|
||||
QString _torrent_url, QString _news_link, QString _description,
|
||||
QDateTime _date, QString _author, bool _read);
|
||||
~RssArticle();
|
||||
bool has_attachment() const;
|
||||
QString getId() const;
|
||||
QHash<QString, QVariant> toHash() const;
|
||||
static RssArticle* fromHash(RssFeed* parent, const QHash<QString, QVariant> &h);
|
||||
RssFeed* getParent() const;
|
||||
bool isValid() const;
|
||||
QString getTitle() const;
|
||||
QString getAuthor() const;
|
||||
QString getTorrentUrl() const;
|
||||
QString getLink() const;
|
||||
QString getDescription() const;
|
||||
QDateTime getDate() const;
|
||||
bool isRead() const;
|
||||
void setRead();
|
||||
|
||||
protected:
|
||||
QDateTime parseDate(const QString &string);
|
||||
|
||||
private:
|
||||
RssFeed* parent;
|
||||
QString id;
|
||||
QString title;
|
||||
QString torrent_url;
|
||||
QString news_link;
|
||||
QString description;
|
||||
QDateTime date;
|
||||
QString author;
|
||||
bool is_valid;
|
||||
bool read;
|
||||
};
|
||||
|
||||
|
||||
#endif // RSSARTICLE_H
|
370
src/rss/rssfeed.cpp
Normal file
370
src/rss/rssfeed.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "rssfeed.h"
|
||||
#include "rssmanager.h"
|
||||
#include "qbtsession.h"
|
||||
#include "rssfolder.h"
|
||||
#include "preferences.h"
|
||||
#include "rssarticle.h"
|
||||
#include "feeddownloader.h"
|
||||
|
||||
RssFeed::RssFeed(RssFolder* parent, RssManager *rssmanager, Bittorrent *BTSession, QString _url): parent(parent), rssmanager(rssmanager), BTSession(BTSession), alias(""), iconPath(":/Icons/rss16.png"), refreshed(false), downloadFailure(false), currently_loading(false) {
|
||||
qDebug("RSSStream constructed");
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
url = QUrl(_url).toString();
|
||||
QHash<QString, QVariant> all_old_items = qBTRSS.value("old_items", QHash<QString, QVariant>()).toHash();
|
||||
QVariantList old_items = all_old_items.value(url, QVariantList()).toList();
|
||||
qDebug("Loading %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
|
||||
foreach(const QVariant &var_it, old_items) {
|
||||
QHash<QString, QVariant> item = var_it.toHash();
|
||||
RssArticle *rss_item = RssArticle::fromHash(this, item);
|
||||
if(rss_item->isValid()) {
|
||||
(*this)[rss_item->getId()] = rss_item;
|
||||
} else {
|
||||
delete rss_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RssFeed::~RssFeed(){
|
||||
qDebug("Deleting a RSS stream: %s", getName().toLocal8Bit().data());
|
||||
if(refreshed) {
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
QVariantList old_items;
|
||||
foreach(RssArticle *item, this->values()) {
|
||||
old_items << item->toHash();
|
||||
}
|
||||
qDebug("Saving %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
|
||||
QHash<QString, QVariant> all_old_items = qBTRSS.value("old_items", QHash<QString, QVariant>()).toHash();
|
||||
all_old_items[url] = old_items;
|
||||
qBTRSS.setValue("old_items", all_old_items);
|
||||
}
|
||||
qDebug("Removing all item from feed");
|
||||
removeAllItems();
|
||||
qDebug("All items were removed");
|
||||
if(QFile::exists(filePath))
|
||||
misc::safeRemove(filePath);
|
||||
if(QFile::exists(iconPath) && !iconPath.startsWith(":/"))
|
||||
misc::safeRemove(iconPath);
|
||||
}
|
||||
|
||||
RssFile::FileType RssFeed::getType() const {
|
||||
return RssFile::FEED;
|
||||
}
|
||||
|
||||
void RssFeed::refresh() {
|
||||
parent->refreshStream(url);
|
||||
}
|
||||
|
||||
// delete all the items saved
|
||||
void RssFeed::removeAllItems() {
|
||||
qDeleteAll(this->values());
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void RssFeed::removeAllSettings() {
|
||||
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
||||
if(feeds_w_downloader.contains(url)) {
|
||||
feeds_w_downloader.remove(url);
|
||||
qBTRSS.setValue("downloader_on", feeds_w_downloader);
|
||||
}
|
||||
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
||||
if(all_feeds_filters.contains(url)) {
|
||||
all_feeds_filters.remove(url);
|
||||
qBTRSS.setValue("feed_filters", all_feeds_filters);
|
||||
}
|
||||
}
|
||||
|
||||
bool RssFeed::itemAlreadyExists(QString name) {
|
||||
return this->contains(name);
|
||||
}
|
||||
|
||||
void RssFeed::setLoading(bool val) {
|
||||
currently_loading = val;
|
||||
}
|
||||
|
||||
bool RssFeed::isLoading() {
|
||||
return currently_loading;
|
||||
}
|
||||
|
||||
QString RssFeed::getTitle() const{
|
||||
return title;
|
||||
}
|
||||
|
||||
void RssFeed::rename(QString new_name){
|
||||
qDebug("Renaming stream to %s", new_name.toLocal8Bit().data());
|
||||
alias = new_name;
|
||||
}
|
||||
|
||||
// Return the alias if the stream has one, the url if it has no alias
|
||||
QString RssFeed::getName() const{
|
||||
if(!alias.isEmpty()) {
|
||||
//qDebug("getName() returned alias: %s", (const char*)alias.toLocal8Bit());
|
||||
return alias;
|
||||
}
|
||||
if(!title.isEmpty()) {
|
||||
//qDebug("getName() returned title: %s", (const char*)title.toLocal8Bit());
|
||||
return title;
|
||||
}
|
||||
//qDebug("getName() returned url: %s", (const char*)url.toLocal8Bit());
|
||||
return url;
|
||||
}
|
||||
|
||||
QString RssFeed::getLink() const{
|
||||
return link;
|
||||
}
|
||||
|
||||
QString RssFeed::getUrl() const{
|
||||
return url;
|
||||
}
|
||||
|
||||
QString RssFeed::getDescription() const{
|
||||
return description;
|
||||
}
|
||||
|
||||
QString RssFeed::getImage() const{
|
||||
return image;
|
||||
}
|
||||
|
||||
QString RssFeed::getFilePath() const{
|
||||
return filePath;
|
||||
}
|
||||
|
||||
QString RssFeed::getIconPath() const{
|
||||
if(downloadFailure)
|
||||
return ":/Icons/oxygen/unavailable.png";
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
bool RssFeed::hasCustomIcon() const{
|
||||
return !iconPath.startsWith(":/");
|
||||
}
|
||||
|
||||
void RssFeed::setIconPath(QString path) {
|
||||
iconPath = path;
|
||||
}
|
||||
|
||||
RssArticle* RssFeed::getItem(QString id) const{
|
||||
return this->value(id);
|
||||
}
|
||||
|
||||
unsigned int RssFeed::getNbNews() const{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void RssFeed::markAllAsRead() {
|
||||
foreach(RssArticle *item, this->values()){
|
||||
item->setRead();
|
||||
}
|
||||
rssmanager->forwardFeedInfosChanged(url, getName(), 0);
|
||||
}
|
||||
|
||||
unsigned int RssFeed::getNbUnRead() const{
|
||||
unsigned int nbUnread=0;
|
||||
foreach(RssArticle *item, this->values()) {
|
||||
if(!item->isRead())
|
||||
++nbUnread;
|
||||
}
|
||||
return nbUnread;
|
||||
}
|
||||
|
||||
QList<RssArticle*> RssFeed::getNewsList() const{
|
||||
return this->values();
|
||||
}
|
||||
|
||||
QList<RssArticle*> RssFeed::getUnreadNewsList() const {
|
||||
QList<RssArticle*> unread_news;
|
||||
foreach(RssArticle *item, this->values()) {
|
||||
if(!item->isRead())
|
||||
unread_news << item;
|
||||
}
|
||||
return unread_news;
|
||||
}
|
||||
|
||||
// download the icon from the adress
|
||||
QString RssFeed::getIconUrl() {
|
||||
QUrl siteUrl(url);
|
||||
return QString::fromUtf8("http://")+siteUrl.host()+QString::fromUtf8("/favicon.ico");
|
||||
}
|
||||
|
||||
// read and create items from a rss document
|
||||
short RssFeed::readDoc(QIODevice* device) {
|
||||
qDebug("Parsing RSS file...");
|
||||
QXmlStreamReader xml(device);
|
||||
// is it a rss file ?
|
||||
if (xml.atEnd()) {
|
||||
qDebug("ERROR: Could not parse RSS file");
|
||||
return -1;
|
||||
}
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
if(xml.isStartElement()) {
|
||||
if(xml.name() != "rss") {
|
||||
qDebug("ERROR: this is not a rss file, root tag is <%s>", qPrintable(xml.name().toString()));
|
||||
return -1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Read channels
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement())
|
||||
break;
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
//qDebug("xml.name() == %s", qPrintable(xml.name().toString()));
|
||||
if(xml.name() == "channel") {
|
||||
qDebug("in channel");
|
||||
|
||||
// Parse channel content
|
||||
while(!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
|
||||
if(xml.isEndElement() && xml.name() == "channel") {
|
||||
break;
|
||||
}
|
||||
|
||||
if(xml.isStartElement()) {
|
||||
//qDebug("xml.name() == %s", qPrintable(xml.name().toString()));
|
||||
if(xml.name() == "title") {
|
||||
title = xml.readElementText();
|
||||
if(alias == getUrl())
|
||||
rename(title);
|
||||
}
|
||||
else if(xml.name() == "link") {
|
||||
link = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "description") {
|
||||
description = xml.readElementText();
|
||||
}
|
||||
else if(xml.name() == "image") {
|
||||
image = xml.attributes().value("url").toString();
|
||||
}
|
||||
else if(xml.name() == "item") {
|
||||
RssArticle * item = new RssArticle(this, xml);
|
||||
if(item->isValid() && !itemAlreadyExists(item->getId())) {
|
||||
this->insert(item->getId(), item);
|
||||
} else {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resizeList();
|
||||
|
||||
// RSS Feed Downloader
|
||||
foreach(RssArticle* item, values()) {
|
||||
if(item->isRead()) continue;
|
||||
QString torrent_url;
|
||||
if(item->has_attachment())
|
||||
torrent_url = item->getTorrentUrl();
|
||||
else
|
||||
torrent_url = item->getLink();
|
||||
// Check if the item should be automatically downloaded
|
||||
FeedFilter * matching_filter = FeedFilters::getFeedFilters(url).matches(item->getTitle());
|
||||
if(matching_filter != 0) {
|
||||
// Download the torrent
|
||||
BTSession->addConsoleMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(item->getTitle()).arg(getName()));
|
||||
if(matching_filter->isValid()) {
|
||||
QString save_path = matching_filter->getSavePath();
|
||||
if(save_path.isEmpty())
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
||||
else
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url, save_path);
|
||||
} else {
|
||||
// All torrents are downloaded from this feed
|
||||
BTSession->downloadUrlAndSkipDialog(torrent_url);
|
||||
}
|
||||
// Item was downloaded, consider it as Read
|
||||
item->setRead();
|
||||
// Clean up
|
||||
delete matching_filter;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RssFeed::resizeList() {
|
||||
unsigned int max_articles = Preferences::getRSSMaxArticlesPerFeed();
|
||||
unsigned int nb_articles = this->size();
|
||||
if(nb_articles > max_articles) {
|
||||
QList<RssArticle*> listItem = RssManager::sortNewsList(this->values());
|
||||
int excess = nb_articles - max_articles;
|
||||
for(int i=0; i<excess; ++i){
|
||||
RssArticle *lastItem = listItem.takeLast();
|
||||
delete this->take(lastItem->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// existing and opening test after download
|
||||
short RssFeed::openRss(){
|
||||
qDebug("openRss() called");
|
||||
QFile fileRss(filePath);
|
||||
if(!fileRss.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qDebug("openRss error: open failed, no file or locked, %s", (const char*)filePath.toLocal8Bit());
|
||||
if(QFile::exists(filePath)) {
|
||||
fileRss.remove();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// start reading the xml
|
||||
short return_lecture = readDoc(&fileRss);
|
||||
fileRss.close();
|
||||
if(QFile::exists(filePath)) {
|
||||
fileRss.remove();
|
||||
}
|
||||
return return_lecture;
|
||||
}
|
||||
|
||||
// read and store the downloaded rss' informations
|
||||
void RssFeed::processDownloadedFile(QString file_path) {
|
||||
filePath = file_path;
|
||||
downloadFailure = false;
|
||||
if(openRss() >= 0) {
|
||||
refreshed = true;
|
||||
} else {
|
||||
qDebug("OpenRss: Feed update Failed");
|
||||
}
|
||||
}
|
||||
|
||||
void RssFeed::setDownloadFailed(){
|
||||
downloadFailure = true;
|
||||
}
|
104
src/rss/rssfeed.h
Normal file
104
src/rss/rssfeed.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSFEED_H
|
||||
#define RSSFEED_H
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "rssfile.h"
|
||||
|
||||
class RssManager;
|
||||
class Bittorrent;
|
||||
|
||||
class RssFeed: public RssFile, public QHash<QString, RssArticle*> {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
RssFolder *parent;
|
||||
RssManager *rssmanager;
|
||||
Bittorrent *BTSession;
|
||||
QString title;
|
||||
QString link;
|
||||
QString description;
|
||||
QString image;
|
||||
QString url;
|
||||
QString alias;
|
||||
QString filePath;
|
||||
QString iconPath;
|
||||
bool read;
|
||||
bool refreshed;
|
||||
bool downloadFailure;
|
||||
bool currently_loading;
|
||||
|
||||
public slots:
|
||||
void processDownloadedFile(QString file_path);
|
||||
void setDownloadFailed();
|
||||
|
||||
public:
|
||||
RssFeed(RssFolder* parent, RssManager *rssmanager, Bittorrent *BTSession, QString _url);
|
||||
~RssFeed();
|
||||
RssFolder* getParent() const { return parent; }
|
||||
void setParent(RssFolder* _parent) { parent = _parent; }
|
||||
FileType getType() const;
|
||||
void refresh();
|
||||
QString getID() const { return url; }
|
||||
void removeAllItems();
|
||||
void removeAllSettings();
|
||||
bool itemAlreadyExists(QString hash);
|
||||
void setLoading(bool val);
|
||||
bool isLoading();
|
||||
QString getTitle() const;
|
||||
void rename(QString _alias);
|
||||
QString getName() const;
|
||||
QString getLink() const;
|
||||
QString getUrl() const;
|
||||
QString getDescription() const;
|
||||
QString getImage() const;
|
||||
QString getFilePath() const;
|
||||
QString getIconPath() const;
|
||||
bool hasCustomIcon() const;
|
||||
void setIconPath(QString path);
|
||||
RssArticle* getItem(QString name) const;
|
||||
unsigned int getNbNews() const;
|
||||
void markAllAsRead();
|
||||
unsigned int getNbUnRead() const;
|
||||
QList<RssArticle*> getNewsList() const;
|
||||
QList<RssArticle*> getUnreadNewsList() const;
|
||||
QString getIconUrl();
|
||||
|
||||
private:
|
||||
short readDoc(QIODevice* device);
|
||||
void resizeList();
|
||||
short openRss();
|
||||
};
|
||||
|
||||
|
||||
#endif // RSSFEED_H
|
72
src/rss/rssfile.h
Normal file
72
src/rss/rssfile.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSFILE_H
|
||||
#define RSSFILE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class RssArticle;
|
||||
class RssFolder;
|
||||
|
||||
class RssFile: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum FileType {FEED, FOLDER};
|
||||
|
||||
RssFile(): QObject() {}
|
||||
virtual ~RssFile() {}
|
||||
|
||||
virtual unsigned int getNbUnRead() const = 0;
|
||||
virtual FileType getType() const = 0;
|
||||
virtual QString getName() const = 0;
|
||||
virtual QString getID() const = 0;
|
||||
virtual void removeAllItems() = 0;
|
||||
virtual void rename(QString new_name) = 0;
|
||||
virtual void markAllAsRead() = 0;
|
||||
virtual RssFolder* getParent() const = 0;
|
||||
virtual void setParent(RssFolder*) = 0;
|
||||
virtual void refresh() = 0;
|
||||
virtual void removeAllSettings() = 0;
|
||||
virtual QList<RssArticle*> getNewsList() const = 0;
|
||||
virtual QList<RssArticle*> getUnreadNewsList() const = 0;
|
||||
QStringList getPath() const {
|
||||
QStringList path;
|
||||
if(getParent()) {
|
||||
path = ((RssFile*)getParent())->getPath();
|
||||
path.append(getID());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RSSFILE_H
|
303
src/rss/rssfolder.cpp
Normal file
303
src/rss/rssfolder.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "rssfolder.h"
|
||||
#include "rssarticle.h"
|
||||
#include "qbtsession.h"
|
||||
#include "downloadthread.h"
|
||||
#include "rssmanager.h"
|
||||
#include "rssfeed.h"
|
||||
|
||||
RssFolder::RssFolder(RssFolder *parent, RssManager *rssmanager, Bittorrent *BTSession, QString name): parent(parent), rssmanager(rssmanager), BTSession(BTSession), name(name) {
|
||||
downloader = new downloadThread(this);
|
||||
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processFinishedDownload(QString, QString)));
|
||||
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
||||
}
|
||||
|
||||
RssFolder::~RssFolder() {
|
||||
qDebug("Deleting a RSS folder, removing elements");
|
||||
qDeleteAll(this->values());
|
||||
qDebug("Deleting downloader thread");
|
||||
delete downloader;
|
||||
qDebug("Downloader thread removed");
|
||||
}
|
||||
|
||||
unsigned int RssFolder::getNbUnRead() const {
|
||||
unsigned int nb_unread = 0;
|
||||
foreach(RssFile *file, this->values()) {
|
||||
nb_unread += file->getNbUnRead();
|
||||
}
|
||||
return nb_unread;
|
||||
}
|
||||
|
||||
RssFile::FileType RssFolder::getType() const {
|
||||
return RssFile::FOLDER;
|
||||
}
|
||||
|
||||
void RssFolder::refreshAll(){
|
||||
qDebug("Refreshing all rss feeds");
|
||||
const QList<RssFile*> items = this->values();
|
||||
for(int i=0; i<items.size(); ++i) {
|
||||
//foreach(RssFile *item, *this){
|
||||
RssFile *item = items.at(i);
|
||||
if(item->getType() == RssFile::FEED) {
|
||||
RssFeed* stream = (RssFeed*) item;
|
||||
QString url = stream->getUrl();
|
||||
if(stream->isLoading()) return;
|
||||
stream->setLoading(true);
|
||||
downloader->downloadUrl(url);
|
||||
if(!stream->hasCustomIcon()){
|
||||
downloader->downloadUrl(stream->getIconUrl());
|
||||
}
|
||||
} else {
|
||||
RssFolder *folder = (RssFolder*)item;
|
||||
folder->refreshAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RssFolder::removeFile(QString ID) {
|
||||
if(this->contains(ID)) {
|
||||
RssFile* child = this->take(ID);
|
||||
child->removeAllSettings();
|
||||
child->removeAllItems();
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
RssFolder* RssFolder::addFolder(QString name) {
|
||||
RssFolder *subfolder;
|
||||
if(!this->contains(name)) {
|
||||
subfolder = new RssFolder(this, rssmanager, BTSession, name);
|
||||
(*this)[name] = subfolder;
|
||||
} else {
|
||||
subfolder = (RssFolder*)this->value(name);
|
||||
}
|
||||
return subfolder;
|
||||
}
|
||||
|
||||
RssFeed* RssFolder::addStream(QString url) {
|
||||
RssFeed* stream = new RssFeed(this, rssmanager, BTSession, url);
|
||||
Q_ASSERT(!this->contains(stream->getUrl()));
|
||||
(*this)[stream->getUrl()] = stream;
|
||||
refreshStream(stream->getUrl());
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Refresh All Children
|
||||
void RssFolder::refresh() {
|
||||
foreach(RssFile *child, this->values()) {
|
||||
// Little optimization child->refresh() would work too
|
||||
if(child->getType() == RssFile::FEED)
|
||||
refreshStream(child->getID());
|
||||
else
|
||||
child->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssArticle*> RssFolder::getNewsList() const {
|
||||
QList<RssArticle*> news;
|
||||
foreach(RssFile *child, this->values()) {
|
||||
news << child->getNewsList();
|
||||
}
|
||||
return news;
|
||||
}
|
||||
|
||||
QList<RssArticle*> RssFolder::getUnreadNewsList() const {
|
||||
QList<RssArticle*> unread_news;
|
||||
foreach(RssFile *child, this->values()) {
|
||||
unread_news << child->getUnreadNewsList();
|
||||
}
|
||||
return unread_news;
|
||||
}
|
||||
|
||||
void RssFolder::refreshStream(QString url) {
|
||||
qDebug("Refreshing feed: %s", url.toLocal8Bit().data());
|
||||
Q_ASSERT(this->contains(url));
|
||||
RssFeed *stream = (RssFeed*)this->value(url);
|
||||
if(stream->isLoading()) {
|
||||
qDebug("Stream %s is already being loaded...", stream->getUrl().toLocal8Bit().data());
|
||||
return;
|
||||
}
|
||||
stream->setLoading(true);
|
||||
qDebug("stream %s : loaded=true", stream->getUrl().toLocal8Bit().data());
|
||||
downloader->downloadUrl(url);
|
||||
if(!stream->hasCustomIcon()){
|
||||
downloader->downloadUrl(stream->getIconUrl());
|
||||
}else{
|
||||
qDebug("No need to download this feed's icon, it was already downloaded");
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssFile*> RssFolder::getContent() const {
|
||||
return this->values();
|
||||
}
|
||||
|
||||
unsigned int RssFolder::getNbFeeds() const {
|
||||
unsigned int nbFeeds = 0;
|
||||
foreach(RssFile* item, this->values()) {
|
||||
if(item->getType() == RssFile::FOLDER)
|
||||
nbFeeds += ((RssFolder*)item)->getNbFeeds();
|
||||
else
|
||||
nbFeeds += 1;
|
||||
}
|
||||
return nbFeeds;
|
||||
}
|
||||
|
||||
void RssFolder::processFinishedDownload(QString url, QString path) {
|
||||
if(url.endsWith("favicon.ico")){
|
||||
// Icon downloaded
|
||||
QImage fileIcon;
|
||||
if(fileIcon.load(path)) {
|
||||
QList<RssFeed*> res = findFeedsWithIcon(url);
|
||||
RssFeed* stream;
|
||||
foreach(stream, res){
|
||||
stream->setIconPath(path);
|
||||
if(!stream->isLoading())
|
||||
rssmanager->forwardFeedIconChanged(stream->getUrl(), stream->getIconPath());
|
||||
}
|
||||
}else{
|
||||
qDebug("Unsupported icon format at %s", (const char*)url.toLocal8Bit());
|
||||
}
|
||||
return;
|
||||
}
|
||||
RssFeed *stream = (RssFeed*)this->value(url, 0);
|
||||
if(!stream){
|
||||
qDebug("This rss stream was deleted in the meantime, nothing to update");
|
||||
return;
|
||||
}
|
||||
stream->processDownloadedFile(path);
|
||||
stream->setLoading(false);
|
||||
qDebug("stream %s : loaded=false", stream->getUrl().toLocal8Bit().data());
|
||||
// If the feed has no alias, then we use the title as Alias
|
||||
// this is more user friendly
|
||||
if(stream->getName().isEmpty()){
|
||||
if(!stream->getTitle().isEmpty())
|
||||
stream->rename(stream->getTitle());
|
||||
}
|
||||
rssmanager->forwardFeedInfosChanged(url, stream->getName(), stream->getNbUnRead());
|
||||
}
|
||||
|
||||
void RssFolder::handleDownloadFailure(QString url, QString reason) {
|
||||
if(url.endsWith("favicon.ico")){
|
||||
// Icon download failure
|
||||
qDebug("Could not download icon at %s, reason: %s", (const char*)url.toLocal8Bit(), (const char*)reason.toLocal8Bit());
|
||||
return;
|
||||
}
|
||||
RssFeed *stream = (RssFeed*)this->value(url, 0);
|
||||
if(!stream){
|
||||
qDebug("This rss stream was deleted in the meantime, nothing to update");
|
||||
return;
|
||||
}
|
||||
stream->setLoading(false);
|
||||
qDebug("Could not download Rss at %s, reason: %s", (const char*)url.toLocal8Bit(), (const char*)reason.toLocal8Bit());
|
||||
stream->setDownloadFailed();
|
||||
rssmanager->forwardFeedInfosChanged(url, stream->getName(), stream->getNbUnRead());
|
||||
}
|
||||
|
||||
QList<RssFeed*> RssFolder::findFeedsWithIcon(QString icon_url) const {
|
||||
QList<RssFeed*> res;
|
||||
RssFile* item;
|
||||
foreach(item, this->values()){
|
||||
if(item->getType() == RssFile::FEED && ((RssFeed*)item)->getIconUrl() == icon_url)
|
||||
res << (RssFeed*)item;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QString RssFolder::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
void RssFolder::rename(QString new_name) {
|
||||
Q_ASSERT(!parent->contains(new_name));
|
||||
if(!parent->contains(new_name)) {
|
||||
// Update parent
|
||||
(*parent)[new_name] = parent->take(name);
|
||||
// Actually rename
|
||||
name = new_name;
|
||||
}
|
||||
}
|
||||
|
||||
void RssFolder::markAllAsRead() {
|
||||
foreach(RssFile *item, this->values()) {
|
||||
item->markAllAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
QList<RssFeed*> RssFolder::getAllFeeds() const {
|
||||
QList<RssFeed*> streams;
|
||||
foreach(RssFile *item, this->values()) {
|
||||
if(item->getType() == RssFile::FEED) {
|
||||
streams << ((RssFeed*)item);
|
||||
} else {
|
||||
foreach(RssFeed* stream, ((RssFolder*)item)->getAllFeeds()) {
|
||||
streams << stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
return streams;
|
||||
}
|
||||
|
||||
void RssFolder::addFile(RssFile * item) {
|
||||
if(item->getType() == RssFile::FEED) {
|
||||
Q_ASSERT(!this->contains(((RssFeed*)item)->getUrl()));
|
||||
(*this)[((RssFeed*)item)->getUrl()] = item;
|
||||
qDebug("Added feed %s to folder ./%s", ((RssFeed*)item)->getUrl().toLocal8Bit().data(), name.toLocal8Bit().data());
|
||||
} else {
|
||||
Q_ASSERT(!this->contains(((RssFolder*)item)->getName()));
|
||||
(*this)[((RssFolder*)item)->getName()] = item;
|
||||
qDebug("Added folder %s to folder ./%s", ((RssFolder*)item)->getName().toLocal8Bit().data(), name.toLocal8Bit().data());
|
||||
}
|
||||
// Update parent
|
||||
item->setParent(this);
|
||||
}
|
||||
|
||||
void RssFolder::removeAllItems() {
|
||||
foreach(RssFile* child, values()) {
|
||||
child->removeAllItems();
|
||||
}
|
||||
qDeleteAll(values());
|
||||
clear();
|
||||
}
|
||||
|
||||
void RssFolder::removeAllSettings() {
|
||||
foreach(RssFile* child, values()) {
|
||||
child->removeAllSettings();
|
||||
}
|
||||
}
|
||||
|
||||
QString RssFolder::getID() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
bool RssFolder::hasChild(QString ID) {
|
||||
return this->contains(ID);
|
||||
}
|
87
src/rss/rssfolder.h
Normal file
87
src/rss/rssfolder.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSFOLDER_H
|
||||
#define RSSFOLDER_H
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "rssfile.h"
|
||||
|
||||
class RssArticle;
|
||||
class Bittorrent;
|
||||
class downloadThread;
|
||||
class RssManager;
|
||||
class RssFeed;
|
||||
|
||||
class RssFolder: public RssFile, public QHash<QString, RssFile*> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RssFolder(RssFolder *parent, RssManager *rssmanager, Bittorrent *BTSession, QString name);
|
||||
~RssFolder();
|
||||
RssFolder* getParent() const { return parent; }
|
||||
void setParent(RssFolder* _parent) { parent = _parent; }
|
||||
unsigned int getNbUnRead() const;
|
||||
FileType getType() const;
|
||||
RssFeed* addStream(QString url);
|
||||
RssFolder* addFolder(QString name);
|
||||
QList<RssFeed*> findFeedsWithIcon(QString icon_url) const;
|
||||
unsigned int getNbFeeds() const;
|
||||
QList<RssFile*> getContent() const;
|
||||
QList<RssFeed*> getAllFeeds() const;
|
||||
QString getName() const;
|
||||
QString getID() const;
|
||||
bool hasChild(QString ID);
|
||||
QList<RssArticle*> getNewsList() const;
|
||||
QList<RssArticle*> getUnreadNewsList() const;
|
||||
void removeAllSettings();
|
||||
void removeAllItems();
|
||||
|
||||
public slots:
|
||||
void refreshAll();
|
||||
void addFile(RssFile * item);
|
||||
void removeFile(QString ID);
|
||||
void refresh();
|
||||
void refreshStream(QString url);
|
||||
void processFinishedDownload(QString url, QString path);
|
||||
void handleDownloadFailure(QString url, QString reason);
|
||||
void rename(QString new_name);
|
||||
void markAllAsRead();
|
||||
|
||||
private:
|
||||
RssFolder *parent;
|
||||
RssManager *rssmanager;
|
||||
downloadThread *downloader;
|
||||
Bittorrent *BTSession;
|
||||
QString name;
|
||||
};
|
||||
|
||||
#endif // RSSFOLDER_H
|
142
src/rss/rssmanager.cpp
Normal file
142
src/rss/rssmanager.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "rssmanager.h"
|
||||
#include "preferences.h"
|
||||
#include "qbtsession.h"
|
||||
#include "rssfeed.h"
|
||||
#include "rssarticle.h"
|
||||
|
||||
RssManager::RssManager(Bittorrent *BTSession): RssFolder(0, this, BTSession, QString::null) {
|
||||
loadStreamList();
|
||||
connect(&newsRefresher, SIGNAL(timeout()), this, SLOT(refreshAll()));
|
||||
refreshInterval = Preferences::getRSSRefreshInterval();
|
||||
newsRefresher.start(refreshInterval*60000);
|
||||
}
|
||||
|
||||
RssManager::~RssManager(){
|
||||
qDebug("Deleting RSSManager");
|
||||
saveStreamList();
|
||||
qDebug("RSSManager deleted");
|
||||
}
|
||||
|
||||
void RssManager::updateRefreshInterval(unsigned int val){
|
||||
if(refreshInterval != val) {
|
||||
refreshInterval = val;
|
||||
newsRefresher.start(refreshInterval*60000);
|
||||
qDebug("New RSS refresh interval is now every %dmin", refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void RssManager::loadStreamList(){
|
||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||
QStringList streamsUrl = settings.value("Rss/streamList").toStringList();
|
||||
QStringList aliases = settings.value("Rss/streamAlias").toStringList();
|
||||
if(streamsUrl.size() != aliases.size()){
|
||||
std::cerr << "Corrupted Rss list, not loading it\n";
|
||||
return;
|
||||
}
|
||||
unsigned int i = 0;
|
||||
foreach(QString s, streamsUrl){
|
||||
QStringList path = s.split("\\");
|
||||
if(path.empty()) continue;
|
||||
QString feed_url = path.takeLast();
|
||||
// Create feed path (if it does not exists)
|
||||
RssFolder * feed_parent = this;
|
||||
foreach(QString folder_name, path) {
|
||||
feed_parent = feed_parent->addFolder(folder_name);
|
||||
}
|
||||
// Create feed
|
||||
RssFeed *stream = feed_parent->addStream(feed_url);
|
||||
QString alias = aliases.at(i);
|
||||
if(!alias.isEmpty()) {
|
||||
stream->rename(alias);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
qDebug("NB RSS streams loaded: %d", streamsUrl.size());
|
||||
}
|
||||
|
||||
void RssManager::forwardFeedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread) {
|
||||
emit feedInfosChanged(url, aliasOrUrl, nbUnread);
|
||||
}
|
||||
|
||||
void RssManager::forwardFeedIconChanged(QString url, QString icon_path) {
|
||||
emit feedIconChanged(url, icon_path);
|
||||
}
|
||||
|
||||
void RssManager::moveFile(RssFile* file, RssFolder* dest_folder) {
|
||||
RssFolder* src_folder = file->getParent();
|
||||
if(dest_folder != src_folder) {
|
||||
// Copy to new Folder
|
||||
dest_folder->addFile(file);
|
||||
// Remove reference in old folder
|
||||
src_folder->remove(file->getID());
|
||||
} else {
|
||||
qDebug("Nothing to move, same destination folder");
|
||||
}
|
||||
}
|
||||
|
||||
void RssManager::saveStreamList(){
|
||||
QStringList streamsUrl;
|
||||
QStringList aliases;
|
||||
const QList<RssFeed*> streams = getAllFeeds();
|
||||
foreach(const RssFeed *stream, streams) {
|
||||
QString stream_path = stream->getPath().join("\\");
|
||||
if(stream_path.isNull()) {
|
||||
stream_path = "";
|
||||
}
|
||||
qDebug("Saving stream path: %s", qPrintable(stream_path));
|
||||
streamsUrl << stream_path;
|
||||
aliases << stream->getName();
|
||||
}
|
||||
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||
settings.beginGroup("Rss");
|
||||
// FIXME: Empty folder are not saved
|
||||
settings.setValue("streamList", streamsUrl);
|
||||
settings.setValue("streamAlias", aliases);
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void RssManager::insertSortElem(QList<RssArticle*> &list, RssArticle *item) {
|
||||
int i = 0;
|
||||
while(i < list.size() && item->getDate() < list.at(i)->getDate()) {
|
||||
++i;
|
||||
}
|
||||
list.insert(i, item);
|
||||
}
|
||||
|
||||
QList<RssArticle*> RssManager::sortNewsList(const QList<RssArticle*>& news_list) {
|
||||
QList<RssArticle*> new_list;
|
||||
foreach(RssArticle *item, news_list) {
|
||||
insertSortElem(new_list, item);
|
||||
}
|
||||
return new_list;
|
||||
}
|
66
src/rss/rssmanager.h
Normal file
66
src/rss/rssmanager.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez, Arnaud Demaiziere
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact: chris@qbittorrent.org, arnaud@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef RSSMANAGER_H
|
||||
#define RSSMANAGER_H
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "rssfolder.h"
|
||||
|
||||
class RssManager: public RssFolder {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RssManager(Bittorrent *BTSession);
|
||||
~RssManager();
|
||||
static void insertSortElem(QList<RssArticle*> &list, RssArticle *item);
|
||||
static QList<RssArticle*> sortNewsList(const QList<RssArticle*>& news_list);
|
||||
|
||||
public slots:
|
||||
void loadStreamList();
|
||||
void saveStreamList();
|
||||
void forwardFeedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread);
|
||||
void forwardFeedIconChanged(QString url, QString icon_path);
|
||||
void moveFile(RssFile* file, RssFolder* dest_folder);
|
||||
void updateRefreshInterval(unsigned int val);
|
||||
|
||||
signals:
|
||||
void feedInfosChanged(QString url, QString aliasOrUrl, unsigned int nbUnread);
|
||||
void feedIconChanged(QString url, QString icon_path);
|
||||
|
||||
private:
|
||||
QTimer newsRefresher;
|
||||
unsigned int refreshInterval;
|
||||
Bittorrent *BTSession;
|
||||
|
||||
};
|
||||
|
||||
#endif // RSSMANAGER_H
|
|
@ -39,14 +39,7 @@
|
|||
#include "misc.h"
|
||||
#include <vector>
|
||||
#include "qinisettings.h"
|
||||
|
||||
#if QT_VERSION < 0x040500
|
||||
#include <QMap>
|
||||
#define QHash QMap
|
||||
#define toHash toMap
|
||||
#else
|
||||
#include <QHash>
|
||||
#endif
|
||||
|
||||
class TorrentTempData {
|
||||
public:
|
||||
|
@ -441,7 +434,4 @@ public:
|
|||
|
||||
};
|
||||
|
||||
#undef QHash
|
||||
#undef toHash
|
||||
|
||||
#endif // TORRENTPERSISTENTDATA_H
|
||||
|
|
Loading…
Reference in a new issue