Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package minitube for openSUSE:Factory 
checked in at 2021-06-26 21:25:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/minitube (Old)
 and      /work/SRC/openSUSE:Factory/.minitube.new.2625 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "minitube"

Sat Jun 26 21:25:27 2021 rev:26 rq:902405 version:3.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/minitube/minitube.changes        2021-04-08 
21:32:40.431840598 +0200
+++ /work/SRC/openSUSE:Factory/.minitube.new.2625/minitube.changes      
2021-06-26 21:25:45.791367083 +0200
@@ -1,0 +2,6 @@
+Wed Jun 23 17:10:35 UTC 2021 - Carsten Ziepke <[email protected]>
+
+- Update to version 3.9:
+  https://github.com/flaviotordini/minitube/compare/3.8.2...3.9
+
+-------------------------------------------------------------------

Old:
----
  minitube-3.8.2.tar.xz

New:
----
  minitube-3.9.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ minitube.spec ++++++
--- /var/tmp/diff_new_pack.uT3Hf6/_old  2021-06-26 21:25:46.227367658 +0200
+++ /var/tmp/diff_new_pack.uT3Hf6/_new  2021-06-26 21:25:46.231367663 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           minitube
-Version:        3.8.2
+Version:        3.9
 Release:        0
 Summary:        Native YouTube Client
 License:        GPL-3.0-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.uT3Hf6/_old  2021-06-26 21:25:46.259367701 +0200
+++ /var/tmp/diff_new_pack.uT3Hf6/_new  2021-06-26 21:25:46.259367701 +0200
@@ -4,7 +4,7 @@
     <param name="url">https://github.com/flaviotordini/minitube.git</param>
     <param name="filename">minitube</param>
     <param name="versionformat">@PARENT_TAG@</param>
-    <param name="revision">3.8.2</param>
+    <param name="revision">3.9</param>
   </service>
   <service mode="disabled" name="recompress">
     <param name="file">*.tar</param>

++++++ minitube-3.8.2.tar.xz -> minitube-3.9.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/.gitignore new/minitube-3.9/.gitignore
--- old/minitube-3.8.2/.gitignore       1970-01-01 01:00:00.000000000 +0100
+++ new/minitube-3.9/.gitignore 2021-06-22 01:31:51.000000000 +0200
@@ -0,0 +1,16 @@
+build/
+Makefile*
+minitube.pro.user*
+.settings/
+.DS_Store
+.cproject
+.project
+local/
+*.swp
+.tx
+android
+qtc_packaging
+debian
+
+
+*.stash
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/.gitmodules 
new/minitube-3.9/.gitmodules
--- old/minitube-3.8.2/.gitmodules      2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/.gitmodules        2021-06-22 01:31:51.000000000 +0200
@@ -18,3 +18,7 @@
        path = lib/js
        url = https://github.com/flaviotordini/js
        branch = master
