Implement download sequence, handle redirection.
[dorian] / search.cpp
1 #include <QNetworkAccessManager>
2 #include <QNetworkReply>
3 #include <QNetworkRequest>
4 #include <QWebFrame>
5 #include <QWebPage>
6 #include <QWebElementCollection>
7 #include <QWebElement>
8
9 #include "search.h"
10 #include "platform.h"
11 #include "trace.h"
12
13 Search *inst = 0;
14
15 Search *Search::instance()
16 {
17     if (!inst) {
18         inst = new Search();
19     }
20     return inst;
21 }
22
23 void Search::close()
24 {
25     delete inst;
26     inst = 0;
27 }
28
29 Search::Search(): QObject(0), reply(0), downloadReply(0)
30 {
31     manager = new QNetworkAccessManager(this);
32     downloadManager = new QNetworkAccessManager(this);
33 }
34
35 void Search::start(const Query &query)
36 {
37     Trace t("Search::start");
38
39     emit beginSearch();
40
41     searchResults.clear();
42     QNetworkRequest request;
43     request.setUrl(QUrl("http://www.gutenberg.org/catalog/world/results"));
44     // request.setRawHeader("User-Agent", "Dorian " + Platform::version());
45     QString title = query.title;
46     if (title.isEmpty()) {
47         title = ".";
48     }
49     QByteArray data;
50     data = "title=" + QUrl::toPercentEncoding(title) + "&author=" +
51            QUrl::toPercentEncoding(query.author);
52     qDebug() << "Request:" << (request.url().toString() + "?" + data);
53     reply = manager->post(request, data);
54     connect(reply, SIGNAL(finished()), this, SLOT(finished()));
55 }
56
57 QList<Search::Result> Search::results()
58 {
59     return searchResults;
60 }
61
62 bool Search::download(const Search::Result &result, const QString &fileName)
63 {
64     Trace t("Search::download");
65     qDebug() << "UID" << result.id;
66     Q_UNUSED(fileName);
67     emit beginDownload(0);
68     QUrl url("http://www.gutenberg.org/ebooks/" + result.id + ".epub");
69     qDebug() << "Requesting" << url;
70     QNetworkRequest request;
71     request.setUrl(url);
72     downloadReply = downloadManager->get(request);
73     connect(downloadReply, SIGNAL(finished()), this, SLOT(downloadFinished()));
74     return true;
75 }
76
77 void Search::finished()
78 {
79     Trace t("Search::finished");
80
81     if (!reply) {
82         return;
83     }
84
85     QByteArray data = reply->readAll();
86
87     // Parse search results
88
89     QWebPage page(this);
90     QWebFrame *frame = page.mainFrame();
91     frame->setHtml(QString(data));
92     QWebElementCollection tables = frame->findAllElements("table");
93     if (tables.count() == 1) {
94         QWebElement table = tables[0];
95         foreach (QWebElement row, table.findAll("tr")) {
96             QWebElementCollection cols = row.findAll("td");
97             if (cols.count() < 5) {
98                 continue;
99             }
100             QString id = cols[0].toPlainText().trimmed();
101             if (id.isEmpty()) {
102                 continue;
103             }
104             QString author = cols[2].toPlainText().trimmed();
105             QStringList titles = cols[3].toPlainText().trimmed().
106                                  split("\n", QString::SkipEmptyParts);
107             Result r;
108             r.authors = author.split("\n", QString::SkipEmptyParts);
109             r.id = id;
110             if (titles.count()) {
111                 r.title = titles[0];
112             }
113             r.language = cols[4].toPlainText().trimmed();
114             r.source = "Project Gutenberg";
115             searchResults.append(r);
116             qDebug() << id;
117             qDebug() << " Authors:" << r.authors;
118             qDebug() << " Title:" << r.title;
119             qDebug() << " Language:" << r.language;
120         }
121     }
122
123     reply->deleteLater();
124     reply = 0;
125     emit endSearch();
126 }
127
128 void Search::downloadFinished()
129 {
130     Trace t("Search::downloadFinished");
131
132     if (!downloadReply) {
133         return;
134     }
135
136     QVariant header = downloadReply->header(QNetworkRequest::LocationHeader);
137     if (header.isValid()) {
138         qDebug() << "Redirected to" << header;
139         downloadReply->deleteLater();
140         QNetworkRequest request;
141         request.setUrl(header.toUrl());
142         downloadReply = downloadManager->get(request);
143         connect(downloadReply, SIGNAL(finished()), this, SLOT(downloadFinished()));
144     } else {
145         QByteArray data = downloadReply->readAll();
146         qDebug() << "Got" << data.size() << "bytes";
147         downloadReply->deleteLater();
148         downloadReply = 0;
149         emit endDownload();
150     }
151 }