Fixed a few bugs introduced by switching to json.
[grr] / src / googlereader.cpp
index 9cf88ba..e9dfc19 100644 (file)
@@ -6,9 +6,10 @@
 #include <QBuffer>
 #include <QTimer>
 #include <QDateTime>
-#include <QDomDocument>
 #include <QDebug>
 
+#include <qjson/parser.h>
+
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -26,16 +27,83 @@ void Feed::updateSubscription(Feed *feed) {
 
 void Feed::fetch(bool cont) {
        QNetworkRequest request;
-       QByteArray ba = "http://www.google.com/reader/atom/";
-
+       QByteArray ba = "http://www.google.com/reader/api/0/stream/contents/";
        ba.append(QUrl::toPercentEncoding(id));
        QUrl url = QUrl::fromEncoded(ba);
 
        if(continuation != "" && cont)
                url.addEncodedQueryItem("c", continuation.toUtf8());
 
+       if(!cont && updated) {
+               /* Add 1 to the timestamp, otherwise we get the latest item
+                * again. Also the order has to be reversed for this to work. */
+               url.addEncodedQueryItem("ot", QByteArray::number(updated + 1));
+               url.addEncodedQueryItem("r", "o");
+       }
+
        request.setUrl(url);
-       reader->getManager()->get(request);
+       reply = reader->getManager()->get(request);
+       connect(reply, SIGNAL(finished()), SLOT(fetchFinished()));
+}
+
+void Feed::fetchFinished() {
+       if (reply->error()) {
+               qDebug() << "Download of" << reply->url() << "failed:" << qPrintable(reply->errorString());
+               return;
+       }
+
+       QJson::Parser parser;
+       bool ok;
+       QVariantMap result = parser.parse(reply->readAll(), &ok).toMap();
+
+       continuation = result["continuation"].toString();
+       updated = result["updated"].toUInt();
+
+       foreach(QVariant l, result["items"].toList()) {
+               QVariantMap e = l.toMap();
+               Entry *entry = new Entry();;
+               QString content, summary;
+
+               entry->id = e["id"].toString();
+               entry->published = QDateTime::fromTime_t(e["published"].toUInt());
+               entry->author = e["author"].toString();
+               entry->source = (e["origin"].toMap())["streamId"].toString();
+               foreach(QVariant a, e["alternate"].toList()) {
+                       QVariantMap alt = a.toMap();
+                       if(alt["type"].toString() == "text/html")
+                               entry->link = alt["href"].toString();
+               }
+
+               content = (e["content"].toMap())["content"].toString();
+               summary = (e["summary"].toMap())["content"].toString();
+               if(content != "") entry->content = content; else entry->content = summary;
+
+               if(e["isReadStateLocked"].toBool())
+                       entry->flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
+
+               QWebPage p;
+               p.mainFrame()->setHtml(e["title"].toString());
+               entry->title = p.mainFrame()->toPlainText();
+
+               foreach(QVariant c, e["categories"].toList()) {
+                       QString cat = c.toString();
+                       if(cat.endsWith("/state/com.google/read"))
+                               entry->flags |= ENTRY_FLAG_READ;
+                       else if(cat.endsWith("/state/com.google/starred"))
+                               entry->flags |= ENTRY_FLAG_STARRED;
+                       else if(cat.endsWith("/state/com.google/broadcast"))
+                               entry->flags |= ENTRY_FLAG_SHARED;
+               }
+
+               entry->feed = this;
+               addEntry(entry);
+       }
+
+       lastUpdated = QDateTime::currentDateTime();
+
+       emit updateFeedComplete();
+
+       reply->deleteLater();
 }
 
 GoogleReader::GoogleReader() {
@@ -48,8 +116,8 @@ GoogleReader::GoogleReader() {
        SIDPending = false;
 
        login_url.setUrl("https://www.google.com/accounts/ClientLogin");
-       subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list");
-       unread_url.setUrl("http://www.google.com/reader/api/0/unread-count");
+       subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list?output=json");
+       unread_url.setUrl("http://www.google.com/reader/api/0/unread-count?output=json");
        edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-");
        token_url.setUrl("http://www.google.com/reader/api/0/token");
        markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-");
@@ -114,11 +182,7 @@ void GoogleReader::downloadFinished(QNetworkReply *reply) {
                qDebug() << "token:" << token;
        }
        else if(url == subscriptions_url) {
-               QByteArray data = reply->readAll();
-               QDomDocument dom;
-               dom.setContent(data);
-               parseSubscriptions(dom);
-               emit updateSubscriptionsComplete();
+               parseSubscriptions(reply->readAll());
 
                /* TODO: Replace this with a proper state machine */
                if(updateUnreadPending) {
@@ -127,11 +191,7 @@ void GoogleReader::downloadFinished(QNetworkReply *reply) {
                }
        }
        else if(url == unread_url) {
-               QByteArray data = reply->readAll();
-               QDomDocument dom;
-               dom.setContent(data);
-               parseUnread(dom);
-               emit updateUnreadComplete();
+               parseUnread(reply->readAll());
        }
        else if(url == edittag_url) {
                QByteArray data = reply->readAll();
@@ -141,143 +201,34 @@ void GoogleReader::downloadFinished(QNetworkReply *reply) {
                QByteArray data = reply->readAll();
                //qDebug() << "Result:" << data;
        }
-       else {
-               QByteArray data = reply->readAll();
-               QDomDocument dom;
-               dom.setContent(data);
-               parseFeed(dom);
-       }
 
        reply->deleteLater();
 }
 
-void GoogleReader::parseFeed(QDomDocument dom) {
-       QDomElement set, e;
-       QDomNode n, c;
-       QString continuation, feedsource;
-       Feed *feed = NULL;
-
-       set = dom.firstChildElement();
-
-       for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
-               e = n.toElement();
-               QString name = e.tagName();
-               if(name == "entry") {
-                       Entry entry;
-                       QString content, summary;
-
-                       QString locked = e.attribute("gr:is-read-state-locked", "false");
-                       if(locked == "true")
-                               entry.flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
-
-                       entry.crawled = e.attribute("gr:crawl-timestamp-msec", "0").toULongLong();
-
-                       for(c = n.firstChild(); !c.isNull(); c = c.nextSibling()) {
-                               e = c.toElement();
-                               name = e.tagName();
-                               if(name == "id")
-                                       entry.id = e.text();
-                               else if(name == "title") {
-                                       QWebPage p;
-                                       p.mainFrame()->setHtml(e.text());
-                                       entry.title = p.mainFrame()->toPlainText();
-                               }
-                               else if(name == "published")
-                                       entry.published = QDateTime::fromString(e.text(), "yyyy-MM-dd'T'HH:mm:ss'Z'");
-                               else if(name == "link")
-                                       entry.link = QUrl(e.attribute("href", ""));
-                               else if(name == "source")
-                                       entry.source = e.attribute("gr:stream-id", "");
-                               else if(name == "content")
-                                       content = e.text();
-                               else if(name == "summary")
-                                       summary = e.text();
-                               else if(name == "author") {
-                                       e = c.firstChild().toElement();
-                                       entry.author = e.text();
-                                       if(entry.author == "(author unknown)")
-                                               entry.author = "";
-                               }
-                               else if(name == "category") {
-                                       QString label = e.attribute("label", "");
-                                       if(label == "read")
-                                               entry.flags |= ENTRY_FLAG_READ;
-                                       else if(label == "starred")
-                                               entry.flags |= ENTRY_FLAG_STARRED;
-                                       else if(label == "broadcast")
-                                               entry.flags |= ENTRY_FLAG_SHARED;
-                               }
-                       }
-
-                       if(content != "")
-                               entry.content = content;
-                       else if(summary != "")
-                               entry.content = summary;
-
-                       if(!feed)
-                               feed = feeds.value(feedsource == "" ? entry.source : feedsource);
-
-                       if(feed) {
-                               entry.feed = feed;
-                               feed->addEntry(new Entry(entry));
-                       }
-               }
-               else if(name == "gr:continuation") {
-                       continuation = e.text();
-               }
-               else if(name == "id") {
-                       if(e.text().endsWith("/state/com.google/starred"))
-                               feedsource = "user/-/state/com.google/starred";
-                       else if(e.text().endsWith("/state/com.google/broadcast"))
-                               feedsource = "user/-/state/com.google/broadcast";
-               }
-       }
-
-       if(feed) {
-               feed->lastUpdated = QDateTime::currentDateTime();
-               feed->continuation = continuation;
-               feed->signalUpdated();
-       }
-}
-
-void GoogleReader::parseSubscriptions(QDomDocument dom) {
-       QDomElement set, e;
-       QDomNode n, c;
+void GoogleReader::parseSubscriptions(QByteArray data) {
+       QJson::Parser parser;
+       bool ok;
+       QVariantMap result = parser.parse(data, &ok).toMap();
 
        /* Clear the subscription updated flag */
        QHash<QString, Feed *>::iterator i;
        for(i = feeds.begin(); i != feeds.end(); ++i)
                i.value()->subscription_updated = false;
 
-       set = dom.firstChildElement();
-       set = set.firstChildElement("list");
-       set = set.firstChildElement("object");
-
-       for (; !set.isNull(); set = set.nextSiblingElement("object")) {
+       foreach(QVariant l, result["subscriptions"].toList()) {
+               QVariantMap subscription = l.toMap();
                Feed *feed = new Feed(this);
                Feed *existing_feed;
 
-               for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
-                       e = n.toElement();
-                       QString name = e.attribute("name");
-                       if(name == "id")
-                               feed->id = e.text();
-                       else if(name == "title")
-                               feed->title = e.text();
-                       else if(name == "sortid")
-                               feed->sortid = e.text();
-                       else if(name == "firstitemmsec")
-                               feed->firstitemmsec = e.text();
-                       else if(name == "categories") {
-                               for(c = n.firstChild().firstChild(); !c.isNull(); c = c.nextSibling()) {
-                                       e = c.toElement();
-                                       QString name = e.attribute("name");
-                                       if(name == "id")
-                                               feed->cat_id = e.text();
-                                       else if(name == "label")
-                                               feed->cat_label = e.text();
-                               }
-                       }
+               feed->id = subscription["id"].toString();
+               feed->title = subscription["title"].toString();
+               feed->sortid = subscription["sortid"].toString();
+               feed->firstitemmsec = subscription["firstitemmsec"].toString();
+
+               foreach(QVariant c, subscription["categories"].toList()) {
+                       QVariantMap cat = c.toMap();
+                       feed->cat_id = cat["id"].toString();
+                       feed->cat_label = cat["label"].toString();
                }
 
                existing_feed = feeds.value(feed->id);
@@ -303,44 +254,33 @@ void GoogleReader::parseSubscriptions(QDomDocument dom) {
        }
 
        lastUpdated = QDateTime::currentDateTime();
+       emit updateSubscriptionsComplete();
 }
 
-void GoogleReader::parseUnread(QDomDocument dom) {
-       QDomElement set, e;
-       QDomNode n, c;
-
-       set = dom.firstChildElement();
-       set = set.firstChildElement("list");
-       set = set.firstChildElement("object");
-
-       for (; !set.isNull(); set = set.nextSiblingElement("object")) {
-               QString id;
-               int count = 0;
-               ulong newestitem = 0;
-
-               for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
-                       e = n.toElement();
-                       QString name = e.attribute("name");
-                       if(name == "id")
-                               id = e.text();
-                       else if(name == "count")
-                               count =  e.text().toInt();
-                       else if(name == "newestItemTimestampUsec")
-                               newestitem = e.text().toULong();
-               }
+void GoogleReader::parseUnread(QByteArray data) {
+       QJson::Parser parser;
+       bool ok;
+       QVariantMap result = parser.parse(data, &ok).toMap();
+
+       foreach(QVariant l, result["unreadcounts"].toList()) {
+               QVariantMap unread = l.toMap();
+               QString id = unread["id"].toString();
+               int count = unread["count"].toInt();
+               ulong newestitem = unread["newestitem"].toUInt();
 
                Feed *f = feeds.value(id);
                if(f) {
                        f->unread = count;
                        f->newestitem = newestitem;
-                       //qDebug() << f->title << "->" << count;
-               }
-               else {
-                       //printf("%s not found\n", id.toLatin1().data());
+
+                       /* Not a good idea if it doesn't happen sequentially. */
+                       /* Pre-fetch feeds with unread items */
+                       /* f->fetch(false); */
                }
        }
 
        lastUpdated = QDateTime::currentDateTime();
+       emit updateUnreadComplete();
 }
 
 void GoogleReader::getSID() {
@@ -435,11 +375,6 @@ void Feed::delEntry(Entry *entry) {
        entries.remove(entry->id);
 }
 
-void Feed::signalUpdated() {
-       //  TODO: Clean this up
-       emit updateFeedComplete();
-}
-
 void Feed::updateUnread(int i) {
        bool allRead = (unread == 0);