]> git.sur5r.net Git - minitube/blob - src/ytsearch.cpp
Imported Upstream version 2.5.1
[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 #include "compatibility/qurlqueryhelper.h"
36
37 namespace The {
38 NetworkAccess* http();
39 QHash<QString, QAction*>* globalActions();
40 }
41
42 namespace {
43
44 QDateTime RFC3339fromString(const QString &s) {
45     return QDateTime::fromString(s, "yyyy-MM-ddThh:mm:ssZ");
46 }
47
48 QString RFC3339toString(const QDateTime &dt) {
49     return dt.toString("yyyy-MM-ddThh:mm:ssZ");
50 }
51
52 }
53
54 YTSearch::YTSearch(SearchParams *searchParams, QObject *parent) :
55     PaginatedVideoSource(parent),
56     searchParams(searchParams) {
57     searchParams->setParent(this);
58 }
59
60 #ifdef APP_YT3
61
62 void YTSearch::loadVideos(int max, int startIndex) {
63     aborted = false;
64
65     QUrl url = YT3::instance().method("search");
66     {
67         QUrlQueryHelper urlHelper(url);
68         urlHelper.addQueryItem("part", "snippet");
69         urlHelper.addQueryItem("type", "video");
70         urlHelper.addQueryItem("maxResults", QString::number(max));
71
72         if (startIndex > 1) {
73             if (maybeReloadToken(max, startIndex)) return;
74             urlHelper.addQueryItem("pageToken", nextPageToken);
75         }
76
77         // TODO interesting params
78         // urlHelper.addQueryItem("videoSyndicated", "true");
79         // urlHelper.addQueryItem("regionCode", "IT");
80         // urlHelper.addQueryItem("videoType", "movie");
81
82         if (!searchParams->keywords().isEmpty()) {
83             if (searchParams->keywords().startsWith("http://") ||
84                     searchParams->keywords().startsWith("https://")) {
85                 urlHelper.addQueryItem("q", YTSearch::videoIdFromUrl(searchParams->keywords()));
86             } else urlHelper.addQueryItem("q", searchParams->keywords());
87         }
88
89         if (!searchParams->channelId().isEmpty())
90             urlHelper.addQueryItem("channelId", searchParams->channelId());
91
92         switch (searchParams->sortBy()) {
93         case SearchParams::SortByNewest:
94             urlHelper.addQueryItem("order", "date");
95             break;
96         case SearchParams::SortByViewCount:
97             urlHelper.addQueryItem("order", "viewCount");
98             break;
99         case SearchParams::SortByRating:
100             urlHelper.addQueryItem("order", "rating");
101             break;
102         }
103
104         switch (searchParams->duration()) {
105         case SearchParams::DurationShort:
106             urlHelper.addQueryItem("videoDuration", "short");
107             break;
108         case SearchParams::DurationMedium:
109             urlHelper.addQueryItem("videoDuration", "medium");
110             break;
111         case SearchParams::DurationLong:
112             urlHelper.addQueryItem("videoDuration", "long");
113             break;
114         }
115
116         switch (searchParams->time()) {
117         case SearchParams::TimeToday:
118             urlHelper.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24)));
119             break;
120         case SearchParams::TimeWeek:
121             urlHelper.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24*7)));
122             break;
123         case SearchParams::TimeMonth:
124             urlHelper.addQueryItem("publishedAfter", RFC3339toString(QDateTime::currentDateTimeUtc().addSecs(-60*60*24*30)));
125             break;
126         }
127
128         if (searchParams->publishedAfter()) {
129             urlHelper.addQueryItem("publishedAfter", RFC3339toString(QDateTime::fromTime_t(searchParams->publishedAfter()).toUTC()));
130         }
131
132         switch (searchParams->quality()) {
133         case SearchParams::QualityHD:
134             urlHelper.addQueryItem("videoDefinition", "high");
135             break;
136         }
137     }
138
139     lastUrl = url;
140
141     // qWarning() << "YT3 search" << url.toString();
142     QObject *reply = The::http()->get(url);
143     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
144     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
145 }
146
147 void YTSearch::parseResults(QByteArray data) {
148     if (aborted) return;
149
150     YT3ListParser parser(data);
151     QList<Video*> videos = parser.getVideos();
152     suggestions = parser.getSuggestions();
153
154     bool tryingWithNewToken = setPageToken(parser.getNextPageToken());
155     if (tryingWithNewToken) return;
156
157     if (name.isEmpty() && !searchParams->channelId().isEmpty()) {
158         if (!videos.isEmpty()) {
159             name = videos.first()->channelTitle();
160         }
161         emit nameChanged(name);
162     }
163
164     if (asyncDetails) {
165         emit gotVideos(videos);
166         emit finished(videos.size());
167     }
168     loadVideoDetails(videos);
169 }
170
171 #else
172
173 void YTSearch::loadVideos(int max, int startIndex) {
174     aborted = false;
175
176     QUrl url("http://gdata.youtube.com/feeds/api/videos/");
177     {
178         QUrlQueryHelper urlHelper(url);
179
180         urlHelper.addQueryItem("v", "2");
181         urlHelper.addQueryItem("max-results", QString::number(max));
182         urlHelper.addQueryItem("start-index", QString::number(startIndex));
183
184         if (!searchParams->keywords().isEmpty()) {
185             if (searchParams->keywords().startsWith("http://") ||
186                     searchParams->keywords().startsWith("https://")) {
187                 urlHelper.addQueryItem("q", YTSearch::videoIdFromUrl(searchParams->keywords()));
188             } else urlHelper.addQueryItem("q", searchParams->keywords());
189         }
190
191         if (!searchParams->channelId().isEmpty())
192             urlHelper.addQueryItem("author", searchParams->channelId());
193
194         switch (searchParams->sortBy()) {
195         case SearchParams::SortByNewest:
196             urlHelper.addQueryItem("orderby", "published");
197             break;
198         case SearchParams::SortByViewCount:
199             urlHelper.addQueryItem("orderby", "viewCount");
200             break;
201         case SearchParams::SortByRating:
202             urlHelper.addQueryItem("orderby", "rating");
203             break;
204         }
205
206         switch (searchParams->duration()) {
207         case SearchParams::DurationShort:
208             urlHelper.addQueryItem("duration", "short");
209             break;
210         case SearchParams::DurationMedium:
211             urlHelper.addQueryItem("duration", "medium");
212             break;
213         case SearchParams::DurationLong:
214             urlHelper.addQueryItem("duration", "long");
215             break;
216         }
217
218         switch (searchParams->time()) {
219         case SearchParams::TimeToday:
220             urlHelper.addQueryItem("time", "today");
221             break;
222         case SearchParams::TimeWeek:
223             urlHelper.addQueryItem("time", "this_week");
224             break;
225         case SearchParams::TimeMonth:
226             urlHelper.addQueryItem("time", "this_month");
227             break;
228         }
229
230         switch (searchParams->quality()) {
231         case SearchParams::QualityHD:
232             urlHelper.addQueryItem("hd", "true");
233             break;
234         }
235
236     }
237     QObject *reply = The::http()->get(url);
238     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
239     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
240 }
241
242 void YTSearch::parseResults(QByteArray data) {
243     if (aborted) return;
244
245     YTFeedReader reader(data);
246     QList<Video*> videos = reader.getVideos();
247     suggestions = reader.getSuggestions();
248
249     if (name.isEmpty() && !searchParams->channelId().isEmpty()) {
250         if (videos.isEmpty()) name = searchParams->channelId();
251         else {
252             name = videos.first()->channelTitle();
253             // also grab the userId
254             userId = videos.first()->channelId();
255         }
256         emit nameChanged(name);
257     }
258
259     emit gotVideos(videos);
260     emit finished(videos.size());
261 }
262
263 #endif
264
265 void YTSearch::abort() {
266     aborted = true;
267 }
268
269 const QStringList & YTSearch::getSuggestions() {
270     return suggestions;
271 }
272
273 QString YTSearch::getName() {
274     if (!name.isEmpty()) return name;
275     if (!searchParams->keywords().isEmpty()) return searchParams->keywords();
276     return QString();
277 }
278
279 void YTSearch::requestError(QNetworkReply *reply) {
280     qWarning() << reply->errorString();
281     emit error(reply->errorString());
282 }
283
284 QString YTSearch::videoIdFromUrl(const QString &url) {
285     QRegExp re = QRegExp("^.*[\\?&]v=([^&#]+).*$");
286     if (re.exactMatch(url)) return re.cap(1);
287     re = QRegExp("^.*://.*/([^&#\\?]+).*$");
288     if (re.exactMatch(url)) return re.cap(1);
289     return QString();
290 }
291
292 QList<QAction*> YTSearch::getActions() {
293     QList<QAction*> channelActions;
294     if (searchParams->channelId().isEmpty())
295         return channelActions;
296     channelActions << The::globalActions()->value("subscribe-channel");
297     return channelActions;
298 }