]> git.sur5r.net Git - minitube/blob - src/invidious/invidious.cpp
New upstream version 3.6.1
[minitube] / src / invidious / invidious.cpp
1 #include "invidious.h"
2
3 #include "cachedhttp.h"
4 #include "http.h"
5 #include "httputils.h"
6 #include "jsfunctions.h"
7 #include "throttledhttp.h"
8
9 #ifdef APP_EXTRA
10 #include "extra.h"
11 #endif
12
13 namespace {
14 QStringList fallbackServers{"https://invidious.snopyta.org"};
15 QStringList preferredServers;
16
17 void shuffle(QStringList &sl) {
18     std::shuffle(sl.begin(), sl.end(), *QRandomGenerator::global());
19 }
20
21 } // namespace
22
23 Invidious &Invidious::instance() {
24     static Invidious i;
25     return i;
26 }
27
28 Http &Invidious::http() {
29     static Http *h = [] {
30         Http *http = new Http;
31         http->addRequestHeader("User-Agent", HttpUtils::stealthUserAgent());
32         http->setMaxRetries(0);
33         return http;
34     }();
35     return *h;
36 }
37
38 Http &Invidious::cachedHttp() {
39     static Http *h = [] {
40         ThrottledHttp *throttledHttp = new ThrottledHttp(http());
41         throttledHttp->setMilliseconds(500);
42
43         CachedHttp *cachedHttp = new CachedHttp(*throttledHttp, "iv");
44         cachedHttp->setMaxSeconds(86400);
45         cachedHttp->setIgnoreHostname(true);
46
47         cachedHttp->getValidators().insert("application/json", [](const auto &reply) -> bool {
48             const auto body = reply.body();
49             if (body.isEmpty() || body == "[]" || body == "{}") return false;
50             return true;
51         });
52
53         return cachedHttp;
54     }();
55     return *h;
56 }
57
58 Invidious::Invidious(QObject *parent) : QObject(parent) {
59 #ifdef APP_EXTRA
60     preferredServers << Extra::extraFunctions()->stringArray("ivPreferred()");
61     shuffle(preferredServers);
62 #endif
63     fallbackServers = JsFunctions::instance()->stringArray("ivFallback()");
64     shuffle(fallbackServers);
65 }
66
67 void Invidious::initServers() {
68     if (!servers.isEmpty()) shuffleServers();
69
70     QString instanceApi = JsFunctions::instance()->string("ivInstances()");
71     if (instanceApi.isEmpty()) return;
72
73     auto reply = http().get(instanceApi);
74     connect(reply, &HttpReply::finished, this, [this](auto &reply) {
75         if (reply.isSuccessful()) {
76             servers.clear();
77
78             QSettings settings;
79             QStringList keywords = settings.value("recentKeywords").toStringList();
80             QString testKeyword = keywords.isEmpty() ? "test" : keywords.first();
81
82             QJsonDocument doc = QJsonDocument::fromJson(reply.body());
83             for (const auto &v : doc.array()) {
84                 if (servers.size() > 3) break;
85
86                 auto serverArray = v.toArray();
87                 QString host = serverArray.first().toString();
88                 QJsonObject serverObj = serverArray.at(1).toObject();
89                 if (serverObj["type"] == "https") {
90                     QString url = "https://" + host;
91
92                     QUrl testUrl(url + "/api/v1/search?q=" + testKeyword);
93                     auto reply = http().get(testUrl);
94                     connect(reply, &HttpReply::finished, this, [this, url](auto &reply) {
95                         if (reply.isSuccessful()) {
96                             QJsonDocument doc = QJsonDocument::fromJson(reply.body());
97                             if (!doc.array().isEmpty()) {
98                                 if (servers.size() < 3) {
99                                     servers << url;
100                                     if (servers.size() == 3) {
101                                         shuffleServers();
102                                         for (const auto &s : qAsConst(preferredServers))
103                                             servers.prepend(s);
104                                         qDebug() << servers;
105                                         emit serversInitialized();
106                                     }
107                                 }
108                             }
109                         }
110                     });
111                 }
112             }
113         }
114     });
115 }
116
117 void Invidious::shuffleServers() {
118     shuffle(servers);
119 }
120
121 QString Invidious::baseUrl() {
122     QString host;
123     if (servers.isEmpty()) {
124         if (preferredServers.isEmpty())
125             host = fallbackServers.first();
126         else
127             host = preferredServers.first();
128     } else
129         host = servers.first();
130
131     QString url = host + QLatin1String("/api/v1/");
132     return url;
133 }
134
135 QUrl Invidious::method(const QString &name) {
136     QUrl url(baseUrl() + name);
137     return url;
138 }