Fixed searchclients to handle new Google URLs correctly; added GUI
[movie-schedule] / src / searchclients / abstractsearchclient.cpp
1 // Copyright 2010 Jochen Becher
2 //
3 // This file is part of MovieSchedule.
4 //
5 // MovieSchedule is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // MovieSchedule is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with MovieSchedule.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "abstractsearchclient.h"
19
20 #include "utils/timeutils.h"
21
22 #include <QMutexLocker>
23 #include <iostream>
24
25 AbstractSearchClient::AbstractSearchClient(QObject *parent)
26     : QObject(parent),
27     _network(new QNetworkAccessManager(this)),
28     _search_task_id(INVALID_SEARCH_TASK_ID),
29     _start(0)
30 {
31     connect(_network, SIGNAL(finished(QNetworkReply *)),
32             this, SLOT(ReplyFinished(QNetworkReply*)));
33     {
34         QMutexLocker locker(&_next_search_task_id_mutex);
35         _search_task_id = _next_search_task_id++;
36     }
37 }
38
39 AbstractSearchClient::~AbstractSearchClient()
40 {
41 }
42
43 void AbstractSearchClient::Search(const QUrl &url, int start)
44 {
45     _start = start;
46     if (start == 0) {
47         emit SearchStarted(_search_task_id);
48     }
49     QNetworkRequest request;
50     QUrl xurl(url);
51     if (_start != 0 && !url.hasQueryItem("start")) {
52         xurl.addQueryItem("start", QString::number(_start));
53     }
54     FixLocation(&xurl);
55     //std::cout << "URL: " << qPrintable(QString(xurl.toEncoded())) << std::endl;
56     request.setUrl(xurl);
57     //request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.8) Gecko/20100723 Ubuntu/9.10 (karmic) Firefox/3.6.8");
58     request.setRawHeader("User-Agent", "Mozilla/5.0");
59     //request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
60     request.setRawHeader("Accept", "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
61     request.setRawHeader("Accept-Language", "en-gb;q=1.0,en;q=0.9,de-de;q=0.5,de;q=0.3");
62     //request.setRawHeader("Accept-Language", "en-us,en;q=0.8,de-de;q=0.5,de;q=0.3");
63     //request.setRawHeader("Accept-Encoding", "gzip,deflate");
64     request.setRawHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
65     request.setRawHeader("Keep-Alive", "115");
66     request.setRawHeader("Connection", "keep-alive");
67     request.setRawHeader("Cache-Control", "max-age=0");
68     QNetworkReply *reply = _network->get(request);
69     connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(DownloadProgress(qint64,qint64)));
70     connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(NetworkError(QNetworkReply::NetworkError)));
71 }
72
73 void AbstractSearchClient::SearchEncodedUrl(const QString &encoded_url, int start)
74 {
75     QUrl url = QUrl::fromEncoded((QString("http://www.google.com") + encoded_url).toAscii());
76     Search(url, start);
77 }
78
79 void AbstractSearchClient::DownloadProgress(qint64 a,qint64 b)
80 {
81     //std::cout << "Search Progress of " << qPrintable(objectName()) << " - " << a << ", " << b << std::endl;
82     //sleep(1);
83     emit Progress(_search_task_id, a, b);
84 }
85
86 void AbstractSearchClient::NetworkError(QNetworkReply::NetworkError error)
87 {
88     emit SearchFinished(_search_task_id, false);
89     std::cout << "ERROR: " << error << std::endl;
90     emit Error(_search_task_id);
91     sender()->deleteLater();
92     deleteLater();
93 }
94
95 QList<QTime> AbstractSearchClient::TimesFromString(const QList<QString> &time_strings)
96 {
97     QList<QTime> schedule_times;
98     bool am_pm = false;
99     Q_FOREACH(const QString time_str, time_strings) {
100         if (time_str.endsWith("am", Qt::CaseInsensitive) || time_str.endsWith("pm", Qt::CaseInsensitive)) {
101             am_pm = true;
102         }
103     }
104     if (am_pm) {
105         int i = 0;
106         bool am = true;
107         while (i < time_strings.length()) {
108             int j = i;
109             while (i < time_strings.length()) {
110                 if (time_strings[i].endsWith("am", Qt::CaseInsensitive)) {
111                     am = true;
112                     break;
113                 } else if (time_strings[i].endsWith("pm", Qt::CaseInsensitive)) {
114                     am = false;
115                     break;
116                 }
117                 ++i;
118             }
119             while (j < i) {
120                 QString time_str = time_strings[j] + (am ? "am" : "pm");
121                 QTime time = TimeUtils::FromTimeString(time_str);
122                 if (time.isValid()) {
123                     schedule_times.append(time);
124                 } else {
125                     std::cout << "ERROR: time " << qPrintable(time_str) << " is invalid." << std::endl;
126                 }
127                 ++j;
128             }
129             if (i < time_strings.length()) {
130                 QString time_str = time_strings[i];
131                 QTime time = TimeUtils::FromTimeString(time_str);
132                 if (time.isValid()) {
133                     schedule_times.append(time);
134                 } else {
135                     std::cout << "ERROR: time " << qPrintable(time_str) << " is invalid." << std::endl;
136                 }
137                 schedule_times.append(time);
138             }
139             ++i;
140         }
141     } else {
142         Q_FOREACH(const QString time_str, time_strings) {
143             QTime time = TimeUtils::FromTimeString(time_str);
144             if (time.isValid()) {
145                 schedule_times.append(time);
146             } else {
147                 std::cout << "ERROR: time " << qPrintable(time_str) << " is invalid." << std::endl;
148             }
149         }
150     }
151     return schedule_times;
152 }
153
154 void AbstractSearchClient::FixLocation(QUrl *url)
155 {
156 #if 0
157     // Try to fix the Google url which returns
158     // wrong locations in some links
159     if (_location.isNull()) {
160         // Fetch location from url on first call (which is still correct)
161         if (url->hasQueryItem("loc")) {
162             _location = url->queryItemValue("loc");
163         } else if (url->hasQueryItem("near")) {
164             _location = url->queryItemValue("near");
165         } else if (url->hasQueryItem("defaultloc")) {
166             _location = url->queryItemValue("defaultloc");
167         }
168     } else {
169         // Replace with fetched location in later calls
170         if (url->hasQueryItem("loc")) {
171             url->removeAllQueryItems("loc");
172             url->addQueryItem("loc", _location);
173         }
174         if (url->hasQueryItem("near")) {
175             url->removeAllQueryItems("near");
176             url->addQueryItem("near", _location);
177         }
178         if (url->hasQueryItem("defaultloc")) {
179             url->removeAllQueryItems("defaultloc");
180             url->addQueryItem("defaultloc", _location);
181         }
182     }
183 #else
184     Q_UNUSED(url);
185 #endif
186 }
187
188 QMutex AbstractSearchClient::_next_search_task_id_mutex;
189 int AbstractSearchClient::_next_search_task_id = 1;