3 #include "cachedhttp.h"
6 #include "jsfunctions.h"
7 #include "throttledhttp.h"
14 QStringList fallbackServers{"https://invidious.snopyta.org"};
15 QStringList preferredServers;
17 void shuffle(QStringList &sl) {
18 std::shuffle(sl.begin(), sl.end(), *QRandomGenerator::global());
23 Invidious &Invidious::instance() {
28 Http &Invidious::http() {
30 Http *http = new Http;
31 http->addRequestHeader("User-Agent", HttpUtils::stealthUserAgent());
32 http->setMaxRetries(0);
38 Http &Invidious::cachedHttp() {
40 ThrottledHttp *throttledHttp = new ThrottledHttp(http());
41 throttledHttp->setMilliseconds(500);
43 CachedHttp *cachedHttp = new CachedHttp(*throttledHttp, "iv");
44 cachedHttp->setMaxSeconds(3600);
45 cachedHttp->setIgnoreHostname(true);
47 cachedHttp->getValidators().insert("application/json", [](const auto &reply) -> bool {
48 const auto body = reply.body();
49 if (body.isEmpty() || body == "[]" || body == "{}") return false;
58 Invidious::Invidious(QObject *parent) : QObject(parent) {
62 void Invidious::initServers() {
63 if (!servers.isEmpty()) shuffleServers();
65 QString instanceApi = JsFunctions::instance()->string("ivInstances()");
66 if (instanceApi.isEmpty()) {
67 qDebug() << "No instances API url";
72 qDebug() << "Already initializing";
78 preferredServers = JsFunctions::instance()->stringArray("ivPreferred()");
80 preferredServers << Extra::extraFunctions()->stringArray("ivPreferred()");
82 shuffle(preferredServers);
83 servers << preferredServers;
85 auto newFallbackServers = JsFunctions::instance()->stringArray("ivFallback()");
86 if (!newFallbackServers.isEmpty()) {
87 fallbackServers = newFallbackServers;
88 shuffle(fallbackServers);
91 auto reply = cachedHttp().get(instanceApi);
92 connect(reply, &HttpReply::finished, this, [this](auto &reply) {
93 if (reply.isSuccessful()) {
95 QStringList keywords = settings.value("recentKeywords").toStringList();
96 QString testKeyword = keywords.isEmpty() ? "test" : keywords.first();
98 QJsonDocument doc = QJsonDocument::fromJson(reply.body());
99 for (const auto &v : doc.array()) {
100 auto serverArray = v.toArray();
101 QString host = serverArray.first().toString();
102 QJsonObject serverObj = serverArray.at(1).toObject();
103 if (serverObj["type"] == "https") {
104 QString url = "https://" + host;
105 QUrl testUrl(url + "/api/v1/search?q=" + testKeyword);
106 auto reply = http().get(testUrl);
107 connect(reply, &HttpReply::finished, this, [this, url](auto &reply) {
108 if (reply.isSuccessful()) {
109 QJsonDocument doc = QJsonDocument::fromJson(reply.body());
110 if (doc.array().isEmpty()) {
111 qDebug() << "Empty results" << url;
113 qDebug() << "Adding" << url;
115 initializing = false;
125 void Invidious::shuffleServers() {
129 QString Invidious::baseUrl() {
131 if (servers.isEmpty()) {
132 if (preferredServers.isEmpty()) {
133 if (!fallbackServers.isEmpty()) host = fallbackServers.first();
135 host = preferredServers.first();
137 host = servers.first();
139 QString url = host + QLatin1String("/api/v1/");
143 QUrl Invidious::method(const QString &name) {
144 QString base = baseUrl();
145 if (base.isEmpty()) return QUrl();
146 QUrl url(baseUrl() + name);