]> git.sur5r.net Git - minitube/blob - src/paginatedvideosource.cpp
Imported Upstream version 2.5.1
[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 #include "compatibility/qurlqueryhelper.h"
27
28 #include "video.h"
29 #include "networkaccess.h"
30
31 namespace The {
32 NetworkAccess* http();
33 QHash<QString, QAction*>* globalActions();
34 }
35
36 PaginatedVideoSource::PaginatedVideoSource(QObject *parent) : VideoSource(parent)
37   , tokenTimestamp(0)
38   , currentMax(0)
39   , currentStartIndex(0)
40   , reloadingToken(false)
41   , asyncDetails(false) { }
42
43 bool PaginatedVideoSource::hasMoreVideos() {
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         this->videos = videos;
101         videoMap.insert(video->id(), video);
102     }
103
104     if (videoIds.isEmpty()) {
105         if (!asyncDetails) {
106             emit gotVideos(videos);
107             emit finished(videos.size());
108         }
109         return;
110     }
111
112     QUrl url = YT3::instance().method("videos");
113     {
114         QUrlQueryHelper urlHelper(url);
115         urlHelper.addQueryItem("part", "contentDetails,statistics");
116         urlHelper.addQueryItem("id", videoIds);
117     }
118
119     QObject *reply = The::http()->get(url);
120     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseVideoDetails(QByteArray)));
121     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(requestError(QNetworkReply*)));
122 }
123
124 void PaginatedVideoSource::parseVideoDetails(const QByteArray &bytes) {
125
126     QScriptEngine engine;
127     QScriptValue json = engine.evaluate("(" + QString::fromUtf8(bytes) + ")");
128
129     QScriptValue items = json.property("items");
130     if (items.isArray()) {
131         QScriptValueIterator it(items);
132         while (it.hasNext()) {
133             it.next();
134             QScriptValue item = it.value();
135             if (!item.isObject()) continue;
136
137             // qDebug() << item.toString();
138
139             QString id = item.property("id").toString();
140             Video *video = videoMap.value(id);
141             if (!video) {
142                 qWarning() << "No video for id" << id;
143                 continue;
144             }
145
146             QString isoPeriod = item.property("contentDetails").property("duration").toString();
147             int duration = DataUtils::parseIsoPeriod(isoPeriod);
148             video->setDuration(duration);
149
150             uint viewCount = item.property("statistics").property("viewCount").toUInt32();
151             video->setViewCount(viewCount);
152
153             // TODO cache by etag?
154         }
155     }
156     if (!asyncDetails) {
157         emit gotVideos(videos);
158         emit finished(videos.size());
159     } else {
160         emit gotDetails();
161     }
162 }