]> git.sur5r.net Git - minitube/blob - src/paginatedvideosource.cpp
Upload 3.9.3-2 to unstable
[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 "http.h"
29 #include "httputils.h"
30
31 PaginatedVideoSource::PaginatedVideoSource(QObject *parent) : VideoSource(parent)
32   , tokenTimestamp(0)
33   , reloadingToken(false)
34   , currentMax(0)
35   , currentStartIndex(0)
36   , asyncDetails(false) { }
37
38 bool PaginatedVideoSource::hasMoreVideos() {
39     qDebug() << __PRETTY_FUNCTION__ << nextPageToken;
40     return !nextPageToken.isEmpty();
41 }
42
43 bool PaginatedVideoSource::maybeReloadToken(int max, int startIndex) {
44     // kind of hackish. Thank the genius who came up with this stateful stuff
45     // in a supposedly RESTful (aka stateless) API.
46
47     if (nextPageToken.isEmpty()) {
48         // previous request did not return a page token. Game over.
49         // emit gotVideos(QVector<Video*>());
50         emit finished(0);
51         return true;
52     }
53
54     if (isPageTokenExpired()) {
55         reloadingToken = true;
56         currentMax = max;
57         currentStartIndex = startIndex;
58         reloadToken();
59         return true;
60     }
61     return false;
62 }
63
64 bool PaginatedVideoSource::setPageToken(const QString &value) {
65     tokenTimestamp = QDateTime::currentDateTime().toTime_t();
66     nextPageToken = value;
67
68     if (reloadingToken) {
69         reloadingToken = false;
70         loadVideos(currentMax, currentStartIndex);
71         currentMax = currentStartIndex = 0;
72         return true;
73     }
74
75     return false;
76 }
77
78 bool PaginatedVideoSource::isPageTokenExpired() {
79     uint now = QDateTime::currentDateTime().toTime_t();
80     return now - tokenTimestamp > 1800;
81 }
82
83 void PaginatedVideoSource::reloadToken() {
84     qDebug() << "Reloading pageToken";
85     QObject *reply = HttpUtils::yt().get(lastUrl);
86     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
87     connect(reply, SIGNAL(error(QString)), SLOT(requestError(QString)));
88 }
89
90 void PaginatedVideoSource::loadVideoDetails(const QVector<Video*> &videos) {
91     this->videos = videos;
92     QString videoIds;
93     videoIds.reserve(videos.size()*12);
94     videoMap.reserve(videos.size());
95     for (Video *video : videos) {
96         // TODO get video details from cache
97         if (!videoIds.isEmpty()) videoIds += QLatin1Char(',');
98         videoIds += video->getId();
99         videoMap.insert(video->getId(), video);
100     }
101
102     if (videoIds.isEmpty()) {
103         if (!asyncDetails) {
104             emit gotVideos(videos);
105             emit finished(videos.size());
106         }
107         return;
108     }
109
110     QUrl url = YT3::instance().method("videos");
111     QUrlQuery q(url);
112     q.addQueryItem(QStringLiteral("part"), QStringLiteral("contentDetails,statistics"));
113     q.addQueryItem(QStringLiteral("id"), videoIds);
114     url.setQuery(q);
115
116     QObject *reply = HttpUtils::yt().get(url);
117     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseVideoDetails(QByteArray)));
118     connect(reply, SIGNAL(error(QString)), SLOT(requestError(QString)));
119 }
120
121 void PaginatedVideoSource::parseVideoDetails(const QByteArray &bytes) {
122     QJsonDocument doc = QJsonDocument::fromJson(bytes);
123     QJsonObject obj = doc.object();
124
125     QJsonValue items = obj["items"];
126     if (items.isArray()) {
127         const auto array = items.toArray();
128         for (const QJsonValue &v : array) {
129             if (!v.isObject()) continue;
130
131             QJsonObject item = v.toObject();
132
133             QString id = item["id"].toString();
134             Video *video = videoMap.value(id);
135             if (!video) {
136                 qWarning() << "No video for id" << id;
137                 continue;
138             }
139
140             QString isoPeriod = item["contentDetails"].toObject()["duration"].toString();
141             int duration = DataUtils::parseIsoPeriod(isoPeriod);
142             video->setDuration(duration);
143
144             int viewCount = item["statistics"].toObject()["viewCount"].toString().toInt();
145             video->setViewCount(viewCount);
146
147             // TODO cache by etag?
148         }
149     }
150     if (!asyncDetails) {
151         emit gotVideos(videos);
152         emit finished(videos.size());
153     } else {
154         emit gotDetails();
155     }
156 }