]> git.sur5r.net Git - minitube/blob - src/yt/ytjs/ytjssinglevideosource.cpp
New upstream version 3.9.1
[minitube] / src / yt / ytjs / ytjssinglevideosource.cpp
1 #include "ytjssinglevideosource.h"
2
3 #include "js.h"
4 #include "video.h"
5
6 namespace {
7
8 QDateTime parsePublishedText(const QString &s) {
9     int num = 0;
10     const auto parts = s.splitRef(' ');
11     for (const auto &part : parts) {
12         num = part.toInt();
13         if (num > 0) break;
14     }
15     if (num == 0) return QDateTime();
16
17     auto now = QDateTime::currentDateTimeUtc();
18     if (s.contains("hour")) {
19         return now.addSecs(-num * 3600);
20     } else if (s.contains("day")) {
21         return now.addDays(-num);
22     } else if (s.contains("week")) {
23         return now.addDays(-num * 7);
24     } else if (s.contains("month")) {
25         return now.addMonths(-num);
26     } else if (s.contains("year")) {
27         return now.addDays(-num * 365);
28     }
29     return QDateTime();
30 }
31
32 } // namespace
33
34 YTJSSingleVideoSource::YTJSSingleVideoSource(QObject *parent)
35     : VideoSource(parent), video(nullptr) {}
36
37 void YTJSSingleVideoSource::loadVideos(int max, int startIndex) {
38     aborted = false;
39
40     if (startIndex == 1) {
41         if (video) {
42             if (name.isEmpty()) {
43                 name = video->getTitle();
44                 qDebug() << "Emitting name changed" << name;
45                 emit nameChanged(name);
46             }
47             emit gotVideos({video->clone()});
48         }
49     }
50
51     JS::instance()
52             .callFunction(new JSResult(this), "videoInfo", {videoId})
53             .onJson([this](auto &doc) {
54                 if (aborted) return;
55
56                 auto obj = doc.object();
57
58                 auto parseVideoObject = [](QJsonObject i) {
59                     Video *video = new Video();
60
61                     QString id = i["id"].toString();
62                     if (id.isEmpty()) id = i["videoId"].toString();
63                     video->setId(id);
64
65                     QString title = i["title"].toString();
66                     video->setTitle(title);
67
68                     QString desc = i["description"].toString();
69                     if (desc.isEmpty()) desc = i["desc"].toString();
70                     if (desc.isEmpty()) desc = i["shortDescription"].toString();
71
72                     video->setDescription(desc);
73
74                     const auto thumbs = i["thumbnails"].toArray();
75                     for (const auto &t : thumbs) {
76                         video->addThumb(t["width"].toInt(), t["height"].toInt(),
77                                         t["url"].toString());
78                     }
79
80                     qDebug() << i["view_count"] << i["viewCount"];
81                     int views = i["view_count"].toString().toInt();
82                     if (views == 0) views = i["viewCount"].toString().toInt();
83                     video->setViewCount(views);
84
85                     int duration = i["length_seconds"].toInt();
86                     video->setDuration(duration);
87
88                     auto author = i["author"];
89                     if (author.isObject()) {
90                         auto authorObject = author.toObject();
91                         video->setChannelId(authorObject["id"].toString());
92                         video->setChannelTitle(authorObject["name"].toString());
93                     } else if (author.isString()) {
94                         video->setChannelId(i["ucid"].toString());
95                         video->setChannelTitle(author.toString());
96                     }
97
98                     auto published = parsePublishedText(i["published"].toString());
99                     if (published.isValid()) video->setPublished(published);
100
101                     return video;
102                 };
103
104                 QVector<Video *> videos;
105
106                 if (!video) {
107                     // first video
108                     auto video = parseVideoObject(obj["videoDetails"].toObject());
109                     videos << video;
110                     name = video->getTitle();
111                     qDebug() << "Emitting name changed" << name;
112                     emit nameChanged(name);
113                 }
114
115                 const auto items = obj["related_videos"].toArray();
116                 videos.reserve(items.size());
117
118                 for (const auto &i : items) {
119                     videos << parseVideoObject(i.toObject());
120                 }
121
122                 if (videos.isEmpty()) {
123                     emit error("No results");
124                 } else {
125                     emit gotVideos(videos);
126                     emit finished(videos.size());
127                 }
128
129                 // fake more videos by loading videos related to the last one
130                 video = nullptr;
131                 if (!videos.isEmpty()) videoId = videos.last()->getId();
132             })
133             .onError([this](auto &msg) { emit error(msg); });
134 }
135
136 void YTJSSingleVideoSource::setVideo(Video *video) {
137     this->video = video;
138     videoId = video->getId();
139 }
140
141 void YTJSSingleVideoSource::abort() {
142     aborted = true;
143 }
144
145 QString YTJSSingleVideoSource::getName() {
146     return name;
147 }