+[submodule "lib/promises"]
+       path = lib/promises
+       url = https://github.com/flaviotordini/promises
+       branch = master
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/http/.gitignore 
new/minitube-3.9/lib/http/.gitignore
--- old/minitube-3.8.2/lib/http/.gitignore      1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/http/.gitignore        2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,5 @@
+.idea
+cmake-build-debug
+*.user
+.DS_Store
+build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/http/src/http.cpp 
new/minitube-3.9/lib/http/src/http.cpp
--- old/minitube-3.8.2/lib/http/src/http.cpp    2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/lib/http/src/http.cpp      2021-06-22 01:31:51.000000000 
+0200
@@ -5,7 +5,13 @@
 namespace {
 
 QNetworkAccessManager *networkAccessManager() {
-    static thread_local QNetworkAccessManager *nam = new 
QNetworkAccessManager();
+    static thread_local QNetworkAccessManager *nam = [] {
+        auto nam = new QNetworkAccessManager();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+        nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
+#endif
+        return nam;
+    }();
     return nam;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/http/src/networkhttpreply.cpp 
new/minitube-3.9/lib/http/src/networkhttpreply.cpp
--- old/minitube-3.8.2/lib/http/src/networkhttpreply.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/lib/http/src/networkhttpreply.cpp  2021-06-22 
01:31:51.000000000 +0200
@@ -54,6 +54,7 @@
 }
 
 void NetworkHttpReply::replyFinished() {
+#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
     QUrl redirection = 
networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
     if (redirection.isValid()) {
         HttpRequest redirectReq;
@@ -71,6 +72,7 @@
         readTimeoutTimer->start();
         return;
     }
+#endif
 
     if (isSuccessful()) {
         bytes = networkReply->readAll();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/js/.gitignore 
new/minitube-3.9/lib/js/.gitignore
--- old/minitube-3.8.2/lib/js/.gitignore        1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/js/.gitignore  2021-06-22 01:31:51.000000000 +0200
@@ -0,0 +1,2 @@
+
+.DS_Store
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/js/js.cpp 
new/minitube-3.9/lib/js/js.cpp
--- old/minitube-3.8.2/lib/js/js.cpp    2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/lib/js/js.cpp      2021-06-22 01:31:51.000000000 +0200
@@ -55,9 +55,7 @@
         return *result;
     }
 
-    auto nam = getEngine().networkAccessManager();
-    nam->clearAccessCache();
-    nam->setCookieJar(new QNetworkCookieJar());
+    resetNAM();
 
     auto function = engine->evaluate(name);
     if (!function.isCallable()) {
@@ -75,6 +73,24 @@
     return *result;
 }
 
+void JS::resetNAM() {
+    class MyCookieJar : public QNetworkCookieJar {
+        bool insertCookie(const QNetworkCookie &cookie) {
+            if (cookie.name().contains("CONSENT")) {
+                qDebug() << "Fixing CONSENT cookie" << cookie;
+                auto cookie2 = cookie;
+                cookie2.setValue(cookie.value().replace("PENDING", "YES"));
+                return QNetworkCookieJar::insertCookie(cookie2);
+            }
+            return QNetworkCookieJar::insertCookie(cookie);
+        }
+    };
+
+    auto nam = getEngine().networkAccessManager();
+    nam->clearAccessCache();
+    nam->setCookieJar(new MyCookieJar());
+}
+
 void JS::initialize() {
     if (url.isEmpty()) {
         qDebug() << "No js url set";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/js/js.h 
new/minitube-3.9/lib/js/js.h
--- old/minitube-3.8.2/lib/js/js.h      2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/lib/js/js.h        2021-06-22 01:31:51.000000000 +0200
@@ -18,14 +18,15 @@
     auto getId() const { return id; }
 
     // This should be static but cannot bind static functions to QJSEngine
-    Q_INVOKABLE QJSValue clearTimeout(QJSValue id) {
+    Q_INVOKABLE void clearTimeout(QJSValue id) {
+        // qDebug() << "Clearing timer" << id.toString();
         auto timer = getTimers().take(id.toUInt());
         if (timer) {
             timer->stop();
             timer->deleteLater();
         } else
             qDebug() << "Unknown id" << id.toUInt();
-        return QJSValue();
+        return;
     }
     // This should be static but cannot bind static functions to QJSEngine
     Q_INVOKABLE QJSValue setTimeout(QJSValue callback, QJSValue delayTime, 
QJSValue args) {
@@ -57,7 +58,7 @@
     }
 
     Q_INVOKABLE JSTimer(QObject *parent = nullptr) : QTimer(parent) {
-        setTimerType(Qt::CoarseTimer);
+        setTimerType(Qt::PreciseTimer);
         setSingleShot(true);
         // avoid 0
         static uint counter = 1;
@@ -87,6 +88,7 @@
     QQmlEngine &getEngine() { return *engine; }
 
     JSResult &callFunction(JSResult *result, const QString &name, const 
QJSValueList &args);
+    void resetNAM();
 
 signals:
     void initialized();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/js/jsnamfactory.cpp 
new/minitube-3.9/lib/js/jsnamfactory.cpp
--- old/minitube-3.8.2/lib/js/jsnamfactory.cpp  2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/lib/js/jsnamfactory.cpp    2021-06-22 01:31:51.000000000 
+0200
@@ -62,8 +62,25 @@
                      << req2.rawHeader(i.key());
     }
 
-    qDebug() << req2.url() << req2.rawHeaderList();
-    return QNetworkAccessManager::createRequest(op, req2, outgoingData);
+#ifndef QT_NO_DEBUG_OUTPUT
+    qDebug() << req2.url();
+    for (const auto &h : req2.rawHeaderList())
+        qDebug() << h << req2.rawHeader(h);
+#endif
+
+    auto reply = QNetworkAccessManager::createRequest(op, req2, outgoingData);
+
+#ifndef QT_NO_DEBUG_OUTPUT
+    connect(reply, &QNetworkReply::finished, this, [reply] {
+        qDebug() << "finished"
+                 << 
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()
+                 << reply->url() << reply->rawHeaderPairs();
+    });
+    connect(reply, &QNetworkReply::redirectAllowed, this,
+            [reply] { qDebug() << "redirectAllowed" << reply->url(); });
+#endif
+
+    return reply;
 }
 
 QNetworkAccessManager *JSNAMFactory::create(QObject *parent) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/media/.gitignore 
new/minitube-3.9/lib/media/.gitignore
--- old/minitube-3.8.2/lib/media/.gitignore     1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/media/.gitignore       2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1 @@
+.DS_Store
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/promises/LICENSE 
new/minitube-3.9/lib/promises/LICENSE
--- old/minitube-3.8.2/lib/promises/LICENSE     1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/promises/LICENSE       2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Flavio Tordini
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/promises/emptypromise.h 
new/minitube-3.9/lib/promises/emptypromise.h
--- old/minitube-3.8.2/lib/promises/emptypromise.h      1970-01-01 
01:00:00.000000000 +0100
+++ new/minitube-3.9/lib/promises/emptypromise.h        2021-06-22 
01:31:51.000000000 +0200
@@ -0,0 +1,33 @@
+#ifndef EMPTYPROMISE_H
+#define EMPTYPROMISE_H
+
+#include <QtCore>
+
+class EmptyPromise : public QObject {
+    Q_OBJECT
+
+public:
+    explicit EmptyPromise(QObject *parent = nullptr) : QObject(parent) {
+        connect(this, &EmptyPromise::success, this, &QObject::deleteLater);
+        connect(this, &EmptyPromise::error, this, &QObject::deleteLater);
+    };
+
+    template <typename Functor> EmptyPromise &onSuccess(Functor func) {
+        connect(this, &EmptyPromise::success, this, func);
+        return *this;
+    }
+    template <typename Functor> EmptyPromise &onError(Functor func) {
+        connect(this, &EmptyPromise::error, this, func);
+        return *this;
+    }
+    template <typename Functor> EmptyPromise &finally(Functor func) {
+        connect(this, &EmptyPromise::destroyed, this, func);
+        return *this;
+    }
+
+signals:
+    void success();
+    void error(const QString &message);
+};
+
+#endif // EMPTYPROMISE_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/promises/promise.h 
new/minitube-3.9/lib/promises/promise.h
--- old/minitube-3.8.2/lib/promises/promise.h   1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/promises/promise.h     2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,46 @@
+#ifndef PROMISE_H
+#define PROMISE_H
+
+#include <QtCore>
+
+/// private, don't use directly
+class BasePromise : public QObject {
+    Q_OBJECT
+
+public:
+    explicit BasePromise(QObject *parent = nullptr) : QObject(parent) {
+        connect(this, &BasePromise::resolve, this, &QObject::deleteLater);
+        connect(this, &BasePromise::reject, this, &QObject::deleteLater);
+    };
+
+    template <typename Function> BasePromise &then(Function func) {
+        connect(this, &BasePromise::resolve, this, func);
+        return *this;
+    }
+    template <typename Function> BasePromise &onFailed(Function func) {
+        connect(this, &BasePromise::reject, this, func);
+        return *this;
+    }
+    template <typename Function> BasePromise &finally(Function func) {
+        connect(this, &BasePromise::destroyed, this, func);
+        return *this;
+    }
+
+signals:
+    void resolve();
+    void reject(const QString &message);
+};
+
+template <class T> class Promise : public BasePromise {
+public:
+    void resolve(T value) {
+        data = value;
+        resolve();
+    }
+    T result() const { return data; }
+
+private:
+    T data;
+};
+
+#endif // PROMISE_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/promises/promises.pri 
new/minitube-3.9/lib/promises/promises.pri
--- old/minitube-3.8.2/lib/promises/promises.pri        1970-01-01 
01:00:00.000000000 +0100
+++ new/minitube-3.9/lib/promises/promises.pri  2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+QT *= core
+
+HEADERS += \
+    $$PWD/emptypromise.h \
+    $$PWD/promise.h \
+    $$PWD/variantpromise.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/promises/variantpromise.h 
new/minitube-3.9/lib/promises/variantpromise.h
--- old/minitube-3.8.2/lib/promises/variantpromise.h    1970-01-01 
01:00:00.000000000 +0100
+++ new/minitube-3.9/lib/promises/variantpromise.h      2021-06-22 
01:31:51.000000000 +0200
@@ -0,0 +1,33 @@
+#ifndef VARIANTPROMISE_H
+#define VARIANTPROMISE_H
+
+#include <QtCore>
+
+class VariantPromise : public QObject {
+    Q_OBJECT
+
+public:
+    explicit VariantPromise(QObject *parent = nullptr) : QObject(parent) {
+        connect(this, &VariantPromise::resolve, this, &QObject::deleteLater);
+        connect(this, &VariantPromise::reject, this, &QObject::deleteLater);
+    };
+
+    template <typename Function> VariantPromise &then(Function func) {
+        connect(this, &VariantPromise::resolve, this, func);
+        return *this;
+    }
+    template <typename Function> VariantPromise &onFailed(Function func) {
+        connect(this, &VariantPromise::reject, this, func);
+        return *this;
+    }
+    template <typename Function> VariantPromise &finally(Function func) {
+        connect(this, &VariantPromise::destroyed, this, func);
+        return *this;
+    }
+
+signals:
+    void resolve(QVariant result);
+    void reject(const QString &message);
+};
+
+#endif // VARIANTPROMISE_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/lib/updater/.gitignore 
new/minitube-3.9/lib/updater/.gitignore
--- old/minitube-3.8.2/lib/updater/.gitignore   1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/lib/updater/.gitignore     2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,3 @@
+
+.DS_Store
+.vscode
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/minitube.pro 
new/minitube-3.9/minitube.pro
--- old/minitube-3.8.2/minitube.pro     2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/minitube.pro       2021-06-22 01:31:51.000000000 +0200
@@ -1,7 +1,7 @@
 CONFIG += c++17 exceptions_off rtti_off object_parallel_to_source
 
 TEMPLATE = app
-VERSION = 3.8.2
+VERSION = 3.9
 DEFINES += APP_VERSION="$$VERSION"
 
 APP_NAME = Minitube
@@ -38,6 +38,7 @@
 include(lib/http/http.pri)
 include(lib/idle/idle.pri)
 include(lib/js/js.pri)
+include(lib/promises/promises.pri)
 
 DEFINES += MEDIA_MPV
 include(lib/media/media.pri)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/minitube-3.8.2/org.tordini.flavio.minitube.metainfo.xml 
new/minitube-3.9/org.tordini.flavio.minitube.metainfo.xml
--- old/minitube-3.8.2/org.tordini.flavio.minitube.metainfo.xml 2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/org.tordini.flavio.minitube.metainfo.xml   2021-06-22 
01:31:51.000000000 +0200
@@ -2,6 +2,7 @@
 <component type="desktop-application">
   <name>Minitube</name>
   <id>org.tordini.flavio.minitube</id>
+  <launchable type="desktop-id">minitube.desktop</launchable>
   <metadata_license>CC0-1.0</metadata_license>
   <project_license>GPL-3.0-or-later</project_license>
   <summary>YouTube app</summary>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/resources.qrc 
new/minitube-3.9/resources.qrc
--- old/minitube-3.8.2/resources.qrc    2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/resources.qrc      2021-06-22 01:31:51.000000000 +0200
@@ -52,5 +52,6 @@
         <file>images/[email protected]</file>
         <file>images/64x64/app.png</file>
         <file>images/64x64/[email protected]</file>
+        <file>flags/us.png</file>
     </qresource>
 </RCC>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/aggregatevideosource.cpp 
new/minitube-3.9/src/aggregatevideosource.cpp
--- old/minitube-3.8.2/src/aggregatevideosource.cpp     2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/aggregatevideosource.cpp       2021-06-22 
01:31:51.000000000 +0200
@@ -63,7 +63,18 @@
         video->setChannelId(query.value(4).toString());
         video->setDescription(query.value(5).toString());
         video->setWebpage(query.value(6).toString());
-        video->setThumbnailUrl(query.value(7).toString());
+
+        QString thumbString = query.value(7).toString();
+        if (thumbString.startsWith('[')) {
+            const auto thumbs = 
QJsonDocument::fromJson(thumbString.toUtf8()).array();
+            for (const auto &t : thumbs) {
+                video->addThumb(t["width"].toInt(), t["height"].toInt(), 
t["url"].toString());
+            }
+        } else {
+            // assume it's a URL
+            video->addThumb(0, 0, thumbString);
+        }
+
         video->setViewCount(query.value(8).toInt());
         video->setDuration(query.value(9).toInt());
         videos << video;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/channelaggregator.cpp 
new/minitube-3.9/src/channelaggregator.cpp
--- old/minitube-3.8.2/src/channelaggregator.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/channelaggregator.cpp  2021-06-22 01:31:51.000000000 
+0200
@@ -132,7 +132,7 @@
     } else {
         currentChannel->updateChecked();
         currentChannel = 0;
-        processNextChannel();
+        QTimer::singleShot(5000, this, &ChannelAggregator::processNextChannel);
     }
 }
 
@@ -273,7 +273,18 @@
     query.bindValue(7, video->getChannelId());
     query.bindValue(8, video->getDescription());
     query.bindValue(9, video->getWebpage());
-    query.bindValue(10, video->getThumbnailUrl());
+
+    QJsonDocument thumbsDoc;
+    auto thumbsArray = thumbsDoc.array();
+    for (const auto &t : video->getThumbs()) {
+        thumbsArray.append(QJsonObject{
+                {"url", t.getUrl()},
+                {"width", t.getWidth()},
+                {"height", t.getHeight()},
+        });
+    }
+    thumbsDoc.setArray(thumbsArray);
+    query.bindValue(10, thumbsDoc.toJson(QJsonDocument::Compact));
     query.bindValue(11, video->getViewCount());
     query.bindValue(12, video->getDuration());
     success = query.exec();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/mainwindow.cpp 
new/minitube-3.9/src/mainwindow.cpp
--- old/minitube-3.8.2/src/mainwindow.cpp       2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/mainwindow.cpp 2021-06-22 01:31:51.000000000 +0200
@@ -649,7 +649,7 @@
     action->setEnabled(false);
     actionMap.insert("refineSearch", action);
 
-    action = new QAction(YTRegions::worldwideRegion().name, this);
+    action = new QAction(YTRegions::defaultRegion().name, this);
     actionMap.insert("worldwideRegion", action);
 
     action = new QAction(YTRegions::localRegion().name, this);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/mediaview.cpp 
new/minitube-3.9/src/mediaview.cpp
--- old/minitube-3.8.2/src/mediaview.cpp        2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/mediaview.cpp  2021-06-22 01:31:51.000000000 +0200
@@ -254,6 +254,7 @@
                 VideoSource *vs = history.takeLast();
                 if (!vs->parent()) {
                     qDebug() << "Deleting VideoSource" << vs->getName() << vs;
+                    vs->abort();
                     vs->deleteLater();
                 }
             }
@@ -376,20 +377,25 @@
 void MediaView::pause() {
     switch (media->state()) {
     case Media::PlayingState:
+        qDebug() << "Pausing";
         media->pause();
         pauseTimer.start();
         break;
     default:
-        if (pauseTimer.hasExpired(60000)) {
+        if (pauseTimer.isValid() && pauseTimer.hasExpired(60000)) {
+            qDebug() << "Pause timer expired";
             pauseTimer.invalidate();
             auto activeVideo = playlistModel->activeVideo();
             if (activeVideo) {
                 connect(activeVideo, &Video::gotStreamUrl, this,
                         &MediaView::resumeWithNewStreamUrl);
                 activeVideo->loadStreamUrl();
-            }
-        } else
+            } else
+                qDebug() << "No active video";
+        } else {
+            qDebug() << "Playing" << media->file();
             media->play();
+        }
         break;
     }
 }
