]> git.sur5r.net Git - minitube/blob - src/ytsearch.cpp
Imported Upstream version 2.4
[minitube] / src / ytsearch.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20
21 #include "ytsearch.h"
22 #include "constants.h"
23 #include "networkaccess.h"
24 #include "searchparams.h"
25 #include "video.h"
26 #include "ytchannel.h"
27
28 #ifdef APP_YT3
29 #include "yt3.h"
30 #include "yt3listparser.h"
31 #include "datautils.h"
32 #else
33 #include "ytfeedreader.h"
34 #endif
35
36 namespace The {
37 NetworkAccess* http();
38 QHash<QString, QAction*>* globalActions();
39 }
40
41 namespace {
42
43 QDateTime RFC3339fromString(const QString &s) {
44     return QDateTime::fromString(s, "yyyy-MM-ddThh:mm:ssZ");
45 }
46
47 QString RFC3339toString(const QDateTime &dt) {
48     return dt.toString("yyyy-MM-ddThh:mm:ssZ");
49 }
50
51 }
52
53 YTSearch::YTSearch(SearchParams *searchParams, QObject *parent) :
54     PaginatedVideoSource(parent),
55     searchParams(searchParams) {
56     searchParams->setParent(this);
57 }
58
59 #ifdef APP_YT3
60
61 void YTSearch::loadVideos(int max, int startIndex) {
62     aborted = false;
63
64     QUrl url = YT3::instance().method("search");
65
66 #if QT_VERSION >= 0x050000
67     {
68         QUrl &u = url;
69         QUrlQuery url;
70 #endif
71
72         url.addQueryItem("part", "snippet");
73         url.addQueryItem("type", "video");
74
75         url.addQueryItem("maxResults", QString::number(max));
76
77         if (startIndex > 1) {
78             if (maybeReloadToken(max, startIndex)) return;
79             url.addQueryItem("pageToken", nextPageToken);
80         }
81
82         // TODO interesting params
83         // url.addQueryItem("videoSyndicated", "true");
84         // url.addQueryItem("regionCode", "IT");
85         // url.addQueryItem("videoType", "movie");
86
87         if (!searchParams->keywords().isEmpty()) {
88             if (searchParams->keywords().startsWith("http://") ||
89                     searchParams->keywords().startsWith("https://")) {
90                 url.addQueryItem("q", YTSearch::videoIdFromUrl(searchParams->keywords()));
91             } else url.addQueryItem("q", searchParams->keywords());
92         }
93
94         if (!searchParams->channelId().isEmpty())
95             url.addQueryItem("channelId", searchParams->channelId());
96
97         switch (searchParams->sortBy()) {
98         case SearchParams::SortByNewest:
99             url.addQueryItem("order", "date");
100             break;
101         case SearchParams::SortByViewCount:
102             url.addQueryItem("order", "viewCount");
103             break;
104         case SearchParams::SortByRating:
105             url.addQueryItem("order", "rating");
106             break;
107         }
108
109         switch (searchParams->duration()) {
110         case SearchParams::DurationShort:
111             url.addQueryItem("videoDuration", "short");
112             break;
113         case SearchParams::DurationMedium:
114             url.addQueryItem("videoDuration", "medium");
115             break;
116         case SearchParams::DurationLong:
117             url.addQueryItem("videoDuration", "long");
118             break;
119         }
120
121         switch (searchParams->time()) {
122         case SearchParams::TimeToday:
123             url.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24)));
124             break;
125         case SearchParams::TimeWeek:
126             url.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24*7)));
127             break;
128         case SearchParams::TimeMonth:
129             url.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24*30)));
130             break;
131         }
132
133         if (searchParams->publishedAfter()) {
134             url.addQueryItem("publishedAfter", RFC3339toString(QDateTime::fromTime_t(searchParams->publishedAfter()).toUTC()));
135         }
136
137         switch (searchParams->quality()) {
138         case SearchParams::QualityHD:
139             url.addQueryItem("videoDefinition", "high");
140             break;
141         }
142
143 #if QT_VERSION >= 0x050000
144         u.setQuery(url);
145     }
146 #endif
147
148     lastUrl = url;
149
150     // qWarning() << "YT3 search" << url.toString();
151     QObject *reply = The::http()->get(url);
152     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
153     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
154 }
155
156 void YTSearch::parseResults(QByteArray data) {
157     if (aborted) return;
158
159     YT3ListParser parser(data);
160     QList<Video*> videos = parser.getVideos();
161     suggestions = parser.getSuggestions();
162
163     bool tryingWithNewToken = setPageToken(parser.getNextPageToken());
164     if (tryingWithNewToken) return;
165
166     if (name.isEmpty() && !searchParams->channelId().isEmpty()) {
167         if (!videos.isEmpty()) {
168             name = videos.first()->channelTitle();
169         }
170         emit nameChanged(name);
171     }
172
173     if (asyncDetails) {
174         emit gotVideos(videos);
175         emit finished(videos.size());
176     }
177     loadVideoDetails(videos);
178 }
179
180 #else
181
182 void YTSearch::loadVideos(int max, int startIndex) {
183     aborted = false;
184
185     QUrl url("http://gdata.youtube.com/feeds/api/videos/");
186 #if QT_VERSION >= 0x050000
187     {
188         QUrl &u = url;
189         QUrlQuery url;
190 #endif
191
192         url.addQueryItem("v", "2");
193         url.addQueryItem("max-results", QString::number(max));
194         url.addQueryItem("start-index", QString::number(startIndex));
195
196         if (!searchParams->keywords().isEmpty()) {
197             if (searchParams->keywords().startsWith("http://") ||
198                     searchParams->keywords().startsWith("https://")) {
199                 url.addQueryItem("q", YTSearch::videoIdFromUrl(searchParams->keywords()));
200             } else url.addQueryItem("q", searchParams->keywords());
201         }
202
203         if (!searchParams->channelId().isEmpty())
204             url.addQueryItem("author", searchParams->channelId());
205
206         switch (searchParams->sortBy()) {
207         case SearchParams::SortByNewest:
208             url.addQueryItem("orderby", "published");
209             break;
210         case SearchParams::SortByViewCount:
211             url.addQueryItem("orderby", "viewCount");
212             break;
213         case SearchParams::SortByRating:
214             url.addQueryItem("orderby", "rating");
215             break;
216         }
217
218         switch (searchParams->duration()) {
219         case SearchParams::DurationShort:
220             url.addQueryItem("duration", "short");
221             break;
222         case SearchParams::DurationMedium:
223             url.addQueryItem("duration", "medium");
224             break;
225         case SearchParams::DurationLong:
226             url.addQueryItem("duration", "long");
227             break;
228         }
229
230         switch (searchParams->time()) {
231         case SearchParams::TimeToday:
232             url.addQueryItem("time", "today");
233             break;
234         case SearchParams::TimeWeek:
235             url.addQueryItem("time", "this_week");
236             break;
237         case SearchParams::TimeMonth:
238             url.addQueryItem("time", "this_month");
239             break;
240         }
241
242         switch (searchParams->quality()) {
243         case SearchParams::QualityHD:
244             url.addQueryItem("hd", "true");
245             break;
246         }
247
248 #if QT_VERSION >= 0x050000
249         u.setQuery(url);
250     }
251 #endif
252     QObject *reply = The::http()->get(url);
253     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
254     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
255 }
256
257 void YTSearch::parseResults(QByteArray data) {
258     if (aborted) return;
259
260     YTFeedReader reader(data);
261     QList<Video*> videos = reader.getVideos();
262     suggestions = reader.getSuggestions();
263
264     if (name.isEmpty() && !searchParams->channelId().isEmpty()) {
265         if (videos.isEmpty()) name = searchParams->channelId();
266         else {
267             name = videos.first()->channelTitle();
268             // also grab the userId
269             userId = videos.first()->channelId();
270         }
271         emit nameChanged(name);
272     }
273
274     emit gotVideos(videos);
275     emit finished(videos.size());
276 }
277
278 #endif
279
280 void YTSearch::abort() {
281     aborted = true;
282 }
283
284 const QStringList & YTSearch::getSuggestions() {
285     return suggestions;
286 }
287
288 QString YTSearch::getName() {
289     if (!name.isEmpty()) return name;
290     if (!searchParams->keywords().isEmpty()) return searchParams->keywords();
291     return QString();
292 }
293
294 void YTSearch::requestError(QNetworkReply *reply) {
295     qWarning() << reply->errorString();
296     emit error(reply->errorString());
297 }
298
299 QString YTSearch::videoIdFromUrl(QString url) {
300     QRegExp re = QRegExp("^.*[\\?&]v=([^&#]+).*$");
301     if (re.exactMatch(url)) return re.cap(1);
302     re = QRegExp("^.*://.*/([^&#\\?]+).*$");
303     if (re.exactMatch(url)) return re.cap(1);
304     return QString();
305 }
306
307 QList<QAction*> YTSearch::getActions() {
308     QList<QAction*> channelActions;
309     if (searchParams->channelId().isEmpty())
310         return channelActions;
311     channelActions << The::globalActions()->value("subscribe-channel");
312     return channelActions;
313 }