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