]> git.sur5r.net Git - minitube/commitdiff
New upstream version 3.5.1
authorJakob Haufe <jakob@haufe.it>
Tue, 29 Sep 2020 08:49:45 +0000 (10:49 +0200)
committerJakob Haufe <jakob@haufe.it>
Tue, 29 Sep 2020 08:49:45 +0000 (10:49 +0200)
19 files changed:
.github/FUNDING.yml [new file with mode: 0644]
README.md
lib/http/src/cachedhttp.cpp
lib/http/src/cachedhttp.h
minitube.pro
src/invidious/invidious.cpp
src/invidious/invidious.h
src/invidious/invidious.pri
src/invidious/ivchannelsource.cpp
src/invidious/ivchannelsource.h
src/invidious/ivsearch.cpp
src/invidious/ivsearch.h
src/invidious/ivsinglevideosource.cpp
src/invidious/ivsinglevideosource.h
src/invidious/ivvideolist.cpp
src/invidious/ivvideolist.h
src/invidious/ivvideosource.cpp [new file with mode: 0644]
src/invidious/ivvideosource.h [new file with mode: 0644]
src/yt3.cpp

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..136a229
--- /dev/null
@@ -0,0 +1,2 @@
+github: flaviotordini
+custom: https://flavio.tordini.org/donate
index 4106a0e1c098e13f9589e8f114c5c30c63ac3e00..8dc26503b82395da43db9ec9aba2375cf22c6b20 100644 (file)
--- a/README.md
+++ b/README.md
@@ -9,13 +9,6 @@ Minitube is a YouTube desktop application. It is written in C++ using the Qt fra
 Translations are done at https://www.transifex.com/flaviotordini/minitube/
 Just register and apply for a language team. Please don't request translation merges on GitHub.
 
-## Google API Key
-Google is now requiring an API key in order to access YouTube Data web services.
-Create a "Browser Key" at https://console.developers.google.com and enable the Youtube Data API.
-
-The key must be specified at compile time as shown below.
-Alternatively Minitube can read an API key from the GOOGLE_API_KEY environment variable.
-
 ## Build instructions
 Clone from Github:
 
@@ -29,7 +22,7 @@ To be able to build on a Debian (or derivative) system:
 
 Compiling:
 
-    qmake "DEFINES += APP_GOOGLE_API_KEY=YourAPIKeyHere"
+    qmake
     make
 
 Running:
index e05534209c47e95ef7d1deff9388b7daad9579f9..2b29fdad6998a547272de8498daf59e6fc038475 100644 (file)
@@ -1,5 +1,6 @@
 #include "cachedhttp.h"
 #include "localcache.h"
