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