@@ -405,6 +411,7 @@
         VideoSource *videoSource = history.takeFirst();
         // Don't delete videoSource in the Browse view
         if (!videoSource->parent()) {
+            videoSource->abort();
             videoSource->deleteLater();
         }
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/playlistitemdelegate.cpp 
new/minitube-3.9/src/playlistitemdelegate.cpp
--- old/minitube-3.8.2/src/playlistitemdelegate.cpp     2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/playlistitemdelegate.cpp       2021-06-22 
01:31:51.000000000 +0200
@@ -25,6 +25,7 @@
 #include "iconutils.h"
 #include "playlistmodel.h"
 #include "playlistview.h"
+#include "variantpromise.h"
 #include "video.h"
 #include "videodefinition.h"
 
@@ -128,17 +129,31 @@
     const bool isActive = index.data(ActiveTrackRole).toBool();
 
     // get the video metadata
-    const Video *video = index.data(VideoRole).value<VideoPointer>().data();
+    Video *video = index.data(VideoRole).value<VideoPointer>().data();
 
     // draw the "current track" highlight underneath the text
     if (isActive && !isSelected) paintActiveOverlay(painter, option, line);
 
     // thumb
-    const QPixmap &thumb = video->getThumbnail();
+    qreal pixelRatio = painter->device()->devicePixelRatioF();
+    QByteArray thumbKey = ("t" + QString::number(pixelRatio)).toUtf8();
+    const QPixmap &thumb = video->property(thumbKey).value<QPixmap>();
     if (!thumb.isNull()) {
         painter->drawPixmap(0, 0, thumb);
         if (video->getDuration() > 0) drawTime(painter, 
video->getFormattedDuration(), line);
-    }
+    } else
+        video->loadThumb({thumbWidth, thumbHeight}, pixelRatio)
+                .then([pixelRatio, thumbKey, video](auto variant) {
+                    QPixmap pixmap;
+                    pixmap.loadFromData(variant.toByteArray());
+                    pixmap.setDevicePixelRatio(pixelRatio);
+                    const int thumbWidth = PlaylistItemDelegate::thumbWidth * 
pixelRatio;
+                    if (pixmap.width() > thumbWidth)
+                        pixmap = pixmap.scaledToWidth(thumbWidth, 
Qt::SmoothTransformation);
+                    video->setProperty(thumbKey, pixmap);
+                    video->changed();
+                })
+                .onFailed([](auto msg) { qDebug() << msg; });
 
     const bool thumbsOnly = line.width() <= thumbWidth + 60;
     const bool isHovered = index.data(HoveredItemRole).toBool();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/playlistmodel.cpp 
new/minitube-3.9/src/playlistmodel.cpp
--- old/minitube-3.8.2/src/playlistmodel.cpp    2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/playlistmodel.cpp      2021-06-22 01:31:51.000000000 
+0200
@@ -20,6 +20,7 @@
 
 #include "playlistmodel.h"
 #include "mediaview.h"
