]> git.sur5r.net Git - minitube/blob - src/paginatedvideosource.cpp
Imported Upstream version 2.4
[minitube] / src / paginatedvideosource.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 "paginatedvideosource.h"
22
23 #include "yt3.h"
24 #include "yt3listparser.h"
25 #include "datautils.h"
26
27 #include "video.h"
28 #include "networkaccess.h"
29
30 namespace The {
31 NetworkAccess* http();
32 QHash<QString, QAction*>* globalActions();
33 }
34
35 PaginatedVideoSource::PaginatedVideoSource(QObject *parent) : VideoSource(parent)
36   , tokenTimestamp(0)
37   , reloadingToken(false)
38   , currentMax(0)
39   , currentStartIndex(0)
40   , asyncDetails(false) { }
41
42 bool PaginatedVideoSource::hasMoreVideos() {
43     qDebug() << __PRETTY_FUNCTION__ << nextPageToken;
44     return !nextPageToken.isEmpty();
45 }
46
47 bool PaginatedVideoSource::maybeReloadToken(int max, int startIndex) {
48     // kind of hackish. Thank the genius who came up with this stateful stuff
49     // in a supposedly RESTful (aka stateless) API.
50
51     if (nextPageToken.isEmpty()) {
52         // previous request did not return a page token. Game over.
53         // emit gotVideos(QList<Video*>());
54         emit finished(0);
55         return true;
56     }
57
58     if (isPageTokenExpired()) {
59         reloadingToken = true;
60         currentMax = max;
61         currentStartIndex = startIndex;
62         reloadToken();
63         return true;
64     }
65     return false;
66 }
67
68 bool PaginatedVideoSource::setPageToken(const QString &value) {
69     tokenTimestamp = QDateTime::currentDateTime().toTime_t();
70     nextPageToken = value;
71
72     if (reloadingToken) {
73         reloadingToken = false;
74         loadVideos(currentMax, currentStartIndex);
75         currentMax = currentStartIndex = 0;
76         return true;
77     }
78
79     return false;
80 }
81
82 bool PaginatedVideoSource::isPageTokenExpired() {
83     uint now = QDateTime::currentDateTime().toTime_t();
84     return now - tokenTimestamp > 1800;
85 }
86
87 void PaginatedVideoSource::reloadToken() {
88     qDebug() << "Reloading pageToken";
89     QObject *reply = The::http()->get(lastUrl);
90     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
91     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
92 }
93
94 void PaginatedVideoSource::loadVideoDetails(const QList<Video*> &videos) {
95     QString videoIds;
96     foreach (Video *video, videos) {
97         // TODO get video details from cache
98         if (!videoIds.isEmpty()) videoIds += ",";
99         videoIds += video->id();
100         videoMap.insert(video->id(), video);
101     }
102
103     if (videoIds.isEmpty()) {
104         if (!asyncDetails) {
105             emit gotVideos(videos);
106             emit finished(videos.size());
107         }
108         return;
109     }
110
111     QUrl url = YT3::instance().method("videos");
112
113 #if QT_VERSION >= 0x050000
114     {
115         QUrl &u = url;
116         QUrlQuery url;
117 #endif
118
119         url.addQueryItem("part", "contentDetails,statistics");
120         url.addQueryItem("id", videoIds);
121
122 #if QT_VERSION >= 0x050000
123         u.setQuery(url);
124     }
125 #endif
126
127     QObject *reply = The::http()->get(url);
128     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseVideoDetails(QByteArray)));
129     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
130 }
131
132 void PaginatedVideoSource::parseVideoDetails(const QByteArray &bytes) {
133
134     QScriptEngine engine;
135     QScriptValue json = engine.evaluate("(" + QString::fromUtf8(bytes) + ")");
136
137     QScriptValue items = json.property("items");
138     if (items.isArray()) {
139         QScriptValueIterator it(items);
140         while (it.hasNext()) {
141             it.next();
142             QScriptValue item = it.value();
143             if (!item.isObject()) continue;
144
145             // qDebug() << item.toString();
146
147             QString id = item.property("id").toString();
148             Video *video = videoMap.value(id);
149             if (!video) {
150                 qWarning() << "No video for id" << id;
151                 continue;
152             }
153
154             QString isoPeriod = item.property("contentDetails").property("duration").toString();
155             int duration = DataUtils::parseIsoPeriod(isoPeriod);
156             video->setDuration(duration);
157
158             uint viewCount = item.property("statistics").property("viewCount").toUInt32();
159             video->setViewCount(viewCount);
160
161             // TODO cache by etag?
162         }
163     }
164     if (!asyncDetails) {
165         emit gotVideos(videoMap.values());
166         emit finished(videoMap.size());
167     } else {
168         emit gotDetails();
169     }
170 }