1 #include "ytjschannelsource.h"
3 #include "mainwindow.h"
4 #include "searchparams.h"
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);
17 QString parseChannelId(const QString &channelUrl) {
18 int pos = channelUrl.lastIndexOf('/');
19 if (pos >= 0) return channelUrl.mid(pos + 1);
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;
42 YTJSChannelSource::YTJSChannelSource(SearchParams *searchParams, QObject *parent)
43 : VideoSource(parent), searchParams(searchParams) {}
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); });
51 auto &engine = ytjs.getEngine();
55 auto function = engine.evaluate("channelVideos");
56 if (!function.isCallable()) {
57 qWarning() << function.toString() << " is not callable";
58 emit error(function.toString());
62 QString channelId = searchParams->channelId();
65 switch (searchParams->sortBy()) {
66 case SearchParams::SortByNewest:
69 case SearchParams::SortByViewCount:
72 case SearchParams::SortByRating:
77 if (startIndex <= 1) continuation.clear();
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();
84 qDebug() << doc.toJson();
86 continuation = obj["continuation"].toString();
88 const auto items = obj["items"].toArray();
89 QVector<Video *> videos;
90 videos.reserve(items.size());
92 for (const auto &i : items) {
93 QString type = i["type"].toString();
94 if (type != "video") continue;
96 Video *video = new Video();
98 QString id = i["videoId"].toString();
101 QString title = i["title"].toString();
102 video->setTitle(title);
104 QString desc = i["description"].toString();
105 if (desc.isEmpty()) desc = i["desc"].toString();
106 video->setDescription(desc);
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();
113 video->setLargeThumbnailUrl(url);
114 else if (width >= 246)
115 video->setMediumThumbnailUrl(url);
116 else if (width >= 168)
117 video->setThumbnailUrl(url);
120 int views = i["viewCount"].toInt();
121 video->setViewCount(views);
123 int duration = i["lengthSeconds"].toInt();
124 video->setDuration(duration);
126 int published = parsePublishedText(i["publishedText"].toString());
127 if (published) video->setPublished(QDateTime::fromSecsSinceEpoch(published));
129 QString channelName = i["author"].toString();
130 if (channelName != name) {
131 this->name = channelName;
132 emit nameChanged(name);
134 video->setChannelTitle(channelName);
135 QString channelId = i["authorId"].toString();
136 video->setChannelId(channelId);
141 emit gotVideos(videos);
142 emit finished(videos.size());
144 QJSValue h = engine.newQObject(handler);
145 auto value = function.call({h, channelId, sortBy, continuation});
146 ytjs.checkError(value);
149 QString YTJSChannelSource::getName() {
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;
160 return channelActions;