]> git.sur5r.net Git - minitube/blob - src/yt/ytjs/ytjschannelsource.cpp
New upstream version 3.8
[minitube] / src / yt / ytjs / ytjschannelsource.cpp
1 #include "ytjschannelsource.h"
2
3 #include "mainwindow.h"
4 #include "searchparams.h"
5 #include "video.h"
6 #include "ytsearch.h"
7
8 #include "js.h"
9
10 namespace {
11
12 int parseDuration(const QString &s) {
13     static const QTime zeroTime(0, 0);
14     QTime time = QTime::fromString(s, QStringLiteral("hh:mm:ss"));
15     return zeroTime.secsTo(time);
16 }
17
18 QString parseChannelId(const QString &channelUrl) {
19     int pos = channelUrl.lastIndexOf('/');
20     if (pos >= 0) return channelUrl.mid(pos + 1);
21     return QString();
22 }
23
24 QDateTime parsePublishedText(const QString &s) {
25     int num = 0;
26     const auto parts = s.splitRef(' ');
27     for (const auto &part : parts) {
28         num = part.toInt();
29         if (num > 0) break;
30     }
31     if (num == 0) return QDateTime();
32
33     auto now = QDateTime::currentDateTimeUtc();
34     if (s.contains("hour")) {
35         return now.addSecs(-num * 3600);
36     } else if (s.contains("day")) {
37         return now.addDays(-num);
38     } else if (s.contains("week")) {
39         return now.addDays(-num * 7);
40     } else if (s.contains("month")) {
41         return now.addMonths(-num);
42     } else if (s.contains("year")) {
43         return now.addDays(-num * 365);
44     }
45     return QDateTime();
46 }
47
48 } // namespace
49
50 YTJSChannelSource::YTJSChannelSource(SearchParams *searchParams, QObject *parent)
51     : VideoSource(parent), searchParams(searchParams) {}
52
53 void YTJSChannelSource::loadVideos(int max, int startIndex) {
54     aborted = false;
55
56     QString channelId = searchParams->channelId();
57
58     QString sortBy;
59     switch (searchParams->sortBy()) {
60     case SearchParams::SortByNewest:
61         sortBy = "newest";
62         break;
63     case SearchParams::SortByViewCount:
64         sortBy = "oldest";
65         break;
66     case SearchParams::SortByRating:
67         sortBy = "popular";
68         break;
69     }
70
71     if (startIndex <= 1) continuation.clear();
72
73     auto &js = JS::instance();
74
75     js.callFunction(new JSResult(this), "channelVideos", {channelId, sortBy, continuation})
76             .onJson([this](auto &doc) {
77                 auto obj = doc.object();
78
79                 qDebug() << doc.toJson();
80
81                 continuation = obj["continuation"].toString();
82
83                 const auto items = obj["items"].toArray();
84                 QVector<Video *> videos;
85                 videos.reserve(items.size());
86
87                 for (const auto &i : items) {
88                     QString type = i["type"].toString();
89                     if (type != "video") continue;
90
91                     Video *video = new Video();
92
93                     QString id = i["videoId"].toString();
94                     video->setId(id);
95
96                     QString title = i["title"].toString();
97                     video->setTitle(title);
98
99                     QString desc = i["description"].toString();
100                     if (desc.isEmpty()) desc = i["desc"].toString();
101                     video->setDescription(desc);
102
103                     const auto thumbs = i["videoThumbnails"].toArray();
104                     for (const auto &thumbObj : thumbs) {
105                         QString url = thumbObj["url"].toString();
106                         int width = thumbObj["width"].toInt();
107                         if (width >= 336)
108                             video->setLargeThumbnailUrl(url);
109                         else if (width >= 246)
110                             video->setMediumThumbnailUrl(url);
111                         else if (width >= 168)
112                             video->setThumbnailUrl(url);
113                     }
114
115                     int views = i["viewCount"].toInt();
116                     video->setViewCount(views);
117
118                     int duration = i["lengthSeconds"].toInt();
119                     video->setDuration(duration);
120
121                     auto published = parsePublishedText(i["publishedText"].toString());
122                     if (published.isValid()) video->setPublished(published);
123
124                     QString channelName = i["author"].toString();
125                     if (channelName != name) {
126                         this->name = channelName;
127                         emit nameChanged(name);
128                     }
129                     video->setChannelTitle(channelName);
130                     QString channelId = i["authorId"].toString();
131                     video->setChannelId(channelId);
132
133                     videos << video;
134                 }
135
136                 emit gotVideos(videos);
137                 emit finished(videos.size());
138             })
139             .onError([this, &js, max, startIndex](auto &msg) {
140                 static int retries = 0;
141                 if (retries < 3) {
142                     qDebug() << "Retrying...";
143                     auto nam = js.getEngine().networkAccessManager();
144                     nam->clearAccessCache();
145                     nam->setCookieJar(new QNetworkCookieJar());
146                     QTimer::singleShot(0, this,
147                                        [this, max, startIndex] { loadVideos(max, startIndex); });
148                     retries++;
149                 } else {
150                     emit error(msg);
151                 }
152             });
153 }
154
155 QString YTJSChannelSource::getName() {
156     return name;
157 }
158
159 const QList<QAction *> &YTJSChannelSource::getActions() {
160     static const QList<QAction *> channelActions = {
161             MainWindow::instance()->getAction("subscribeChannel")};
162     if (searchParams->channelId().isEmpty()) {
163         static const QList<QAction *> noActions;
164         return noActions;
165     }
166     return channelActions;
167 }