mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-27 03:39:39 +03:00
Improve RSS parsing logic.
This commit is contained in:
parent
28ed981082
commit
6662081044
6 changed files with 263 additions and 354 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2012 Christophe Dumez
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -30,248 +31,226 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QFile>
|
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "rssparser.h"
|
#include "rssparser.h"
|
||||||
|
|
||||||
namespace Rss
|
namespace
|
||||||
{
|
{
|
||||||
namespace Private
|
const char shortDay[][4] = {
|
||||||
|
"Mon", "Tue", "Wed",
|
||||||
|
"Thu", "Fri", "Sat",
|
||||||
|
"Sun"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char longDay[][10] = {
|
||||||
|
"Monday", "Tuesday", "Wednesday",
|
||||||
|
"Thursday", "Friday", "Saturday",
|
||||||
|
"Sunday"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char shortMonth[][4] = {
|
||||||
|
"Jan", "Feb", "Mar", "Apr",
|
||||||
|
"May", "Jun", "Jul", "Aug",
|
||||||
|
"Sep", "Oct", "Nov", "Dec"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ported to Qt from KDElibs4
|
||||||
|
QDateTime parseDate(const QString &string)
|
||||||
{
|
{
|
||||||
struct ParsingJob
|
const QString str = string.trimmed();
|
||||||
{
|
if (str.isEmpty())
|
||||||
QString feedUrl;
|
|
||||||
QByteArray feedData;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char shortDay[][4] = {
|
|
||||||
"Mon", "Tue", "Wed",
|
|
||||||
"Thu", "Fri", "Sat",
|
|
||||||
"Sun"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char longDay[][10] = {
|
|
||||||
"Monday", "Tuesday", "Wednesday",
|
|
||||||
"Thursday", "Friday", "Saturday",
|
|
||||||
"Sunday"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char shortMonth[][4] = {
|
|
||||||
"Jan", "Feb", "Mar", "Apr",
|
|
||||||
"May", "Jun", "Jul", "Aug",
|
|
||||||
"Sep", "Oct", "Nov", "Dec"
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace Rss::Private;
|
|
||||||
|
|
||||||
Parser::Parser(QObject *parent)
|
|
||||||
: QThread(parent)
|
|
||||||
, m_running(true)
|
|
||||||
{
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Parser::~Parser()
|
|
||||||
{
|
|
||||||
m_running = false;
|
|
||||||
m_waitCondition.wakeOne();
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ported to Qt from KDElibs4
|
|
||||||
QDateTime Parser::parseDate(const QString &string)
|
|
||||||
{
|
|
||||||
const QString str = string.trimmed();
|
|
||||||
if (str.isEmpty())
|
|
||||||
return QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
int nyear = 6; // indexes within string to values
|
|
||||||
int nmonth = 4;
|
|
||||||
int nday = 2;
|
|
||||||
int nwday = 1;
|
|
||||||
int nhour = 7;
|
|
||||||
int nmin = 8;
|
|
||||||
int nsec = 9;
|
|
||||||
// Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm"
|
|
||||||
QRegExp rx("^(?:([A-Z][a-z]+),\\s*)?(\\d{1,2})(\\s+|-)([^-\\s]+)(\\s+|-)(\\d{2,4})\\s+(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s+(\\S+)$");
|
|
||||||
QStringList parts;
|
|
||||||
if (!str.indexOf(rx)) {
|
|
||||||
// Check that if date has '-' separators, both separators are '-'.
|
|
||||||
parts = rx.capturedTexts();
|
|
||||||
bool h1 = (parts[3] == QLatin1String("-"));
|
|
||||||
bool h2 = (parts[5] == QLatin1String("-"));
|
|
||||||
if (h1 != h2)
|
|
||||||
return QDateTime::currentDateTime();
|
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];
|
int nyear = 6; // indexes within string to values
|
||||||
const int day = parts[nday].toInt(&ok[0]);
|
int nmonth = 4;
|
||||||
int year = parts[nyear].toInt(&ok[1]);
|
int nday = 2;
|
||||||
const int hour = parts[nhour].toInt(&ok[2]);
|
int nwday = 1;
|
||||||
const int minute = parts[nmin].toInt(&ok[3]);
|
int nhour = 7;
|
||||||
if (!ok[0] || !ok[1] || !ok[2] || !ok[3])
|
int nmin = 8;
|
||||||
return QDateTime::currentDateTime();
|
int nsec = 9;
|
||||||
|
// Also accept obsolete form "Weekday, DD-Mon-YY HH:MM:SS ±hhmm"
|
||||||
int second = 0;
|
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+)$");
|
||||||
if (!parts[nsec].isEmpty()) {
|
QStringList parts;
|
||||||
second = parts[nsec].toInt(&ok[0]);
|
if (!str.indexOf(rx)) {
|
||||||
if (!ok[0])
|
// Check that if date has '-' separators, both separators are '-'.
|
||||||
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();
|
parts = rx.capturedTexts();
|
||||||
offset = parts[2].toInt(&ok[0]) * 3600;
|
bool h1 = (parts[3] == QLatin1String("-"));
|
||||||
int offsetMin = parts[3].toInt(&ok[1]);
|
bool h2 = (parts[5] == QLatin1String("-"));
|
||||||
if (!ok[0] || !ok[1] || offsetMin > 59)
|
if (h1 != h2)
|
||||||
return QDateTime();
|
return QDateTime::currentDateTime();
|
||||||
offset += offsetMin * 60;
|
|
||||||
negOffset = (parts[1] == QLatin1String("-"));
|
|
||||||
if (negOffset)
|
|
||||||
offset = -offset;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Check for an obsolete time zone name
|
// Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY"
|
||||||
QByteArray zone = parts[10].toLatin1();
|
rx = QRegExp("^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$");
|
||||||
if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') {
|
if (str.indexOf(rx))
|
||||||
negOffset = true; // military zone: RFC 2822 treats as '-0000'
|
return QDateTime::currentDateTime();
|
||||||
|
nyear = 7;
|
||||||
|
nmonth = 2;
|
||||||
|
nday = 3;
|
||||||
|
nwday = 1;
|
||||||
|
nhour = 4;
|
||||||
|
nmin = 5;
|
||||||
|
nsec = 6;
|
||||||
|
parts = rx.capturedTexts();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok[4];
|
||||||
|
const int day = parts[nday].toInt(&ok[0]);
|
||||||
|
int year = parts[nyear].toInt(&ok[1]);
|
||||||
|
const int hour = parts[nhour].toInt(&ok[2]);
|
||||||
|
const int minute = parts[nmin].toInt(&ok[3]);
|
||||||
|
if (!ok[0] || !ok[1] || !ok[2] || !ok[3])
|
||||||
|
return QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
int second = 0;
|
||||||
|
if (!parts[nsec].isEmpty()) {
|
||||||
|
second = parts[nsec].toInt(&ok[0]);
|
||||||
|
if (!ok[0])
|
||||||
|
return QDateTime::currentDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool leapSecond = (second == 60);
|
||||||
|
if (leapSecond)
|
||||||
|
second = 59; // apparently a leap second - validate below, once time zone is known
|
||||||
|
int month = 0;
|
||||||
|
for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month);
|
||||||
|
int dayOfWeek = -1;
|
||||||
|
if (!parts[nwday].isEmpty()) {
|
||||||
|
// Look up the weekday name
|
||||||
|
while (++dayOfWeek < 7 && (shortDay[dayOfWeek] != parts[nwday]));
|
||||||
|
if (dayOfWeek >= 7)
|
||||||
|
for (dayOfWeek = 0; dayOfWeek < 7 && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (month >= 12 || dayOfWeek >= 7
|
||||||
|
// || (dayOfWeek < 0 && format == RFCDateDay))
|
||||||
|
// return QDateTime;
|
||||||
|
int i = parts[nyear].size();
|
||||||
|
if (i < 4) {
|
||||||
|
// It's an obsolete year specification with less than 4 digits
|
||||||
|
year += (i == 2 && year < 50) ? 2000 : 1900;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the UTC offset part
|
||||||
|
int offset = 0; // set default to '-0000'
|
||||||
|
bool negOffset = false;
|
||||||
|
if (parts.count() > 10) {
|
||||||
|
rx = QRegExp("^([+-])(\\d\\d)(\\d\\d)$");
|
||||||
|
if (!parts[10].indexOf(rx)) {
|
||||||
|
// It's a UTC offset ±hhmm
|
||||||
|
parts = rx.capturedTexts();
|
||||||
|
offset = parts[2].toInt(&ok[0]) * 3600;
|
||||||
|
int offsetMin = parts[3].toInt(&ok[1]);
|
||||||
|
if (!ok[0] || !ok[1] || offsetMin > 59)
|
||||||
|
return QDateTime();
|
||||||
|
offset += offsetMin * 60;
|
||||||
|
negOffset = (parts[1] == QLatin1String("-"));
|
||||||
|
if (negOffset)
|
||||||
|
offset = -offset;
|
||||||
}
|
}
|
||||||
else if (zone != "UT" && zone != "GMT") { // treated as '+0000'
|
else {
|
||||||
offset = (zone == "EDT")
|
// Check for an obsolete time zone name
|
||||||
? -4 * 3600
|
QByteArray zone = parts[10].toLatin1();
|
||||||
: ((zone == "EST") || (zone == "CDT"))
|
if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') {
|
||||||
? -5 * 3600
|
negOffset = true; // military zone: RFC 2822 treats as '-0000'
|
||||||
: ((zone == "CST") || (zone == "MDT"))
|
}
|
||||||
? -6 * 3600
|
else if (zone != "UT" && zone != "GMT") { // treated as '+0000'
|
||||||
: (zone == "MST" || zone == "PDT")
|
offset = (zone == "EDT")
|
||||||
? -7 * 3600
|
? -4 * 3600
|
||||||
: (zone == "PST")
|
: ((zone == "EST") || (zone == "CDT"))
|
||||||
? -8 * 3600
|
? -5 * 3600
|
||||||
: 0;
|
: ((zone == "CST") || (zone == "MDT"))
|
||||||
if (!offset) {
|
? -6 * 3600
|
||||||
// Check for any other alphabetic time zone
|
: (zone == "MST" || zone == "PDT")
|
||||||
bool nonalpha = false;
|
? -7 * 3600
|
||||||
for (int i = 0, end = zone.size(); (i < end) && !nonalpha; ++i)
|
: (zone == "PST")
|
||||||
nonalpha = !isalpha(zone[i]);
|
? -8 * 3600
|
||||||
if (nonalpha)
|
: 0;
|
||||||
return QDateTime();
|
if (!offset) {
|
||||||
// TODO: Attempt to recognize the time zone abbreviation?
|
// Check for any other alphabetic time zone
|
||||||
negOffset = true; // unknown time zone: RFC 2822 treats as '-0000'
|
bool nonalpha = false;
|
||||||
|
for (int i = 0, end = zone.size(); (i < end) && !nonalpha; ++i)
|
||||||
|
nonalpha = !isalpha(zone[i]);
|
||||||
|
if (nonalpha)
|
||||||
|
return QDateTime();
|
||||||
|
// TODO: Attempt to recognize the time zone abbreviation?
|
||||||
|
negOffset = true; // unknown time zone: RFC 2822 treats as '-0000'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDate qdate(year, month + 1, day); // convert date, and check for out-of-range
|
||||||
|
if (!qdate.isValid())
|
||||||
|
return QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
QTime qTime(hour, minute, second);
|
||||||
|
QDateTime result(qdate, qTime, Qt::UTC);
|
||||||
|
if (offset)
|
||||||
|
result = result.addSecs(-offset);
|
||||||
|
if (!result.isValid())
|
||||||
|
return QDateTime::currentDateTime(); // invalid date/time
|
||||||
|
|
||||||
|
if (leapSecond) {
|
||||||
|
// Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC.
|
||||||
|
// Convert the time to UTC and check that it is 00:00:00.
|
||||||
|
if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours)
|
||||||
|
return QDateTime::currentDateTime(); // the time isn't the last second of the day
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDate qdate(year, month + 1, day); // convert date, and check for out-of-range
|
|
||||||
if (!qdate.isValid())
|
|
||||||
return QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
QTime qTime(hour, minute, second);
|
|
||||||
QDateTime result(qdate, qTime, Qt::UTC);
|
|
||||||
if (offset)
|
|
||||||
result = result.addSecs(-offset);
|
|
||||||
if (!result.isValid())
|
|
||||||
return QDateTime::currentDateTime(); // invalid date/time
|
|
||||||
|
|
||||||
if (leapSecond) {
|
|
||||||
// Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC.
|
|
||||||
// Convert the time to UTC and check that it is 00:00:00.
|
|
||||||
if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours)
|
|
||||||
return QDateTime::currentDateTime(); // the time isn't the last second of the day
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parseFeedData(const QString &feedUrl, const QByteArray &feedData)
|
using namespace Rss::Private;
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO << feedUrl;
|
|
||||||
m_mutex.lock();
|
|
||||||
ParsingJob job = { feedUrl, feedData };
|
|
||||||
m_queue.enqueue(job);
|
|
||||||
// Wake up thread.
|
|
||||||
if (m_queue.count() == 1) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "Waking up thread";
|
|
||||||
m_waitCondition.wakeOne();
|
|
||||||
}
|
|
||||||
m_mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::clearFeedData(const QString &feedUrl)
|
// read and create items from a rss document
|
||||||
|
void Parser::parse(const QByteArray &feedData)
|
||||||
{
|
{
|
||||||
m_mutex.lock();
|
qDebug() << Q_FUNC_INFO;
|
||||||
m_lastBuildDates.remove(feedUrl);
|
|
||||||
m_mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::run()
|
QXmlStreamReader xml(feedData);
|
||||||
{
|
bool foundChannel = false;
|
||||||
while (m_running) {
|
while (xml.readNextStartElement()) {
|
||||||
m_mutex.lock();
|
if (xml.name() == "rss") {
|
||||||
if (!m_queue.empty()) {
|
// Find channels
|
||||||
ParsingJob job = m_queue.dequeue();
|
while (xml.readNextStartElement()) {
|
||||||
m_mutex.unlock();
|
if (xml.name() == "channel") {
|
||||||
parseFeed(job);
|
parseRSSChannel(xml);
|
||||||
|
foundChannel = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug() << "Skip rss item: " << xml.name();
|
||||||
|
xml.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (xml.name() == "feed") { // Atom feed
|
||||||
|
parseAtomChannel(xml);
|
||||||
|
foundChannel = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qDebug() << Q_FUNC_INFO << "Thread is waiting.";
|
qDebug() << "Skip root item: " << xml.name();
|
||||||
m_waitCondition.wait(&m_mutex);
|
xml.skipCurrentElement();
|
||||||
qDebug() << Q_FUNC_INFO << "Thread woke up.";
|
|
||||||
m_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xml.hasError())
|
||||||
|
emit finished(xml.errorString());
|
||||||
|
else if (!foundChannel)
|
||||||
|
emit finished(tr("Invalid RSS feed."));
|
||||||
|
else
|
||||||
|
emit finished(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl)
|
void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||||
{
|
{
|
||||||
QVariantHash article;
|
QVariantHash article;
|
||||||
|
|
||||||
|
@ -332,12 +311,12 @@ void Parser::parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit newArticle(feedUrl, article);
|
emit newArticle(article);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl)
|
void Parser::parseRSSChannel(QXmlStreamReader &xml)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO << feedUrl;
|
qDebug() << Q_FUNC_INFO;
|
||||||
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
|
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
|
||||||
|
|
||||||
while(!xml.atEnd()) {
|
while(!xml.atEnd()) {
|
||||||
|
@ -346,27 +325,26 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl)
|
||||||
if (xml.isStartElement()) {
|
if (xml.isStartElement()) {
|
||||||
if (xml.name() == "title") {
|
if (xml.name() == "title") {
|
||||||
QString title = xml.readElementText();
|
QString title = xml.readElementText();
|
||||||
emit feedTitle(feedUrl, title);
|
emit feedTitle(title);
|
||||||
}
|
}
|
||||||
else if (xml.name() == "lastBuildDate") {
|
else if (xml.name() == "lastBuildDate") {
|
||||||
QString lastBuildDate = xml.readElementText();
|
QString lastBuildDate = xml.readElementText();
|
||||||
if (!lastBuildDate.isEmpty()) {
|
if (!lastBuildDate.isEmpty()) {
|
||||||
QMutexLocker locker(&m_mutex);
|
if (m_lastBuildDate == lastBuildDate) {
|
||||||
if (m_lastBuildDates.value(feedUrl, "") == lastBuildDate) {
|
|
||||||
qDebug() << "The RSS feed has not changed since last time, aborting parsing.";
|
qDebug() << "The RSS feed has not changed since last time, aborting parsing.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_lastBuildDates[feedUrl] = lastBuildDate;
|
m_lastBuildDate = lastBuildDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (xml.name() == "item") {
|
else if (xml.name() == "item") {
|
||||||
parseRssArticle(xml, feedUrl);
|
parseRssArticle(xml);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl)
|
void Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||||
{
|
{
|
||||||
QVariantHash article;
|
QVariantHash article;
|
||||||
bool doubleContent = false;
|
bool doubleContent = false;
|
||||||
|
@ -392,7 +370,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, con
|
||||||
// Atom feeds can have relative links, work around this and
|
// Atom feeds can have relative links, work around this and
|
||||||
// take the stress of figuring article full URI from UI
|
// take the stress of figuring article full URI from UI
|
||||||
// Assemble full URI
|
// Assemble full URI
|
||||||
article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link );
|
article["news_link"] = ( m_baseUrl.isEmpty() ? link : m_baseUrl + link );
|
||||||
|
|
||||||
}
|
}
|
||||||
else if ((xml.name() == "summary") || (xml.name() == "content")){
|
else if ((xml.name() == "summary") || (xml.name() == "content")){
|
||||||
|
@ -453,15 +431,15 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit newArticle(feedUrl, article);
|
emit newArticle(article);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl)
|
void Parser::parseAtomChannel(QXmlStreamReader &xml)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO << feedUrl;
|
qDebug() << Q_FUNC_INFO;
|
||||||
Q_ASSERT(xml.isStartElement() && xml.name() == "feed");
|
Q_ASSERT(xml.isStartElement() && xml.name() == "feed");
|
||||||
|
|
||||||
QString baseURL = xml.attributes().value("xml:base").toString();
|
m_baseUrl = xml.attributes().value("xml:base").toString();
|
||||||
|
|
||||||
while (!xml.atEnd()) {
|
while (!xml.atEnd()) {
|
||||||
xml.readNext();
|
xml.readNext();
|
||||||
|
@ -469,74 +447,21 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl)
|
||||||
if (xml.isStartElement()) {
|
if (xml.isStartElement()) {
|
||||||
if (xml.name() == "title") {
|
if (xml.name() == "title") {
|
||||||
QString title = xml.readElementText();
|
QString title = xml.readElementText();
|
||||||
emit feedTitle(feedUrl, title);
|
emit feedTitle(title);
|
||||||
}
|
}
|
||||||
else if (xml.name() == "updated") {
|
else if (xml.name() == "updated") {
|
||||||
QString lastBuildDate = xml.readElementText();
|
QString lastBuildDate = xml.readElementText();
|
||||||
if (!lastBuildDate.isEmpty()) {
|
if (!lastBuildDate.isEmpty()) {
|
||||||
QMutexLocker locker(&m_mutex);
|
if (m_lastBuildDate == lastBuildDate) {
|
||||||
if (m_lastBuildDates.value(feedUrl) == lastBuildDate) {
|
|
||||||
qDebug() << "The RSS feed has not changed since last time, aborting parsing.";
|
qDebug() << "The RSS feed has not changed since last time, aborting parsing.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_lastBuildDates[feedUrl] = lastBuildDate;
|
m_lastBuildDate = lastBuildDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (xml.name() == "entry") {
|
else if (xml.name() == "entry") {
|
||||||
parseAtomArticle(xml, feedUrl, baseURL);
|
parseAtomArticle(xml);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read and create items from a rss document
|
|
||||||
void Parser::parseFeed(const ParsingJob &job)
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO << job.feedUrl;
|
|
||||||
|
|
||||||
QXmlStreamReader xml(job.feedData);
|
|
||||||
bool foundChannel = false;
|
|
||||||
while (xml.readNextStartElement()) {
|
|
||||||
if (xml.name() == "rss") {
|
|
||||||
// Find channels
|
|
||||||
while (xml.readNextStartElement()) {
|
|
||||||
if (xml.name() == "channel") {
|
|
||||||
parseRSSChannel(xml, job.feedUrl);
|
|
||||||
foundChannel = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Skip rss item: " << xml.name();
|
|
||||||
xml.skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (xml.name() == "feed") { // Atom feed
|
|
||||||
parseAtomChannel(xml, job.feedUrl);
|
|
||||||
foundChannel = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Skip root item: " << xml.name();
|
|
||||||
xml.skipCurrentElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xml.hasError()) {
|
|
||||||
reportFailure(job, xml.errorString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundChannel) {
|
|
||||||
reportFailure(job, tr("Invalid RSS feed at '%1'.").arg(job.feedUrl));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit feedParsingFinished(job.feedUrl, QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::reportFailure(const ParsingJob &job, const QString &error)
|
|
||||||
{
|
|
||||||
emit feedParsingFinished(job.feedUrl, error);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2012 Christophe Dumez
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -31,12 +32,9 @@
|
||||||
#ifndef RSSPARSER_H
|
#ifndef RSSPARSER_H
|
||||||
#define RSSPARSER_H
|
#define RSSPARSER_H
|
||||||
|
|
||||||
#include <QHash>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QString>
|
||||||
#include <QQueue>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QVariantHash>
|
#include <QVariantHash>
|
||||||
#include <QWaitCondition>
|
|
||||||
|
|
||||||
class QXmlStreamReader;
|
class QXmlStreamReader;
|
||||||
|
|
||||||
|
@ -44,41 +42,26 @@ namespace Rss
|
||||||
{
|
{
|
||||||
namespace Private
|
namespace Private
|
||||||
{
|
{
|
||||||
struct ParsingJob;
|
class Parser: public QObject
|
||||||
|
|
||||||
class Parser: public QThread
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public slots:
|
||||||
explicit Parser(QObject *parent = 0);
|
void parse(const QByteArray &feedData);
|
||||||
~Parser();
|
|
||||||
|
|
||||||
void parseFeedData(const QString &feedUrl, const QByteArray &feedData);
|
|
||||||
void clearFeedData(const QString &feedUrl);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newArticle(const QString &feedUrl, const QVariantHash &rssArticle);
|
void newArticle(const QVariantHash &rssArticle);
|
||||||
void feedTitle(const QString &feedUrl, const QString &title);
|
void feedTitle(const QString &title);
|
||||||
void feedParsingFinished(const QString &feedUrl, const QString &error);
|
void finished(const QString &error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void run() override;
|
void parseRssArticle(QXmlStreamReader &xml);
|
||||||
|
void parseRSSChannel(QXmlStreamReader &xml);
|
||||||
|
void parseAtomArticle(QXmlStreamReader &xml);
|
||||||
|
void parseAtomChannel(QXmlStreamReader &xml);
|
||||||
|
|
||||||
static QDateTime parseDate(const QString &string);
|
QString m_lastBuildDate; // Optimization
|
||||||
|
QString m_baseUrl;
|
||||||
void parseRssArticle(QXmlStreamReader &xml, const QString &feedUrl);
|
|
||||||
void parseRSSChannel(QXmlStreamReader &xml, const QString &feedUrl);
|
|
||||||
void parseAtomArticle(QXmlStreamReader &xml, const QString &feedUrl, const QString &baseUrl);
|
|
||||||
void parseAtomChannel(QXmlStreamReader &xml, const QString &feedUrl);
|
|
||||||
void parseFeed(const ParsingJob &job);
|
|
||||||
void reportFailure(const ParsingJob &job, const QString &error);
|
|
||||||
|
|
||||||
bool m_running;
|
|
||||||
QMutex m_mutex;
|
|
||||||
QQueue<ParsingJob> m_queue;
|
|
||||||
QWaitCondition m_waitCondition;
|
|
||||||
QHash<QString/*feedUrl*/, QString/*lastBuildDate*/> m_lastBuildDates; // Optimization
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||||
*
|
*
|
||||||
|
@ -67,11 +68,13 @@ Feed::Feed(const QString &url, Manager *manager)
|
||||||
, m_loading(false)
|
, m_loading(false)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO << m_url;
|
qDebug() << Q_FUNC_INFO << m_url;
|
||||||
|
m_parser = new Private::Parser;
|
||||||
|
m_parser->moveToThread(m_manager->workingThread());
|
||||||
|
connect(this, SIGNAL(destroyed()), m_parser, SLOT(deleteLater()));
|
||||||
// Listen for new RSS downloads
|
// Listen for new RSS downloads
|
||||||
Private::Parser *const parser = m_manager->rssParser();
|
connect(m_parser, SIGNAL(feedTitle(QString)), SLOT(handleFeedTitle(QString)));
|
||||||
connect(parser, SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString)));
|
connect(m_parser, SIGNAL(newArticle(QVariantHash)), SLOT(handleNewArticle(QVariantHash)));
|
||||||
connect(parser, SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash)));
|
connect(m_parser, SIGNAL(finished(QString)), SLOT(handleParsingFinished(QString)));
|
||||||
connect(parser, SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleParsingFinished(QString,QString)));
|
|
||||||
|
|
||||||
// Download the RSS Feed icon
|
// Download the RSS Feed icon
|
||||||
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true);
|
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true);
|
||||||
|
@ -87,7 +90,6 @@ Feed::~Feed()
|
||||||
{
|
{
|
||||||
if (!m_icon.startsWith(":/") && QFile::exists(m_icon))
|
if (!m_icon.startsWith(":/") && QFile::exists(m_icon))
|
||||||
Utils::Fs::forceRemove(m_icon);
|
Utils::Fs::forceRemove(m_icon);
|
||||||
m_manager->rssParser()->clearFeedData(m_url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::saveItemsToDisk()
|
void Feed::saveItemsToDisk()
|
||||||
|
@ -320,7 +322,6 @@ QString Feed::iconUrl() const
|
||||||
void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath)
|
void Feed::handleIconDownloadFinished(const QString &url, const QString &filePath)
|
||||||
{
|
{
|
||||||
Q_UNUSED(url);
|
Q_UNUSED(url);
|
||||||
|
|
||||||
m_icon = filePath;
|
m_icon = filePath;
|
||||||
qDebug() << Q_FUNC_INFO << "icon path:" << m_icon;
|
qDebug() << Q_FUNC_INFO << "icon path:" << m_icon;
|
||||||
m_manager->forwardFeedIconChanged(m_url, m_icon);
|
m_manager->forwardFeedIconChanged(m_url, m_icon);
|
||||||
|
@ -328,30 +329,31 @@ void Feed::handleIconDownloadFinished(const QString &url, const QString &filePat
|
||||||
|
|
||||||
void Feed::handleRssDownloadFinished(const QString &url, const QByteArray &data)
|
void Feed::handleRssDownloadFinished(const QString &url, const QByteArray &data)
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url;
|
Q_UNUSED(url);
|
||||||
|
qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << m_url;
|
||||||
// Parse the download RSS
|
// Parse the download RSS
|
||||||
m_manager->rssParser()->parseFeedData(m_url, data);
|
QMetaObject::invokeMethod(m_parser, "parse", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::handleRssDownloadFailed(const QString &url, const QString &error)
|
void Feed::handleRssDownloadFailed(const QString &url, const QString &error)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(url);
|
||||||
m_inErrorState = true;
|
m_inErrorState = true;
|
||||||
m_loading = false;
|
m_loading = false;
|
||||||
m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount);
|
m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount);
|
||||||
qWarning() << "Failed to download RSS feed at" << url;
|
qWarning() << "Failed to download RSS feed at" << m_url;
|
||||||
qWarning() << "Reason:" << error;
|
qWarning() << "Reason:" << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::handleFeedTitle(const QString &feedUrl, const QString &title)
|
void Feed::handleFeedTitle(const QString &title)
|
||||||
{
|
{
|
||||||
if (feedUrl != m_url) return;
|
|
||||||
if (m_title == title) return;
|
if (m_title == title) return;
|
||||||
|
|
||||||
m_title = title;
|
m_title = title;
|
||||||
|
|
||||||
// Notify that we now have something better than a URL to display
|
// Notify that we now have something better than a URL to display
|
||||||
if (m_alias.isEmpty())
|
if (m_alias.isEmpty())
|
||||||
m_manager->forwardFeedInfosChanged(feedUrl, title, m_unreadCount);
|
m_manager->forwardFeedInfosChanged(m_url, title, m_unreadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article)
|
void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article)
|
||||||
|
@ -406,13 +408,11 @@ void Feed::recheckRssItemsForDownload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleData)
|
void Feed::handleNewArticle(const QVariantHash &articleData)
|
||||||
{
|
{
|
||||||
if (feedUrl != m_url) return;
|
|
||||||
|
|
||||||
ArticlePtr article = Article::fromHash(this, articleData);
|
ArticlePtr article = Article::fromHash(this, articleData);
|
||||||
if (article.isNull()) {
|
if (article.isNull()) {
|
||||||
qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << feedUrl;
|
qDebug() << "Article hash corrupted or guid is uncomputable; feed url: " << m_url;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Q_ASSERT(article);
|
Q_ASSERT(article);
|
||||||
|
@ -424,12 +424,10 @@ void Feed::handleNewArticle(const QString &feedUrl, const QVariantHash &articleD
|
||||||
//m_manager->forwardFeedContentChanged(m_url);
|
//m_manager->forwardFeedContentChanged(m_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::handleParsingFinished(const QString &feedUrl, const QString &error)
|
void Feed::handleParsingFinished(const QString &error)
|
||||||
{
|
{
|
||||||
if (feedUrl != m_url) return;
|
|
||||||
|
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
qWarning() << "Failed to parse RSS feed at" << feedUrl;
|
qWarning() << "Failed to parse RSS feed at" << m_url;
|
||||||
qWarning() << "Reason:" << error;
|
qWarning() << "Reason:" << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||||
*
|
*
|
||||||
|
@ -51,6 +52,11 @@ namespace Rss
|
||||||
typedef QSharedPointer<Feed> FeedPtr;
|
typedef QSharedPointer<Feed> FeedPtr;
|
||||||
typedef QList<FeedPtr> FeedList;
|
typedef QList<FeedPtr> FeedList;
|
||||||
|
|
||||||
|
namespace Private
|
||||||
|
{
|
||||||
|
class Parser;
|
||||||
|
}
|
||||||
|
|
||||||
bool articleDateRecentThan(const ArticlePtr &left, const ArticlePtr &right);
|
bool articleDateRecentThan(const ArticlePtr &left, const ArticlePtr &right);
|
||||||
|
|
||||||
class Feed: public QObject, public File
|
class Feed: public QObject, public File
|
||||||
|
@ -86,9 +92,9 @@ namespace Rss
|
||||||
void handleIconDownloadFinished(const QString &url, const QString &filePath);
|
void handleIconDownloadFinished(const QString &url, const QString &filePath);
|
||||||
void handleRssDownloadFinished(const QString &url, const QByteArray &data);
|
void handleRssDownloadFinished(const QString &url, const QByteArray &data);
|
||||||
void handleRssDownloadFailed(const QString &url, const QString &error);
|
void handleRssDownloadFailed(const QString &url, const QString &error);
|
||||||
void handleFeedTitle(const QString &feedUrl, const QString &title);
|
void handleFeedTitle(const QString &title);
|
||||||
void handleNewArticle(const QString &feedUrl, const QVariantHash &article);
|
void handleNewArticle(const QVariantHash &article);
|
||||||
void handleParsingFinished(const QString &feedUrl, const QString &error);
|
void handleParsingFinished(const QString &error);
|
||||||
void handleArticleRead();
|
void handleArticleRead();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -99,6 +105,7 @@ namespace Rss
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Manager *m_manager;
|
Manager *m_manager;
|
||||||
|
Private::Parser *m_parser;
|
||||||
ArticleHash m_articles;
|
ArticleHash m_articles;
|
||||||
ArticleList m_articlesByDate; // Articles sorted by date (more recent first)
|
ArticleList m_articlesByDate; // Articles sorted by date (more recent first)
|
||||||
QString m_title;
|
QString m_title;
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "private/rssparser.h"
|
|
||||||
#include "rssfolder.h"
|
#include "rssfolder.h"
|
||||||
#include "rssfeed.h"
|
#include "rssfeed.h"
|
||||||
#include "rssarticle.h"
|
#include "rssarticle.h"
|
||||||
|
@ -48,9 +47,10 @@ using namespace Rss::Private;
|
||||||
Manager::Manager(QObject *parent)
|
Manager::Manager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_downloadRules(new DownloadRuleList)
|
, m_downloadRules(new DownloadRuleList)
|
||||||
, m_rssParser(new Parser(this))
|
|
||||||
, m_rootFolder(new Folder)
|
, m_rootFolder(new Folder)
|
||||||
|
, m_workingThread(new QThread(this))
|
||||||
{
|
{
|
||||||
|
m_workingThread->start();
|
||||||
connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh()));
|
connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(refresh()));
|
||||||
m_refreshInterval = Preferences::instance()->getRSSRefreshInterval();
|
m_refreshInterval = Preferences::instance()->getRSSRefreshInterval();
|
||||||
m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN);
|
m_refreshTimer.start(m_refreshInterval * MSECS_PER_MIN);
|
||||||
|
@ -59,8 +59,9 @@ Manager::Manager(QObject *parent)
|
||||||
Manager::~Manager()
|
Manager::~Manager()
|
||||||
{
|
{
|
||||||
qDebug("Deleting RSSManager...");
|
qDebug("Deleting RSSManager...");
|
||||||
|
m_workingThread->quit();
|
||||||
|
m_workingThread->wait();
|
||||||
delete m_downloadRules;
|
delete m_downloadRules;
|
||||||
delete m_rssParser;
|
|
||||||
m_rootFolder->saveItemsToDisk();
|
m_rootFolder->saveItemsToDisk();
|
||||||
saveStreamList();
|
saveStreamList();
|
||||||
m_rootFolder.clear();
|
m_rootFolder.clear();
|
||||||
|
@ -178,9 +179,9 @@ FolderPtr Manager::rootFolder() const
|
||||||
return m_rootFolder;
|
return m_rootFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser *Manager::rssParser() const
|
QThread *Manager::workingThread() const
|
||||||
{
|
{
|
||||||
return m_rssParser;
|
return m_workingThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::refresh()
|
void Manager::refresh()
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
namespace Rss
|
namespace Rss
|
||||||
{
|
{
|
||||||
|
@ -48,11 +49,6 @@ namespace Rss
|
||||||
typedef QSharedPointer<Folder> FolderPtr;
|
typedef QSharedPointer<Folder> FolderPtr;
|
||||||
typedef QSharedPointer<Feed> FeedPtr;
|
typedef QSharedPointer<Feed> FeedPtr;
|
||||||
|
|
||||||
namespace Private
|
|
||||||
{
|
|
||||||
class Parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef QSharedPointer<Manager> ManagerPtr;
|
typedef QSharedPointer<Manager> ManagerPtr;
|
||||||
|
|
||||||
class Manager: public QObject
|
class Manager: public QObject
|
||||||
|
@ -65,8 +61,7 @@ namespace Rss
|
||||||
|
|
||||||
DownloadRuleList *downloadRules() const;
|
DownloadRuleList *downloadRules() const;
|
||||||
FolderPtr rootFolder() const;
|
FolderPtr rootFolder() const;
|
||||||
|
QThread *workingThread() const;
|
||||||
Private::Parser *rssParser() const;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void refresh();
|
void refresh();
|
||||||
|
@ -87,8 +82,8 @@ namespace Rss
|
||||||
QTimer m_refreshTimer;
|
QTimer m_refreshTimer;
|
||||||
uint m_refreshInterval;
|
uint m_refreshInterval;
|
||||||
DownloadRuleList *m_downloadRules;
|
DownloadRuleList *m_downloadRules;
|
||||||
Private::Parser *m_rssParser;
|
|
||||||
FolderPtr m_rootFolder;
|
FolderPtr m_rootFolder;
|
||||||
|
QThread *m_workingThread;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue