From 1f8b9378a30173429f98cb6764e7a7469474aa12 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Sun, 9 Sep 2007 19:19:33 +0000 Subject: [PATCH] - FEATURE: Display RSS article date and author if available --- TODO | 5 +- src/rss.h | 178 ++++++++++++++++++++++++++++++++++++++++++++++-- src/rss_imp.cpp | 13 +++- 3 files changed, 188 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index f61d79dce..2fd5f1540 100644 --- a/TODO +++ b/TODO @@ -54,9 +54,7 @@ - Use tooltips in options to explain - Keep documention up to date - Windows port (Chris - Peerkoel) -- write patches for libtorrent for file_priority(int index), actual_size() ? -- valgrind --tool=memcheck --leak-check=full src/qbittorrent (Looks ok) -- 128m 29m 16m S 4.8 2.9 0:02.28 qbittorrent +- Fix rss (read new on refresh) * beta 7 - update doc for plugins (and add screenies) - update doc for options @@ -102,6 +100,7 @@ beta6->beta7 changelog: - FEATURE: Added an option to display current transfer speeds in title bar - FEATURE: Added "Mark all as read" function for RSS feeds - FEATURE: Added some RSS settings in program preferences +- FEATURE: Display RSS article date and author if available - BUGFIX: In torrent content, it is now easier to filter all torrents using right click menu - BUGFIX: Updated man page / README / INSTALL - BUGFIX: Paused torrents could be displayed as connected for a sec after checking diff --git a/src/rss.h b/src/rss.h index 5e596e7f0..452f1c5f5 100644 --- a/src/rss.h +++ b/src/rss.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "misc.h" #include "downloadThread.h" @@ -45,6 +46,28 @@ class RssManager; 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" +}; + // Item of a rss stream, single information class RssItem : public QObject { Q_OBJECT @@ -54,9 +77,143 @@ class RssItem : public QObject { QString link; QString description; QString image; + QString author; + QDateTime date; bool read; QString downloadLink; + protected: + // Ported to Qt4 from KDElibs4 + QDateTime parseDate(const QString &string) { + QString str = string.trimmed(); + if (str.isEmpty()) + return QDateTime(); + + 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(); + } 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(); + nyear = 7; + nmonth = 2; + nday = 3; + nwday = 1; + nhour = 4; + nmin = 5; + nsec = 6; + parts = rx.capturedTexts(); + } + bool ok[4]; + int day = parts[nday].toInt(&ok[0]); + int year = parts[nyear].toInt(&ok[1]); + int hour = parts[nhour].toInt(&ok[2]); + int minute = parts[nmin].toInt(&ok[3]); + if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) + return QDateTime(); + int second = 0; + if (!parts[nsec].isEmpty()) { + second = parts[nsec].toInt(&ok[0]); + if (!ok[0]) + return QDateTime(); + } + 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(); + QDateTime result(qdate, QTime(hour, minute, second)); + if (!result.isValid() + || (dayOfWeek >= 0 && result.date().dayOfWeek() != dayOfWeek+1)) + return QDateTime(); // 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(); // the time isn't the last second of the day + } + return result; + } + public: // public constructor RssItem(const QDomElement& properties) : read(false), downloadLink("none") { @@ -70,6 +227,10 @@ class RssItem : public QObject { description = property.text(); else if (property.tagName() == "image") image = property.text(); + else if (property.tagName() == "pubDate") + date = parseDate(property.text()); + else if (property.tagName() == "author") + author = property.text(); property = property.nextSibling().toElement(); } } @@ -81,6 +242,10 @@ class RssItem : public QObject { return title; } + QString getAuthor() const { + return author; + } + QString getLink() const{ return link; } @@ -95,6 +260,10 @@ class RssItem : public QObject { return image; } + QDateTime getDate() const { + return date; + } + QString getDownloadLink() const{ return downloadLink; } @@ -274,6 +443,7 @@ class RssStream : public QObject{ } private: + // TODO: Read only news more recent than last refresh // read and create items from a rss document short readDoc(const QDomDocument& doc) { QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); @@ -291,10 +461,10 @@ class RssStream : public QObject{ } QDomNode rss = root.firstChild(); QDomElement channel = root.firstChild().toElement(); - unsigned short listsize = getNbNews(); - for(unsigned short i=0; igetFeed(selectedFeedUrl)->getItem(listNews->row(item)); - textBrowser->setHtml(article->getTitle()+":
"+article->getDescription()); + QString html; + html += "
"; + html += "
"+article->getTitle() + "
"; + if(article->getDate().isValid()) { + html += "
"+tr("Date: ")+""+article->getDate().toString()+"
"; + } + if(!article->getAuthor().isEmpty()) { + html += "
"+tr("Author: ")+""+article->getAuthor()+"
"; + } + html += "
"; + html += ""+article->getDescription()+""; + textBrowser->setHtml(html); article->setRead(); item->setData(Qt::ForegroundRole, QVariant(QColor("grey"))); item->setData(Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere.png")));