+#include <QtNetwork>
 
 CachedHttpReply::CachedHttpReply(const QByteArray &body, const HttpRequest &req)
     : bytes(body), req(req) {
@@ -16,8 +17,11 @@ void CachedHttpReply::emitSignals() {
     deleteLater();
 }
 
-WrappedHttpReply::WrappedHttpReply(LocalCache *cache, const QByteArray &key, HttpReply *httpReply)
-    : HttpReply(httpReply), cache(cache), key(key), httpReply(httpReply) {
+WrappedHttpReply::WrappedHttpReply(CachedHttp &cachedHttp,
+                                   LocalCache *cache,
+                                   const QByteArray &key,
+                                   HttpReply *httpReply)
+    : HttpReply(httpReply), cachedHttp(cachedHttp), cache(cache), key(key), httpReply(httpReply) {
     connect(httpReply, SIGNAL(data(QByteArray)), SIGNAL(data(QByteArray)));
     connect(httpReply, SIGNAL(error(QString)), SIGNAL(error(QString)));
     connect(httpReply, SIGNAL(finished(HttpReply)), SLOT(originFinished(HttpReply)));
@@ -25,7 +29,31 @@ WrappedHttpReply::WrappedHttpReply(LocalCache *cache, const QByteArray &key, Htt
 
 void WrappedHttpReply::originFinished(const HttpReply &reply) {
     qDebug() << reply.statusCode() << reply.url();
-    if (reply.isSuccessful()) cache->insert(key, reply.body());
+    bool doCache = reply.isSuccessful();
+
+    if (doCache) {
+        const auto &validators = cachedHttp.getValidators();
+        if (!validators.isEmpty()) {
+            const QByteArray &mime = reply.header("Content-Type");
+            auto i = validators.constFind(mime);
+            if (i != validators.constEnd()) {
+                auto validator = i.value();
+                doCache = validator(reply);
+            } else {
+                i = validators.constFind("*");
+                if (i != validators.constEnd()) {
+                    auto validator = i.value();
+                    doCache = validator(reply);
+                }
+            }
+        }
+    }
+
+    if (doCache)
+        cache->insert(key, reply.body());
+    else
+        qDebug() << "Not caching" << reply.statusCode() << reply.url();
+
     emit finished(reply);
 }
 
@@ -54,7 +82,7 @@ HttpReply *CachedHttp::request(const HttpRequest &req) {
         return new CachedHttpReply(value, req);
     }
     qDebug() << "MISS" << key << req.url;
-    return new WrappedHttpReply(cache, key, http.request(req));
+    return new WrappedHttpReply(*this, cache, key, http.request(req));
 }
 
 QByteArray CachedHttp::requestHash(const HttpRequest &req) {
index 01fef133a624386eccf50072f6bb94d85c788abb..4ce5e1294f1dfdbf8c4c0f0e8639182815a2dbbc 100644 (file)
@@ -12,6 +12,7 @@ public:
     void setMaxSize(uint maxSize);
     void setCachePostRequests(bool value) { cachePostRequests = value; }
     void setIgnoreHostname(bool value) { ignoreHostname = value; }
+    auto &getValidators() { return validators; };
     HttpReply *request(const HttpRequest &req);
 
 private:
@@ -21,6 +22,10 @@ private:
     LocalCache *cache;
     bool cachePostRequests;
     bool ignoreHostname = false;
+
+    /// Mapping is MIME -> validating function
+    /// Use * as MIME to define a catch-all validator
+    QMap<QByteArray, std::function<bool(const HttpReply &)>> validators;
 };
 
 class CachedHttpReply : public HttpReply {
@@ -44,7 +49,10 @@ class WrappedHttpReply : public HttpReply {
     Q_OBJECT
 
 public:
-    WrappedHttpReply(LocalCache *cache, const QByteArray &key, HttpReply *httpReply);
+    WrappedHttpReply(CachedHttp &cachedHttp,
+                     LocalCache *cache,
+                     const QByteArray &key,
+                     HttpReply *httpReply);
     QUrl url() const { return httpReply->url(); }
     int statusCode() const { return httpReply->statusCode(); }
     QByteArray body() const { return httpReply->body(); }
@@ -53,6 +61,7 @@ private slots:
     void originFinished(const HttpReply &reply);
 
 private:
+    CachedHttp &cachedHttp;
     LocalCache *cache;
     QByteArray key;
     HttpReply *httpReply;
index 187db44e2d8e2967be06fdc3afb233e8eba63cdf..5598e04b2fad696a9944219e34a914db5655afd1 100644 (file)
@@ -1,7 +1,7 @@
 CONFIG += c++17 exceptions_off rtti_off optimize_full object_parallel_to_source
 
 TEMPLATE = app
-VERSION = 3.5
+VERSION = 3.5.1
 DEFINES += APP_VERSION="$$VERSION"
 
 APP_NAME = Minitube
@@ -43,6 +43,8 @@ include(lib/media/media.pri)
 include(src/qtsingleapplication/qtsingleapplication.pri)
 include(src/invidious/invidious.pri)
 
+INCLUDEPATH += $$PWD/src
+
 HEADERS += src/video.h \
     src/messagebar.h \
     src/spacer.h \
index 38faf9abd2c6c47867275f2acd0f23ff037c9972..572b50ea5534f5b70b726a5018fb2bfb18e21939 100644 (file)
@@ -3,8 +3,23 @@
 #include "cachedhttp.h"
 #include "http.h"
 #include "httputils.h"
+#include "jsfunctions.h"
 #include "throttledhttp.h"
 
+#ifdef APP_EXTRA
+#include "extra.h"
+#endif
+
+namespace {
+QStringList fallbackServers{"https://invidious.snopyta.org"};
+QStringList preferredServers;
+
+void shuffle(QStringList &sl) {
+    std::shuffle(sl.begin(), sl.end(), *QRandomGenerator::global());
+}
+
+} // namespace
+
 Invidious &Invidious::instance() {
     static Invidious i;
     return i;
@@ -23,68 +38,96 @@ Http &Invidious::http() {
 Http &Invidious::cachedHttp() {
     static Http *h = [] {
         ThrottledHttp *throttledHttp = new ThrottledHttp(http());
-        throttledHttp->setMilliseconds(300);
+        throttledHttp->setMilliseconds(500);
 
         CachedHttp *cachedHttp = new CachedHttp(*throttledHttp, "iv");
         cachedHttp->setMaxSeconds(86400);
         cachedHttp->setIgnoreHostname(true);
+
+        cachedHttp->getValidators().insert("application/json", [](const auto &reply) -> bool {
+            const auto body = reply.body();
+            if (body.isEmpty() || body == "[]" || body == "{}") return false;
+            return true;
+        });
+
         return cachedHttp;
     }();
     return *h;
 }
 
-Invidious::Invidious(QObject *parent) : QObject(parent) {}
+Invidious::Invidious(QObject *parent) : QObject(parent) {
+#ifdef APP_EXTRA
+    preferredServers << Extra::extraFunctions()->stringArray("ivPreferred()");
+    shuffle(preferredServers);
+#endif
+    fallbackServers = JsFunctions::instance()->stringArray("ivFallback()");
+    shuffle(fallbackServers);
+}
 
 void Invidious::initServers() {
-    servers.clear();
-    QUrl url("https://instances.invidio.us/instances.json?sort_by=type,health,users");
-    auto reply = HttpUtils::yt().get(url);
+    if (!servers.isEmpty()) shuffleServers();
+
+    QString instanceApi = JsFunctions::instance()->string("ivInstances()");
+    if (instanceApi.isEmpty()) return;
+
+    auto reply = http().get(instanceApi);
     connect(reply, &HttpReply::finished, this, [this](auto &reply) {
         if (reply.isSuccessful()) {
+            servers.clear();
+
             QSettings settings;
             QStringList keywords = settings.value("recentKeywords").toStringList();
             QString testKeyword = keywords.isEmpty() ? "test" : keywords.first();
 
-            bool haveEnoughServers = false;
             QJsonDocument doc = QJsonDocument::fromJson(reply.body());
             for (const auto &v : doc.array()) {
+                if (servers.size() > 3) break;
+
                 auto serverArray = v.toArray();
                 QString host = serverArray.first().toString();
                 QJsonObject serverObj = serverArray.at(1).toObject();
                 if (serverObj["type"] == "https") {
                     QString url = "https://" + host;
 
-                    if (haveEnoughServers) break;
                     QUrl testUrl(url + "/api/v1/search?q=" + testKeyword);
                     auto reply = http().get(testUrl);
-                    connect(reply, &HttpReply::finished, this,
-                            [this, url, &haveEnoughServers](auto &reply) {
-                                if (!haveEnoughServers && reply.isSuccessful()) {
-                                    QJsonDocument doc = QJsonDocument::fromJson(reply.body());
-                                    if (!doc.array().isEmpty()) {
-                                        servers << url;
-                                        if (servers.size() > 4) {
-                                            haveEnoughServers = true;
-                                            std::shuffle(servers.begin(), servers.end(),
-                                                         *QRandomGenerator::global());
-                                            qDebug() << servers;
-                                            emit serversInitialized();
-                                        }
+                    connect(reply, &HttpReply::finished, this, [this, url](auto &reply) {
+                        if (reply.isSuccessful()) {
+                            QJsonDocument doc = QJsonDocument::fromJson(reply.body());
+                            if (!doc.array().isEmpty()) {
+                                if (servers.size() < 3) {
+                                    servers << url;
+                                    if (servers.size() == 3) {
+                                        shuffleServers();
+                                        for (const auto &s : qAsConst(preferredServers))
+                                            servers.prepend(s);
+                                        qDebug() << servers;
+                                        emit serversInitialized();
                                     }
                                 }
-                            });
+                            }
+                        }
+                    });
                 }
             }
         }
     });
 }
 
+void Invidious::shuffleServers() {
+    shuffle(servers);
+}
+
 QString Invidious::baseUrl() {
     QString host;
-    if (servers.isEmpty())
-        host = "https://invidious.snopyta.org";
-    else
+    if (servers.isEmpty()) {
+        if (preferredServers.isEmpty())
+            host = fallbackServers.first();
+        else
+            host = preferredServers.first();
+    } else
         host = servers.first();
+
     QString url = host + QLatin1String("/api/v1/");
     return url;
 }
index 65de4c04e37704243b20f743a7eeb10dd8748d81..f6fe09a2134b976655e7ef7d4c2d38e8ffd3fc08 100644 (file)
@@ -15,6 +15,7 @@ public:
 
     explicit Invidious(QObject *parent = nullptr);
     void initServers();
+    void shuffleServers();
     QString baseUrl();
     QUrl method(const QString &name);
 
index b6256bd87c52287802a13292b1db448f43eb6296..400937d22bd26b5c064393a6b42d96692f53d2c4 100644 (file)
@@ -7,7 +7,8 @@ HEADERS += $$PWD/invidious.h \
     $$PWD/ivlistparser.h \
     $$PWD/ivsearch.h \
     $$PWD/ivsinglevideosource.h \
-    $$PWD/ivvideolist.h
+    $$PWD/ivvideolist.h \
+    $$PWD/ivvideosource.h
 
 SOURCES += $$PWD/invidious.cpp \
     $$PWD/ivchannel.cpp \
@@ -15,5 +16,6 @@ SOURCES += $$PWD/invidious.cpp \
     $$PWD/ivlistparser.cpp \
     $$PWD/ivsearch.cpp \
     $$PWD/ivsinglevideosource.cpp \
-    $$PWD/ivvideolist.cpp
+    $$PWD/ivvideolist.cpp \
+    $$PWD/ivvideosource.cpp
 
index f4865172fe68266098e82ecbf0c2d313854607f3..ff3096506e1c993e37a5836b9d673f883349a3e7 100644 (file)
@@ -13,13 +13,11 @@ int invidiousFixedMax = 20;
 }
 
 IVChannelSource::IVChannelSource(SearchParams *searchParams, QObject *parent)
-    : VideoSource(parent), searchParams(searchParams) {
+    : IVVideoSource(parent), searchParams(searchParams) {
     searchParams->setParent(this);
 }
 
-void IVChannelSource::loadVideos(int max, int startIndex) {
-    aborted = false;
-
+void IVChannelSource::reallyLoadVideos(int max, int startIndex) {
     QUrl url = Invidious::instance().method("channels/videos/");
     url.setPath(url.path() + searchParams->channelId());
 
@@ -43,6 +41,11 @@ void IVChannelSource::loadVideos(int max, int startIndex) {
     connect(reply, &HttpReply::data, this, [this](auto data) {
         QJsonDocument doc = QJsonDocument::fromJson(data);
         const QJsonArray items = doc.array();
+        if (items.isEmpty()) {
+            handleError("No videos");
+            return;
+        }
+
         IVListParser parser(items);
         const QVector<Video *> &videos = parser.getVideos();
 
@@ -61,14 +64,7 @@ void IVChannelSource::loadVideos(int max, int startIndex) {
         emit gotVideos(videos);
         emit finished(videos.size());
     });
-    connect(reply, &HttpReply::error, this, [this](auto message) {
-        Invidious::instance().initServers();
-        emit error(message);
-    });
-}
-
-void IVChannelSource::abort() {
-    aborted = true;
+    connect(reply, &HttpReply::error, this, &IVChannelSource::handleError);
 }
 
 QString IVChannelSource::getName() {
index a1deaf733128431beaf699e8b9436bbe0f630339..91bb96573156270429bed67de70fb41580d8820b 100644 (file)
@@ -1,19 +1,18 @@
 #ifndef IVCHANNELSOURCE_H
 #define IVCHANNELSOURCE_H
 
-#include "videosource.h"
+#include "ivvideosource.h"
 #include <QtNetwork>
 
 class SearchParams;
 
-class IVChannelSource : public VideoSource {
+class IVChannelSource : public IVVideoSource {
     Q_OBJECT
 
 public:
     IVChannelSource(SearchParams *searchParams, QObject *parent = nullptr);
 
-    void loadVideos(int max, int startIndex);
-    void abort();
+    void reallyLoadVideos(int max, int startIndex);
     QString getName();
     const QList<QAction *> &getActions();
     int maxResults();
@@ -22,7 +21,6 @@ public:
 
 private:
     SearchParams *searchParams;
-    bool aborted;
     QString name;
 };
 
index 96ae9f9800556b7b0472c5455a747b0eed4dfdff..a275d787526d047f818d0002c831ac6802bd3996 100644 (file)
@@ -13,13 +13,11 @@ int invidiousFixedMax = 20;
 }
 
 IVSearch::IVSearch(SearchParams *searchParams, QObject *parent)
-    : VideoSource(parent), searchParams(searchParams) {
+    : IVVideoSource(parent), searchParams(searchParams) {
     searchParams->setParent(this);
 }
 
-void IVSearch::loadVideos(int max, int startIndex) {
-    aborted = false;
-
+void IVSearch::reallyLoadVideos(int max, int startIndex) {
     QUrl url = Invidious::instance().method("search");
 
     QUrlQuery q(url);
@@ -80,9 +78,9 @@ void IVSearch::loadVideos(int max, int startIndex) {
     url.setQuery(q);
 
     // qWarning() << "YT3 search" << url.toString();
-    QObject *reply = Invidious::cachedHttp().get(url);
+    auto reply = Invidious::cachedHttp().get(url);
     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
-    connect(reply, SIGNAL(error(QString)), SLOT(requestError(QString)));
+    connect(reply, &HttpReply::error, this, &IVSearch::handleError);
 }
 
 void IVSearch::parseResults(const QByteArray &data) {
@@ -90,6 +88,11 @@ void IVSearch::parseResults(const QByteArray &data) {
 
     QJsonDocument doc = QJsonDocument::fromJson(data);
     const QJsonArray items = doc.array();
+    if (items.isEmpty()) {
+        handleError("No videos");
+        return;
+    }
+
     IVListParser parser(items);
     const QVector<Video *> &videos = parser.getVideos();
 
@@ -109,22 +112,12 @@ void IVSearch::parseResults(const QByteArray &data) {
     emit finished(videos.size());
 }
 
-void IVSearch::abort() {
-    aborted = true;
-}
-
 QString IVSearch::getName() {
     if (!name.isEmpty()) return name;
     if (!searchParams->keywords().isEmpty()) return searchParams->keywords();
     return QString();
 }
 
-void IVSearch::requestError(const QString &message) {
-    Invidious::instance().initServers();
-    QString msg = message;
-    emit error(msg);
-}
-
 const QList<QAction *> &IVSearch::getActions() {
     static const QList<QAction *> channelActions = {
             MainWindow::instance()->getAction("subscribeChannel")};
index 814877e6ef5fc058473fdb582abf47659bce57a2..25583ce51062299cc107dbbbfc3cebb218100404 100644 (file)
@@ -1,19 +1,18 @@
 #ifndef IVSEARCH_H
 #define IVSEARCH_H
 
-#include "videosource.h"
+#include "ivvideosource.h"
 #include <QtNetwork>
 
 class SearchParams;
 class Video;
 
-class IVSearch : public VideoSource {
+class IVSearch : public IVVideoSource {
     Q_OBJECT
 
 public:
     IVSearch(SearchParams *params, QObject *parent = 0);
-    void loadVideos(int max, int startIndex);
-    void abort();
+    void reallyLoadVideos(int max, int startIndex);
     QString getName();
     const QList<QAction *> &getActions();
     int maxResults();
@@ -21,11 +20,9 @@ public:
 
 private slots:
     void parseResults(const QByteArray &data);
-    void requestError(const QString &message);
 
 private:
     SearchParams *searchParams;
-    bool aborted;
     QString name;
 };
 
index ff0c8047d730987161bb3df1d91763e78f0f6b22..0443887fc34a5c97dcfaa013807703e2328a1874 100644 (file)
@@ -8,9 +8,9 @@
 #include "ivlistparser.h"
 
 IVSingleVideoSource::IVSingleVideoSource(QObject *parent)
-    : VideoSource(parent), video(nullptr), startIndex(0), max(0) {}
+    : IVVideoSource(parent), video(nullptr), startIndex(0), max(0) {}
 
-void IVSingleVideoSource::loadVideos(int max, int startIndex) {
+void IVSingleVideoSource::reallyLoadVideos(int max, int startIndex) {
     aborted = false;
     this->startIndex = startIndex;
     this->max = max;
@@ -39,9 +39,9 @@ void IVSingleVideoSource::loadVideos(int max, int startIndex) {
         url.setPath(url.path() + "/" + videoId);
     }
 
-    QObject *reply = Invidious::cachedHttp().get(url);
+    auto reply = Invidious::cachedHttp().get(url);
     connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
-    connect(reply, SIGNAL(error(QString)), SLOT(requestError(QString)));
+    connect(reply, &HttpReply::error, this, &IVSingleVideoSource::handleError);
 }
 
 void IVSingleVideoSource::parseResults(QByteArray data) {
@@ -49,6 +49,7 @@ void IVSingleVideoSource::parseResults(QByteArray data) {
 
     QJsonDocument doc = QJsonDocument::fromJson(data);
     const QJsonArray items = doc.object()["recommendedVideos"].toArray();
+
     IVListParser parser(items);
     const QVector<Video *> &videos = parser.getVideos();
 
@@ -61,10 +62,6 @@ void IVSingleVideoSource::parseResults(QByteArray data) {
         emit finished(videos.size());
 }
 
-void IVSingleVideoSource::abort() {
-    aborted = true;
-}
-
 QString IVSingleVideoSource::getName() {
     return name;
 }
@@ -73,7 +70,3 @@ void IVSingleVideoSource::setVideo(Video *video) {
     this->video = video;
     videoId = video->getId();
 }
-
-void IVSingleVideoSource::requestError(const QString &message) {
-    emit error(message);
-}
index c5431b99f4522d4da692a0892eb98e6d399f0ccf..d87108bcbb66af3fae7c1fbeaf5fb11deb9adb54 100644 (file)
@@ -3,15 +3,14 @@
 
 #include <QtCore>
 
-#include "videosource.h"
+#include "ivvideosource.h"
 
-class IVSingleVideoSource : public VideoSource {
+class IVSingleVideoSource : public IVVideoSource {
     Q_OBJECT
 public:
     IVSingleVideoSource(QObject *parent = 0);
 
-    void loadVideos(int max, int startIndex);
-    void abort();
+    void reallyLoadVideos(int max, int startIndex);
     QString getName();
 
     void setVideoId(const QString &value) { videoId = value; }
@@ -19,12 +18,10 @@ public:
 
 private slots:
     void parseResults(QByteArray data);
-    void requestError(const QString &message);
 
 private:
     Video *video;
     QString videoId;
-    bool aborted;
     int startIndex;
     int max;
     QString name;
index 2ab47a6e6b3e6882b92fd1ce5a57d7b3ec4a9322..6026113b8f3d497e4f7cad75be3661f047ce319f 100644 (file)
@@ -7,9 +7,9 @@
 #include "video.h"
 
 IVVideoList::IVVideoList(const QString &req, const QString &name, QObject *parent)
-    : VideoSource(parent), name(name), req(req) {}
+    : IVVideoSource(parent), name(name), req(req) {}
 
-void IVVideoList::loadVideos(int max, int startIndex) {
+void IVVideoList::reallyLoadVideos(int max, int startIndex) {
     aborted = false;
 
     QUrl url(Invidious::instance().baseUrl() + req);
@@ -18,19 +18,16 @@ void IVVideoList::loadVideos(int max, int startIndex) {
     connect(reply, &HttpReply::data, this, [this](auto data) {
         QJsonDocument doc = QJsonDocument::fromJson(data);
         const QJsonArray items = doc.array();
+        if (items.isEmpty()) {
+            handleError("No videos");
+            return;
+        }
+
         IVListParser parser(items);
         const QVector<Video *> &videos = parser.getVideos();
-        qDebug() << "CAOCAO" << req << name << videos.size();
 
         emit gotVideos(videos);
         emit finished(videos.size());
     });
-    connect(reply, &HttpReply::error, this, [this](auto message) {
-        Invidious::instance().initServers();
-        emit error(message);
-    });
-}
-
-void IVVideoList::abort() {
-    aborted = true;
+    connect(reply, &HttpReply::error, this, &IVVideoList::handleError);
 }
index 34b091e9f9874d21e01e97fd70f38161a3b48951..3f04fc47887c759123cc1cbfcd6c385cb43308f0 100644 (file)
@@ -1,22 +1,20 @@
 #ifndef IVVIDEOLIST_H
 #define IVVIDEOLIST_H
 
-#include "videosource.h"
+#include "ivvideosource.h"
 #include <QtCore>
 
-class IVVideoList : public VideoSource {
+class IVVideoList : public IVVideoSource {
     Q_OBJECT
 
 public:
     IVVideoList(const QString &req, const QString &name, QObject *parent = nullptr);
 
-    void loadVideos(int max, int startIndex);
-    void abort();
+    void reallyLoadVideos(int max, int startIndex);
     QString getName() { return name; };
     bool hasMoreVideos() { return false; }
 
 private:
-    bool aborted;
     QString name;
     QString req;
 };
diff --git a/src/invidious/ivvideosource.cpp b/src/invidious/ivvideosource.cpp
new file mode 100644 (file)
index 0000000..ad9895c
--- /dev/null
@@ -0,0 +1,29 @@
+#include "ivvideosource.h"
+
+void IVVideoSource::loadVideos(int max, int startIndex) {
+    aborted = false;
+    retryCount = 0;
+    this->max = max;
+    this->startIndex = startIndex;
+    reallyLoadVideos(max, startIndex);
+}
+
+void IVVideoSource::abort() {
+    aborted = true;
+    retryCount = 0;
+    max = 0;
+    startIndex = 0;
+}
+
+void IVVideoSource::handleError(QString message) {
+    qDebug() << message;
+    if (retryCount < 4) {
+        qDebug() << "Retrying...";
+        Invidious::instance().shuffleServers();
+        reallyLoadVideos(max, startIndex);
+        retryCount++;
+    } else {
+        qWarning() << message;
+        emit error("Error loading videos");
+    }
+}
diff --git a/src/invidious/ivvideosource.h b/src/invidious/ivvideosource.h
new file mode 100644 (file)
index 0000000..71b6327
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef IVVIDEOSOURCE_H
+#define IVVIDEOSOURCE_H
+
+#include <QtCore>
+
+#include "invidious.h"
+#include "videosource.h"
+
+class IVVideoSource : public VideoSource {
+    Q_OBJECT
+
+public:
+    IVVideoSource(QObject *parent = nullptr) : VideoSource(parent) {}
+
+    void loadVideos(int max, int startIndex);
+    void abort();
+
+    virtual void reallyLoadVideos(int max, int startIndex) = 0;
+
+protected slots:
+    void handleError(QString message);
+
+protected:
+    bool aborted = false;
+
+private:
+    int retryCount = 0;
+    int max = 0;
+    int startIndex = 0;
+};
+
+#endif // IVVIDEOSOURCE_H
index 95bb2550976e688edbc00049029a91a75bf870a9..dcd21332d9687a97f891af25433ede5272d224c9 100644 (file)
@@ -60,7 +60,7 @@ void YT3::initApiKeys() {
 
     if (keys.isEmpty()) {
         qWarning() << "No available API keys";
-#ifdef APP_LINUX
+#ifdef APP_LINUX_NO
         QMetaObject::invokeMethod(MainWindow::instance(), "missingKeyWarning",
                                   Qt::QueuedConnection);
 #endif