+#include "playlistitemdelegate.h"
 #include "searchparams.h"
 #include "video.h"
 #include "videomimedata.h"
@@ -242,8 +243,10 @@
     videos.append(newVideos);
     endInsertRows();
     for (Video *video : newVideos) {
-        connect(video, SIGNAL(gotThumbnail()), SLOT(updateVideoSender()), 
Qt::UniqueConnection);
-        video->loadThumbnail();
+        connect(video, &Video::changed, this, [video, this] {
+            int row = rowForVideo(video);
+            emit dataChanged(createIndex(row, 0), createIndex(row, 
columnCount() - 1));
+        });
     }
 }
 
@@ -297,16 +300,6 @@
     }
 }
 
-void PlaylistModel::updateVideoSender() {
-    Video *video = static_cast<Video *>(sender());
-    if (!video) {
-        qDebug() << "Cannot get sender";
-        return;
-    }
-    int row = rowForVideo(video);
-    emit dataChanged(createIndex(row, 0), createIndex(row, columnCount() - 1));
-}
-
 void PlaylistModel::emitDataChanged() {
     QModelIndex index = createIndex(rowCount() - 1, 0);
     emit dataChanged(index, index);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/playlistmodel.h 
new/minitube-3.9/src/playlistmodel.h
--- old/minitube-3.8.2/src/playlistmodel.h      2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/playlistmodel.h        2021-06-22 01:31:51.000000000 
+0200
@@ -89,7 +89,6 @@
     void addVideos(const QVector<Video *> &newVideos);
     void searchFinished(int total);
     void searchError(const QString &message);
-    void updateVideoSender();
     void emitDataChanged();
 
     void setHoveredRow(int row);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/regionsview.cpp 
new/minitube-3.9/src/regionsview.cpp
--- old/minitube-3.8.2/src/regionsview.cpp      2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/regionsview.cpp        2021-06-22 01:31:51.000000000 
+0200
@@ -32,7 +32,7 @@
     layout->setSpacing(0);
     l->addLayout(layout);
 
-    addRegion(YTRegions::worldwideRegion());
+    addRegion(YTRegions::defaultRegion());
     foreach (YTRegion region, YTRegions::list())
         addRegion(region);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/searchview.cpp 
new/minitube-3.9/src/searchview.cpp
--- old/minitube-3.8.2/src/searchview.cpp       2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/searchview.cpp 2021-06-22 01:31:51.000000000 +0200
@@ -488,7 +488,7 @@
 #ifdef APP_ACTIVATION
             oneYearUsage = (QDateTime::currentSecsSinceEpoch() -
                             Activation::instance().getLicenseTimestamp()) > 
86400 * 365;
-#elif APP_MAC_STORE
+#elif defined APP_MAC_STORE
             oneYearUsage = false;
 #endif
             if (oneYearUsage) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/standardfeedsview.cpp 
new/minitube-3.9/src/standardfeedsview.cpp
--- old/minitube-3.8.2/src/standardfeedsview.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/standardfeedsview.cpp  2021-06-22 01:31:51.000000000 
+0200
@@ -29,6 +29,8 @@
 #include "ivvideolist.h"
 #include "videoapi.h"
 
+#include "ytjstrending.h"
+
 StandardFeedsView::StandardFeedsView(QWidget *parent) : View(parent), 
layout(0) {
     setBackgroundRole(QPalette::Base);
     setAutoFillBackground(true);
@@ -46,12 +48,26 @@
 
     YTRegion region = YTRegions::currentRegion();
 
+    // TODO consolidate in YT
     if (VideoAPI::impl() == VideoAPI::YT3) {
         YTCategories *youTubeCategories = new YTCategories(this);
         connect(youTubeCategories, SIGNAL(categoriesLoaded(const 
QVector<YTCategory> &)),
                 SLOT(layoutCategories(const QVector<YTCategory> &)));
         youTubeCategories->loadCategories();
         addVideoSourceWidget(buildStandardFeed("most_popular", tr("Most 
Popular")));
+    } else if (VideoAPI::impl() == VideoAPI::JS) {
+        const QMap<QString, QString> pages = {{"default", tr("Trending")},
+                                              {"music", tr("Music")},
+                                              {"movies", tr("Movies")},
+                                              {"gaming", tr("Gaming")}};
+        auto i = pages.constBegin();
+        while (i != pages.constEnd()) {
+            addVideoSourceWidget(
+                    new YTJSTrending(i.value(), {{"page", i.key()}, 
{"geoLocation", region.id}}));
+            ++i;
+        }
+
+        setUpdatesEnabled(true);
     } else {
         QString regionParam = "region=" + region.id;
         addVideoSourceWidget(new IVVideoList("popular?" + regionParam, 
tr("Most Popular")));
@@ -88,7 +104,7 @@
     connect(w, SIGNAL(unavailable(VideoSourceWidget *)),
             SLOT(removeVideoSourceWidget(VideoSourceWidget *)));
     int i = layout->count();
-    const int cols = VideoAPI::impl() == VideoAPI::YT3 ? 5 : 3;
+    const int cols = VideoAPI::impl() == VideoAPI::YT3 ? 5 : 2;
     layout->addWidget(w, i / cols, i % cols);
 }
 
@@ -107,7 +123,7 @@
     }
 
     const int itemCount = items.size();
-    const int cols = 4; // itemCount / 3;
+    const int cols = 2; // itemCount / 3;
     for (int i = itemCount - 1; i >= 0; i--) {
         QLayoutItem *item = items.at(i);
         int index = itemCount - 1 - i;
@@ -155,7 +171,7 @@
 }
 
 void StandardFeedsView::selectWorldwideRegion() {
-    YTRegions::setRegion(YTRegions::worldwideRegion().id);
+    YTRegions::setRegion(YTRegions::defaultRegion().id);
     load();
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/video.cpp 
new/minitube-3.9/src/video.cpp
--- old/minitube-3.8.2/src/video.cpp    2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/src/video.cpp      2021-06-22 01:31:51.000000000 +0200
@@ -29,9 +29,11 @@
 #include "ytjsvideo.h"
 #include "ytvideo.h"
 
+#include "variantpromise.h"
+
 Video::Video()
-    : duration(0), viewCount(-1), license(LicenseYouTube), definitionCode(0),
-      loadingThumbnail(false), ytVideo(nullptr), ytjsVideo(nullptr) {}
+    : duration(0), viewCount(-1), license(LicenseYouTube), definitionCode(0), 
ytVideo(nullptr),
+      ytjsVideo(nullptr) {}
 
 Video::~Video() {
     qDebug() << "Deleting" << id;
@@ -45,9 +47,8 @@
     clone->channelId = channelId;
     clone->webpage = webpage;
     clone->streamUrl = streamUrl;
-    clone->thumbnail = thumbnail;
-    clone->thumbnailUrl = thumbnailUrl;
-    clone->mediumThumbnailUrl = mediumThumbnailUrl;
+    clone->thumbs = thumbs;
+    clone->thumbsNeedSorting = thumbsNeedSorting;
     clone->duration = duration;
     clone->formattedDuration = formattedDuration;
     clone->published = published;
@@ -81,17 +82,6 @@
     }
 }
 
-void Video::loadThumbnail() {
-    if (thumbnailUrl.isEmpty() || loadingThumbnail) return;
-    loadingThumbnail = true;
-    auto reply = HttpUtils::yt().get(thumbnailUrl);
-    connect(reply, SIGNAL(data(QByteArray)), SLOT(setThumbnail(QByteArray)));
-    connect(reply, &HttpReply::error, this, [this](auto &msg) {
-        qWarning() << msg;
-        loadingThumbnail = false;
-    });
-}
-
 void Video::setDuration(int value) {
     duration = value;
     formattedDuration = DataUtils::formatDuration(duration);
@@ -107,17 +97,6 @@
     formattedPublished = DataUtils::formatDateTime(published);
 }
 
-void Video::setThumbnail(const QByteArray &bytes) {
-    qreal ratio = qApp->devicePixelRatio();
-    thumbnail.loadFromData(bytes);
-    thumbnail.setDevicePixelRatio(ratio);
-    const int thumbWidth = PlaylistItemDelegate::thumbWidth * ratio;
-    if (thumbnail.width() > thumbWidth)
-        thumbnail = thumbnail.scaledToWidth(thumbWidth, 
Qt::SmoothTransformation);
-    emit gotThumbnail();
-    loadingThumbnail = false;
-}
-
 void Video::streamUrlLoaded(const QString &streamUrl, const QString &audioUrl) 
{
     qDebug() << "Streams loaded";
     this->streamUrl = streamUrl;
@@ -145,7 +124,8 @@
         qDebug() << msg;
         ytjsVideo->deleteLater();
         ytjsVideo = nullptr;
-        loadStreamUrlYT();
+        // loadStreamUrlYT();
+        emit errorStreamUrl(msg);        
     });
     ytjsVideo->loadStreamUrl();
 }
@@ -170,10 +150,85 @@
     loadStreamUrlJS();
 }
 
+bool Video::isLoadingStreamUrl() const {
+    return ytVideo != nullptr || ytjsVideo != nullptr;
+}
+
 void Video::abortLoadStreamUrl() {
     if (ytVideo) {
         ytVideo->disconnect(this);
         ytVideo->deleteLater();
         ytVideo = nullptr;
     }
+    if (ytjsVideo) {
+        ytjsVideo->disconnect(this);
+        ytjsVideo->deleteLater();
+        ytjsVideo = nullptr;
+    }
+}
+
+void Video::addThumb(int width, int height, QString url) {
+    thumbs << YTThumb(width, height, url);
+    thumbsNeedSorting = true;
+}
+
+VariantPromise &Video::loadThumb(QSize size, qreal pixelRatio) {
+    if (thumbsNeedSorting) {
+        std::sort(thumbs.begin(), thumbs.end(),
+                  [](auto a, auto b) { return a.getWidth() < b.getWidth(); });
+        thumbsNeedSorting = false;
+    }
+
+    auto promise = new VariantPromise(this);
+    if (thumbs.isEmpty()) {
+        QTimer::singleShot(0, promise, [promise] { promise->reject("Empty 
thumbs"); });
+        return *promise;
+    }
+
+    auto reallyLoad = [this, promise, size, pixelRatio](auto &&self,
+                                                        YTThumb *previous = 
nullptr) -> void {
+        YTThumb *selected = nullptr;
+
+        static bool fallback = false;
+        if (fallback) {
+            qDebug() << "Doing fallback loop";
+            bool skip = previous != nullptr;
+            for (int i = thumbs.size() - 1; i >= 0; --i) {
+                auto &thumb = thumbs.at(i);
+                if (!skip) {
+                    selected = (YTThumb *)&thumb;
+                    qDebug() << "selected" << selected->getUrl();
+                    break;
+                }
+                if (&thumb == previous) skip = false;
+            }
+        } else {
+            bool skip = previous != nullptr;
+            for (auto &thumb : qAsConst(thumbs)) {
+                if (!skip && thumb.getWidth() * pixelRatio >= size.width() &&
+                    thumb.getHeight() * pixelRatio >= size.height()) {
+                    selected = (YTThumb *)&thumb;
+                    qDebug() << "selected" << selected->getUrl();
+                    break;
+                }
+                if (&thumb == previous) skip = false;
+            }
+        }
+        if (!selected && !fallback) {
+            qDebug() << "Falling back";
+            fallback = true;
+            self(self);
+            return;
+        }
+        if (selected) {
+            qDebug() << "Loading" << selected->getUrl();
+            selected->load(promise)
+                    .then([promise](auto variant) { promise->resolve(variant); 
})
+                    .onFailed([self, selected] { self(self, selected); });
+        } else
+            promise->reject("No thumb");
+    };
+    reallyLoad(reallyLoad);
+
+    return *promise;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/video.h 
new/minitube-3.9/src/video.h
--- old/minitube-3.8.2/src/video.h      2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/src/video.h        2021-06-22 01:31:51.000000000 +0200
@@ -24,8 +24,11 @@
 #include <QtCore>
 #include <QtGui>
 
+#include "ytthumb.h"
+
 class YTVideo;
 class YTJSVideo;
+class VariantPromise;
 
 class Video : public QObject {
     Q_OBJECT
@@ -53,18 +56,6 @@
     const QString &getWebpage();
     void setWebpage(const QString &value);
 
-    void loadThumbnail();
-    const QPixmap &getThumbnail() const { return thumbnail; }
-
-    const QString &getThumbnailUrl() const { return thumbnailUrl; }
-    void setThumbnailUrl(const QString &value) { thumbnailUrl = value; }
-
-    const QString &getMediumThumbnailUrl() const { return mediumThumbnailUrl; }
-    void setMediumThumbnailUrl(const QString &value) { mediumThumbnailUrl = 
value; }
-
-    const QString &getLargeThumbnailUrl() const { return largeThumbnailUrl; }
-    void setLargeThumbnailUrl(const QString &value) { largeThumbnailUrl = 
value; }
-
     int getDuration() const { return duration; }
     void setDuration(int value);
     const QString &getFormattedDuration() const { return formattedDuration; }
@@ -81,7 +72,7 @@
 
     void loadStreamUrl();
     const QString &getStreamUrl() { return streamUrl; }
-    bool isLoadingStreamUrl() const { return ytVideo != nullptr; }
+    bool isLoadingStreamUrl() const;
     void abortLoadStreamUrl();
 
     const QString &getId() const { return id; }
@@ -90,15 +81,16 @@
     License getLicense() const { return license; }
     void setLicense(License value) { license = value; }
 
+    const auto &getThumbs() const { return thumbs; }
+    void addThumb(int width, int height, QString url);
+    VariantPromise &loadThumb(QSize size, qreal pixelRatio);
+
 signals:
-    void gotThumbnail();
-    void gotMediumThumbnail(const QByteArray &bytes);
-    void gotLargeThumbnail(const QByteArray &bytes);
     void gotStreamUrl(const QString &videoUrl, const QString &audioUrl);
     void errorStreamUrl(const QString &message);
+    void changed();
 
 private slots:
-    void setThumbnail(const QByteArray &bytes);
     void streamUrlLoaded(const QString &streamUrl, const QString &audioUrl);
 
 private:
@@ -111,10 +103,6 @@
     QString channelId;
     QString webpage;
     QString streamUrl;
-    QPixmap thumbnail;
-    QString thumbnailUrl;
-    QString mediumThumbnailUrl;
-    QString largeThumbnailUrl;
     int duration;
     QString formattedDuration;
 
@@ -126,10 +114,11 @@
     QString id;
     int definitionCode;
 
-    bool loadingThumbnail;
-
     YTVideo *ytVideo;
     YTJSVideo *ytjsVideo;
+
+    QVector<YTThumb> thumbs;
+    bool thumbsNeedSorting = false;
 };
 
 // This is required in order to use QPointer<Video> as a QVariant
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/videosourcewidget.cpp 
new/minitube-3.9/src/videosourcewidget.cpp
--- old/minitube-3.8.2/src/videosourcewidget.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/videosourcewidget.cpp  2021-06-22 01:31:51.000000000 
+0200
@@ -19,12 +19,13 @@
 $END_LICENSE */
 
 #include "videosourcewidget.h"
-#include "videosource.h"
-#include "video.h"
 #include "fontutils.h"
-#include "iconutils.h"
 #include "http.h"
 #include "httputils.h"
+#include "iconutils.h"
+#include "variantpromise.h"
+#include "video.h"
+#include "videosource.h"
 
 VideoSourceWidget::VideoSourceWidget(VideoSource *videoSource, QWidget *parent)
     : GridWidget(parent),
@@ -32,9 +33,7 @@
       lastPixelRatio(0) {
     videoSource->setParent(this);
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-
     loadPreview();
-
     connect(this, SIGNAL(activated()), SLOT(activate()));
 }
 
@@ -50,13 +49,15 @@
         return;
     }
     Video *video = videos.at(0);
-    lastPixelRatio = window()->devicePixelRatio();
-    bool needLargeThumb = lastPixelRatio > 1.0 || window()->width() > 1000;
-    QString url =  needLargeThumb ? video->getLargeThumbnailUrl() : 
video->getMediumThumbnailUrl();
-    if (url.isEmpty()) url = video->getThumbnailUrl();
-    video->deleteLater();
-    QObject *reply = HttpUtils::yt().get(url);
-    connect(reply, SIGNAL(data(QByteArray)), SLOT(setPixmapData(QByteArray)));
+    lastPixelRatio = devicePixelRatio();
+
+    video->loadThumb(size(), lastPixelRatio)
+            .then([this](auto variant) { setPixmapData(variant.toByteArray()); 
})
+            .onFailed([](auto msg) { qDebug() << msg; })
+            .finally([videos] {
+                for (auto v : videos)
+                    v->deleteLater();
+            });
 }
 
 void VideoSourceWidget::setPixmapData(const QByteArray &bytes) {
@@ -75,7 +76,7 @@
     const int s = height() / 2;
     const int padding = s / 8;
 
-    qreal ratio = window()->devicePixelRatio();
+    qreal ratio = devicePixelRatio();
     QPixmap playIcon = QPixmap(s * ratio, s * ratio);
     playIcon.setDevicePixelRatio(ratio);
     playIcon.fill(Qt::transparent);
@@ -101,8 +102,9 @@
 
 void VideoSourceWidget::paintEvent(QPaintEvent *event) {
     GridWidget::paintEvent(event);
+    // if (devicePixelRatio() != lastPixelRatio) loadPreview();
+
     if (pixmap.isNull()) return;
-    if (window()->devicePixelRatio() != lastPixelRatio) loadPreview();
 
     QPainter p(this);
 
@@ -149,7 +151,7 @@
     QString name = videoSource->getName();
     bool tooBig = false;
     p.save();
-    p.setFont(FontUtils::medium());
+    p.setFont(FontUtils::big());
     QRect textBox = p.boundingRect(nameBox, Qt::AlignCenter | 
Qt::TextWordWrap, name);
     if (textBox.height() > nameBox.height()) {
         p.setFont(font());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/invidious/ivlistparser.cpp 
new/minitube-3.9/src/yt/invidious/ivlistparser.cpp
--- old/minitube-3.8.2/src/yt/invidious/ivlistparser.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/invidious/ivlistparser.cpp  2021-06-22 
01:31:51.000000000 +0200
@@ -42,16 +42,9 @@
     video->setTitle(title);
     video->setDescription(item[QLatin1String("descriptionHtml")].toString());
 
-    const auto thumbnails = item[QLatin1String("videoThumbnails")].toArray();
-    for (const auto &thumbnail : thumbnails) {
-        auto q = thumbnail["quality"];
-        if (q == QLatin1String("medium")) {
-            video->setThumbnailUrl(thumbnail["url"].toString());
-        } else if (q == QLatin1String("high")) {
-            video->setMediumThumbnailUrl(thumbnail["url"].toString());
-        } else if (q == QLatin1String("sddefault")) {
-            video->setLargeThumbnailUrl(thumbnail["url"].toString());
-        }
+    const auto thumbs = item[QLatin1String("videoThumbnails")].toArray();
+    for (const auto &t : thumbs) {
+        video->addThumb(t["width"].toInt(), t["height"].toInt(), 
t["url"].toString());
     }
 
     video->setChannelTitle(item[QLatin1String("author")].toString());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/yt.pri 
new/minitube-3.9/src/yt/yt.pri
--- old/minitube-3.8.2/src/yt/yt.pri    2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/yt.pri      2021-06-22 01:31:51.000000000 +0200
@@ -6,10 +6,12 @@
 
 HEADERS += \
     $$PWD/searchvideosource.h \
-    $$PWD/singlevideosource.h
+    $$PWD/singlevideosource.h \
+    $$PWD/ytthumb.h
 
 SOURCES += \
     $$PWD/searchvideosource.cpp \
-    $$PWD/singlevideosource.cpp
+    $$PWD/singlevideosource.cpp \
+    $$PWD/ytthumb.cpp
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjs.pri 
new/minitube-3.9/src/yt/ytjs/ytjs.pri
--- old/minitube-3.8.2/src/yt/ytjs/ytjs.pri     2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/yt/ytjs/ytjs.pri       2021-06-22 01:31:51.000000000 
+0200
@@ -6,6 +6,7 @@
     $$PWD/ytjschannelsource.h \
     $$PWD/ytjssearch.h \
     $$PWD/ytjssinglevideosource.h \
+    $$PWD/ytjstrending.h \
     $$PWD/ytjsvideo.h
 
 SOURCES += \
@@ -13,4 +14,5 @@
     $$PWD/ytjschannelsource.cpp \
     $$PWD/ytjssearch.cpp \
     $$PWD/ytjssinglevideosource.cpp \
+    $$PWD/ytjstrending.cpp \
     $$PWD/ytjsvideo.cpp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjschannel.cpp 
new/minitube-3.9/src/yt/ytjs/ytjschannel.cpp
--- old/minitube-3.8.2/src/yt/ytjs/ytjschannel.cpp      2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/ytjs/ytjschannel.cpp        2021-06-22 
01:31:51.000000000 +0200
@@ -18,10 +18,10 @@
                 const auto thumbs = obj["authorThumbnails"].toArray();
                 int maxFoundWidth = 0;
                 for (const auto &thumbObj : thumbs) {
-                    QString url = thumbObj["url"].toString();
                     int width = thumbObj["width"].toInt();
                     if (width > maxFoundWidth) {
                         maxFoundWidth = width;
+                        QString url = thumbObj["url"].toString();
                         thumbnailUrl = url;
                     }
                 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjschannelsource.cpp 
new/minitube-3.9/src/yt/ytjs/ytjschannelsource.cpp
--- old/minitube-3.8.2/src/yt/ytjs/ytjschannelsource.cpp        2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/ytjs/ytjschannelsource.cpp  2021-06-22 
01:31:51.000000000 +0200
@@ -99,15 +99,9 @@
                     video->setDescription(desc);
 
                     const auto thumbs = i["videoThumbnails"].toArray();
-                    for (const auto &thumbObj : thumbs) {
-                        QString url = thumbObj["url"].toString();
-                        int width = thumbObj["width"].toInt();
-                        if (width >= 336)
-                            video->setLargeThumbnailUrl(url);
-                        else if (width >= 246)
-                            video->setMediumThumbnailUrl(url);
-                        else if (width >= 168)
-                            video->setThumbnailUrl(url);
+                    for (const auto &t : thumbs) {
+                        video->addThumb(t["width"].toInt(), 
t["height"].toInt(),
+                                        t["url"].toString());
                     }
 
                     int views = i["viewCount"].toInt();
@@ -134,13 +128,10 @@
                 emit gotVideos(videos);
                 emit finished(videos.size());
             })
-            .onError([this, &js, max, startIndex](auto &msg) {
+            .onError([this, max, startIndex](auto &msg) {
                 static int retries = 0;
                 if (retries < 3) {
                     qDebug() << "Retrying...";
-                    auto nam = js.getEngine().networkAccessManager();
-                    nam->clearAccessCache();
-                    nam->setCookieJar(new QNetworkCookieJar());
                     QTimer::singleShot(0, this,
                                        [this, max, startIndex] { 
loadVideos(max, startIndex); });
                     retries++;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjssearch.cpp 
new/minitube-3.9/src/yt/ytjs/ytjssearch.cpp
--- old/minitube-3.8.2/src/yt/ytjs/ytjssearch.cpp       2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/ytjs/ytjssearch.cpp 2021-06-22 01:31:51.000000000 
+0200
@@ -127,6 +127,8 @@
         addFilter("Duration", "Short");
         break;
     case SearchParams::DurationMedium:
+        addFilter("Duration", "Medium");
+        break;
     case SearchParams::DurationLong:
         addFilter("Duration", "Long");
         break;
@@ -142,6 +144,9 @@
     case SearchParams::TimeMonth:
         addFilter("Upload date", "This month");
         break;
+    case SearchParams::TimeYear:
+        addFilter("Upload date", "This year");
+        break;
     }
 
     switch (searchParams->quality()) {
@@ -183,8 +188,11 @@
                     QString desc = i["description"].toString();
                     video->setDescription(desc);
 
-                    QString thumb = i["thumbnail"].toString();
-                    video->setThumbnailUrl(thumb);
+                    const auto thumbs = i["thumbnails"].toArray();
+                    for (const auto &t : thumbs) {
+                        video->addThumb(t["width"].toInt(), 
t["height"].toInt(),
+                                        t["url"].toString());
+                    }
 
                     int views = i["views"].toInt();
                     video->setViewCount(views);
@@ -211,13 +219,10 @@
                     emit finished(videos.size());
                 }
             })
-            .onError([this, &js, max, startIndex](auto &msg) {
+            .onError([this, max, startIndex](auto &msg) {
                 static int retries = 0;
                 if (retries < 3) {
                     qDebug() << "Retrying...";
-                    auto nam = js.getEngine().networkAccessManager();
-                    nam->clearAccessCache();
-                    nam->setCookieJar(new QNetworkCookieJar());
                     QTimer::singleShot(0, this,
                                        [this, max, startIndex] { 
loadVideos(max, startIndex); });
                     retries++;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjssinglevideosource.cpp 
new/minitube-3.9/src/yt/ytjs/ytjssinglevideosource.cpp
--- old/minitube-3.8.2/src/yt/ytjs/ytjssinglevideosource.cpp    2021-04-02 
01:43:59.000000000 +0200
+++ new/minitube-3.9/src/yt/ytjs/ytjssinglevideosource.cpp      2021-06-22 
01:31:51.000000000 +0200
@@ -3,6 +3,34 @@
 #include "js.h"
 #include "video.h"
 
+namespace {
+
+QDateTime parsePublishedText(const QString &s) {
+    int num = 0;
+    const auto parts = s.splitRef(' ');
+    for (const auto &part : parts) {
+        num = part.toInt();
+        if (num > 0) break;
+    }
+    if (num == 0) return QDateTime();
+
+    auto now = QDateTime::currentDateTimeUtc();
+    if (s.contains("hour")) {
+        return now.addSecs(-num * 3600);
+    } else if (s.contains("day")) {
+        return now.addDays(-num);
+    } else if (s.contains("week")) {
+        return now.addDays(-num * 7);
+    } else if (s.contains("month")) {
+        return now.addMonths(-num);
+    } else if (s.contains("year")) {
+        return now.addDays(-num * 365);
+    }
+    return QDateTime();
+}
+
+} // namespace
+
 YTJSSingleVideoSource::YTJSSingleVideoSource(QObject *parent)
     : VideoSource(parent), video(nullptr) {}
 
@@ -26,12 +54,12 @@
                 if (aborted) return;
 
                 auto obj = doc.object();
-                // qDebug() << doc.toJson();
 
                 auto parseVideoObject = [](QJsonObject i) {
                     Video *video = new Video();
 
                     QString id = i["id"].toString();
+                    if (id.isEmpty()) id = i["videoId"].toString();
                     video->setId(id);
 
                     QString title = i["title"].toString();
@@ -39,31 +67,36 @@
 
                     QString desc = i["description"].toString();
                     if (desc.isEmpty()) desc = i["desc"].toString();
+                    if (desc.isEmpty()) desc = 
i["shortDescription"].toString();
+
                     video->setDescription(desc);
 
                     const auto thumbs = i["thumbnails"].toArray();
-                    for (const auto &thumb : thumbs) {
-                        QString url = thumb["url"].toString();
-                        int width = thumb["width"].toInt();
-                        if (width >= 336)
-                            video->setLargeThumbnailUrl(url);
-                        else if (width >= 246)
-                            video->setMediumThumbnailUrl(url);
-                        else if (width >= 168)
-                            video->setThumbnailUrl(url);
+                    for (const auto &t : thumbs) {
+                        video->addThumb(t["width"].toInt(), 
t["height"].toInt(),
+                                        t["url"].toString());
                     }
 
-                    int views = i["view_count"].toInt();
+                    qDebug() << i["view_count"] << i["viewCount"];
+                    int views = i["view_count"].toString().toInt();
+                    if (views == 0) views = i["viewCount"].toString().toInt();
                     video->setViewCount(views);
 
                     int duration = i["length_seconds"].toInt();
                     video->setDuration(duration);
 
-                    QString channelId = i["ucid"].toString();
-                    video->setChannelId(channelId);
+                    auto author = i["author"];
+                    if (author.isObject()) {
+                        auto authorObject = author.toObject();
+                        video->setChannelId(authorObject["id"].toString());
+                        
video->setChannelTitle(authorObject["name"].toString());
+                    } else if (author.isString()) {
+                        video->setChannelId(i["ucid"].toString());
+                        video->setChannelTitle(author.toString());
+                    }
 
-                    QString channelName = i["author"].toString();
-                    video->setChannelTitle(channelName);
+                    auto published = 
parsePublishedText(i["published"].toString());
+                    if (published.isValid()) video->setPublished(published);
 
                     return video;
                 };
@@ -71,8 +104,12 @@
                 QVector<Video *> videos;
 
                 if (!video) {
-                    // parse video details
-                    videos << parseVideoObject(obj["videoDetails"].toObject());
+                    // first video
+                    auto video = 
parseVideoObject(obj["videoDetails"].toObject());
+                    videos << video;
+                    name = video->getTitle();
+                    qDebug() << "Emitting name changed" << name;
+                    emit nameChanged(name);
                 }
 
                 const auto items = obj["related_videos"].toArray();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjstrending.cpp 
new/minitube-3.9/src/yt/ytjs/ytjstrending.cpp
--- old/minitube-3.8.2/src/yt/ytjs/ytjstrending.cpp     1970-01-01 
01:00:00.000000000 +0100
+++ new/minitube-3.9/src/yt/ytjs/ytjstrending.cpp       2021-06-22 
01:31:51.000000000 +0200
@@ -0,0 +1,104 @@
+#include "ytjstrending.h"
+
+#include "js.h"
+#include "video.h"
+
+namespace {
+
+QDateTime parsePublishedText(const QString &s) {
+    int num = 0;
+    const auto parts = s.splitRef(' ');
+    for (const auto &part : parts) {
+        num = part.toInt();
+        if (num > 0) break;
+    }
+    if (num == 0) return QDateTime();
+
+    auto now = QDateTime::currentDateTimeUtc();
+    if (s.contains("hour")) {
+        return now.addSecs(-num * 3600);
+    } else if (s.contains("day")) {
+        return now.addDays(-num);
+    } else if (s.contains("week")) {
+        return now.addDays(-num * 7);
+    } else if (s.contains("month")) {
+        return now.addMonths(-num);
+    } else if (s.contains("year")) {
+        return now.addDays(-num * 365);
+    }
+    return QDateTime();
+}
+
+} // namespace
+
+YTJSTrending::YTJSTrending(QString name, QVariantMap params, QObject *parent)
+    : VideoSource(parent), name(name), params(params) {}
+
+void YTJSTrending::loadVideos(int max, int startIndex) {
+    aborted = false;
+
+    auto &js = JS::instance();
+
+    QJSValue options = js.getEngine().toScriptValue(params);
+
+    js.callFunction(new JSResult(this), "trending", {options})
+            .onJson([this](auto &doc) {
+                const auto items = doc.array();
+
+                QVector<Video *> videos;
+                videos.reserve(items.size());
+
+                for (const auto &i : items) {
+                    QString type = i["type"].toString();
+                    if (type != "video") continue;
+
+                    Video *video = new Video();
+
+                    QString id = i["videoId"].toString();
+                    video->setId(id);
+
+                    QString title = i["title"].toString();
+                    video->setTitle(title);
+
+                    QString desc = i["description"].toString();
+                    if (desc.isEmpty()) desc = i["desc"].toString();
+                    video->setDescription(desc);
+
+                    const auto thumbs = i["videoThumbnails"].toArray();
+                    for (const auto &t : thumbs) {
+                        video->addThumb(t["width"].toInt(), 
t["height"].toInt(),
+                                        t["url"].toString());
+                    }
+
+                    int views = i["viewCount"].toInt();
+                    video->setViewCount(views);
+
+                    int duration = i["lengthSeconds"].toInt();
+                    video->setDuration(duration);
+
+                    auto published = 
parsePublishedText(i["publishedText"].toString());
+                    if (published.isValid()) video->setPublished(published);
+
+                    QString channelName = i["author"].toString();
+                    video->setChannelTitle(channelName);
+                    QString channelId = i["authorId"].toString();
+                    video->setChannelId(channelId);
+
+                    videos << video;
+                }
+
+                emit gotVideos(videos);
+                emit finished(videos.size());
+            })
+            .onError([this, max, startIndex](auto &msg) {
+                static int retries = 0;
+                if (retries < 3) {
+                    qDebug() << "Retrying...";
+                    QTimer::singleShot(0, this,
+                                       [this, max, startIndex] { 
loadVideos(max, startIndex); });
+                    retries++;
+                } else {
+                    emit error(msg);
+                }
+            });
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytjs/ytjstrending.h 
new/minitube-3.9/src/yt/ytjs/ytjstrending.h
--- old/minitube-3.8.2/src/yt/ytjs/ytjstrending.h       1970-01-01 
01:00:00.000000000 +0100
+++ new/minitube-3.9/src/yt/ytjs/ytjstrending.h 2021-06-22 01:31:51.000000000 
+0200
@@ -0,0 +1,23 @@
+#ifndef YTJSTRENDING_H
+#define YTJSTRENDING_H
+
+#include "videosource.h"
+
+class Video;
+
+class YTJSTrending : public VideoSource {
+    Q_OBJECT
+
+public:
+    YTJSTrending(QString name, QVariantMap params, QObject *parent = 0);
+    void loadVideos(int max, int startIndex);
+    void abort() { aborted = true; }
+    QString getName() { return name; }
+
+private:
+    const QString name;
+    const QVariantMap params;
+    bool aborted = false;
+};
+
+#endif // YTJSTRENDING_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytthumb.cpp 
new/minitube-3.9/src/yt/ytthumb.cpp
--- old/minitube-3.8.2/src/yt/ytthumb.cpp       1970-01-01 01:00:00.000000000 
+0100
+++ new/minitube-3.9/src/yt/ytthumb.cpp 2021-06-22 01:31:51.000000000 +0200
@@ -0,0 +1,28 @@
+#include "ytthumb.h"
+
+#include "http.h"
+#include "httpreply.h"
+#include "httputils.h"
+
+YTThumb::YTThumb(int width, int height, const QString &url)
+    : width(width), height(height), url(url) {}
+
+VariantPromise &YTThumb::load(QObject *parent) {
+    qDebug() << parent;
+    if (promise) {
+        qDebug() << "Already loading" << promise;
+        return *promise;
+    }
+    promise = new VariantPromise(parent);
+    promise->connect(HttpUtils::yt().get(url), &HttpReply::finished, promise, 
[this](auto &reply) {
+        // clear promise member before emitting signals
+        auto promise2 = promise;
+        promise = nullptr;
+        if (reply.isSuccessful()) {
+            promise2->resolve(reply.body());
+        } else {
+            promise2->reject(reply.reasonPhrase());
+        }
+    });
+    return *promise;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt/ytthumb.h 
new/minitube-3.9/src/yt/ytthumb.h
--- old/minitube-3.8.2/src/yt/ytthumb.h 1970-01-01 01:00:00.000000000 +0100
+++ new/minitube-3.9/src/yt/ytthumb.h   2021-06-22 01:31:51.000000000 +0200
@@ -0,0 +1,25 @@
+#ifndef YTTHUMB_H
+#define YTTHUMB_H
+
+#include <QtCore>
+
+#include "variantpromise.h"
+
+class YTThumb {
+public:
+    YTThumb() {} // needed by QVector
+    YTThumb(int width, int height, const QString &url);
+    int getWidth() const { return width; }
+    int getHeight() const { return height; }
+    const QString &getUrl() const { return url; }
+
+    VariantPromise &load(QObject *parent);
+
+private:
+    int width;
+    int height;
+    QString url;
+    VariantPromise *promise = nullptr;
+};
+
+#endif // YTTHUMB_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/yt3listparser.cpp 
new/minitube-3.9/src/yt3listparser.cpp
--- old/minitube-3.8.2/src/yt3listparser.cpp    2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/yt3listparser.cpp      2021-06-22 01:31:51.000000000 
+0200
@@ -59,10 +59,7 @@
     video->setDescription(snippet[QLatin1String("description")].toString());
 
     QJsonObject thumbnails = snippet[QLatin1String("thumbnails")].toObject();
-    QLatin1String url("url");
-    
video->setThumbnailUrl(thumbnails[QLatin1String("medium")].toObject()[url].toString());
-    
video->setMediumThumbnailUrl(thumbnails[QLatin1String("high")].toObject()[url].toString());
-    
video->setLargeThumbnailUrl(thumbnails[QLatin1String("standard")].toObject()[url].toString());
+    // TODO
 
     video->setChannelTitle(snippet[QLatin1String("channelTitle")].toString());
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/ytregions.cpp 
new/minitube-3.9/src/ytregions.cpp
--- old/minitube-3.8.2/src/ytregions.cpp        2021-04-02 01:43:59.000000000 
+0200
+++ new/minitube-3.9/src/ytregions.cpp  2021-06-22 01:31:51.000000000 +0200
@@ -95,8 +95,8 @@
     return region;
 }
 
-const YTRegion &YTRegions::worldwideRegion() {
-    static const YTRegion region = {"", tr("Worldwide")};
+const YTRegion &YTRegions::defaultRegion() {
+    static const YTRegion region = {"US", tr("United States")};
     return region;
 }
 
@@ -115,11 +115,11 @@
 }
 
 const YTRegion &YTRegions::regionById(const QString &id) {
-    if (id.isEmpty()) return worldwideRegion();
+    if (id.isEmpty()) return defaultRegion();
     for (const YTRegion &r : list()) {
         if (r.id == id) return r;
     }
-    return worldwideRegion();
+    return defaultRegion();
 }
 
 QIcon YTRegions::iconForRegionId(const QString &regionId) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/minitube-3.8.2/src/ytregions.h 
new/minitube-3.9/src/ytregions.h
--- old/minitube-3.8.2/src/ytregions.h  2021-04-02 01:43:59.000000000 +0200
+++ new/minitube-3.9/src/ytregions.h    2021-06-22 01:31:51.000000000 +0200
@@ -38,7 +38,7 @@
 public:
     static const QVector<YTRegion> & list();
     static const YTRegion & localRegion();
-    static const YTRegion & worldwideRegion();
+    static const YTRegion & defaultRegion();
     static void setRegion(const QString &regionId);
     static QString currentRegionId();
     static const YTRegion &currentRegion();

Reply via email to