]> git.sur5r.net Git - minitube/blob - src/yt/ytjs/ytjschannelsource.cpp
New upstream version 3.9.1
[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                 continuation = obj["continuation"].toString();
80
81                 const auto items = obj["items"].toArray();
82                 QVector<Video *> videos;
83                 videos.reserve(items.size());
84
85                 for (const auto &i : items) {
86                     QString type = i["type"].toString();
87                     if (type != "video") continue;
88
89                     Video *video = new Video();
90
91                     QString id = i["videoId"].toString();
92                     video->setId(id);
93
94                     QString title = i["title"].toString();
95                     video->setTitle(title);
96
97                     QString desc = i["description"].toString();
98                     if (desc.isEmpty()) desc = i["desc"].toString();
99                     video->setDescription(desc);
100
101                     const auto thumbs = i["videoThumbnails"].toArray();
102                     for (const auto &t : thumbs) {
103                         video->addThumb(t["width"].toInt(), t["height"].toInt(),
104                                         t["url"].toString());
105                     }
106
107                     int views = i["viewCount"].toInt();
108                     video->setViewCount(views);
109
110                     int duration = i["lengthSeconds"].toInt();
111                     video->setDuration(duration);
112
113                     auto published = parsePublishedText(i["publishedText"].toString());
114                     if (published.isValid()) video->setPublished(published);
115
116                     QString channelName = i["author"].toString();
117                     if (channelName != name) {
118                         name = channelName;
119                         emit nameChanged(name);
120                     }
121                     video->setChannelTitle(channelName);
122                     QString channelId = i["authorId"].toString();
123                     video->setChannelId(channelId);
124
125                     videos << video;
126                 }
127
128                 emit gotVideos(videos);
129                 emit finished(videos.size());
130             })
131             .onError([this, max, startIndex](auto &msg) {
132                 static int retries = 0;
133                 if (retries < 3) {
134                     qDebug() << "Retrying...";
135                     QTimer::singleShot(0, this,
136                                        [this, max, startIndex] { loadVideos(max, startIndex); });
137                     retries++;
138                 } else {
139                     emit error(msg);
140                 }
141             });
142 }
143
144 QString YTJSChannelSource::getName() {
145     return name;
146 }
147
148 const QList<QAction *> &YTJSChannelSource::getActions() {
149     static const QList<QAction *> channelActions = {
150             MainWindow::instance()->getAction("subscribeChannel")};
151     if (searchParams->channelId().isEmpty()) {
152         static const QList<QAction *> noActions;
153         return noActions;
154     }
155     return channelActions;
156 }