diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd7189aa..7166ec73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,9 +19,13 @@ else()
     find_package(DummyPkgConfig REQUIRED)
 endif()
 
-find_package(Qt5Widgets 5.6 REQUIRED)
-if(Qt5Widgets_VERSION VERSION_LESS 5.6.3)
-    message(AUTHOR_WARNING "Qt5 >= 5.6.3, >= 5.9.1 is recommended")
+option(USE_QT4 "Build with Qt4" ON)
+
+if(USE_QT4)
+    find_package(Qt4 REQUIRED QtCore QtGui)
+    include("${QT_USE_FILE}")
+else()
+    find_package(Qt5Widgets 5.10 REQUIRED)
 endif()
 
 set(CMAKE_LINK_DEPENDS_NO_SHARED ON)
@@ -49,6 +53,10 @@ endif()
 
 add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DQT_USE_FAST_OPERATOR_PLUS)
 
+if(APPLE AND USE_QT4)
+    add_definitions(-DQ_OS_MACOS)
+endif()
+
 if(WIN32)
     option(USE_CMD "Show CMD when running QMPlay2" OFF)
     mark_as_advanced(USE_CMD)
@@ -283,7 +291,7 @@ endif()
 
 if(APPLE)
     find_library(APPKIT_LIBRARY AppKit)
-    find_library(IOKIT_LIBRARY IoKit)
+    find_library(IOKIT_LIBRARY IOKit)
     set(SOLID_ACTIONS_DEFAULT "None")
     set(DEFAULT_INSTALL_RPATH ON)
 endif()
@@ -306,7 +314,11 @@ if(USE_MEDIABROWSER)
 endif()
 
 if(NOT ANDROID)
+  if(USE_QT4)
+    find_package(Qt4 QUIET OPTIONAL_COMPONENTS QtSvg)
+  else()
     find_package(Qt5Svg QUIET)
+  endif()
 else()
     find_package(Qt5Svg REQUIRED)
     find_package(Qt5AndroidExtras REQUIRED)
@@ -337,7 +349,7 @@ if(NOT APPLE AND NOT WIN32)
 endif()
 
 # Show warning if QtSvg doesn't exist
-if(NOT Qt5Svg_FOUND)
+if(NOT Qt5Svg_FOUND AND NOT Qt4_FOUND)
     message(WARNING "Missing QtSvg module - SVG icons will not be visible!")
 endif()
 
diff --git a/lang/CMakeLists.txt b/lang/CMakeLists.txt
index db28debd..bb5043e7 100644
--- a/lang/CMakeLists.txt
+++ b/lang/CMakeLists.txt
@@ -12,8 +12,12 @@ else()
     endforeach()
 endif()
 
-find_package(Qt5LinguistTools REQUIRED)
-qt5_add_translation(QM_FILES ${QMPLAY2_TSS})
+if(USE_QT4)
+    qt4_add_translation(QM_FILES ${QMPLAY2_TSS})
+else()
+    find_package(Qt5LinguistTools REQUIRED)
+    qt5_add_translation(QM_FILES ${QMPLAY2_TSS})
+endif()
 
 add_custom_target(translations ALL DEPENDS ${QM_FILES})
 
diff --git a/src/gui/AboutWidget.cpp b/src/gui/AboutWidget.cpp
index dfc52ccd..0f867e67 100644
--- a/src/gui/AboutWidget.cpp
+++ b/src/gui/AboutWidget.cpp
@@ -52,7 +52,8 @@ AboutWidget::AboutWidget()
 
     QTabWidget *tabW = new QTabWidget;
 
-    const QFont font(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+    QFont font("Monospace");
+    font.setStyleHint(QFont::TypeWriter);
 
     logE = new QPlainTextEdit;
     logE->setFont(font);
diff --git a/src/gui/AboutWidget.hpp b/src/gui/AboutWidget.hpp
index 98994f7a..59af0de5 100644
--- a/src/gui/AboutWidget.hpp
+++ b/src/gui/AboutWidget.hpp
@@ -24,14 +24,14 @@
 class QPlainTextEdit;
 class QPushButton;
 
-class AboutWidget final : public QWidget
+class AboutWidget : public QWidget
 {
     Q_OBJECT
 public:
     AboutWidget();
 private:
-    void showEvent(QShowEvent *) override;
-    void closeEvent(QCloseEvent *) override;
+    void showEvent(QShowEvent *) override final;
+    void closeEvent(QCloseEvent *) override final;
 
     QPlainTextEdit *logE, *clE, *auE;
     QPushButton *clrLogB;
diff --git a/src/gui/AddressDialog.hpp b/src/gui/AddressDialog.hpp
index 4a6f779a..3835b97d 100644
--- a/src/gui/AddressDialog.hpp
+++ b/src/gui/AddressDialog.hpp
@@ -24,12 +24,12 @@
 #include <QCheckBox>
 #include <QDialog>
 
-class AddressDialog final : public QDialog
+class AddressDialog : public QDialog
 {
     Q_DECLARE_TR_FUNCTIONS(AddressDialog)
 public:
     AddressDialog(QWidget *);
-    ~AddressDialog();
+    ~AddressDialog() final;
 
     inline bool addAndPlay() const
     {
diff --git a/src/gui/AudioThr.hpp b/src/gui/AudioThr.hpp
index f2382376..c0842611 100644
--- a/src/gui/AudioThr.hpp
+++ b/src/gui/AudioThr.hpp
@@ -28,14 +28,14 @@ class QMPlay2Extensions;
 class PlayClass;
 class AudioFilter;
 
-class AudioThr final : public AVThread
+class AudioThr : public AVThread
 {
     Q_OBJECT
 public:
     AudioThr(PlayClass &, const QStringList &pluginsName = {});
-    ~AudioThr();
+    ~AudioThr() final;
 
-    void stop(bool terminate = false) override;
+    void stop(bool terminate = false) override final;
     void clearVisualizations();
 
     bool setParams(uchar realChn, uint realSRate, uchar chn, uint sRate, bool resamplerFirst);
@@ -47,7 +47,7 @@ public:
         allowAudioDrain = true;
     }
 private:
-    void run() override;
+    void run() override final;
 
     bool resampler_create();
 
@@ -55,7 +55,7 @@ private:
     inline uint currentSampleRate() const;
 
 #ifdef Q_OS_WIN
-    void timerEvent(QTimerEvent *) override;
+    void timerEvent(QTimerEvent *) override final;
 #endif
 
     SndResampler sndResampler;
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 7f8c22b5..8bd630af 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -34,10 +34,15 @@ set(GUI_HDR
     KeyBindingsDialog.hpp
     Updater.hpp
     RepeatMode.hpp
-    PanGestureEventFilter.hpp
-    EventFilterWorkarounds.hpp
 )
 
+if(NOT USE_QT4)
+    list(APPEND GUI_HDR
+        PanGestureEventFilter.hpp
+        EventFilterWorkarounds.hpp
+    )
+endif()
+
 set(GUI_SRC
     Main.cpp
     MenuBar.cpp
@@ -65,7 +70,6 @@ set(GUI_SRC
     ShortcutHandler.cpp
     KeyBindingsDialog.cpp
     Updater.cpp
-    EventFilterWorkarounds.cpp
 )
 
 set(GUI_FORMS # *.ui files
@@ -79,24 +83,35 @@ set(GUI_RESOURCES
     resources.qrc
 )
 
-if(NOT WIN32)
+if(NOT USE_QT4)
     list(APPEND GUI_SRC
-        PanGestureEventFilter.cpp
+        EventFilterWorkarounds.cpp
     )
+    if(NOT WIN32)
+        list(APPEND GUI_SRC
+            PanGestureEventFilter.cpp
+        )
+    endif()
 endif()
 
 if(APPLE)
     list(APPEND GUI_HDR
         macOS/QMPlay2MacExtensions.hpp
     )
-    list(APPEND GUI_SRC
-        macOS/QMPlay2MacExtensions.mm
-    )
+    if(NOT USE_QT4)
+        list(APPEND GUI_SRC
+           macOS/QMPlay2MacExtensions.mm
+        )
+    endif()
 
     include_directories("macOS")
 endif()
 
-qt5_wrap_ui(GUI_FORM_HDR ${GUI_FORMS})
+if(USE_QT4)
+    qt4_wrap_ui(GUI_FORM_HDR ${GUI_FORMS})
+else()
+    qt5_wrap_ui(GUI_FORM_HDR ${GUI_FORMS})
+endif()
 set_property(SOURCE ${GUI_FORM_HDR} PROPERTY SKIP_AUTOMOC ON)
 
 include_directories(../qmplay2/headers)
@@ -121,7 +136,7 @@ if(USE_TAGLIB)
     list(APPEND GUI_SRC TagEditor.cpp)
 endif()
 
-if(NOT APPLE)
+if(USE_QT4 OR NOT APPLE)
     add_definitions(-DQMPLAY2_ALLOW_ONLY_ONE_INSTANCE)
 endif()
 
@@ -216,11 +231,13 @@ if(WIN32)
 elseif(APPLE)
     install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX})
 
-    set(QT_LIBS_DIR "${Qt5Widgets_DIR}/../..")
-    set(QT_PLUGINS_DIR "${QT_LIBS_DIR}/../plugins")
+    set(QT_LIBS_DIR "@qt_libs_dir@")
+    set(QT_PLUGINS_DIR "@qt_plugins_dir@")
+if(NOT USE_QT4)
     install(FILES
         "${QT_PLUGINS_DIR}/platforms/libqcocoa.dylib"
         DESTINATION "${MAC_BUNDLE_PATH}/Contents/plugins/platforms")
+endif()
     install(FILES
         "${QT_PLUGINS_DIR}/iconengines/libqsvgicon.dylib"
         DESTINATION "${MAC_BUNDLE_PATH}/Contents/plugins/iconengines")
@@ -238,23 +255,7 @@ elseif(APPLE)
         DESTINATION "${MAC_BUNDLE_PATH}/Contents"
         FILES_MATCHING
         PATTERN "qtbase_*.qm")
-    if(EXISTS "/usr/local/bin/ffmpeg")
-        install(PROGRAMS
-            "/usr/local/bin/ffmpeg"
-            DESTINATION "${MAC_BUNDLE_PATH}/Contents/MacOS")
-    else()
-        message(WARNING "FFmpeg executable not copied!")
-    endif()
-    install(CODE "
-        include(BundleUtilities)
-        set(BU_CHMOD_BUNDLE_ITEMS ON)
-        list(APPEND DIRS /usr/local/lib ${QT_LIBS_DIR})
-        file(GLOB_RECURSE QMPLAY2_MODULES_AND_QT_PLUGINS
-            \"${MAC_BUNDLE_PATH}/Contents/MacOS/modules/*\"
-            \"${MAC_BUNDLE_PATH}/Contents/plugins/*.dylib\")
-        file(WRITE \"${MAC_BUNDLE_PATH}/Contents/Resources/qt.conf\")
-        fixup_bundle(${MAC_BUNDLE_PATH} \"\${QMPLAY2_MODULES_AND_QT_PLUGINS}\" \"\${DIRS}\")
-    ")
+    file(WRITE \"@destroot@${MAC_BUNDLE_PATH}/Contents/Resources/qt.conf\")
 else()
     # executable
     install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/gui/DeintSettingsW.hpp b/src/gui/DeintSettingsW.hpp
index ea74fac2..92787425 100644
--- a/src/gui/DeintSettingsW.hpp
+++ b/src/gui/DeintSettingsW.hpp
@@ -24,14 +24,14 @@ class QCheckBox;
 class QComboBox;
 class Module;
 
-class DeintSettingsW final : public QGroupBox
+class DeintSettingsW : public QGroupBox
 {
     Q_OBJECT
 public:
     static void init();
 
     DeintSettingsW();
-    ~DeintSettingsW();
+    ~DeintSettingsW() final;
 
     void writeSettings();
 private slots:
diff --git a/src/gui/DemuxerThr.cpp b/src/gui/DemuxerThr.cpp
index 2fdedd42..60dbf30b 100644
--- a/src/gui/DemuxerThr.cpp
+++ b/src/gui/DemuxerThr.cpp
@@ -451,6 +451,12 @@ void DemuxerThr::run()
     if (stillImage && playC.paused)
         playC.paused = false;
 
+    const bool isHls = (demuxer->name() == "hls");
+    QTime waitForDataTimer;
+    bool waitForDataTimerValid = false;
+    bool firstWaitForData = true;
+    bool canWaitForData = true;
+
     while (!demuxer.isAborted())
     {
         {
@@ -534,9 +540,33 @@ void DemuxerThr::run()
             )
         )
         {
+            if (isHls && !firstWaitForData && canWaitForData && !playC.endOfStream)
+            {
+                // Wait a bit longer for HLS streams to prevent stuttering on every HLS chunk. Do a short
+                // sleep and continue, because reading from demuxer might block for HLS chunk length.
+                if (!waitForDataTimerValid)
+                {
+                    waitForDataTimer.start();
+                    waitForDataTimerValid = true;
+                }
+                if (waitForDataTimer.elapsed() / 1000.0 <= playIfBuffered)
+                {
+                    msleep(50);
+                    continue;
+                }
+                waitForDataTimerValid = false;
+                canWaitForData = false;
+            }
+
             playC.waitForData = false;
             if (!paused)
                 playC.emptyBufferCond.wakeAll();
+
+            firstWaitForData = false;
+        }
+        else
+        {
+            canWaitForData = true;
         }
 
         if (playC.endOfStream || bufferedAllPackets(vS, aS, forwardPackets))
diff --git a/src/gui/DemuxerThr.hpp b/src/gui/DemuxerThr.hpp
index 35db9e36..9fdfd88d 100644
--- a/src/gui/DemuxerThr.hpp
+++ b/src/gui/DemuxerThr.hpp
@@ -32,14 +32,14 @@ class AVThread;
 class Demuxer;
 class BasicIO;
 
-class DemuxerThr final : public QThread
+class DemuxerThr : public QThread
 {
     friend class DemuxerTimer;
     friend class PlayClass;
     Q_OBJECT
 private:
     DemuxerThr(PlayClass &);
-    ~DemuxerThr();
+    ~DemuxerThr() final;
 
     QByteArray getCoverFromStream() const;
 
@@ -65,7 +65,7 @@ private:
 
     void checkReadyWrite(AVThread *avThr);
 
-    void run() override;
+    void run() override final;
 
     inline void ensureTrueUpdateBuffered();
     inline bool canUpdateBuffered() const;
diff --git a/src/gui/EntryProperties.cpp b/src/gui/EntryProperties.cpp
index d537bb9e..21c8da7d 100644
--- a/src/gui/EntryProperties.cpp
+++ b/src/gui/EntryProperties.cpp
@@ -107,12 +107,6 @@ EntryProperties::EntryProperties(QWidget *p, QTreeWidgetItem *_tWI, bool &sync,
         addrB = new AddressBox(Qt::Horizontal, url);
         layout.addWidget(addrB, row, 0, 1, 2);
 
-        openUrlB = new QToolButton;
-        openUrlB->setToolTip(tr("Open URL or directory containing chosen file"));
-        openUrlB->setIcon(QMPlay2Core.getIconFromTheme("folder-open"));
-        connect(openUrlB, &QToolButton::clicked, this, &EntryProperties::openUrl);
-        layout.addWidget(openUrlB, row, 2, 1, 1);
-
         fileSizeL = new QLabel;
 
 #ifdef QMPlay2_TagEditor
diff --git a/src/gui/EntryProperties.hpp b/src/gui/EntryProperties.hpp
index d89c2ccb..ba203300 100644
--- a/src/gui/EntryProperties.hpp
+++ b/src/gui/EntryProperties.hpp
@@ -30,7 +30,7 @@ class QCheckBox;
 #endif
 class QLabel;
 
-class EntryProperties final : public QDialog
+class EntryProperties : public QDialog
 {
     Q_OBJECT
 public:
@@ -54,6 +54,6 @@ private slots:
 #endif
     void browse();
     void openUrl();
-    void accept() override;
-    void reject() override;
+    void accept() override final;
+    void reject() override final;
 };
diff --git a/src/gui/EventFilterWorkarounds.hpp b/src/gui/EventFilterWorkarounds.hpp
index e8c28cff..ce32dda1 100644
--- a/src/gui/EventFilterWorkarounds.hpp
+++ b/src/gui/EventFilterWorkarounds.hpp
@@ -20,7 +20,7 @@
 
 #include <QObject>
 
-class EventFilterWorkarounds final : public QObject
+class EventFilterWorkarounds : public QObject
 {
     Q_OBJECT
 
@@ -29,5 +29,5 @@ public:
     ~EventFilterWorkarounds();
 
 private:
-    bool eventFilter(QObject *watched, QEvent *event) override;
+    bool eventFilter(QObject *watched, QEvent *event) override final;
 };
diff --git a/src/gui/InfoDock.hpp b/src/gui/InfoDock.hpp
index cd3354a6..c46c4c7b 100644
--- a/src/gui/InfoDock.hpp
+++ b/src/gui/InfoDock.hpp
@@ -22,11 +22,11 @@
 
 #include <DockWidget.hpp>
 
-class TextEdit final : public QTextEdit
+class TextEdit : public QTextEdit
 {
 private:
-    void mouseMoveEvent(QMouseEvent *) override;
-    void mousePressEvent(QMouseEvent *) override;
+    void mouseMoveEvent(QMouseEvent *) override final;
+    void mousePressEvent(QMouseEvent *) override final;
 };
 
 /**/
diff --git a/src/gui/KeyBindingsDialog.cpp b/src/gui/KeyBindingsDialog.cpp
index 3dca5428..4c71bdd0 100644
--- a/src/gui/KeyBindingsDialog.cpp
+++ b/src/gui/KeyBindingsDialog.cpp
@@ -35,8 +35,8 @@ KeyBindingsDialog::KeyBindingsDialog(QWidget *p) :
     shortcuts->setModel(QMPlay2GUI.shortcutHandler);
     shortcuts->setFrameShape(QFrame::NoFrame);
     shortcuts->setAlternatingRowColors(true);
-    shortcuts->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
-    shortcuts->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
+    shortcuts->horizontalHeader()->setResizeMode(0, QHeaderView::ResizeToContents);
+    shortcuts->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
     shortcuts->setSelectionMode(QAbstractItemView::SingleSelection);
     shortcuts->verticalHeader()->setVisible(false);
 
diff --git a/src/gui/Main.cpp b/src/gui/Main.cpp
index f6f61584..01ce78c6 100644
--- a/src/gui/Main.cpp
+++ b/src/gui/Main.cpp
@@ -18,8 +18,6 @@
 
 #include <Main.hpp>
 
-#include <EventFilterWorkarounds.hpp>
-#include <PanGestureEventFilter.hpp>
 #include <ScreenSaver.hpp>
 #include <VideoFrame.hpp>
 #include <MainWidget.hpp>
@@ -31,7 +29,6 @@
 #include <Module.hpp>
 #include <IPC.hpp>
 
-#include <QCommandLineParser>
 #include <QDesktopWidget>
 #include <QApplication>
 #include <QImageReader>
@@ -42,6 +39,7 @@
 #include <QBuffer>
 #include <QFile>
 #include <QDir>
+#include <QTextCodec>
 #ifdef Q_OS_MACOS
     #include <QProcess>
 #endif
@@ -52,6 +50,8 @@
 #include <csignal>
 #include <ctime>
 
+    static QPair<QStringList, QStringList> g_arguments;
+
 static ScreenSaver *g_screenSaver = nullptr;
 static bool g_useGui = true;
 #ifdef Q_OS_MACOS
@@ -111,12 +111,14 @@ void QMPlay2GUIClass::setTreeWidgetItemIcon(QTreeWidgetItem *tWI, const QIcon &i
         if (!treeWidget)
             treeWidget = tWI->treeWidget();
         if (treeWidget)
-            tWI->setData(column, Qt::DecorationRole, icon.pixmap(treeWidget->window()->windowHandle(), treeWidget->iconSize()));
+            tWI->setData(column, Qt::DecorationRole, icon.pixmap(treeWidget->iconSize()));
         else
             setDefaultIcon = true;
     }
     if (setDefaultIcon)
+    {
         tWI->setIcon(column, icon);
+    }
 }
 
 #ifdef UPDATER
@@ -168,7 +170,7 @@ QString QMPlay2GUIClass::getCurrentPth(QString pth, bool leaveFilename)
         pth.remove(0, 7);
     if (!leaveFilename)
         pth = Functions::filePath(pth);
-    if (!QFileInfo::exists(pth))
+    if (!QFileInfo(pth).exists())
         pth = settings->getString("currPth");
     return pth;
 }
@@ -216,58 +218,6 @@ QMPlay2GUIClass::~QMPlay2GUIClass()
 
 /**/
 
-static QCommandLineParser *createCmdParser(bool descriptions)
-{
-    static constexpr const char *translations[] = {
-        QT_TRANSLATE_NOOP("Help", "Opens and plays specified <url>."),
-        QT_TRANSLATE_NOOP("Help", "Opens new QMPlay2 instance and plays specified <url>."),
-        QT_TRANSLATE_NOOP("Help", "Adds specified <url> to playlist."),
-        QT_TRANSLATE_NOOP("Help", "Starts the application with given <profile name>."),
-        QT_TRANSLATE_NOOP("Help", "Doesn't play after run (bypass \"Remember playback position\" option)."),
-        QT_TRANSLATE_NOOP("Help", "Toggles playback."),
-        QT_TRANSLATE_NOOP("Help", "Starts playback."),
-        QT_TRANSLATE_NOOP("Help", "Stops playback."),
-        QT_TRANSLATE_NOOP("Help", "Ensures that the window will be visible if the application is running."),
-        QT_TRANSLATE_NOOP("Help", "Toggles fullscreen."),
-        QT_TRANSLATE_NOOP("Help", "Sets specified volume."),
-        QT_TRANSLATE_NOOP("Help", "Sets specified playback speed."),
-        QT_TRANSLATE_NOOP("Help", "Seeks to the specified value."),
-        QT_TRANSLATE_NOOP("Help", "Plays next entry on playlist."),
-        QT_TRANSLATE_NOOP("Help", "Plays previous entry on playlist."),
-        QT_TRANSLATE_NOOP("Help", "Terminates the application."),
-        QT_TRANSLATE_NOOP("Help", "Displays this help."),
-    };
-
-    const auto maybeGetTranslatedText = [&](const char *text) {
-        if (descriptions)
-            return QCoreApplication::translate("Help", text);
-        return QString();
-    };
-
-    QCommandLineParser *parser = new QCommandLineParser;
-    parser->addPositionalArgument("<url>", maybeGetTranslatedText(translations[0]), "[url]");
-    parser->addOptions({
-        {"open", maybeGetTranslatedText(translations[0]), "url"},
-        {"opennew", maybeGetTranslatedText(translations[1]), "url"},
-        {"enqueue", maybeGetTranslatedText(translations[2]), "url"},
-        {"profile", maybeGetTranslatedText(translations[3]), "profile name"},
-        {"noplay", maybeGetTranslatedText(translations[4])},
-        {"toggle", maybeGetTranslatedText(translations[5])},
-        {"play", maybeGetTranslatedText(translations[6])},
-        {"stop", maybeGetTranslatedText(translations[7])},
-        {"show", maybeGetTranslatedText(translations[8])},
-        {"fullscreen", maybeGetTranslatedText(translations[9])},
-        {"volume", maybeGetTranslatedText(translations[10]), "0..100"},
-        {"speed", maybeGetTranslatedText(translations[11]), "0.05..100.0"},
-        {"seek", maybeGetTranslatedText(translations[12]), "s"},
-        {"next", maybeGetTranslatedText(translations[13])},
-        {"prev", maybeGetTranslatedText(translations[14])},
-        {"quit", maybeGetTranslatedText(translations[15])},
-        {{"h", "help"}, maybeGetTranslatedText(translations[16])},
-    });
-
-    return parser;
-}
 static QString fileArg(const QString &arg)
 {
     if (!arg.contains("://"))
@@ -278,62 +228,90 @@ static QString fileArg(const QString &arg)
     }
     return arg;
 }
-static QList<QPair<QString, QString>> parseArguments(const QCommandLineParser &parser)
-{
-    QList<QPair<QString, QString>> arguments;
-    for (const QString &option : parser.optionNames())
-    {
-        QString value = parser.value(option);
-        if (option == "open" || option == "enqueue")
-            value = fileArg(value);
-        arguments += {option, value};
-    }
 
-    QString urlLines;
-    for (const QString &url : parser.positionalArguments())
-        urlLines += fileArg(url) + "\n";
-    urlLines.chop(1);
-    if (!urlLines.isEmpty())
+static void parseArguments(QStringList &arguments)
+{
+    QString param;
+    while (arguments.count())
     {
-        bool found = false;
-        for (int i = arguments.count() - 1; i >= 0; --i)
+        const QString arg = arguments.takeFirst();
+        if (arg.startsWith('-'))
         {
-            if (arguments.at(i).first == "open" || arguments.at(i).first == "enqueue")
+            param = arg;
+            while (param.startsWith('-'))
+                param.remove(0, 1);
+            if (!param.isEmpty() && !g_arguments.first.contains(param))
             {
-                arguments[i].second += "\n" + urlLines;
-                found = true;
-                break;
+                g_arguments.first  += param;
+                g_arguments.second += QString();
+            }
+            else
+            {
+                param.clear();
             }
         }
-        if (!found)
-            arguments += {"open", urlLines};
+        else if (!param.isEmpty())
+        {
+            QString &data = g_arguments.second.last();
+            if (!data.isEmpty())
+                data += '\n';
+            if (param == "open" || param == "enqueue")
+                data += fileArg(arg);
+            else
+                data += arg;
+        }
+        else if (!g_arguments.first.contains("open"))
+        {
+            param = "open";
+            g_arguments.first  += param;
+            g_arguments.second += fileArg(arg);
+        }
     }
+}
 
-    return arguments;
+static void showHelp()
+{
+    QFile f;
+    f.open(stdout, QFile::WriteOnly);
+    f.write("QMPlay2 - Qt Media Player 2 (" + Version::get() + ")\n");
+    f.write(QObject::tr(
+"  Parameters list:\n"
+"    -open         \"address\"\n"
+"    -enqueue      \"address\"\n"
+"    -profile      \"name\" - starts application with given profile name\n"
+"    -noplay     - doesn't play after run (bypass \"Remember playback position\" option)\n"
+"    -toggle     - toggles play/pause\n"
+"    -show       - ensures that the window will be visible if the application is running\n"
+"    -fullscreen - toggles fullscreen\n"
+"    -volume     - sets volume [0..100]\n"
+"    -speed      - sets playback speed [0.05..100.0]\n"
+"    -seek       - seeks to the specified value [s]\n"
+"    -stop       - stops playback\n"
+"    -next       - plays next on the list\n"
+"    -prev       - plays previous on the list\n"
+"    -quit       - terminates the application"
+    ).toLocal8Bit() + "\n");
 }
 
-static bool writeToSocket(IPCSocket &socket, QList<QPair<QString, QString>> &arguments)
+static bool writeToSocket(IPCSocket &socket)
 {
     bool ret = false;
-
-    for (auto &&argument : arguments)
+    for (int i = g_arguments.first.count() - 1; i >= 0; i--)
     {
-        if (argument.first == "noplay" || argument.first == "profile")
+        if (g_arguments.first[i] == "noplay" || g_arguments.first[i] == "profile")
             continue;
-
-        if (argument.first == "open" || argument.first == "enqueue")
+        else if (g_arguments.first[i] == "open" || g_arguments.first[i] == "enqueue")
         {
-            if (!argument.second.isEmpty())
-                argument.second = Functions::Url(argument.second);
+            if (!g_arguments.second[i].isEmpty())
+                g_arguments.second[i] = Functions::Url(g_arguments.second[i]);
 #ifdef Q_OS_WIN
-            if (argument.second.startsWith("file://"))
-                argument.second.remove(0, 7);
+            if (g_arguments.second[i].startsWith("file://"))
+                g_arguments.second[i].remove(0, 7);
 #endif
         }
-        socket.write(QString(argument.first + '\t' + argument.second).toUtf8() + '\0');
+        socket.write(QString(g_arguments.first[i] + '\t' + g_arguments.second[i]).toUtf8() + '\0');
         ret = true;
     }
-
     return ret;
 }
 
@@ -352,13 +330,6 @@ static inline void exitProcedure()
     g_screenSaver = nullptr;
 }
 
-#ifndef Q_OS_WIN
-    #include <csetjmp>
-    static jmp_buf env;
-    static bool qAppOK;
-    static bool canDeleteApp = true;
-#endif
-
 static inline void forceKill()
 {
 #ifdef Q_OS_WIN
@@ -400,13 +371,6 @@ static void signal_handler(int s)
             }
             break;
         case SIGABRT:
-#ifndef Q_OS_WIN
-            if (!qAppOK && g_useGui)
-            {
-                canDeleteApp = g_useGui = false;
-                longjmp(env, 1);
-            }
-#endif
             QMPlay2Core.log("QMPlay2 has been aborted (SIGABRT)", ErrorLog | AddTimeToLog | (qApp ? SaveLog : DontShowInGUI));
             callDefaultSignalHandler(s);
             break;
@@ -432,6 +396,12 @@ static void signal_handler(int s)
     }
 }
 
+static inline void noAutoPlay()
+{
+    g_arguments.first += "noplay";
+    g_arguments.second += QString();
+}
+
 #ifdef Q_OS_WIN
 static LRESULT CALLBACK MMKeysHookProc(int code, WPARAM wparam, LPARAM lparam)
 {
@@ -461,50 +431,24 @@ static LRESULT CALLBACK MMKeysHookProc(int code, WPARAM wparam, LPARAM lparam)
 }
 #endif
 
-static QtMessageHandler g_defaultMsgHandler = nullptr;
-static QMutex g_messageHandlerMutex;
-static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
+    static QtMsgHandler g_defaultMsgHandler = nullptr;
+
+void messageHandler(QtMsgType type, const char *message)
 {
-    bool qmplay2Log = false;
-    if (QCoreApplication::instance())
-    {
-        // Use QMPlay2 logger only when we have a "QApplication" instance (we're still executing "main()"),
-        // so any static data including "QSystemLocaleSingleton" and "QMPlay2CoreClass" are still valid.
-        switch (type)
-        {
-            case QtWarningMsg:
-            case QtCriticalMsg:
-            case QtFatalMsg:
-                g_messageHandlerMutex.lock();
-                QMPlay2Core.logError(qFormatLogMessage(type, context, message), false);
-                g_messageHandlerMutex.unlock();
-                qmplay2Log = true;
-                break;
-            case QtDebugMsg:
-            case QtInfoMsg:
-                if (type != QtDebugMsg || qstrcmp(context.category, "js") == 0)
-                {
-                    g_messageHandlerMutex.lock();
-                    QMPlay2Core.logInfo(qFormatLogMessage(type, context, message), false);
-                    g_messageHandlerMutex.unlock();
-                    qmplay2Log = true;
-                }
-                break;
-            default:
-                break;
-        }
-    }
-    if (!qmplay2Log)
+    switch (type)
     {
-#ifdef Q_OS_ANDROID
-        if (g_defaultMsgHandler)
-            g_defaultMsgHandler(type, context, message);
-#else
-        g_messageHandlerMutex.lock();
-        fprintf(stderr, "%s\n", qFormatLogMessage(type, context, message).toLocal8Bit().constData());
-        fflush(stderr);
-        g_messageHandlerMutex.unlock();
-#endif
+        case QtDebugMsg:
+            fprintf(stderr, "Debug: %s\n", message);
+            break;
+        case QtWarningMsg:
+            fprintf(stderr, "Warning: %s\n", message);
+            break;
+        case QtCriticalMsg:
+            fprintf(stderr, "Critical: %s\n", message);
+            break;
+        case QtFatalMsg:
+            fprintf(stderr, "Fatal: %s\n", message);
+            abort(); // Abort on fatal messages
     }
 }
 
@@ -608,51 +552,27 @@ int main(int argc, char *argv[])
     checkForEGL();
 #endif
 
-    QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-#ifndef Q_OS_WIN
-    QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
-#endif
+        new QApplication(argc, argv, g_useGui);
 
-#ifndef Q_OS_WIN
-    if (!setjmp(env))
-#endif
-    new QApplication(argc, argv);
-#ifndef Q_OS_WIN
-    qAppOK = true;
-#endif
+    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
+    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
     QCoreApplication::setApplicationName("QMPlay2");
 
     QMPlay2GUIClass &qmplay2Gui = QMPlay2GUI; //Create "QMPlay2GUI" instance
-    g_defaultMsgHandler = qInstallMessageHandler(messageHandler);
+    g_defaultMsgHandler = qInstallMsgHandler(messageHandler);
 
-    QCommandLineParser *parser = createCmdParser(false);
-    parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
-    parser->process(*qApp);
-    QList<QPair<QString, QString>> arguments = parseArguments(*parser);
-    const bool help = parser->isSet("help");
-    QString cmdLineProfile = parser->value("profile");
-    delete parser;
+    QStringList arguments = QCoreApplication::arguments();
+    arguments.removeFirst();
+    const bool help = arguments.contains("-help") || arguments.contains("-h");
 
     if (!help)
     {
-        bool useSocket = true;
-
-        for (auto &&argument : arguments)
-        {
-            if (argument.first == "opennew")
-            {
-                argument.first = "open";
-                useSocket = false;
-                break;
-            }
-        }
-
-        if (useSocket)
-        {
+// FIXME: try sorting this mess out.
             IPCSocket socket(qmplay2Gui.getPipe());
+            parseArguments(arguments);
             if (socket.open(IPCSocket::WriteOnly))
             {
-                if (writeToSocket(socket, arguments))
+                if (writeToSocket(socket))
                     g_useGui = false;
                 socket.close();
             }
@@ -660,22 +580,27 @@ int main(int argc, char *argv[])
             else if (QFile::exists(qmplay2Gui.getPipe()))
             {
                 QFile::remove(qmplay2Gui.getPipe());
-                arguments.append({"noplay", QString()});
+                noAutoPlay();
             }
 #endif
-        }
 
         if (!g_useGui)
         {
-#ifndef Q_OS_WIN
-            if (canDeleteApp)
-#endif
                 delete qApp;
             return 0;
         }
     }
 
-    qmplay2Gui.cmdLineProfile = std::move(cmdLineProfile);
+    for (int i = 0; i < g_arguments.first.count(); ++i)
+    {
+        if (g_arguments.first.at(i) == "profile")
+        {
+            qmplay2Gui.cmdLineProfile = g_arguments.second.at(i);
+            g_arguments.first.removeAt(i);
+            g_arguments.second.removeAt(i);
+            break;
+        }
+    }
 
     QString libPath, sharePath = QCoreApplication::applicationDirPath();
     bool cmakeBuildFound = false;
@@ -717,7 +642,6 @@ int main(int argc, char *argv[])
 
     qRegisterMetaType<VideoFrame>("VideoFrame");
 
-    QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
     QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
 
     QDir::setCurrent(QCoreApplication::applicationDirPath()); //Is it really needed?
@@ -726,8 +650,6 @@ int main(int argc, char *argv[])
     {
         qmplay2Gui.screenSaver = g_screenSaver = new ScreenSaver;
         QApplication::setQuitOnLastWindowClosed(false);
-        qApp->installEventFilter(new EventFilterWorkarounds(qApp));
-        PanGestureEventFilter::install();
     }
 
 #ifdef Q_OS_WIN
@@ -751,16 +673,6 @@ int main(int argc, char *argv[])
         /* QMPlay2GUI musi być stworzone już wcześniej */
         QMPlay2Core.init(!help, cmakeBuildFound, libPath, sharePath, qmplay2Gui.cmdLineProfile);
 
-        if (help)
-        {
-            parser = createCmdParser(true);
-            parser->setApplicationDescription(QString("QMPlay2 - Qt Media Player 2 (%1)").arg((QString)Version::get()));
-            printf("%s", parser->helpText().toLocal8Bit().constData());
-            fflush(stdout);
-            delete parser;
-            break;
-        }
-
         if (!qmplay2Gui.cmdLineProfile.isEmpty() && QMPlay2Core.getSettingsProfile() == "/")
             qmplay2Gui.cmdLineProfile = QMPlay2Core.getSettingsProfile(); // Default profile
 
@@ -783,6 +695,12 @@ int main(int argc, char *argv[])
             settings.remove("Volume");
         }
 
+        if (help)
+        {
+            showHelp();
+            break;
+        }
+
         qmplay2Gui.loadIcons();
         {
             const QIcon scaledIcon = QMPlay2Core.getQMPlay2Icon();
@@ -861,7 +779,7 @@ int main(int argc, char *argv[])
 
         qmplay2Gui.restartApp = qmplay2Gui.removeSettings = qmplay2Gui.noAutoPlay = false;
         qmplay2Gui.newProfileName.clear();
-        new MainWidget(arguments);
+        new MainWidget(g_arguments);
         do
         {
             QCoreApplication::exec();
@@ -894,22 +812,15 @@ int main(int argc, char *argv[])
         }
 
         if (qmplay2Gui.noAutoPlay)
-            arguments.append({"noplay", QString()});
+            noAutoPlay();
 
         delete qmplay2Gui.pipe;
     } while (qmplay2Gui.restartApp);
 
     qmplay2Gui.deleteIcons();
 
-#ifdef Q_OS_WIN
-    UnhookWindowsHookEx(keyboardHook);
-#endif
-
     exitProcedure();
 
-#ifndef Q_OS_WIN
-    if (canDeleteApp)
-#endif
         delete qApp;
     return 0;
 }
diff --git a/src/gui/Main.hpp b/src/gui/Main.hpp
index e4916950..26f17d54 100644
--- a/src/gui/Main.hpp
+++ b/src/gui/Main.hpp
@@ -35,7 +35,7 @@ class VideoDock;
 class MenuBar;
 class QWidget;
 
-class QMPlay2GUIClass final : private QMPlay2CoreClass
+class QMPlay2GUIClass : private QMPlay2CoreClass
 {
     Q_DECLARE_TR_FUNCTIONS(QMPlay2GUIClass)
 
@@ -68,7 +68,7 @@ public:
 
     void updateInDockW();
 
-    const QWidget *getVideoDock() const override;
+    const QWidget *getVideoDock() const override final;
 
     QColor grad1, grad2, qmpTxt;
     QIcon *groupIcon, *mediaIcon, *folderIcon;
@@ -84,7 +84,7 @@ public:
     QString newProfileName, cmdLineProfile;
 private:
     QMPlay2GUIClass();
-    ~QMPlay2GUIClass();
+    ~QMPlay2GUIClass() final;
 };
 
 #define QMPlay2GUI \
diff --git a/src/gui/MainWidget.cpp b/src/gui/MainWidget.cpp
index 139dd2a4..4bbc7a61 100644
--- a/src/gui/MainWidget.cpp
+++ b/src/gui/MainWidget.cpp
@@ -32,8 +32,6 @@
 #include <QListWidget>
 #ifdef Q_OS_MACOS
     #include <QProcess>
-    #include <QScreen>
-    #include <QWindow>
 #endif
 #ifdef Q_OS_WIN
     #include <QWinThumbnailToolButton>
@@ -62,7 +60,8 @@
 #include <VolWidget.hpp>
 #include <ScreenSaver.hpp>
 #ifdef Q_OS_MACOS
-    #include <QMPlay2MacExtensions.hpp>
+    #include <Carbon/Carbon.h>
+    extern void qt_mac_set_dock_menu(QMenu *);
 #endif
 
 using Functions::timeToStr;
@@ -76,21 +75,6 @@ using Functions::timeToStr;
 
 #include <cmath>
 
-/* MainWidgetTmpStyle -  dock widget separator extent must be larger for touch screens */
-class MainWidgetTmpStyle final : public QCommonStyle
-{
-public:
-    ~MainWidgetTmpStyle() = default;
-
-    int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override
-    {
-        const int pM = QCommonStyle::pixelMetric(metric, option, widget);
-        if (metric == QStyle::PM_DockWidgetSeparatorExtent)
-            return pM * 5 / 2;
-        return pM;
-    }
-};
-
 #ifndef Q_OS_MACOS
 static void copyMenu(QMenu *dest, QMenu *src, QMenu *dontCopy = nullptr)
 {
@@ -110,25 +94,13 @@ static void copyMenu(QMenu *dest, QMenu *src, QMenu *dontCopy = nullptr)
 #endif
 
 /* MainWidget */
-MainWidget::MainWidget(QList<QPair<QString, QString>> &arguments) :
+MainWidget::MainWidget(QPair<QStringList, QStringList> &arguments) :
     updater(this)
 {
     QMPlay2GUI.videoAdjustment = new VideoAdjustmentW;
     QMPlay2GUI.shortcutHandler = new ShortcutHandler(this);
     QMPlay2GUI.mainW = this;
 
-    /* Looking for touch screen */
-    for (const QTouchDevice *touchDev : QTouchDevice::devices())
-    {
-        /* Touchscreen found */
-        if (touchDev->type() == QTouchDevice::TouchScreen)
-        {
-            MainWidgetTmpStyle mainWidgetTmpStyle;
-            setStyle(&mainWidgetTmpStyle);
-            break;
-        }
-    }
-
     setObjectName("MainWidget");
 
     settingsW = nullptr;
@@ -284,9 +256,7 @@ MainWidget::MainWidget(QList<QPair<QString, QString>> &arguments) :
     connect(playlistDock, SIGNAL(play(const QString &)), &playC, SLOT(play(const QString &)));
     connect(playlistDock, SIGNAL(repeatEntry(bool)), &playC, SLOT(repeatEntry(bool)));
     connect(playlistDock, SIGNAL(stop()), &playC, SLOT(stop()));
-    connect(playlistDock, &PlaylistDock::addAndPlayRestoreWindow, this, [this] {
-        m_restoreWindowOnVideo = true;
-    });
+    connect(playlistDock, SIGNAL(addAndPlayRestoreWindow()), this, SLOT(handleAddAndPlayRestoreWindow()));
 
     connect(seekS, SIGNAL(valueChanged(int)), this, SLOT(seek(int)));
     connect(seekS, SIGNAL(mousePosition(int)), this, SLOT(mousePositionOnSlider(int)));
@@ -320,17 +290,10 @@ MainWidget::MainWidget(QList<QPair<QString, QString>> &arguments) :
     connect(&playC, SIGNAL(updateBufferedRange(int, int)), seekS, SLOT(drawRange(int, int)));
     connect(&playC, SIGNAL(updateWindowTitle(const QString &)), this, SLOT(updateWindowTitle(const QString &)));
     connect(&playC, SIGNAL(updateImage(const QImage &)), videoDock, SLOT(updateImage(const QImage &)));
-    connect(&playC, &PlayClass::videoStarted, this, &MainWidget::videoStarted);
-    connect(&playC, &PlayClass::videoNotStarted, this, [this] {
-        m_restoreWindowOnVideo = false;
-    });
+    connect(&playC, SIGNAL(videoNotStarted()), this, SLOT(handleVideoNotStarted()));
+    connect(playlistDock, SIGNAL(addAndPlayRestoreWindow()), this, SLOT(onAddAndPlayRestoreWindow()));
     connect(&playC, SIGNAL(uncheckSuspend()), this, SLOT(uncheckSuspend()));
-    connect(&playC, &PlayClass::setVideoCheckState, this, [this](bool rotate90, bool hFlip, bool vFlip, bool spherical) {
-        menuBar->playback->videoFilters->rotate90->setChecked(rotate90);
-        menuBar->playback->videoFilters->hFlip->setChecked(hFlip);
-        menuBar->playback->videoFilters->vFlip->setChecked(vFlip);
-        menuBar->playback->videoFilters->spherical->setChecked(spherical);
-    });
+    connect(&playC, SIGNAL(setVideoCheckState(bool, bool, bool, bool)), this, SLOT(handleSetVideoCheckState(bool, bool, bool, bool)));
     /**/
 
     if (settings.getBool("MainWidget/TabPositionNorth"))
@@ -390,21 +353,21 @@ MainWidget::MainWidget(QList<QPair<QString, QString>> &arguments) :
         if (tabBar && tabBar->property("changeCurrentOnDrag").isValid())
         {
             tabBar->setAcceptDrops(true);
-            tabBar->setChangeCurrentOnDrag(true);
         }
     }
 
     playlistDock->load(QMPlay2Core.getSettingsDir() + "Playlist.pls");
 
     bool noplay = false;
-    for (const auto &argument : asConst(arguments))
+    for (int i = 0; i < arguments.first.size(); ++i) // Iterate over the size of the first QStringList
     {
-        const QString &param = argument.first;
-        const QString &data  = argument.second;
+        const QString &param = arguments.first.at(i);  // Access the elements of the first QStringList
+        const QString &data  = arguments.second.at(i); // Access the corresponding elements of the second QStringList
         noplay |= (param == "open" || param == "noplay");
         processParam(param, data);
     }
-    arguments.clear();
+    arguments.first.clear();  // Clear the first QStringList
+    arguments.second.clear(); // Clear the second QStringList
 
     if (!noplay)
     {
@@ -421,22 +384,11 @@ MainWidget::MainWidget(QList<QPair<QString, QString>> &arguments) :
             playStateChanged(false);
     }
 
-#ifdef Q_OS_MACOS
-    qApp->installEventFilter(this);
-    fileOpenTimer.setSingleShot(true);
-    connect(&fileOpenTimer, &QTimer::timeout, this, &MainWidget::fileOpenTimerTimeout);
-    if (QMPlay2GUI.pipe) // Register media keys only for first QMPlay2 instance
-        QMPlay2MacExtensions::registerMacOSMediaKeys(std::bind(&MainWidget::processParam, this, std::placeholders::_1, QString()));
-#endif
-
     if (settings.getBool("AutoUpdates"))
         updater.downloadUpdate();
 }
 MainWidget::~MainWidget()
 {
-#ifdef Q_OS_MACOS
-    QMPlay2MacExtensions::unregisterMacOSMediaKeys();
-#endif
     QMPlay2Extensions::closeExtensions();
     emit QMPlay2Core.restoreCursor();
     Notifies::finalize();
@@ -557,6 +509,15 @@ void MainWidget::updateWindowTitle(const QString &t)
     title.replace('\n', ' ');
     setWindowTitle(title);
 }
+
+void MainWidget::handleAddAndPlayRestoreWindow() {
+    m_restoreWindowOnVideo = true;
+}
+
+void MainWidget::handleVideoNotStarted() {
+    m_restoreWindowOnVideo = false;
+}
+
 void MainWidget::videoStarted(bool noVideo)
 {
     const bool autoRestoreMainWindowOnVideo = m_restoreWindowOnVideo ? QMPlay2Core.getSettings().getBool("AutoRestoreMainWindowOnVideo") : false;
@@ -693,20 +654,22 @@ void MainWidget::resetSpherical()
 void MainWidget::visualizationFullScreen()
 {
     QWidget *senderW = (QWidget *)sender();
-    const auto maybeGoFullScreen = [this, senderW] {
-        if (!fullScreen)
-        {
-            videoDock->setWidget(senderW);
-            toggleFullScreen();
-        }
-    };
 #ifdef Q_OS_MACOS
-    // On macOS if full screen is toggled to fast after double click, mouse remains in clicked state...
-    QTimer::singleShot(200, maybeGoFullScreen);
+    QTimer::singleShot(200, this, SLOT(maybeGoFullScreen()));
 #else
-    maybeGoFullScreen();
+    maybeGoFullScreen(senderW);
 #endif
 }
+
+void MainWidget::maybeGoFullScreen(QWidget *senderW)
+{
+    if (!fullScreen)
+    {
+        videoDock->setWidget(senderW);
+        toggleFullScreen();
+    }
+}
+
 void MainWidget::hideAllExtensions()
 {
     for (QMPlay2Extensions *QMPlay2Ext : QMPlay2Extensions::QMPlay2ExtensionsList())
@@ -724,11 +687,7 @@ void MainWidget::toggleVisibility()
             toggleFullScreen();
         if (!isTray)
         {
-#ifndef Q_OS_MACOS
             showMinimized();
-#else
-            QMPlay2MacExtensions::setApplicationVisible(false);
-#endif
         }
         else
         {
@@ -764,6 +723,7 @@ void MainWidget::toggleVisibility()
     }
 #endif
 }
+
 void MainWidget::createMenuBar()
 {
     menuBar = QMPlay2GUI.menuBar;
@@ -779,7 +739,7 @@ void MainWidget::createMenuBar()
     connect(menuBar->window->toggleVisibility, SIGNAL(triggered()), this, SLOT(toggleVisibility()));
     connect(menuBar->window->toggleFullScreen, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
     connect(menuBar->window->toggleCompactView, SIGNAL(triggered()), this, SLOT(toggleCompactView()));
-    connect(menuBar->window->alwaysOnTop, &QAction::triggered, this, &MainWidget::toggleAlwaysOnTop);
+    connect(menuBar->window->alwaysOnTop, SIGNAL(triggered(bool)), this, SLOT(toggleAlwaysOnTop(bool)));
     connect(menuBar->window->close, SIGNAL(triggered()), this, SLOT(close()));
 
     connect(menuBar->playlist->add->address, SIGNAL(triggered()), this, SLOT(openUrl()));
@@ -794,7 +754,7 @@ void MainWidget::createMenuBar()
     connect(menuBar->playlist->newGroup, SIGNAL(triggered()), playlistDock, SLOT(newGroup()));
     connect(menuBar->playlist->renameGroup, SIGNAL(triggered()), playlistDock, SLOT(renameGroup()));
     connect(menuBar->playlist->lock, SIGNAL(triggered()), playlistDock, SLOT(toggleLock()));
-    connect(menuBar->playlist->alwaysSync, &QAction::triggered, playlistDock, &PlaylistDock::alwaysSyncTriggered);
+    connect(menuBar->playlist->alwaysSync, SIGNAL(triggered(bool)), playlistDock, SLOT(alwaysSyncTriggered(bool)));
     connect(menuBar->playlist->delEntries, SIGNAL(triggered()), playlistDock, SLOT(delEntries()));
     connect(menuBar->playlist->delNonGroupEntries, SIGNAL(triggered()), playlistDock, SLOT(delNonGroupEntries()));
     connect(menuBar->playlist->clear, SIGNAL(triggered()), playlistDock, SLOT(clear()));
@@ -931,19 +891,18 @@ void MainWidget::createMenuBar()
     secondMenu->addSeparator();
     // Copy action, because PreferencesRole doesn't show in dock menu.
     QAction *settings = new QAction(menuBar->options->settings->icon(), menuBar->options->settings->text(), menuBar->options->settings->parent());
-    connect(settings, &QAction::triggered, menuBar->options->settings, &QAction::trigger);
+    connect(settings, SIGNAL(triggered()), menuBar->options->settings, SLOT(trigger()));
     secondMenu->addAction(settings);
 
     QAction *newInstanceAct = new QAction(tr("New window"), secondMenu);
-    connect(newInstanceAct, &QAction::triggered, [] {
-        QProcess::startDetached(QCoreApplication::applicationFilePath(), {"-noplay"}, QCoreApplication::applicationDirPath());
-    });
+    connect(newInstanceAct, SIGNAL(triggered()), this, SLOT(createNewWindow()));
     secondMenu->addSeparator();
     secondMenu->addAction(newInstanceAct);
 
     qt_mac_set_dock_menu(secondMenu);
 #endif
 }
+
 void MainWidget::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
 {
 #if !defined Q_OS_MACOS && !defined Q_OS_ANDROID
@@ -966,6 +925,7 @@ void MainWidget::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
     Q_UNUSED(reason)
 #endif
 }
+
 void MainWidget::toggleCompactView()
 {
     if (!isCompactView)
@@ -1004,6 +964,7 @@ void MainWidget::toggleCompactView()
         statusBar->show();
     }
 }
+
 void MainWidget::toggleAlwaysOnTop(bool checked)
 {
     auto flags = windowFlags();
@@ -1016,6 +977,7 @@ void MainWidget::toggleAlwaysOnTop(bool checked)
     if (visible)
         show();
 }
+
 void MainWidget::toggleFullScreen()
 {
     static bool visible, compact_view, tb_movable;
@@ -1082,30 +1044,17 @@ void MainWidget::toggleFullScreen()
         videoDock->fullScreen(true);
         videoDock->show();
 
-#ifdef Q_OS_MACOS
-        menuBar->window->toggleVisibility->setEnabled(false);
-#endif
         menuBar->window->toggleCompactView->setEnabled(false);
         menuBar->window->toggleFullScreen->setShortcuts(QList<QKeySequence>() << menuBar->window->toggleFullScreen->shortcut() << QKeySequence("ESC"));
         fullScreen = true;
 
-#ifndef Q_OS_MACOS
         showFullScreen();
-#else
-        setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
-        setGeometry(window()->windowHandle()->screen()->geometry());
-        QMPlay2MacExtensions::showSystemUi(windowHandle(), false);
-        show();
-#endif
 
         if (playC.isPlaying())
             QMPlay2GUI.screenSaver->inhibit(1);
     }
     else
     {
-#ifdef Q_OS_MACOS
-        menuBar->window->toggleVisibility->setEnabled(true);
-#endif
         menuBar->window->toggleCompactView->setEnabled(true);
         menuBar->window->toggleFullScreen->setShortcuts(QList<QKeySequence>() << menuBar->window->toggleFullScreen->shortcut());
 
@@ -1113,19 +1062,11 @@ void MainWidget::toggleFullScreen()
         fullScreen = false;
 
 #ifndef Q_OS_ANDROID
-#ifdef Q_OS_MACOS
-        QMPlay2MacExtensions::showSystemUi(windowHandle(), true);
-        setWindowFlags(Qt::Window);
-#else
         showNormal();
-#endif // Q_OS_MACOS
         if (maximized)
             showMaximized();
         else
         {
-#ifdef Q_OS_MACOS
-            showNormal();
-#endif
             setGeometry(savedGeo);
         }
 #else // Q_OS_ANDROID
@@ -1168,6 +1109,7 @@ void MainWidget::toggleFullScreen()
     }
     emit QMPlay2Core.fullScreenChanged(fullScreen);
 }
+
 void MainWidget::showMessage(const QString &msg, const QString &title, int messageIcon, int ms)
 {
     if (ms < 1 || !Notifies::notify(title, msg, ms, messageIcon))
@@ -1182,6 +1124,7 @@ void MainWidget::showMessage(const QString &msg, const QString &title, int messa
         messageBox->show();
     }
 }
+
 void MainWidget::statusBarMessage(const QString &msg, int ms)
 {
     statusBar->showMessage(msg, ms);
@@ -1199,14 +1142,17 @@ void MainWidget::openUrl()
 
     }
 }
+
 void MainWidget::openFiles()
 {
     playlistDock->add(QFileDialog::getOpenFileNames(this, tr("Choose files"), QMPlay2GUI.getCurrentPth(playlistDock->getUrl(), true)));
 }
+
 void MainWidget::openDir()
 {
     playlistDock->add(QFileDialog::getExistingDirectory(this, tr("Choose directory"), QMPlay2GUI.getCurrentPth(playlistDock->getUrl())));
 }
+
 void MainWidget::loadPlist()
 {
     QString filter = tr("Playlists") + " (";
@@ -1218,14 +1164,17 @@ void MainWidget::loadPlist()
         return;
     playlistDock->load(QFileDialog::getOpenFileName(this, tr("Choose playlist"), QMPlay2GUI.getCurrentPth(playlistDock->getUrl()), filter));
 }
+
 void MainWidget::savePlist()
 {
     savePlistHelper(tr("Save playlist"), QMPlay2GUI.getCurrentPth(playlistDock->getUrl()), false);
 }
+
 void MainWidget::saveGroup()
 {
     savePlistHelper(tr("Save group"), QMPlay2GUI.getCurrentPth(playlistDock->getUrl()) + playlistDock->getCurrentItemName(), true);
 }
+
 void MainWidget::showSettings(const QString &moduleName)
 {
     if (!settingsW)
@@ -1247,6 +1196,7 @@ void MainWidget::showSettings(const QString &moduleName)
             showSettings(moduleName);
     }
 }
+
 void MainWidget::showSettings()
 {
     showSettings(QString());
@@ -1259,6 +1209,7 @@ void MainWidget::itemDropped(const QString &pth, bool subs)
     else
         playlistDock->addAndPlay(pth);
 }
+
 void MainWidget::browseSubsFile()
 {
     QString dir = Functions::filePath(playC.getUrl());
@@ -1301,6 +1252,7 @@ void MainWidget::setSeekSMaximum(int max)
         seekS->setEnabled(false);
     }
 }
+
 void MainWidget::updatePos(double pos)
 {
     static int currPos;
@@ -1322,6 +1274,7 @@ void MainWidget::updatePos(double pos)
 #endif
     }
 }
+
 void MainWidget::mousePositionOnSlider(int pos)
 {
     statusBar->showMessage(tr("Pointed position") + ": " + timeToStr(pos / 10.0, true), 750);
@@ -1332,6 +1285,7 @@ void MainWidget::newConnection(IPCSocket *socket)
     connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
     connect(socket, SIGNAL(aboutToClose()), socket, SLOT(deleteLater()));
 }
+
 void MainWidget::readSocket()
 {
     IPCSocket *socket = (IPCSocket *)sender();
@@ -1348,6 +1302,10 @@ void MainWidget::readSocket()
         socket->deleteLater();
 }
 
+void MainWidget::createNewWindow() {
+    QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-noplay", QCoreApplication::applicationDirPath());
+}
+
 void MainWidget::about()
 {
     if (!aboutW)
@@ -1374,6 +1332,7 @@ void MainWidget::hideMenu(bool h)
     }
 }
 #endif
+
 void MainWidget::lockWidgets(bool l)
 {
     if (fullScreen || isCompactView)
@@ -1405,18 +1364,14 @@ void MainWidget::hideDocksSlot()
             hideDocks();
     }
 }
+
 void MainWidget::doRestoreState(const QByteArray &data)
 {
     if (isMaximized())
     {
         setUpdatesEnabled(false);
-        QTimer::singleShot(0, this, [=] {
-            QTimer::singleShot(0, this, [=] {
-                restoreState(data);
-                setUpdatesEnabled(true);
-                repaint();
-            });
-        });
+        QTimer::singleShot(0, this, SLOT(doRestoreStateStep1()));
+        m_restoreStateData = data; // Store the data for use in later steps
     }
     else
     {
@@ -1424,6 +1379,18 @@ void MainWidget::doRestoreState(const QByteArray &data)
     }
 }
 
+void MainWidget::doRestoreStateStep1()
+{
+    QTimer::singleShot(0, this, SLOT(doRestoreStateStep2()));
+}
+
+void MainWidget::doRestoreStateStep2()
+{
+    restoreState(m_restoreStateData);
+    setUpdatesEnabled(true);
+    repaint();
+}
+
 void MainWidget::uncheckSuspend()
 {
     if (menuBar->player->suspend)
@@ -1486,6 +1453,7 @@ void MainWidget::showToolBar(bool showTB)
         statusBar->hide();
     }
 }
+
 void MainWidget::hideDocks()
 {
     fullScreenDockWidgetState = saveState();
@@ -1595,6 +1563,7 @@ void MainWidget::keyPressEvent(QKeyEvent *e)
     }
     QMainWindow::keyPressEvent(e);
 }
+
 void MainWidget::mouseMoveEvent(QMouseEvent *e)
 {
     if ((fullScreen || isCompactView) && (e->buttons() == Qt::NoButton || videoDock->isTouch))
@@ -1663,12 +1632,14 @@ void MainWidget::mouseMoveEvent(QMouseEvent *e)
     }
     QMainWindow::mouseMoveEvent(e);
 }
+
 void MainWidget::leaveEvent(QEvent *e)
 {
     if (fullScreen || isCompactView)
         QMetaObject::invokeMethod(this, "hideDocksSlot", Qt::QueuedConnection); //Qt5 can't hide docks properly here
     QMainWindow::leaveEvent(e);
 }
+
 void MainWidget::closeEvent(QCloseEvent *e)
 {
     const QString quitMsg = tr("Are you sure you want to quit?");
@@ -1735,12 +1706,14 @@ void MainWidget::closeEvent(QCloseEvent *e)
 
     playC.stop(true);
 }
+
 void MainWidget::moveEvent(QMoveEvent *e)
 {
     if (videoDock->isVisible() && !videoDock->isFloating())
         emit QMPlay2Core.videoDockMoved();
     QMainWindow::moveEvent(e);
 }
+
 void MainWidget::showEvent(QShowEvent *)
 {
     if (!wasShow)
@@ -1759,6 +1732,7 @@ void MainWidget::showEvent(QShowEvent *)
     }
     menuBar->window->toggleVisibility->setText(tr("&Hide"));
 }
+
 void MainWidget::hideEvent(QHideEvent *)
 {
 #ifndef Q_OS_ANDROID
@@ -1776,7 +1750,7 @@ bool MainWidget::eventFilter(QObject *obj, QEvent *event)
     if (tray && obj == tray && event->type() == QEvent::Wheel)
     {
         QWheelEvent *we = static_cast<QWheelEvent *>(event);
-        volW->changeVolume(we->angleDelta().y() / 30);
+        volW->changeVolume(we->delta() / 30);
     }
 #ifdef Q_OS_MACOS
     else if (event->type() == QEvent::FileOpen)
@@ -1798,3 +1772,10 @@ void MainWidget::fileOpenTimerTimeout()
     filesToAdd.clear();
 }
 #endif
+
+void MainWidget::handleSetVideoCheckState(bool rotate90, bool hFlip, bool vFlip, bool spherical) {
+    menuBar->playback->videoFilters->rotate90->setChecked(rotate90);
+    menuBar->playback->videoFilters->hFlip->setChecked(hFlip);
+    menuBar->playback->videoFilters->vFlip->setChecked(vFlip);
+    menuBar->playback->videoFilters->spherical->setChecked(spherical);
+}
diff --git a/src/gui/MainWidget.hpp b/src/gui/MainWidget.hpp
index 2fe04bc5..daaf545c 100644
--- a/src/gui/MainWidget.hpp
+++ b/src/gui/MainWidget.hpp
@@ -42,14 +42,18 @@ class QMPlay2Extensions;
     class QWinTaskbarProgress;
 #endif
 
-class MainWidget final : public QMainWindow
+class MainWidget : public QMainWindow
 {
     friend class QMPlay2GUIClass;
     Q_PROPERTY(bool fullScreen READ getFullScreen)
     Q_OBJECT
 public:
-    MainWidget(QList<QPair<QString, QString>> &argument);
-    ~MainWidget();
+    MainWidget(QPair<QStringList, QStringList> &argument);
+    ~MainWidget() final;
+public slots:
+    void handleAddAndPlayRestoreWindow();
+    void handleVideoNotStarted();
+    void handleSetVideoCheckState(bool rotate90, bool hFlip, bool vFlip, bool spherical);
 private slots:
     void detachFromPipe();
 
@@ -77,6 +81,7 @@ private slots:
     void resetSpherical();
 
     void visualizationFullScreen();
+    void maybeGoFullScreen(QWidget *senderW);
     void hideAllExtensions();
     void toggleVisibility();
     void createMenuBar();
@@ -86,6 +91,7 @@ private slots:
     void toggleFullScreen();
     void showMessage(const QString &, const QString &, int, int);
     void statusBarMessage(const QString &, int ms);
+    void createNewWindow();
 
     void openUrl();
     void openFiles();
@@ -117,12 +123,14 @@ private slots:
 
     void hideDocksSlot();
     void doRestoreState(const QByteArray &data);
+    void doRestoreStateStep1();
+    void doRestoreStateStep2();
 
     void uncheckSuspend();
 private:
     void savePlistHelper(const QString &, const QString &, bool);
 
-    QMenu *createPopupMenu() override;
+    QMenu *createPopupMenu() override final;
 
     void showToolBar(bool);
     void hideDocks();
@@ -135,15 +143,15 @@ private:
     void setWindowsTaskBarFeatures();
 #endif
 
-    void keyPressEvent(QKeyEvent *) override;
-    void mouseMoveEvent(QMouseEvent *) override;
-    void leaveEvent(QEvent *) override;
-    void closeEvent(QCloseEvent *) override;
-    void moveEvent(QMoveEvent *) override;
-    void showEvent(QShowEvent *) override;
-    void hideEvent(QHideEvent *) override;
+    void keyPressEvent(QKeyEvent *) override final;
+    void mouseMoveEvent(QMouseEvent *) override final;
+    void leaveEvent(QEvent *) override final;
+    void closeEvent(QCloseEvent *) override final;
+    void moveEvent(QMoveEvent *) override final;
+    void showEvent(QShowEvent *) override final;
+    void hideEvent(QHideEvent *) override final;
 
-    bool eventFilter(QObject *obj, QEvent *event) override;
+    bool eventFilter(QObject *obj, QEvent *event) override final;
 
 #ifdef Q_OS_MACOS
     void fileOpenTimerTimeout();
@@ -185,6 +193,8 @@ private:
 #endif
     QAction *lockWidgetsAct;
 
+    QByteArray m_restoreStateData; // Temporary storage for the state data
+
     Updater updater;
 
 #ifdef Q_OS_WIN
diff --git a/src/gui/MenuBar.cpp b/src/gui/MenuBar.cpp
index b3ea2162..7210239d 100644
--- a/src/gui/MenuBar.cpp
+++ b/src/gui/MenuBar.cpp
@@ -366,27 +366,6 @@ MenuBar::Playback::VideoFilters::VideoFilters(QMenu *parent) :
     widgetAction->setDefaultWidget(QMPlay2GUI.videoAdjustment);
     QMPlay2GUI.videoAdjustment->setObjectName(videoAdjustmentMenu->title().remove('&'));
     videoAdjustmentMenu->addAction(widgetAction);
-#ifdef Q_OS_MACOS
-    // Update visibility and update geometry of video adjustment widget
-    connect(videoAdjustmentMenu, &VideoFilters::aboutToShow, [] {
-        QWidget *parent = QMPlay2GUI.videoAdjustment->parentWidget();
-        if (parent)
-        {
-            const QString parentObject = parent->metaObject()->className();
-            if (parentObject == "QMacNativeWidget")
-            {
-                QMPlay2GUI.videoAdjustment->update();
-                QMPlay2GUI.videoAdjustment->show();
-            }
-            if (parentObject == "QMacNativeWidget" || parentObject == "QMenu")
-            {
-                QTimer::singleShot(1, [parent] {
-                    QMPlay2GUI.videoAdjustment->setGeometry(parent->rect());
-                });
-            }
-        }
-    });
-#endif
     /**/
     addSeparator();
     newAction(VideoFilters::tr("&Spherical view"), this, spherical, true, QIcon(), true);
diff --git a/src/gui/OSDSettingsW.cpp b/src/gui/OSDSettingsW.cpp
index 7b988cbd..b6a67ca1 100644
--- a/src/gui/OSDSettingsW.cpp
+++ b/src/gui/OSDSettingsW.cpp
@@ -38,7 +38,7 @@ static void appendColon(const QObjectList &objects)
 void OSDSettingsW::init(const QString &prefix, int a, int b, int c, int d, int e, int f, double g, double h, const QColor &i, const QColor &j, const QColor &k)
 {
     Settings &QMPSettings = QMPlay2Core.getSettings();
-    QMPSettings.init(prefix + "/FontName", QGuiApplication::font().family());
+    QMPSettings.init(prefix + "/FontName", QFontComboBox().currentText());
     QMPSettings.init(prefix + "/FontSize", a);
     QMPSettings.init(prefix + "/Spacing", b);
     QMPSettings.init(prefix + "/LeftMargin", c);
diff --git a/src/gui/OtherVFiltersW.cpp b/src/gui/OtherVFiltersW.cpp
index 6d449b42..b4136468 100644
--- a/src/gui/OtherVFiltersW.cpp
+++ b/src/gui/OtherVFiltersW.cpp
@@ -37,7 +37,7 @@ OtherVFiltersW::OtherVFiltersW(bool hw) :
         for (const QString &filter : QMPlay2Core.getSettings().getStringList("VideoFilters"))
         {
             videoFilters.first += filter.mid(1);
-            videoFilters.second += filter.leftRef(1).toInt();
+            videoFilters.second += filter.left(1).toInt();
         }
 
         for (int i = 0; i < videoFilters.first.count(); ++i)
diff --git a/src/gui/PlayClass.cpp b/src/gui/PlayClass.cpp
index 1138db76..5b103071 100644
--- a/src/gui/PlayClass.cpp
+++ b/src/gui/PlayClass.cpp
@@ -311,14 +311,14 @@ void PlayClass::seek(double pos, bool allowAccurate)
 void PlayClass::chStream(const QString &s)
 {
     if (s.startsWith("audio"))
-        chosenAudioStream = s.rightRef(s.length() - 5).toInt();
+        chosenAudioStream = s.rightRef(s.length() - 5).toString().toInt();
     else if (s.startsWith("video"))
-        chosenVideoStream = s.rightRef(s.length() - 5).toInt();
+        chosenVideoStream = s.rightRef(s.length() - 5).toString().toInt();
     else if (s.startsWith("subtitles"))
-        chosenSubtitlesStream = s.rightRef(s.length() - 9).toInt();
+        chosenSubtitlesStream = s.rightRef(s.length() - 9).toString().toInt();
     else if (s.startsWith("fileSubs"))
     {
-        int idx = s.rightRef(s.length() - 8).toInt();
+        int idx = s.rightRef(s.length() - 5).toString().toInt();
         if (fileSubsList.count() > idx)
             loadSubsFile(fileSubsList[idx]);
     }
diff --git a/src/gui/PlayClass.hpp b/src/gui/PlayClass.hpp
index 41e4e629..bc4152ed 100644
--- a/src/gui/PlayClass.hpp
+++ b/src/gui/PlayClass.hpp
@@ -44,7 +44,7 @@ enum
     SEEK_REPEAT = -3
 };
 
-class PlayClass final : public QObject
+class PlayClass : public QObject
 {
     Q_OBJECT
     friend class DemuxerThr;
@@ -53,7 +53,7 @@ class PlayClass final : public QObject
     friend class AudioThr;
 public:
     PlayClass();
-    ~PlayClass();
+    ~PlayClass() final;
 
     Q_SLOT void play(const QString &);
     Q_SLOT void stop(bool quitApp = false);
diff --git a/src/gui/PlaylistDock.cpp b/src/gui/PlaylistDock.cpp
index f2872ab0..29e5de72 100644
--- a/src/gui/PlaylistDock.cpp
+++ b/src/gui/PlaylistDock.cpp
@@ -56,7 +56,7 @@ PlaylistDock::PlaylistDock() :
 
     connect(list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem *)));
     connect(list, SIGNAL(returnItem(QTreeWidgetItem *)), this, SLOT(addAndPlay(QTreeWidgetItem *)));
-    connect(list, &PlaylistWidget::itemExpanded, this, &PlaylistDock::maybeDoQuickSync, Qt::QueuedConnection); // Must be queued to not crash at startup in some cases
+    connect(list, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(maybeDoQuickSync(QTreeWidgetItem *)), Qt::QueuedConnection);
     connect(list, SIGNAL(visibleItemsCount(int)), this, SLOT(visibleItemsCount(int)));
     connect(list, SIGNAL(addStatus(bool)), findE, SLOT(setDisabled(bool)));
     connect(findE, SIGNAL(textChanged(const QString &)), this, SLOT(findItems(const QString &)));
diff --git a/src/gui/PlaylistWidget.cpp b/src/gui/PlaylistWidget.cpp
index 87164c7c..3c3a38bc 100644
--- a/src/gui/PlaylistWidget.cpp
+++ b/src/gui/PlaylistWidget.cpp
@@ -57,20 +57,6 @@ static inline MenuBar::Playlist *playlistMenu()
     return QMPlay2GUI.menuBar->playlist;
 }
 
-/* PlaylistItem class */
-class PlaylistItem : public QTreeWidgetItem
-{
-public:
-    bool operator <(const QTreeWidgetItem &other) const override
-    {
-        if (treeWidget() && treeWidget()->sortColumn() == 2)
-        {
-            return (data(2, Qt::UserRole) < other.data(2, Qt::UserRole));
-        }
-        return QTreeWidgetItem::operator <(other);
-    }
-};
-
 /* UpdateEntryThr class */
 UpdateEntryThr::UpdateEntryThr(PlaylistWidget &pLW) :
     pendingUpdates(0),
@@ -595,7 +581,7 @@ PlaylistWidget::PlaylistWidget() :
     setAnimated(true);
     header()->setStretchLastSection(false);
     setHeaderHidden(true);
-    header()->setSectionResizeMode(0, QHeaderView::Stretch);
+    header()->setResizeMode(0, QHeaderView::Stretch);
     header()->hideSection(1);
     setItemsResizeToContents(true);
     setIconSize({22, 22});
@@ -608,7 +594,7 @@ PlaylistWidget::PlaylistWidget() :
     connect(&animationTimer, SIGNAL(timeout()), this, SLOT(animationUpdate()));
     connect(&addTimer, SIGNAL(timeout()), this, SLOT(addTimerElapsed()));
     connect(&addThr, SIGNAL(status(bool)), this, SIGNAL(addStatus(bool)));
-    connect(playlistMenu(), &MenuBar::Playlist::aboutToShow, this, &PlaylistWidget::createExtensionsMenu);
+    connect(playlistMenu(), SIGNAL(aboutToShow()), this, SLOT(createExtensionsMenu()));
 }
 
 QString PlaylistWidget::getUrl(QTreeWidgetItem *tWI) const
@@ -622,7 +608,7 @@ void PlaylistWidget::setItemsResizeToContents(bool b)
 {
     const QHeaderView::ResizeMode rm = b ? QHeaderView::ResizeToContents : QHeaderView::Fixed;
     for (int i = 1; i <= 2; ++i)
-        header()->setSectionResizeMode(i, rm);
+        header()->setResizeMode(i, rm);
 }
 
 void PlaylistWidget::sortCurrentGroup(int column, Qt::SortOrder sortOrder)
@@ -915,7 +901,7 @@ void PlaylistWidget::setEntryFont(QTreeWidgetItem *tWI, const int flags)
 
 QTreeWidgetItem *PlaylistWidget::newGroup(const QString &name, const QString &url, QTreeWidgetItem *parent, int insertChildAt, QStringList *existingEntries)
 {
-    QTreeWidgetItem *tWI = new PlaylistItem;
+    QTreeWidgetItem *tWI = new QTreeWidgetItem;
 
     tWI->setFlags(tWI->flags() | Qt::ItemIsEditable);
     QMPlay2GUI.setTreeWidgetItemIcon(tWI, url.isEmpty() ? *QMPlay2GUI.groupIcon : *QMPlay2GUI.folderIcon, 0, this);
@@ -928,9 +914,10 @@ QTreeWidgetItem *PlaylistWidget::newGroup(const QString &name, const QString &ur
     QMetaObject::invokeMethod(this, "insertItem", Q_ARG(QTreeWidgetItem *, tWI), Q_ARG(QTreeWidgetItem *, parent), Q_ARG(int, insertChildAt));
     return tWI;
 }
+
 QTreeWidgetItem *PlaylistWidget::newEntry(const Playlist::Entry &entry, QTreeWidgetItem *parent, const Functions::DemuxersInfo &demuxersInfo, int insertChildAt, QStringList *existingEntries)
 {
-    QTreeWidgetItem *tWI = new PlaylistItem;
+    QTreeWidgetItem *tWI = new QTreeWidgetItem;
 
     QIcon icon;
     Functions::getDataIfHasPluginPrefix(entry.url, nullptr, nullptr, &icon, nullptr, demuxersInfo);
diff --git a/src/gui/PlaylistWidget.hpp b/src/gui/PlaylistWidget.hpp
index 1f20e523..dfbd11c1 100644
--- a/src/gui/PlaylistWidget.hpp
+++ b/src/gui/PlaylistWidget.hpp
@@ -34,7 +34,7 @@ class QTreeWidgetItem;
 class PlaylistWidget;
 class Demuxer;
 
-class UpdateEntryThr final : public QThread
+class UpdateEntryThr : public QThread
 {
     Q_OBJECT
 
@@ -51,7 +51,7 @@ public:
     }
 
 private:
-    void run() override;
+    void run() override final;
 
     QAtomicInt pendingUpdates;
     IOController<> ioCtrl;
@@ -84,7 +84,7 @@ private slots:
     void finished();
 };
 
-class AddThr final : public QThread
+class AddThr : public QThread
 {
     Q_OBJECT
 public:
@@ -101,7 +101,7 @@ private slots:
     void modifyItemFlags(QTreeWidgetItem *tWI, int flags);
     void deleteTreeWidgetItem(QTreeWidgetItem *tWI);
 private:
-    void run() override;
+    void run() override final;
 
     bool add(const QStringList &urls, QTreeWidgetItem *parent, const Functions::DemuxersInfo &demuxersInfo, QStringList *existingEntries = nullptr, bool loadList = false);
     QTreeWidgetItem *insertPlaylistEntries(const Playlist::Entries &entries, QTreeWidgetItem *parent, const Functions::DemuxersInfo &demuxersInfo, int insertChildAt, QStringList *existingEntries);
@@ -120,7 +120,7 @@ signals:
     void status(bool s);
 };
 
-class PlaylistWidget final : public QTreeWidget
+class PlaylistWidget : public QTreeWidget
 {
     friend class AddThr;
     friend class UpdateEntryThr;
@@ -194,14 +194,12 @@ private:
 
     void quickSyncScanDirs(const QString &pth, QTreeWidgetItem *par, bool &mustRefresh, bool recursive, QTreeWidgetItem *&itemToNull);
 
-    void createExtensionsMenu();
-
-    void mouseMoveEvent(QMouseEvent *) override;
-    void dragEnterEvent(QDragEnterEvent *) override;
-    void dragMoveEvent(QDragMoveEvent *) override;
-    void dropEvent(QDropEvent *) override;
-    void paintEvent(QPaintEvent *) override;
-    void scrollContentsBy(int dx, int dy) override;
+    void mouseMoveEvent(QMouseEvent *) override final;
+    void dragEnterEvent(QDragEnterEvent *) override final;
+    void dragMoveEvent(QDragMoveEvent *) override final;
+    void dropEvent(QDropEvent *) override final;
+    void paintEvent(QPaintEvent *) override final;
+    void scrollContentsBy(int dx, int dy) override final;
 
     QRect getArcRect(int size);
 
@@ -226,8 +224,10 @@ private slots:
     void addTimerElapsed();
 public slots:
     void modifyMenu();
+    void createExtensionsMenu();
 signals:
     void returnItem(QTreeWidgetItem *);
     void visibleItemsCount(int);
     void addStatus(bool s);
+    void aboutToShow();
 };
diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp
index d5b5a702..32809a17 100644
--- a/src/gui/SettingsWidget.cpp
+++ b/src/gui/SettingsWidget.cpp
@@ -26,7 +26,7 @@
 #include <Notifies.hpp>
 #include <Main.hpp>
 
-#include <QStandardPaths>
+#include <QDesktopServices>
 #include <QStyleFactory>
 #include <QRadioButton>
 #include <QApplication>
@@ -55,6 +55,8 @@
 #include <MenuBar.hpp>
 #include <Module.hpp>
 
+#include <stdlib.h>
+
 #include "ui_SettingsGeneral.h"
 #include "ui_SettingsPlayback.h"
 #include "ui_SettingsPlaybackModulesList.h"
@@ -124,7 +126,7 @@ void SettingsWidget::InitSettings()
 
     QMPSettings.init("AudioLanguage", QString());
     QMPSettings.init("SubtitlesLanguage", QString());
-    QMPSettings.init("screenshotPth", QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QDir::homePath()));
+    QMPSettings.init("screenshotPth", QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
 #ifdef Q_OS_WIN
     QMPSettings.init("screenshotFormat", ".bmp");
 #else
@@ -230,6 +232,31 @@ void SettingsWidget::SetAudioChannels(int chn)
     QMPlay2Core.getSettings().set("ForceChannels", forceChannels);
 }
 
+void SettingsWidget::onStoreARatioAndZoomToggled(bool checked)
+{
+    if (checked)
+    {
+        page2->keepZoom->setChecked(true);
+        page2->keepARatio->setChecked(true);
+    }
+}
+
+void SettingsWidget::onKeepZoomToggled(bool checked)
+{
+    if (!checked && !page2->keepARatio->isChecked())
+    {
+        page2->storeARatioAndZoomB->setChecked(false);
+    }
+}
+
+void SettingsWidget::onKeepARatioToggled(bool checked)
+{
+    if (!checked && !page2->keepZoom->isChecked())
+    {
+        page2->storeARatioAndZoomB->setChecked(false);
+    }
+}
+
 SettingsWidget::SettingsWidget(int page, const QString &moduleName, QWidget *videoEq) :
     videoEq(videoEq), videoEqOriginalParent(videoEq->parentWidget()),
     wasShow(false),
@@ -451,29 +478,13 @@ SettingsWidget::SettingsWidget(int page, const QString &moduleName, QWidget *vid
         page2->wheelVolumeB->setChecked(QMPSettings.getBool("WheelVolume"));
 
         page2->storeARatioAndZoomB->setChecked(QMPSettings.getBool("StoreARatioAndZoom"));
-        connect(page2->storeARatioAndZoomB, &QCheckBox::toggled, this, [this](bool checked) {
-            if (checked)
-            {
-                page2->keepZoom->setChecked(true);
-                page2->keepARatio->setChecked(true);
-            }
-        });
+        connect(page2->storeARatioAndZoomB, SIGNAL(toggled(bool)), this, SLOT(onStoreARatioAndZoomToggled(bool)));
 
         page2->keepZoom->setChecked(QMPSettings.getBool("KeepZoom"));
-        connect(page2->keepZoom, &QCheckBox::toggled, this, [this](bool checked) {
-            if (!checked && !page2->keepARatio->isChecked())
-            {
-                page2->storeARatioAndZoomB->setChecked(false);
-            }
-        });
+        connect(page2->keepZoom, SIGNAL(toggled(bool)), this, SLOT(onKeepZoomToggled(bool)));
 
         page2->keepARatio->setChecked(QMPSettings.getBool("KeepARatio"));
-        connect(page2->keepARatio, &QCheckBox::toggled, this, [this](bool checked) {
-            if (!checked && !page2->keepZoom->isChecked())
-            {
-                page2->storeARatioAndZoomB->setChecked(false);
-            }
-        });
+        connect(page2->keepARatio, SIGNAL(toggled(bool)), this, SLOT(onKeepARatioToggled(bool)));
 
         page2->showBufferedTimeOnSlider->setChecked(QMPSettings.getBool("ShowBufferedTimeOnSlider"));
         page2->savePos->setChecked(QMPSettings.getBool("SavePos"));
@@ -677,7 +688,7 @@ void SettingsWidget::applyProxy()
     Settings &QMPSettings = QMPlay2Core.getSettings();
     if (!QMPSettings.getBool("Proxy/Use"))
     {
-        qunsetenv("http_proxy");
+        unsetenv("http_proxy");
     }
     else
     {
diff --git a/src/gui/SettingsWidget.hpp b/src/gui/SettingsWidget.hpp
index b36ac00c..0047b120 100644
--- a/src/gui/SettingsWidget.hpp
+++ b/src/gui/SettingsWidget.hpp
@@ -35,7 +35,7 @@ namespace Ui {
     class ModulesList;
 }
 
-class SettingsWidget final : public QWidget
+class SettingsWidget : public QWidget
 {
     Q_OBJECT
 public:
@@ -44,7 +44,7 @@ public:
     static void SetAudioChannels(int chn);
 
     SettingsWidget(int page, const QString &module, QWidget *videoEq);
-    ~SettingsWidget();
+    ~SettingsWidget() final;
 
     void setAudioChannels();
 private:
@@ -56,8 +56,8 @@ private:
 
     inline QString getSelectedProfile();
 
-    void showEvent(QShowEvent *) override;
-    void closeEvent(QCloseEvent *) override;
+    void showEvent(QShowEvent *) override final;
+    void closeEvent(QCloseEvent *) override final;
 
     Ui::GeneralSettings *page1;
     Ui::PlaybackSettings *page2;
@@ -89,6 +89,9 @@ private slots:
     void resetSettings();
     void profileListIndexChanged(int index);
     void removeProfile();
+    void onStoreARatioAndZoomToggled(bool checked);
+    void onKeepZoomToggled(bool checked);
+    void onKeepARatioToggled(bool checked);
 signals:
     void settingsChanged(int, bool);
     void setWheelStep(int);
diff --git a/src/gui/ShortcutHandler.hpp b/src/gui/ShortcutHandler.hpp
index 1723eb78..ad140325 100644
--- a/src/gui/ShortcutHandler.hpp
+++ b/src/gui/ShortcutHandler.hpp
@@ -23,23 +23,23 @@
 
 class QAction;
 
-class ShortcutHandler final : public QAbstractTableModel
+class ShortcutHandler : public QAbstractTableModel
 {
     Q_DECLARE_TR_FUNCTIONS(ShortcutHandler)
 
 public:
     ShortcutHandler(QObject *parent);
-    ~ShortcutHandler();
+    ~ShortcutHandler() final;
 
-    int columnCount(const QModelIndex &parent) const override;
-    int rowCount(const QModelIndex &parent) const override;
+    int columnCount(const QModelIndex &parent) const override final;
+    int rowCount(const QModelIndex &parent) const override final;
 
-    Qt::ItemFlags flags(const QModelIndex &index) const override;
+    Qt::ItemFlags flags(const QModelIndex &index) const override final;
 
-    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+    QVariant headerData(int section, Qt::Orientation orientation, int role) const override final;
 
-    QVariant data(const QModelIndex &index, int role) const override;
-    bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+    QVariant data(const QModelIndex &index, int role) const override final;
+    bool setData(const QModelIndex &index, const QVariant &value, int role) override final;
 
     void appendAction(QAction *action, const QString &settingsName, const QString &defaultShortcut);
 
diff --git a/src/gui/TagEditor.cpp b/src/gui/TagEditor.cpp
index e780f555..00d82972 100644
--- a/src/gui/TagEditor.cpp
+++ b/src/gui/TagEditor.cpp
@@ -26,6 +26,11 @@
 #define TAGLIB19 (TAGLIB_VERSION >= 0x109)
 #define TAGLIB1B (TAGLIB_VERSION >= 0x10B)
 
+/* Find taglib */
+#ifndef TAGLIB_FULL_INCLUDE_PATH
+#define TAGLIB_FULL_INCLUDE_PATH
+#endif
+
 #ifdef TAGLIB_FULL_INCLUDE_PATH
     #include <taglib/taglib.h>
 #else
diff --git a/src/gui/TagEditor.hpp b/src/gui/TagEditor.hpp
index 24edcf0e..283c1497 100644
--- a/src/gui/TagEditor.hpp
+++ b/src/gui/TagEditor.hpp
@@ -29,12 +29,12 @@ class QLineEdit;
 class QSpinBox;
 class QLabel;
 
-class PictureW final : public QWidget
+class PictureW : public QWidget
 {
 public:
     PictureW(TagLib::ByteVector &picture);
 private:
-    void paintEvent(QPaintEvent *) override;
+    void paintEvent(QPaintEvent *) override final;
 
     TagLib::ByteVector &picture;
 };
@@ -44,7 +44,7 @@ class TagEditor : public QGroupBox
     Q_OBJECT
 public:
     TagEditor();
-    ~TagEditor();
+    ~TagEditor() final;
 
     bool open(const QString &fileName);
     void clear();
diff --git a/src/gui/Updater.hpp b/src/gui/Updater.hpp
index 027ce819..a18d1158 100644
--- a/src/gui/Updater.hpp
+++ b/src/gui/Updater.hpp
@@ -30,7 +30,7 @@
     class QLabel;
 #endif
 
-class Updater final : public
+class Updater : public
 #ifdef UPDATER
     QDialog
 #else
@@ -45,7 +45,7 @@ public:
 #else
     Updater(QObject *parent);
 #endif
-    ~Updater();
+    ~Updater() final;
 
 #ifdef UPDATER
     bool downloading() const;
diff --git a/src/gui/VideoAdjustmentW.cpp b/src/gui/VideoAdjustmentW.cpp
index d3f71cca..ad9d1af0 100644
--- a/src/gui/VideoAdjustmentW.cpp
+++ b/src/gui/VideoAdjustmentW.cpp
@@ -27,7 +27,7 @@
 #include <QGridLayout>
 #include <QPushButton>
 #include <QAction>
-#include <QLabel>
+#include <QVariant>
 
 enum CONTROLS
 {
@@ -69,37 +69,31 @@ VideoAdjustmentW::VideoAdjustmentW()
         slider->setRange(-100, 100);
         slider->setWheelStep(1);
         slider->setValue(0);
-        connect(slider, &Slider::valueChanged, this, [=](int v) {
-            valueL->setText(QString::number(v));
-            emit videoAdjustmentChanged(titleL->text() + QString::number(v));
-        });
+
+        connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int)));
+
         m_sliders.push_back(slider);
 
         QAction *actionDown = new QAction(this);
-        connect(actionDown, &QAction::triggered, this, [=] {
-            slider->setValue(slider->value() - g_step);
-        });
-
         QAction *actionUp = new QAction(this);
-        connect(actionUp, &QAction::triggered, this, [=] {
-            slider->setValue(slider->value() + g_step);
-        });
+
+        connect(actionDown, SIGNAL(triggered()), this, SLOT(onActionDownTriggered()));
+        connect(actionUp, SIGNAL(triggered()), this, SLOT(onActionUpTriggered()));
 
         m_actions.push_back({actionDown, actionUp});
 
         layout->addWidget(titleL, i, 0);
         layout->addWidget(slider, i, 1);
         layout->addWidget(valueL, i, 2);
+
+        m_labelMap[slider] = std::make_pair(valueL, titleL);
     }
 
     QPushButton *resetB = new QPushButton(tr("Reset"));
-    connect(resetB, &QPushButton::clicked, this, [this] {
-        for (int i = 0; i < CONTROLS_COUNT; ++i)
-            m_sliders[i]->setValue(0);
-    });
+    connect(resetB, SIGNAL(clicked()), this, SLOT(onResetClicked()));
 
     m_resetAction = new QAction(tr("Reset video adjustments"), this);
-    connect(m_resetAction, &QAction::triggered, resetB, &QPushButton::click);
+    connect(m_resetAction, SIGNAL(triggered()), resetB, SLOT(click()));
 
     layout->addWidget(resetB, layout->rowCount(), 0, 1, 3);
     layout->addItem(new QSpacerItem(40, 0, QSizePolicy::Maximum, QSizePolicy::Minimum), layout->rowCount(), 2);
@@ -114,6 +108,7 @@ void VideoAdjustmentW::restoreValues()
     for (int i = 0; i < CONTROLS_COUNT; ++i)
         m_sliders[i]->setValue(QMPlay2Core.getSettings().getInt(QString("VideoAdjustment/") + g_controlsNames[i]));
 }
+
 void VideoAdjustmentW::saveValues()
 {
     for (int i = 0; i < CONTROLS_COUNT; ++i)
@@ -162,6 +157,7 @@ void VideoAdjustmentW::setKeyShortcuts()
 
     appendAction(m_resetAction, QString(), "reset", "0");
 }
+
 void VideoAdjustmentW::addActionsToWidget(QWidget *w)
 {
     for (int i = 0; i < CONTROLS_COUNT; ++i)
@@ -171,3 +167,45 @@ void VideoAdjustmentW::addActionsToWidget(QWidget *w)
     }
     w->addAction(m_resetAction);
 }
+
+void VideoAdjustmentW::onSliderValueChanged(int value)
+{
+    Slider *slider = qobject_cast<Slider *>(sender());
+    if (slider && m_labelMap.count(slider)) {
+        QLabel *valueL = m_labelMap[slider].first;
+        QLabel *titleL = m_labelMap[slider].second;
+        valueL->setText(QString::number(value));
+        emit videoAdjustmentChanged(titleL->text() + QString::number(value));
+    }
+}
+
+void VideoAdjustmentW::onActionDownTriggered()
+{
+    QAction *action = qobject_cast<QAction *>(sender());
+    for (size_t i = 0; i < m_actions.size(); ++i) {
+        if (m_actions[i][0] == action) { // Use array index 0 for "first"
+            Slider *slider = m_sliders[i];
+            slider->setValue(slider->value() - g_step);
+            break;
+        }
+    }
+}
+
+void VideoAdjustmentW::onActionUpTriggered()
+{
+    QAction *action = qobject_cast<QAction *>(sender());
+    for (size_t i = 0; i < m_actions.size(); ++i) {
+        if (m_actions[i][1] == action) { // Use array index 1 for "second"
+            Slider *slider = m_sliders[i];
+            slider->setValue(slider->value() + g_step);
+            break;
+        }
+    }
+}
+
+void VideoAdjustmentW::onResetClicked()
+{
+    for (int i = 0; i < CONTROLS_COUNT; ++i) {
+        m_sliders[i]->setValue(0);
+    }
+}
diff --git a/src/gui/VideoAdjustmentW.hpp b/src/gui/VideoAdjustmentW.hpp
index 419ebb01..9e9c1ece 100644
--- a/src/gui/VideoAdjustmentW.hpp
+++ b/src/gui/VideoAdjustmentW.hpp
@@ -18,22 +18,25 @@
 
 #pragma once
 
+#include <QLabel>
 #include <QWidget>
 
-#include <vector>
 #include <array>
+#include <map>
+#include <utility>
+#include <vector>
 
 class ModuleParams;
 class QAction;
 class Slider;
 
-class VideoAdjustmentW final : public QWidget
+class VideoAdjustmentW : public QWidget
 {
     Q_OBJECT
 
 public:
     VideoAdjustmentW();
-    ~VideoAdjustmentW();
+    ~VideoAdjustmentW() final;
 
     void restoreValues();
     void saveValues();
@@ -51,4 +54,11 @@ private:
     std::vector<Slider *> m_sliders;
     std::vector<std::array<QAction *, 2>> m_actions;
     QAction *m_resetAction = nullptr;
+    std::map<Slider *, std::pair<QLabel *, QLabel *>> m_labelMap;
+
+private slots:
+    void onSliderValueChanged(int value);
+    void onActionDownTriggered();
+    void onActionUpTriggered();
+    void onResetClicked();
 };
diff --git a/src/gui/VideoDock.cpp b/src/gui/VideoDock.cpp
index 74485f76..e90c5773 100644
--- a/src/gui/VideoDock.cpp
+++ b/src/gui/VideoDock.cpp
@@ -38,6 +38,12 @@
 
 constexpr int g_hideCursorTimeout = 750;
 
+void VideoDock::onDockVideo(QWidget *w)
+{
+    iDW.setWidget(w);
+    mouseMoveEvent(nullptr);
+}
+
 VideoDock::VideoDock() :
     isTouch(false), touchEnded(false),
     iDW(QMPlay2GUI.grad1, QMPlay2GUI.grad2, QMPlay2GUI.qmpTxt),
@@ -88,10 +94,7 @@ VideoDock::VideoDock() :
     connect(&iDW, SIGNAL(resized(int, int)), this, SLOT(resizedIDW(int, int)));
     connect(&iDW, SIGNAL(hasCoverImage(bool)), this, SLOT(hasCoverImage(bool)));
     connect(this, SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityChanged(bool)));
-    connect(&QMPlay2Core, &QMPlay2CoreClass::dockVideo, this, [this](QWidget *w) {
-        iDW.setWidget(w);
-        mouseMoveEvent(nullptr);
-    });
+    connect(&QMPlay2Core, SIGNAL(dockVideo(QWidget *)), this, SLOT(onDockVideo(QWidget *)));
 
     if ((isBreeze = QApplication::style()->objectName() == "breeze"))
         setStyle(&commonStyle);
diff --git a/src/gui/VideoDock.hpp b/src/gui/VideoDock.hpp
index bd26b3f9..c19f4627 100644
--- a/src/gui/VideoDock.hpp
+++ b/src/gui/VideoDock.hpp
@@ -26,7 +26,7 @@
 
 class QMenu;
 
-class VideoDock final : public DockWidget
+class VideoDock : public DockWidget
 {
     Q_OBJECT
 public:
@@ -49,17 +49,17 @@ private:
 
     void unsetCursor(QWidget *w);
 
-    void dragEnterEvent(QDragEnterEvent *) override;
-    void dropEvent(QDropEvent *) override;
-    void mouseMoveEvent(QMouseEvent *) override;
-    void mouseDoubleClickEvent(QMouseEvent *) override;
-    void mousePressEvent(QMouseEvent *) override;
-    void mouseReleaseEvent(QMouseEvent *) override;
-    void moveEvent(QMoveEvent *) override;
-    void wheelEvent(QWheelEvent *) override;
-    void leaveEvent(QEvent *) override;
-    void enterEvent(QEvent *) override;
-    bool event(QEvent *) override;
+    void dragEnterEvent(QDragEnterEvent *) override final;
+    void dropEvent(QDropEvent *) override final;
+    void mouseMoveEvent(QMouseEvent *) override final;
+    void mouseDoubleClickEvent(QMouseEvent *) override final;
+    void mousePressEvent(QMouseEvent *) override final;
+    void mouseReleaseEvent(QMouseEvent *) override final;
+    void moveEvent(QMoveEvent *) override final;
+    void wheelEvent(QWheelEvent *) override final;
+    void leaveEvent(QEvent *) override final;
+    void enterEvent(QEvent *) override final;
+    bool event(QEvent *) override final;
 
     QTimer hideCursorTim, leftButtonPlayTim;
     InDockW iDW;
@@ -76,6 +76,7 @@ private slots:
     void updateImage(const QImage &);
     void visibilityChanged(bool);
     void hasCoverImage(bool);
+    void onDockVideo(QWidget *w);
 signals:
     void resized(int, int);
     void itemDropped(const QString &, bool);
diff --git a/src/gui/VideoThr.cpp b/src/gui/VideoThr.cpp
index 83c03089..85a503a8 100644
--- a/src/gui/VideoThr.cpp
+++ b/src/gui/VideoThr.cpp
@@ -181,7 +181,7 @@ void VideoThr::initFilters(bool processParams)
     {
         for (QString filterName : QMPSettings.getStringList("VideoFilters"))
         {
-            if (filterName.leftRef(1).toInt()) //if filter is enabled
+            if (filterName.left(1).toInt()) // if filter is enabled
             {
                 VideoFilter *filter = filters.on((filterName = filterName.mid(1)));
                 bool ok = false;
@@ -231,7 +231,7 @@ inline VideoWriter *VideoThr::videoWriter() const
 
 void VideoThr::run()
 {
-    bool skip = false, paused = false, oneFrame = false, useLastDelay = false, lastOSDListEmpty = true, maybeFlush = false, lastAVDesync = false, interlaced = false, err = false;
+    bool skip = false, paused = false, oneFrame = false, useLastDelay = false, lastOSDListEmpty = true, maybeFlush = false, lastAVDesync = false, interlaced = false, err = false, skipNonKey = false;
     double tmp_time = 0.0, sync_last_pts = 0.0, frame_timer = -1.0, sync_timer = 0.0, framesDisplayedTime = 0.0;
     QMutex emptyBufferMutex;
     VideoFrame videoFrame;
@@ -398,17 +398,18 @@ void VideoThr::run()
         /**/
 
         filtersMutex.lock();
-        if (playC.flushVideo)
+        if (playC.flushVideo || skipNonKey)
         {
             filters.clearBuffers();
-            frame_timer = -1.0;
+            if (playC.flushVideo)
+                frame_timer = -1.0;
         }
 
-        if (!packet.isEmpty() || maybeFlush)
+        if ((!packet.isEmpty() || maybeFlush) && (!skipNonKey || packet.hasKeyFrame))
         {
             VideoFrame decoded;
             QByteArray newPixelFormat;
-            const int bytes_consumed = dec->decodeVideo(packet, decoded, newPixelFormat, playC.flushVideo, skip ? ~0 : (fast >> 1));
+            const int bytes_consumed = dec->decodeVideo(packet, decoded, newPixelFormat, playC.flushVideo || skipNonKey, (skip && !skipNonKey) ? ~0 : (fast >> 1));
             if (!newPixelFormat.isEmpty())
                 emit playC.pixelFormatUpdate(newPixelFormat);
             if (playC.flushVideo)
@@ -437,7 +438,9 @@ void VideoThr::run()
                 gotFrameOrError = true;
             }
             else if (skip)
+            {
                 filters.removeLastFromInputBuffer();
+            }
             if (bytes_consumed < 0)
             {
                 gotFrameOrError = true;
@@ -447,6 +450,7 @@ void VideoThr::run()
             {
                 tmp_br += bytes_consumed;
             }
+            skipNonKey = false;
         }
 
         // This thread will wait for "DemuxerThr" which'll detect this error and restart with new decoder.
@@ -485,7 +489,7 @@ void VideoThr::run()
                 playC.chPos(packet.ts);
 
             double delay = packet.ts - playC.frame_last_pts;
-            if (useLastDelay || delay <= 0.0 || (playC.frame_last_pts <= 0.0 && delay > playC.frame_last_delay))
+            if (useLastDelay || delay <= 0.0 || (playC.frame_last_pts <= 0.0 && delay > playC.frame_last_delay) || (delay > 1.0 && delay / playC.frame_last_delay > 10.0))
             {
                 delay = playC.frame_last_delay;
                 useLastDelay = false;
@@ -494,11 +498,11 @@ void VideoThr::run()
             tmp_time += delay;
             ++frames;
 
-            delay /= playC.speed;
-
             playC.frame_last_delay = delay;
             playC.frame_last_pts = packet.ts;
 
+            delay /= playC.speed;
+
             if (playC.skipAudioFrame < 0.0)
                 playC.skipAudioFrame = 0.0;
 
@@ -559,6 +563,8 @@ void VideoThr::run()
                         delay = 0.0;
                         if (fast >= 7)
                             skip = true;
+                        if (fast >= 56 || (fast >= 28 && fDiff >= max_threshold * 4.0))
+                            skipNonKey = true;
                     }
                     else if (diff > 0.0) //obraz idzie za szybko
                     {
@@ -616,7 +622,7 @@ void VideoThr::run()
                 {
                     oneFrame = canWrite = false;
                     QMetaObject::invokeMethod(this, "write", Q_ARG(VideoFrame, videoFrame), Q_ARG(quint32, seq));
-                    if (canSkipFrames)
+                    if (canSkipFrames && !skipNonKey)
                         ++framesDisplayed;
                 }
                 if (canSkipFrames)
@@ -660,7 +666,7 @@ void VideoThr::screenshot(VideoFrame videoFrame)
             quint16 num = 0;
             for (const QString &f : QDir(dir).entryList({"QMPlay2_snap_?????" + ext}, QDir::Files, QDir::Name))
             {
-                const quint16 n = f.midRef(13, 5).toUShort();
+                const quint16 n = f.mid(13, 5).toUShort();
                 if (n > num)
                     num = n;
             }
diff --git a/src/gui/VideoThr.hpp b/src/gui/VideoThr.hpp
index 728edce8..6713829f 100644
--- a/src/gui/VideoThr.hpp
+++ b/src/gui/VideoThr.hpp
@@ -25,16 +25,16 @@
 class QMPlay2OSD;
 class VideoWriter;
 
-class VideoThr final : public AVThread
+class VideoThr : public AVThread
 {
     Q_OBJECT
 public:
     VideoThr(PlayClass &, VideoWriter *, const QStringList &pluginsName = {});
-    ~VideoThr();
+    ~VideoThr() final;
 
-    void stop(bool terminate = false) override;
+    void stop(bool terminate = false) override final;
 
-    bool hasDecoderError() const override;
+    bool hasDecoderError() const override final;
 
     inline VideoWriter *getHWAccelWriter() const
     {
@@ -80,7 +80,7 @@ public:
 private:
     inline VideoWriter *videoWriter() const;
 
-    void run() override;
+    void run() override final;
 
     bool deleteSubs, syncVtoA, doScreenshot, canWrite, deleteOSD, deleteFrame, gotFrameOrError, decoderError;
     double lastSampleAspectRatio;
diff --git a/src/gui/macOS/ScreenSaver.cpp b/src/gui/macOS/ScreenSaver.cpp
index 0165d5cc..d71b25b7 100644
--- a/src/gui/macOS/ScreenSaver.cpp
+++ b/src/gui/macOS/ScreenSaver.cpp
@@ -20,6 +20,10 @@
 
 #include <IOKit/pwr_mgt/IOPMLib.h>
 
+#ifdef __APPLE__
+    #include <AvailabilityMacros.h>
+#endif
+
 #define QMPLAY2_MEDIA_PLAYBACK CFSTR("QMPlay2 media playback")
 
 class ScreenSaverPriv
@@ -32,8 +36,13 @@ public:
 
     inline void inhibit()
     {
-        m_okDisp = (IOPMAssertionCreateWithName(kIOPMAssertPreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, QMPLAY2_MEDIA_PLAYBACK, &m_idDisp) == kIOReturnSuccess);
-        m_okSys  = (IOPMAssertionCreateWithName(kIOPMAssertPreventUserIdleSystemSleep,  kIOPMAssertionLevelOn, QMPLAY2_MEDIA_PLAYBACK, &m_idSys)  == kIOReturnSuccess);
+#if defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1060)
+        m_okDisp = (IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &m_idDisp) == kIOReturnSuccess);
+        m_okSys  = (IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &m_idSys)  == kIOReturnSuccess);
+#else
+        m_okDisp = (IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, QMPLAY2_MEDIA_PLAYBACK, &m_idDisp) == kIOReturnSuccess);
+        m_okSys  = (IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, QMPLAY2_MEDIA_PLAYBACK, &m_idSys) == kIOReturnSuccess);
+#endif
     }
     inline void unInhibit()
     {
diff --git a/src/modules/AudioCD/AudioCD.hpp b/src/modules/AudioCD/AudioCD.hpp
index 95065ae0..bd19f789 100644
--- a/src/modules/AudioCD/AudioCD.hpp
+++ b/src/modules/AudioCD/AudioCD.hpp
@@ -24,19 +24,19 @@
 
 class CDIODestroyTimer;
 
-class AudioCD final : public Module
+class AudioCD : public Module
 {
     Q_OBJECT
 public:
     AudioCD();
-    ~AudioCD();
+    ~AudioCD() final;
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    QList<QAction *> getAddActions() override;
+    QList<QAction *> getAddActions() override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 
     QIcon CD;
 
@@ -52,13 +52,13 @@ class QGridLayout;
 class QGroupBox;
 class QCheckBox;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_DECLARE_TR_FUNCTIONS(ModuleSettingsWidget)
 public:
     ModuleSettingsWidget(Module &);
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     QGroupBox *audioCDB;
     QCheckBox *useCDDB, *useCDTEXT;
diff --git a/src/modules/AudioCD/AudioCDDemux.hpp b/src/modules/AudioCD/AudioCDDemux.hpp
index 3a0c6f3d..d98bc518 100644
--- a/src/modules/AudioCD/AudioCDDemux.hpp
+++ b/src/modules/AudioCD/AudioCDDemux.hpp
@@ -26,19 +26,19 @@
 #include <cdio/cdio.h>
 #include <cddb/cddb.h>
 
-class CDIODestroyTimer final : public QObject
+class CDIODestroyTimer : public QObject
 {
     Q_OBJECT
 public:
     CDIODestroyTimer();
-    ~CDIODestroyTimer();
+    ~CDIODestroyTimer() final;
 
     Q_SIGNAL void setInstance(CdIo_t *_cdio, const QString &_device, unsigned _discID);
     CdIo_t *getInstance(const QString &_device, unsigned &_discID);
 private slots:
     void setInstanceSlot(CdIo_t *_cdio, const QString &_device, unsigned _discID);
 private:
-    void timerEvent(QTimerEvent *e) override;
+    void timerEvent(QTimerEvent *e) override final;
 
     QAtomicInt timerId;
     CdIo_t *cdio;
@@ -48,7 +48,7 @@ private:
 
 /**/
 
-class AudioCDDemux final : public Demuxer
+class AudioCDDemux : public Demuxer
 {
     Q_DECLARE_TR_FUNCTIONS(AudioCDDemux)
 public:
@@ -56,23 +56,23 @@ public:
 
     AudioCDDemux(Module &, CDIODestroyTimer &destroyTimer);
 private:
-    ~AudioCDDemux();
+    ~AudioCDDemux() final;
 
-    bool set() override;
+    bool set() override final;
 
-    QString name() const override;
-    QString title() const override;
-    QList<QMPlay2Tag> tags() const override;
-    double length() const override;
-    int bitrate() const override;
+    QString name() const override final;
+    QString title() const override final;
+    QList<QMPlay2Tag> tags() const override final;
+    double length() const override final;
+    int bitrate() const override final;
 
-    bool seek(double, bool) override;
-    bool read(Packet &, int & ) override;
-    void abort() override;
+    bool seek(double, bool) override final;
+    bool read(Packet &, int & ) override final;
+    void abort() override final;
 
-    bool open(const QString &) override;
+    bool open(const QString &) override final;
 
-    Playlist::Entries fetchTracks(const QString &url, bool &ok) override;
+    Playlist::Entries fetchTracks(const QString &url, bool &ok) override final;
 
     /**/
 
diff --git a/src/modules/AudioFilters/AudioFilters.hpp b/src/modules/AudioFilters/AudioFilters.hpp
index 6823f625..02d3d1c1 100644
--- a/src/modules/AudioFilters/AudioFilters.hpp
+++ b/src/modules/AudioFilters/AudioFilters.hpp
@@ -20,15 +20,15 @@
 
 #include <Module.hpp>
 
-class AudioFilters final : public Module
+class AudioFilters : public Module
 {
 public:
     AudioFilters();
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 };
 
 /**/
@@ -41,7 +41,7 @@ class QComboBox;
 class QSpinBox;
 class Slider;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_OBJECT
 public:
@@ -55,7 +55,7 @@ private slots:
     void compressor();
     void defaultSettings();
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     bool restoringDefault;
 
diff --git a/src/modules/AudioFilters/BS2B.hpp b/src/modules/AudioFilters/BS2B.hpp
index f62e4235..00822620 100644
--- a/src/modules/AudioFilters/BS2B.hpp
+++ b/src/modules/AudioFilters/BS2B.hpp
@@ -22,17 +22,17 @@
 
 #include <bs2b/bs2b.hpp>
 
-class BS2B final : public AudioFilter
+class BS2B : public AudioFilter
 {
 public:
     BS2B(Module &module);
-    ~BS2B();
+    ~BS2B() final;
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool setAudioParameters(uchar, uint srate) override;
-    void clearBuffers() override;
-    double filter(Buffer &data, bool flush) override;
+    bool setAudioParameters(uchar, uint srate) override final;
+    void clearBuffers() override final;
+    double filter(Buffer &data, bool flush) override final;
 
     void alloc();
 
diff --git a/src/modules/AudioFilters/DysonCompressor.hpp b/src/modules/AudioFilters/DysonCompressor.hpp
index 2f2b086f..69409429 100644
--- a/src/modules/AudioFilters/DysonCompressor.hpp
+++ b/src/modules/AudioFilters/DysonCompressor.hpp
@@ -25,19 +25,19 @@
 #define NFILT  12
 #define NEFILT 17
 
-class DysonCompressor final : public AudioFilter
+class DysonCompressor : public AudioFilter
 {
 public:
     DysonCompressor(Module &module);
-    ~DysonCompressor();
+    ~DysonCompressor() final;
 
-    bool set() override;
+    bool set() override final;
 
 private:
-    bool setAudioParameters(uchar chn, uint srate) override;
-    int bufferedSamples() const override;
-    void clearBuffers() override;
-    double filter(Buffer &data, bool flush) override;
+    bool setAudioParameters(uchar chn, uint srate) override final;
+    int bufferedSamples() const override final;
+    void clearBuffers() override final;
+    double filter(Buffer &data, bool flush) override final;
 
     using FloatVector = QVector<float>;
 
diff --git a/src/modules/AudioFilters/Echo.hpp b/src/modules/AudioFilters/Echo.hpp
index c418ec3c..5ad34e3e 100644
--- a/src/modules/AudioFilters/Echo.hpp
+++ b/src/modules/AudioFilters/Echo.hpp
@@ -20,15 +20,15 @@
 
 #include <AudioFilter.hpp>
 
-class Echo final : public AudioFilter
+class Echo : public AudioFilter
 {
 public:
     Echo(Module &);
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool setAudioParameters(uchar, uint) override;
-    double filter(Buffer &, bool) override;
+    bool setAudioParameters(uchar, uint) override final;
+    double filter(Buffer &, bool) override final;
 
     void alloc(bool);
 
diff --git a/src/modules/AudioFilters/Equalizer.hpp b/src/modules/AudioFilters/Equalizer.hpp
index 7c8d9665..f6b79283 100644
--- a/src/modules/AudioFilters/Equalizer.hpp
+++ b/src/modules/AudioFilters/Equalizer.hpp
@@ -23,7 +23,7 @@
 struct FFTContext;
 struct FFTComplex;
 
-class Equalizer final : public AudioFilter
+class Equalizer : public AudioFilter
 {
 public:
     static QVector<float> interpolate(const QVector<float> &, const int);
@@ -31,14 +31,14 @@ public:
     static float getAmpl(int val);
 
     Equalizer(Module &);
-    ~Equalizer();
+    ~Equalizer() final;
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool setAudioParameters(uchar, uint) override;
-    int bufferedSamples() const override;
-    void clearBuffers() override;
-    double filter(Buffer &data, bool flush) override;
+    bool setAudioParameters(uchar, uint) override final;
+    int bufferedSamples() const override final;
+    void clearBuffers() override final;
+    double filter(Buffer &data, bool flush) override final;
 
     /**/
 
diff --git a/src/modules/AudioFilters/EqualizerGUI.hpp b/src/modules/AudioFilters/EqualizerGUI.hpp
index eb48d902..52aa4293 100644
--- a/src/modules/AudioFilters/EqualizerGUI.hpp
+++ b/src/modules/AudioFilters/EqualizerGUI.hpp
@@ -20,7 +20,7 @@
 
 #include <QMPlay2Extensions.hpp>
 
-class GraphW final : public QWidget
+class GraphW : public QWidget
 {
 public:
     GraphW();
@@ -31,7 +31,7 @@ public:
         values.resize(vals);
     }
 private:
-    void paintEvent(QPaintEvent *) override;
+    void paintEvent(QPaintEvent *) override final;
 
     QVector<float> values;
     float preamp;
@@ -44,15 +44,15 @@ class QCheckBox;
 class QSlider;
 class QMenu;
 
-class EqualizerGUI final : public QWidget, public QMPlay2Extensions
+class EqualizerGUI : public QWidget, public QMPlay2Extensions
 {
     Q_OBJECT
 public:
     EqualizerGUI(Module &);
 
-    bool set() override;
+    bool set() override final;
 
-    DockWidget *getDockWidget() override;
+    DockWidget *getDockWidget() override final;
 private slots:
     void wallpaperChanged(bool hasWallpaper, double alpha);
     void enabled(bool);
@@ -80,7 +80,7 @@ private:
 
     void loadPresets();
 
-    void showEvent(QShowEvent *event) override;
+    void showEvent(QShowEvent *event) override final;
 
     QMap<int, int> getPresetValues(const QString &name);
 
diff --git a/src/modules/AudioFilters/PhaseReverse.hpp b/src/modules/AudioFilters/PhaseReverse.hpp
index 470aad80..59fe4456 100644
--- a/src/modules/AudioFilters/PhaseReverse.hpp
+++ b/src/modules/AudioFilters/PhaseReverse.hpp
@@ -20,15 +20,15 @@
 
 #include <AudioFilter.hpp>
 
-class PhaseReverse final : public AudioFilter
+class PhaseReverse : public AudioFilter
 {
 public:
     PhaseReverse(Module &);
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool setAudioParameters(uchar, uint) override;
-    double filter(Buffer &, bool) override;
+    bool setAudioParameters(uchar, uint) override final;
+    double filter(Buffer &, bool) override final;
 
     bool enabled, hasParameters, canFilter, reverseRight;
     uchar chn;
diff --git a/src/modules/AudioFilters/VoiceRemoval.hpp b/src/modules/AudioFilters/VoiceRemoval.hpp
index b67e6844..024c98af 100644
--- a/src/modules/AudioFilters/VoiceRemoval.hpp
+++ b/src/modules/AudioFilters/VoiceRemoval.hpp
@@ -20,15 +20,15 @@
 
 #include <AudioFilter.hpp>
 
-class VoiceRemoval final : public AudioFilter
+class VoiceRemoval : public AudioFilter
 {
 public:
     VoiceRemoval(Module &);
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool setAudioParameters(uchar, uint) override;
-    double filter(Buffer &, bool) override;
+    bool setAudioParameters(uchar, uint) override final;
+    double filter(Buffer &, bool) override final;
 
     bool enabled, hasParameters, canFilter;
     uchar chn;
diff --git a/src/modules/Extensions/CMakeLists.txt b/src/modules/Extensions/CMakeLists.txt
index 9be7afeb..9263b6f1 100644
--- a/src/modules/Extensions/CMakeLists.txt
+++ b/src/modules/Extensions/CMakeLists.txt
@@ -55,7 +55,11 @@ if(USE_MEDIABROWSER)
     set(QML Qt5::Qml)
 endif()
 
-qt5_wrap_ui(Extensions_FORM_HDR ${Extensions_FORMS})
+if(USE_QT4)
+    qt4_wrap_ui(Extensions_FORM_HDR ${Extensions_FORMS})
+else()
+    qt5_wrap_ui(Extensions_FORM_HDR ${Extensions_FORMS})
+endif()
 set_property(SOURCE ${Extensions_FORM_HDR} PROPERTY SKIP_AUTOMOC ON)
 
 include_directories(../../qmplay2/headers
@@ -69,9 +73,15 @@ add_library(${PROJECT_NAME} ${QMPLAY2_MODULE}
     ${Extensions_RESOURCES}
 )
 
+if(USE_QT4)
+    add_definitions(-I@prefix@/include/QJson4)
+    target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui QJson4)
+else()
+    target_link_libraries(${PROJECT_NAME} ${QML})
+endif()
+
 target_link_libraries(${PROJECT_NAME}
     ${DBUS}
-    ${QML}
     libqmplay2
 )
 
diff --git a/src/modules/Extensions/Downloader.cpp b/src/modules/Extensions/Downloader.cpp
index 78f30efe..0160b026 100644
--- a/src/modules/Extensions/Downloader.cpp
+++ b/src/modules/Extensions/Downloader.cpp
@@ -29,8 +29,6 @@
 #include <QTimer>
 #include <QLabel>
 #include <QAction>
-#include <QScreen>
-#include <QWindow>
 #include <QProcess>
 #include <QMimeData>
 #include <QClipboard>
@@ -46,14 +44,10 @@
 #include <QProgressBar>
 #include <QApplication>
 #include <QElapsedTimer>
-#include <QStandardPaths>
-#include <QLoggingCategory>
 #include <QDialogButtonBox>
 
 #include <functional>
 
-Q_LOGGING_CATEGORY(downloader, "Downloader")
-
 /**/
 
 constexpr const char *g_defaultMp3ConvertCommand = "ffmpeg -i <input/> -vn -sn -c:a libmp3lame -ab 224k -f mp3 -y <output>%f.mp3</output>";
@@ -358,7 +352,7 @@ void DownloadItemW::downloadStop(bool ok)
     finished = true;
     if (!dontDeleteDownloadThr)
     {
-        if (visibleRegion().isNull())
+        if (visibleRegion().isEmpty())
             emit QMPlay2Core.sendMessage(titleL->text(), sizeL->text());
     }
 }
@@ -368,30 +362,12 @@ void DownloadItemW::startConversion()
     deleteConvertProcess();
 
     m_convertProcess = new QProcess(this);
-    m_convertProcessConn[0] = connect(m_convertProcess, Overload<int>::of(&QProcess::finished), this, [this](int exitCode) {
-        if (exitCode == 0)
-        {
-            sizeL->setText(tr(g_downloadComplete));
-            QFile::remove(filePath);
-            m_needsConversion = false;
-            filePath = m_convertedFilePath;
-            downloadStop(true);
-        }
-        else
-        {
-            sizeL->setText(tr(g_conversionError));
-            qCWarning(downloader) << "Failed to convert:" << m_convertProcess->program() << m_convertProcess->arguments() << m_convertProcess->readAllStandardError().constData();
-            downloadStop(false);
-        }
-    });
-    m_convertProcessConn[1] = connect(m_convertProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
-        if (error == QProcess::FailedToStart)
-        {
-            sizeL->setText(tr(g_conversionError));
-            downloadStop(false);
-            qCWarning(downloader) << "Failed to start process:" << m_convertProcess->program();
-        }
-    });
+
+    // Use old-style connect for Qt4 compatibility
+    connect(m_convertProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
+            this, SLOT(handleConversionFinished(int, QProcess::ExitStatus)));
+    connect(m_convertProcess, SIGNAL(error(QProcess::ProcessError)),
+            this, SLOT(handleConversionError(QProcess::ProcessError)));
 
     m_needsConversion = true;
     finished = false;
@@ -404,7 +380,7 @@ void DownloadItemW::startConversion()
     const auto conversionError = [&](const QString &errStr) {
         sizeL->setText(tr(g_conversionError));
         downloadStop(false);
-        qCWarning(downloader).noquote() << errStr;
+        qWarning() << errStr;
     };
 
     QString convertCommand;
@@ -448,21 +424,63 @@ void DownloadItemW::startConversion()
     convertCommand.replace(idx1, idx2 - idx1 + 9, "\"" + m_convertedFilePath + "\"");
     maybeAddAbsolutePath(convertCommand);
 
-    qDebug() << "Starting conversion:" << convertCommand.toUtf8().constData();
-    m_convertProcess->start(convertCommand);
+    // Store program name and arguments for Qt4 compatibility
+    m_processProgram = convertCommand.section(' ', 0, 0); // Extract the program name
+    m_processArguments = convertCommand.split(' ').mid(1); // Extract the arguments (everything after the program name)
+
+    qDebug() << "Starting conversion:" << m_processProgram << m_processArguments;
+
+    // Start the process using the program name and arguments
+    m_convertProcess->start(m_processProgram, m_processArguments);
 }
+
 void DownloadItemW::deleteConvertProcess()
 {
     if (m_convertProcess)
     {
-        disconnect(m_convertProcessConn[0]);
-        disconnect(m_convertProcessConn[1]);
+        disconnect(m_convertProcess, SIGNAL(finished(int)), this, SLOT(yourSlotForFinished(int)));
+        disconnect(m_convertProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(yourSlotForError(QProcess::ProcessError)));
         m_convertProcess->close();
         delete m_convertProcess;
         m_convertProcess = nullptr;
     }
 }
 
+void DownloadItemW::handleConversionFinished(int exitCode, QProcess::ExitStatus)
+{
+    if (exitCode == 0) // Conversion succeeded
+    {
+        sizeL->setText(tr(g_downloadComplete));
+        QFile::remove(filePath);
+        m_needsConversion = false;
+        filePath = m_convertedFilePath;
+        downloadStop(true);
+    }
+    else // Conversion failed
+    {
+        sizeL->setText(tr(g_conversionError));
+
+        // Manually log the program and arguments stored during the process start
+        qWarning() << "Failed to convert: Program - " << m_processProgram
+                   << " Arguments - " << m_processArguments.join(" ")
+                   << " Error - " << m_convertProcess->readAllStandardError().constData();
+
+        downloadStop(false);
+    }
+}
+
+void DownloadItemW::handleConversionError(QProcess::ProcessError error)
+{
+    if (error == QProcess::FailedToStart)
+    {
+        sizeL->setText(tr(g_conversionError));
+        downloadStop(false);
+
+        // Manually log the program name stored earlier
+        qWarning() << "Failed to start process: Program - " << m_processProgram;
+    }
+}
+
 /**/
 
 DownloaderThread::DownloaderThread(QDataStream *stream, const QString &url, DownloadListW *downloadLW, const QMenu *convertsMenu, const QString &name, const QString &prefix, const QString &param, const QString &preset) :
@@ -843,7 +861,7 @@ void Downloader::init()
     connect(downloadLW, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem *)));
 
     m_convertsMenu = new QMenu(this);
-    connect(m_convertsMenu->addAction(tr("&Add")), &QAction::triggered, this, &Downloader::addConvertPreset);
+    connect(m_convertsMenu->addAction(tr("&Add")), SIGNAL(triggered()), this, SLOT(addConvertPreset()));
     m_convertsMenu->addSeparator();
 
     setDownloadsDirB = new QToolButton;
@@ -879,7 +897,7 @@ void Downloader::init()
     layout->addItem(new QSpacerItem(10, 0, QSizePolicy::Fixed, QSizePolicy::Minimum), 1, 4, 1, 1);
     layout->addWidget(m_convertsPresetsB, 1, 5, 1, 1);
 
-    QString defDownloadPath = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).value(0, QDir::homePath()) + "/";
+    QString defDownloadPath = QDir::homePath() + "/";
 #ifdef Q_OS_WIN
     defDownloadPath.replace('\\', '/');
 #endif
@@ -930,7 +948,7 @@ void Downloader::init()
         const auto createPreset = [this](const QString &name, const QString &data) {
             QAction *act = m_convertsMenu->addAction(name);
             act->setData(data);
-            connect(act, &QAction::triggered, this, &Downloader::editConvertAction);
+            connect(act, SIGNAL(triggered()), this, SLOT(editConvertAction()));
             return act;
         };
 
@@ -976,7 +994,7 @@ QVector<QAction *> Downloader::getActions(const QString &name, double, const QSt
     const auto createAction = [&](const QString &actionName, const QString &preset) {
         QAction *act = new QAction(actionName, nullptr);
         act->setIcon(QIcon(":/downloader.svgz"));
-        act->connect(act, &QAction::triggered, this, &Downloader::download);
+        act->connect(act, SIGNAL(triggered()), this, SLOT(download()));
         act->setProperty("name", name);
         if (!prefix.isEmpty())
         {
@@ -1009,15 +1027,17 @@ void Downloader::addConvertPreset()
     QAction *action = m_convertsMenu->addAction("MP3 224k");
     action->setData(g_defaultMp3ConvertCommand);
     if (modifyConvertAction(action, false))
-        connect(action, &QAction::triggered, this, &Downloader::editConvertAction);
+        connect(action, SIGNAL(triggered()), this, SLOT(editConvertAction()));
     else
         action->deleteLater();
 }
+
 void Downloader::editConvertAction()
 {
     if (QAction *action = qobject_cast<QAction *>(sender()))
         modifyConvertAction(action);
 }
+
 bool Downloader::modifyConvertAction(QAction *action, bool addRemoveButton)
 {
     QDialog dialog(this);
@@ -1026,83 +1046,60 @@ bool Downloader::modifyConvertAction(QAction *action, bool addRemoveButton)
     QLineEdit *nameE = new QLineEdit(action->text());
     QLineEdit *commandE = new QLineEdit(action->data().toString());
 
-    commandE->setToolTip(tr("Command line to execute after download.\n\n<input/> - specifies downloaded file.\n<output>%f.mp3</output> - converted file will be input file with \"mp3\" extension."));
-
     QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
-    connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
-    connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+    connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept()));
+    connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject()));
+
+    QPushButton *removeB = nullptr;
     if (addRemoveButton)
     {
-        QPushButton *removeB = buttons->addButton(tr("Remove"), QDialogButtonBox::DestructiveRole);
+        removeB = buttons->addButton(tr("Remove"), QDialogButtonBox::DestructiveRole);
         removeB->setIcon(QMPlay2Core.getIconFromTheme("list-remove"));
-        connect(buttons, &QDialogButtonBox::clicked, &dialog, [&](QAbstractButton *button) {
-            if (button == removeB)
-            {
-                action->deleteLater();
-                dialog.reject();
-            }
-        });
+
+        // Store the button, action, and dialog in member variables for later access
+        currentAction = action;
+        currentDialog = &dialog;
+
+        // Connect the remove button to the slot
+        connect(removeB, SIGNAL(clicked()), this, SLOT(handleRemoveButtonClicked()));
     }
 
     QFormLayout *layout = new QFormLayout(&dialog);
-    layout->setMargin(4);
-    layout->setSpacing(4);
     layout->addRow(tr("Preset name"), nameE);
     layout->addRow(tr("Command line"), commandE);
     layout->addRow(buttons);
 
-    if (QWindow *win = window()->windowHandle())
+    if (dialog.exec() == QDialog::Accepted)
     {
-        if (QScreen *screen = win->screen())
-            dialog.resize(screen->availableGeometry().width() / 2, 1);
+        action->setText(nameE->text().simplified());
+        action->setData(commandE->text().trimmed());
+        return true;
     }
-
-    while (dialog.exec() == QDialog::Accepted)
+    else if (removeB && dialog.result() == QDialogButtonBox::DestructiveRole)
     {
-        const QString name = nameE->text().simplified();
-        const QString command = commandE->text();
-
-        if (name.isEmpty() || !command.contains(' '))
-        {
-            QMessageBox::warning(this, dialog.windowTitle(), tr("Incorrect/empty name or command line!"));
-            continue;
-        }
-
-        if (!command.contains("<input/>"))
-        {
-            QMessageBox::warning(this, dialog.windowTitle(), tr("Command must contain <input/> tag!"));
-            continue;
-        }
-        if (getCommandOutput(command).isEmpty())
-        {
-            QMessageBox::warning(this, dialog.windowTitle(), tr("Command must contain correct <output>file</output/> tag!"));
-            continue;
-        }
-
-        const QList<QAction *> actions = m_convertsMenu->actions();
-        bool ok = true;
-        for (int i = 1; i < actions.count(); ++i) // Skip first "Add" action
-        {
-            if (actions[i] != action && actions[i]->text().compare(name, Qt::CaseInsensitive) == 0)
-            {
-                ok = false;
-                break;
-            }
-        }
-
-        if (!ok)
-        {
-            QMessageBox::warning(this, dialog.windowTitle(), tr("Preset name already exists!"));
-            continue;
-        }
+        action->deleteLater();
+        return false;
+    }
 
-        action->setText(name);
-        action->setData(command.trimmed());
+    return false;
+}
 
-        return true;
+void Downloader::handleButtonClicked(QAbstractButton *button)
+{
+    if (button == removeB && currentAction && currentDialog) // Ensure valid pointers
+    {
+        currentAction->deleteLater();
+        currentDialog->reject();
     }
+}
 
-    return false;
+void Downloader::handleRemoveButtonClicked()
+{
+    if (removeB && currentAction && currentDialog)  // Ensure valid pointers
+    {
+        currentAction->deleteLater();
+        currentDialog->reject();
+    }
 }
 
 void Downloader::setDownloadsDir()
@@ -1120,6 +1117,7 @@ void Downloader::setDownloadsDir()
     else if (dir.filePath() != QString())
         QMessageBox::warning(this, DownloaderName, tr("Cannot change downloading files directory"));
 }
+
 void Downloader::clearFinished()
 {
     const QList<QTreeWidgetItem *> items = downloadLW->findItems(QString(), Qt::MatchContains);
@@ -1127,6 +1125,7 @@ void Downloader::clearFinished()
         if (((DownloadItemW *)downloadLW->itemWidget(items[i], 0))->isFinished())
             delete items[i];
 }
+
 void Downloader::addUrl()
 {
     QString clipboardUrl;
@@ -1141,6 +1140,7 @@ void Downloader::addUrl()
     if (!url.isEmpty())
         new DownloaderThread(nullptr, url, downloadLW, m_convertsMenu);
 }
+
 void Downloader::download()
 {
     QAction *action = qobject_cast<QAction *>(sender());
@@ -1158,6 +1158,7 @@ void Downloader::download()
     );
     downloadLW->setCurrentItem(downloadLW->invisibleRootItem()->child(0));
 }
+
 void Downloader::itemDoubleClicked(QTreeWidgetItem *item)
 {
     DownloadItemW *downloadItemW = (DownloadItemW *)downloadLW->itemWidget(item, 0);
diff --git a/src/modules/Extensions/Downloader.hpp b/src/modules/Extensions/Downloader.hpp
index a19787ac..c14ce2ac 100644
--- a/src/modules/Extensions/Downloader.hpp
+++ b/src/modules/Extensions/Downloader.hpp
@@ -21,9 +21,12 @@
 #include <QMPlay2Extensions.hpp>
 #include <IOController.hpp>
 
+#include <QProcess>
 #include <QTreeWidget>
 #include <QToolButton>
 #include <QThread>
+#include <QWidget>
+#include <QPushButton>
 
 class QLabel;
 class QProcess;
@@ -32,11 +35,11 @@ class QProgressBar;
 class QTreeWidgetItem;
 class DownloaderThread;
 
-class DownloadItemW final : public QWidget
+class DownloadItemW : public QWidget
 {
     Q_OBJECT
 public:
-    DownloadItemW(DownloaderThread *downloaderThr, QString name, const QIcon &icon, QDataStream *stream, QString preset);
+    explicit DownloadItemW(DownloaderThread *downloaderThr, QString name, const QIcon &icon, QDataStream *stream, QString preset);
     ~DownloadItemW();
 
     void setName(const QString &);
@@ -69,6 +72,8 @@ signals:
     void stop();
 private slots:
     void toggleStartStop();
+    void handleConversionFinished(int exitCode, QProcess::ExitStatus);
+    void handleConversionError(QProcess::ProcessError);
 private:
     void downloadStop(bool);
 
@@ -89,8 +94,10 @@ private:
         QProgressBar *progressB;
     } *speedProgressW = nullptr;
 
-    QProcess *m_convertProcess = nullptr;
-    QMetaObject::Connection m_convertProcessConn[2];
+    QString m_processProgram;      // To store the program name
+    QStringList m_processArguments; // To store the process arguments
+    QProcess *m_convertProcess; // QProcess is now fully included
+    int m_convertProcessConn[2]; // Replace QMetaObject::Connection with int
     bool finished, readyToPlay, m_needsConversion = false;
     QString m_convertPreset;
     QString filePath;
@@ -99,7 +106,7 @@ private:
 
 /**/
 
-class DownloadListW final : public QTreeWidget
+class DownloadListW : public QTreeWidget
 {
     friend class Downloader;
 public:
@@ -113,13 +120,13 @@ private:
 
 /**/
 
-class DownloaderThread final : public QThread
+class DownloaderThread : public QThread
 {
     Q_OBJECT
     enum {ADD_ENTRY, NAME, SET, SET_POS, SET_SPEED, DOWNLOAD_ERROR, FINISH};
 public:
     DownloaderThread(QDataStream *stream, const QString &url, DownloadListW *downloadLW, const QMenu *convertsMenu, const QString &name = QString(), const QString &prefix = QString(), const QString &param = QString(), const QString &preset = QString());
-    ~DownloaderThread();
+    ~DownloaderThread() final;
 
     void serialize(QDataStream &stream);
 
@@ -131,7 +138,7 @@ private slots:
     void stop();
     void finished();
 private:
-    void run() override;
+    void run() override final;
 
     QIcon getIcon();
 
@@ -145,19 +152,19 @@ private:
 
 /**/
 
-class Downloader final : public QWidget, public QMPlay2Extensions
+class Downloader : public QWidget, public QMPlay2Extensions
 {
     Q_OBJECT
 
 public:
     Downloader(Module &module);
-    ~Downloader();
+    ~Downloader() final;
 
-    void init() override;
+    void init() override final;
 
-    DockWidget *getDockWidget() override;
+    DockWidget *getDockWidget() override final;
 
-    QVector<QAction *> getActions(const QString &, double, const QString &, const QString &, const QString &) override;
+    QVector<QAction *> getActions(const QString &, double, const QString &, const QString &, const QString &) override final;
 
 private:
     void addConvertPreset();
@@ -170,6 +177,8 @@ private slots:
     void addUrl();
     void download();
     void itemDoubleClicked(QTreeWidgetItem *);
+    void handleButtonClicked(QAbstractButton *button);
+    void handleRemoveButtonClicked();
 
 private:
     Settings m_sets;
@@ -182,6 +191,10 @@ private:
 
     QToolButton *m_convertsPresetsB;
     QMenu *m_convertsMenu;
+
+    QAction *currentAction;      // Pointer to the current action being modified
+    QDialog *currentDialog;      // Pointer to the current dialog being used
+    QPushButton *removeB; 
 };
 
 #define DownloaderName "QMPlay2 Downloader"
diff --git a/src/modules/Extensions/Extensions.cpp b/src/modules/Extensions/Extensions.cpp
index e4f8f9be..e2b4a8d1 100644
--- a/src/modules/Extensions/Extensions.cpp
+++ b/src/modules/Extensions/Extensions.cpp
@@ -150,17 +150,31 @@ ModuleSettingsWidget::ModuleSettingsWidget(Module &module) :
     subtitlesB->setToolTip(tr("Displays subtitles from YouTube. Follows default subtitles language and QMPlay2 language."));
     subtitlesB->setChecked(sets().getBool("YouTube/Subtitles"));
 
+    int idx;
+
+    m_preferredCodec = new QComboBox;
+    m_preferredCodec->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
+    m_preferredCodec->addItems({"VP9", "H.264", "AV1"}); // Must match "PreferredCodec" enum
+    idx = m_preferredCodec->findText(sets().getString("YouTube/PreferredCodec"));
+    m_preferredCodec->setCurrentIndex(idx > -1 ? idx : 0);
+
     qualityPreset = new QComboBox;
     qualityPreset->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
     qualityPreset->addItems(YouTube::getQualityPresets());
-    int idx = qualityPreset->findText(sets().getString("YouTube/QualityPreset"));
+    idx = qualityPreset->findText(sets().getString("YouTube/QualityPreset"));
     qualityPreset->setCurrentIndex(idx > -1 ? idx : 3);
 
+    m_allowVp9Hdr = new QCheckBox(tr("Allow HDR content for VP9 codec"));
+    m_allowVp9Hdr->setChecked(sets().getBool("YouTube/AllowVp9HDR"));
+
     layout = new QGridLayout(youTubeB);
     layout->addWidget(userNameB, 0, 0, 1, 2);
     layout->addWidget(subtitlesB, 1, 0, 1, 2);
-    layout->addWidget(new QLabel(tr("Preferred quality") + ": "), 2, 0, 1, 1);
-    layout->addWidget(qualityPreset, 2, 1, 1, 1);
+    layout->addWidget(new QLabel(tr("Preferred video codec") + ": "), 2, 0, 1, 1);
+    layout->addWidget(m_preferredCodec, 2, 1, 1, 1);
+    layout->addWidget(new QLabel(tr("Preferred quality") + ": "), 3, 0, 1, 1);
+    layout->addWidget(qualityPreset, 3, 1, 1, 1);
+    layout->addWidget(m_allowVp9Hdr, 4, 0, 1, 2);
     layout->setMargin(2);
 
 #ifdef USE_LASTFM
@@ -233,6 +247,8 @@ void ModuleSettingsWidget::saveSettings()
     sets().set("YouTube/ShowUserName", userNameB->isChecked());
     sets().set("YouTube/Subtitles", subtitlesB->isChecked());
     sets().set("YouTube/QualityPreset", qualityPreset->currentText());
+    sets().set("YouTube/PreferredCodec", m_preferredCodec->currentText());
+    sets().set("YouTube/AllowVp9HDR", m_allowVp9Hdr->isChecked());
 
 #ifdef USE_LASTFM
     sets().set("LastFM/DownloadCovers", downloadCoversGB->isChecked());
diff --git a/src/modules/Extensions/Extensions.hpp b/src/modules/Extensions/Extensions.hpp
index c2a424e1..07d6c163 100644
--- a/src/modules/Extensions/Extensions.hpp
+++ b/src/modules/Extensions/Extensions.hpp
@@ -20,15 +20,15 @@
 
 #include <Module.hpp>
 
-class Extensions final : public Module
+class Extensions : public Module
 {
 public:
     Extensions();
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 
     QIcon downloader, youtube, radio;
 #ifdef USE_LASTFM
@@ -45,7 +45,7 @@ class QGroupBox;
 class QCheckBox;
 class LineEdit;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_OBJECT
 public:
@@ -56,14 +56,15 @@ private slots:
     void passwordEdited();
 #endif
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
 #ifdef USE_MPRIS2
     QCheckBox *MPRIS2B;
 #endif
 
     QCheckBox *userNameB, *subtitlesB;
-    QComboBox *qualityPreset;
+    QComboBox *m_preferredCodec, *qualityPreset;
+    QCheckBox *m_allowVp9Hdr;
 
 #ifdef USE_LASTFM
     QGroupBox *downloadCoversGB;
diff --git a/src/modules/Extensions/LastFM.cpp b/src/modules/Extensions/LastFM.cpp
index 99af0d25..e765dd9a 100644
--- a/src/modules/Extensions/LastFM.cpp
+++ b/src/modules/Extensions/LastFM.cpp
@@ -29,9 +29,20 @@ Q_DECLARE_METATYPE(LastFM::Scrobble)
 
 #include <QCryptographicHash>
 #include <QStringList>
-#include <QUrlQuery>
+#include <QUrl>
 #include <QImage>
 
+static QString buildQueryString(const QList<QPair<QString, QString> > &params)
+{
+    QStringList result;
+    for (int i = 0; i < params.size(); ++i)
+    {
+        const QPair<QString, QString> &pair = params.at(i);
+        result << QUrl::toPercentEncoding(pair.first) + "=" + QUrl::toPercentEncoding(pair.second);
+    }
+    return result.join("&");
+}
+
 LastFM::LastFM(Module &module) :
     coverReply(nullptr),
     loginReply(nullptr),
@@ -53,7 +64,10 @@ bool LastFM::set()
     imageSizes.clear();
     if (sets().getBool("LastFM/AllowBigCovers"))
         imageSizes += "mega";
-    imageSizes += {"extralarge", "large", "medium", "small"};
+    imageSizes += "extralarge";
+    imageSizes += "large";
+    imageSizes += "medium";
+    imageSizes += "small";
 
     const QString _user = sets().getString("LastFM/Login");
     const QString _md5pass = sets().getString("LastFM/Password");
@@ -161,19 +175,20 @@ void LastFM::updateNowPlayingAndScrobble(const Scrobble &scrobble)
         QCryptographicHash::Md5
     ).toHex();
 
-    QUrlQuery updateNowPlayingQuery;
-    updateNowPlayingQuery.addQueryItem("method", "track.updatenowplaying");
-    updateNowPlayingQuery.addQueryItem("artist", scrobble.artist);
-    updateNowPlayingQuery.addQueryItem("track", scrobble.title);
-    updateNowPlayingQuery.addQueryItem("album", scrobble.album.isEmpty() ? "" : scrobble.album);
-    updateNowPlayingQuery.addQueryItem("duration", duration);
-    updateNowPlayingQuery.addQueryItem("api_key", api_key);
-    updateNowPlayingQuery.addQueryItem("api_sig", apiSig);
-    updateNowPlayingQuery.addQueryItem("sk", session_key);
+    QList<QPair<QString, QString> > updateNowPlayingParams;
+    updateNowPlayingParams << qMakePair(QString("method"), QString("track.updatenowplaying"));
+    updateNowPlayingParams << qMakePair(QString("artist"), scrobble.artist);
+    updateNowPlayingParams << qMakePair(QString("track"), scrobble.title);
+    updateNowPlayingParams << qMakePair(QString("album"), scrobble.album.isEmpty() ? "" : scrobble.album);
+    updateNowPlayingParams << qMakePair(QString("duration"), duration);
+    updateNowPlayingParams << qMakePair(QString("api_key"), QString(api_key));
+    updateNowPlayingParams << qMakePair(QString("api_sig"), QString(apiSig));
+    updateNowPlayingParams << qMakePair(QString("sk"), session_key);
+
+    QString updateNowPlayingQueryString = buildQueryString(updateNowPlayingParams);
 
-    reply = net.start(audioScrobbler2URL, updateNowPlayingQuery.toString(QUrl::EncodeDelimiters).toUtf8(), NetworkAccess::UrlEncoded);
-    connect(reply, &NetworkReply::finished,
-            reply, &NetworkReply::deleteLater);
+    reply = net.start(audioScrobbler2URL, updateNowPlayingQueryString.toUtf8(), NetworkAccess::UrlEncoded);
+    connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
 
     // scrobble
     const auto ts = QString::number(scrobble.startTime);
@@ -185,25 +200,30 @@ void LastFM::updateNowPlayingAndScrobble(const Scrobble &scrobble)
         QCryptographicHash::Md5
     ).toHex();
 
-    QUrlQuery scrobbleQuery;
-    scrobbleQuery.addQueryItem("method", "track.scrobble");
-    scrobbleQuery.addQueryItem("artist", scrobble.artist);
-    scrobbleQuery.addQueryItem("track", scrobble.title);
-    scrobbleQuery.addQueryItem("timestamp", ts);
-    scrobbleQuery.addQueryItem("album", scrobble.album.isEmpty() ? "" : scrobble.album);
-    scrobbleQuery.addQueryItem("api_key", api_key);
-    scrobbleQuery.addQueryItem("api_sig", apiSig);
-    scrobbleQuery.addQueryItem("sk", session_key);
-
-    reply = net.start(audioScrobbler2URL, scrobbleQuery.toString(QUrl::EncodeDelimiters).toUtf8(), NetworkAccess::UrlEncoded);
+    QList<QPair<QString, QString> > scrobbleParams;
+    scrobbleParams << qMakePair(QString("method"), QString("track.scrobble"));
+    scrobbleParams << qMakePair(QString("artist"), scrobble.artist);
+    scrobbleParams << qMakePair(QString("track"), scrobble.title);
+    scrobbleParams << qMakePair(QString("timestamp"), ts);
+    scrobbleParams << qMakePair(QString("album"), scrobble.album.isEmpty() ? "" : scrobble.album);
+    scrobbleParams << qMakePair(QString("api_key"), QString(api_key));
+    scrobbleParams << qMakePair(QString("api_sig"), QString(apiSig));
+    scrobbleParams << qMakePair(QString("sk"), session_key);
+
+    QString scrobbleQueryString = buildQueryString(scrobbleParams);
+
+    reply = net.start(audioScrobbler2URL, scrobbleQueryString.toUtf8(), NetworkAccess::UrlEncoded);
     reply->setProperty("scrobble", QVariant::fromValue(scrobble));
     m_scrobbleReplies.push_back(reply);
-    connect(reply, &NetworkReply::destroyed,
-            this, [=] {
+    connect(reply, SIGNAL(destroyed(QObject*)), this, SLOT(onReplyDestroyed(QObject*)));
+    connect(reply, SIGNAL(finished()), this, SLOT(scrobbleFinished()));
+}
+
+void LastFM::onReplyDestroyed(QObject* obj)
+{
+    NetworkReply* reply = qobject_cast<NetworkReply*>(obj);
+    if (reply)
         m_scrobbleReplies.removeOne(reply);
-    });
-    connect(reply, &NetworkReply::finished,
-            this, &LastFM::scrobbleFinished);
 }
 
 void LastFM::clear()
@@ -274,7 +294,7 @@ void LastFM::albumFinished()
             emit QMPlay2Core.updateCover(taa[0], taa[1], taa[2], reply);
         else
         {
-            for (const QString &size : asConst(imageSizes))
+            foreach (const QString &size, imageSizes)
             {
                 int idx = reply.indexOf(size);
                 if (idx > -1)
@@ -316,6 +336,7 @@ void LastFM::albumFinished()
     coverReply->deleteLater();
     coverReply = nullptr;
 }
+
 void LastFM::loginFinished()
 {
     if (loginReply->hasError())
@@ -351,6 +372,7 @@ void LastFM::loginFinished()
     loginReply->deleteLater();
     loginReply = nullptr;
 }
+
 void LastFM::scrobbleFinished()
 {
     const auto reply = qobject_cast<NetworkReply *>(sender());
diff --git a/src/modules/Extensions/LastFM.hpp b/src/modules/Extensions/LastFM.hpp
index 3d2b3e73..40b87d48 100644
--- a/src/modules/Extensions/LastFM.hpp
+++ b/src/modules/Extensions/LastFM.hpp
@@ -29,9 +29,10 @@
 
 class QImage;
 
-class LastFM final : public QObject, public QMPlay2Extensions
+class LastFM : public QObject, public QMPlay2Extensions
 {
     Q_OBJECT
+
 public:
     class Scrobble
     {
@@ -48,8 +49,9 @@ public:
     };
 
     LastFM(Module &module);
+
 private:
-    bool set() override;
+    bool set() override final;
 
     void getAlbumCover(const QString &title, const QString &artist, const QString &album, bool titleAsAlbum = false);
 
@@ -59,6 +61,7 @@ private:
     void updateNowPlayingAndScrobble(const Scrobble &scrobble);
 
     void clear();
+
 private slots:
     void updatePlaying(bool play, const QString &title, const QString &artist, const QString &album, int length, bool needCover, const QString &fileName);
 
@@ -67,6 +70,9 @@ private slots:
     void scrobbleFinished();
 
     void processScrobbleQueue();
+
+    void onReplyDestroyed(QObject* obj);
+
 private:
     NetworkReply *coverReply, *loginReply;
     QList<NetworkReply *> m_scrobbleReplies;
diff --git a/src/modules/Extensions/Lyrics.cpp b/src/modules/Extensions/Lyrics.cpp
index 87c1fc18..560d4ea1 100644
--- a/src/modules/Extensions/Lyrics.cpp
+++ b/src/modules/Extensions/Lyrics.cpp
@@ -56,8 +56,10 @@ Lyrics::Lyrics(Module &module) :
 {
     SetModule(module);
 
-    connect(&QMPlay2Core, &QMPlay2CoreClass::updatePlaying,
-            this, &Lyrics::updatePlaying);
+    connect(&QMPlay2Core,
+        SIGNAL(updatePlaying(bool, const QString &, const QString &, const QString &, int, bool, const QString &, const QString &)),
+        this,
+        SLOT(updatePlaying(bool, const QString &, const QString &, const QString &, int, bool, const QString &, const QString &)));
     connect(&m_net, SIGNAL(finished(NetworkReply *)), this, SLOT(finished(NetworkReply *)));
 
     m_dW = new DockWidget;
diff --git a/src/modules/Extensions/Lyrics.hpp b/src/modules/Extensions/Lyrics.hpp
index d4f954a3..572571cc 100644
--- a/src/modules/Extensions/Lyrics.hpp
+++ b/src/modules/Extensions/Lyrics.hpp
@@ -25,15 +25,15 @@
 #include <QTextEdit>
 #include <QPointer>
 
-class Lyrics final : public QTextEdit, public QMPlay2Extensions
+class Lyrics : public QTextEdit, public QMPlay2Extensions
 {
     Q_OBJECT
 
 public:
     Lyrics(Module &module);
-    ~Lyrics();
+    ~Lyrics() final;
 
-    DockWidget *getDockWidget() override;
+    DockWidget *getDockWidget() override final;
 
 private slots:
     void visibilityChanged(bool v);
diff --git a/src/modules/Extensions/Radio.cpp b/src/modules/Extensions/Radio.cpp
index c0310b5c..92348b29 100644
--- a/src/modules/Extensions/Radio.cpp
+++ b/src/modules/Extensions/Radio.cpp
@@ -24,19 +24,20 @@
 #include <Functions.hpp>
 
 #include <QDesktopServices>
-#include <QJsonDocument>
 #include <QInputDialog>
 #include <QMessageBox>
 #include <QFileDialog>
-#include <QJsonObject>
 #include <QScrollBar>
-#include <QJsonArray>
 #include <QMimeData>
 #include <qevent.h>
 #include <QTimer>
 #include <QMenu>
 #include <QUrl>
 
+#include <QJsonArray.h>
+#include <QJsonDocument.h>
+#include <QJsonObject.h>
+
 constexpr const char *g_fileDialogFilter = "QMPlay2 radio station list (*.qmplay2radio)";
 
 Radio::Radio(Module &module) :
@@ -84,8 +85,8 @@ Radio::Radio(Module &module) :
     ui->radioView->setIconSize({m_radioBrowserModel->elementHeight(), m_radioBrowserModel->elementHeight()});
 
     QHeaderView *header = ui->radioView->header();
-    header->setSectionResizeMode(0, QHeaderView::Stretch);
-    header->setSectionResizeMode(4, QHeaderView::ResizeToContents);
+    header->setResizeMode(0, QHeaderView::Stretch);
+    header->setResizeMode(4, QHeaderView::ResizeToContents);
 
     connect(m_radioBrowserMenu->addAction(tr("Play")), SIGNAL(triggered(bool)), this, SLOT(radioBrowserPlay()));
     connect(m_radioBrowserMenu->addAction(tr("Enqueue")), SIGNAL(triggered(bool)), this, SLOT(radioBrowserEnqueue()));
@@ -109,9 +110,7 @@ Radio::Radio(Module &module) :
     connect(m_net, SIGNAL(finished(NetworkReply *)), this, SLOT(replyFinished(NetworkReply *)));
 
     m_tabChangedOnVisibilityTimer->setSingleShot(true);
-    connect(m_tabChangedOnVisibilityTimer, &QTimer::timeout, this, [=] {
-        tabChanged(currentIndex());
-    });
+    connect(m_tabChangedOnVisibilityTimer, SIGNAL(timeout()), this, SLOT(triggerTabChanged()));
 }
 Radio::~Radio()
 {
@@ -221,6 +220,7 @@ void Radio::searchData()
     ui->radioView->setEnabled(false);
     ui->filterEdit->clear();
 }
+
 void Radio::searchFinished()
 {
     ui->radioView->setEnabled(true);
@@ -285,6 +285,7 @@ void Radio::on_addMyRadioStationButton_clicked()
             addMyRadioStation(name, address);
     }
 }
+
 void Radio::on_editMyRadioStationButton_clicked()
 {
     if (QListWidgetItem *item = ui->myRadioListWidget->currentItem())
@@ -300,10 +301,12 @@ void Radio::on_editMyRadioStationButton_clicked()
         }
     }
 }
+
 void Radio::on_removeMyRadioStationButton_clicked()
 {
     delete ui->myRadioListWidget->currentItem();
 }
+
 void Radio::on_loadMyRadioStationButton_clicked()
 {
     const QString filePath = QFileDialog::getOpenFileName(this, tr("Load radio station list"), QString(), g_fileDialogFilter);
@@ -312,6 +315,7 @@ void Radio::on_loadMyRadioStationButton_clicked()
         loadMyRadios(QSettings(filePath, QSettings::IniFormat).value("Radia").toStringList());
     }
 }
+
 void Radio::on_saveMyRadioStationButton_clicked()
 {
     QString filePath = QFileDialog::getSaveFileName(this, tr("Save radio station list"), QString(), g_fileDialogFilter);
@@ -327,6 +331,7 @@ void Radio::on_myRadioListWidget_itemDoubleClicked(QListWidgetItem *item)
 {
     firstTabItemDoubleClicked(item);
 }
+
 void Radio::on_qmplay2RadioListWidget_itemDoubleClicked(QListWidgetItem *item)
 {
     firstTabItemDoubleClicked(item);
@@ -370,14 +375,17 @@ void Radio::on_searchByComboBox_activated(int idx)
         ui->searchComboBox->setInsertPolicy(QComboBox::InsertAtBottom);
     }
 }
+
 void Radio::on_addRadioBrowserStationButton_clicked()
 {
     QDesktopServices::openUrl(QUrl("http://www.radio-browser.info/gui/#/add"));
 }
+
 void Radio::on_radioView_doubleClicked(const QModelIndex &index)
 {
     radioBrowserPlayOrEnqueue(index, "open");
 }
+
 void Radio::on_radioView_customContextMenuRequested(const QPoint &pos)
 {
     if (ui->radioView->currentIndex().isValid())
@@ -390,6 +398,7 @@ void Radio::radioBrowserPlay()
     if (index.isValid())
         radioBrowserPlayOrEnqueue(index, "open");
 }
+
 void Radio::radioBrowserAdd()
 {
     const QModelIndex index = ui->radioView->currentIndex();
@@ -400,18 +409,21 @@ void Radio::radioBrowserAdd()
         addMyRadioStation(title, url);
     }
 }
+
 void Radio::radioBrowserEnqueue()
 {
     const QModelIndex index = ui->radioView->currentIndex();
     if (index.isValid())
         radioBrowserPlayOrEnqueue(index, "enqueue");
 }
+
 void Radio::radioBrowserOpenHomePage()
 {
     const QModelIndex index = ui->radioView->currentIndex();
     if (index.isValid())
         QDesktopServices::openUrl(m_radioBrowserModel->getHomePageUrl(index));
 }
+
 void Radio::radioBrowserEdit()
 {
     const QModelIndex index = ui->radioView->currentIndex();
@@ -495,6 +507,7 @@ QStringList Radio::getMyRadios() const
         myRadios += item->text() + '\n' + item->data(Qt::UserRole).toString();
     return myRadios;
 }
+
 void Radio::loadMyRadios(const QStringList &radios)
 {
     ui->myRadioListWidget->clear();
@@ -539,3 +552,8 @@ bool Radio::eventFilter(QObject *watched, QEvent *event)
     }
     return QTabWidget::eventFilter(watched, event);
 }
+
+void Radio::triggerTabChanged()
+{
+    tabChanged(currentIndex());
+}
diff --git a/src/modules/Extensions/Radio.hpp b/src/modules/Extensions/Radio.hpp
index 1bf167a5..a5e95949 100644
--- a/src/modules/Extensions/Radio.hpp
+++ b/src/modules/Extensions/Radio.hpp
@@ -24,6 +24,7 @@
 #include <QPointer>
 #include <QIcon>
 #include <QMap>
+#include <QModelIndex>
 
 namespace Ui {
     class Radio;
@@ -36,21 +37,23 @@ class NetworkReply;
 class QTimer;
 class QMenu;
 
-class Radio final : public QTabWidget, public QMPlay2Extensions
+class Radio : public QTabWidget, public QMPlay2Extensions
 {
     Q_OBJECT
 
 public:
     Radio(Module &);
-    ~Radio();
+    ~Radio() final;
 
-    DockWidget *getDockWidget() override;
+    DockWidget *getDockWidget() override final;
 
 private slots:
     void visibilityChanged(const bool v);
 
     void tabChanged(int index);
 
+    void triggerTabChanged();
+
     void qmplay2RadioStationsFinished();
 
     void searchData();
@@ -95,7 +98,7 @@ private:
     void loadMyRadios(const QStringList &radios);
 
 private:
-    bool eventFilter(QObject *watched, QEvent *event) override;
+    bool eventFilter(QObject *watched, QEvent *event) override final;
 
 private:
     const QString m_newStationTxt;
diff --git a/src/modules/Extensions/Radio/RadioBrowserModel.cpp b/src/modules/Extensions/Radio/RadioBrowserModel.cpp
index 395ed835..22641a2e 100644
--- a/src/modules/Extensions/Radio/RadioBrowserModel.cpp
+++ b/src/modules/Extensions/Radio/RadioBrowserModel.cpp
@@ -22,13 +22,14 @@
 #include <Functions.hpp>
 #include <CppUtils.hpp>
 
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
 #include <QPainter>
 #include <QWidget>
 #include <QUrl>
 
+#include <QJsonArray.h>
+#include <QJsonDocument.h>
+#include <QJsonObject.h>
+
 #include <algorithm>
 
 struct Column
@@ -394,10 +395,9 @@ void RadioBrowserModel::replyFinished(NetworkReply *reply)
                         if (!image.isNull())
                         {
                             const int s = elementHeight();
-                            const qreal dpr = m_widget->devicePixelRatioF();
+                            const qreal dpr = 1.0;
 
                             column->icon = QPixmap(s * dpr, s * dpr);
-                            column->icon.setDevicePixelRatio(dpr);
                             column->icon.fill(Qt::transparent);
 
                             QPainter painter(&column->icon);
diff --git a/src/modules/Extensions/Radio/RadioBrowserModel.hpp b/src/modules/Extensions/Radio/RadioBrowserModel.hpp
index 9ecd5270..42586e28 100644
--- a/src/modules/Extensions/Radio/RadioBrowserModel.hpp
+++ b/src/modules/Extensions/Radio/RadioBrowserModel.hpp
@@ -30,7 +30,7 @@ class NetworkAccess;
 class NetworkReply;
 struct Column;
 
-class RadioBrowserModel final : public QAbstractItemModel
+class RadioBrowserModel : public QAbstractItemModel
 {
     Q_OBJECT
 
@@ -49,14 +49,14 @@ public:
     QUrl getEditUrl(const QModelIndex &index) const;
     QUrl getHomePageUrl(const QModelIndex &index) const;
 
-    QModelIndex index(int row, int column, const QModelIndex &parent) const override;
-    QModelIndex parent(const QModelIndex &child) const override;
-    int rowCount(const QModelIndex &parent) const override;
-    int columnCount(const QModelIndex &parent) const override;
-    QVariant data(const QModelIndex &index, int role) const override;
-    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
-    Qt::ItemFlags flags(const QModelIndex &index) const override;
-    void sort(int columnIdx, Qt::SortOrder order = Qt::AscendingOrder) override;
+    QModelIndex index(int row, int column, const QModelIndex &parent) const override final;
+    QModelIndex parent(const QModelIndex &child) const override final;
+    int rowCount(const QModelIndex &parent) const override final;
+    int columnCount(const QModelIndex &parent) const override final;
+    QVariant data(const QModelIndex &index, int role) const override final;
+    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override final;
+    Qt::ItemFlags flags(const QModelIndex &index) const override final;
+    void sort(int columnIdx, Qt::SortOrder order = Qt::AscendingOrder) override final;
 
 public slots:
     void setFiltrText(const QString &text = QString());
diff --git a/src/modules/Extensions/YouTube.cpp b/src/modules/Extensions/YouTube.cpp
index 4d7b794f..d168f9bb 100644
--- a/src/modules/Extensions/YouTube.cpp
+++ b/src/modules/Extensions/YouTube.cpp
@@ -21,19 +21,14 @@
 #include <YouTubeDL.hpp>
 #include <LineEdit.hpp>
 
-#include <QLoggingCategory>
 #include <QStringListModel>
 #include <QDesktopServices>
-#include <QJsonParseError>
 #include <QTextDocument>
-#include <QJsonDocument>
 #include <QProgressBar>
 #include <QApplication>
-#include <QJsonObject>
 #include <QHeaderView>
 #include <QGridLayout>
 #include <QToolButton>
-#include <QJsonArray>
 #include <QCompleter>
 #include <QClipboard>
 #include <QMimeData>
@@ -41,8 +36,12 @@
 #include <QAction>
 #include <QMenu>
 #include <QUrl>
+#include <QSignalMapper>
+#include <QDebug>
 
-Q_LOGGING_CATEGORY(youtube, "Extensions/YouTube")
+#include <QJsonParseError.h>
+#include <QJsonObject.h>
+#include <QJsonArray.h>
 
 #define YOUTUBE_URL "https://www.youtube.com"
 
@@ -53,7 +52,7 @@ static inline QString toPercentEncoding(const QString &txt)
 
 static inline QString getYtUrl(const QString &title, const int page, const int sortByIdx)
 {
-    static constexpr const char *sortBy[4] {
+    static const char *sortBy[4] {
         "",             // Relevance ("&sp=CAA%253D")
         "&sp=CAI%253D", // Upload date
         "&sp=CAM%253D", // View count
@@ -89,8 +88,8 @@ ResultsYoutube::ResultsYoutube()
     headerItem()->setText(2, tr("User"));
 
     header()->setStretchLastSection(false);
-    header()->setSectionResizeMode(0, QHeaderView::Stretch);
-    header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+    header()->setResizeMode(0, QHeaderView::Stretch);
+    header()->setResizeMode(1, QHeaderView::ResizeToContents);
 
     connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(playEntry(QTreeWidgetItem *)));
     connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenu(const QPoint &)));
@@ -126,6 +125,24 @@ void ResultsYoutube::playEntry(QTreeWidgetItem *tWI)
     playOrEnqueue("open", tWI);
 }
 
+void ResultsYoutube::playCurrentItem()
+{
+    QTreeWidgetItem *tWI = currentItem();
+    if (!tWI)
+        return;
+
+    playOrEnqueue("open", tWI, currentParam);
+}
+
+void ResultsYoutube::enqueueCurrentItem()
+{
+    QTreeWidgetItem *tWI = currentItem();
+    if (!tWI)
+        return;
+
+    playOrEnqueue("enqueue", tWI, currentParam);
+}
+
 void ResultsYoutube::openPage()
 {
     QTreeWidgetItem *tWI = currentItem();
@@ -155,17 +172,19 @@ void ResultsYoutube::contextMenu(const QPoint &point)
 
     for (int i = 0; i < 2; ++i)
     {
-        menu->addSection(i == 0 ? tr("Audio and video") : tr("Audio only"));
+        QAction *section = new QAction(i == 0 ? tr("Audio and video") : tr("Audio only"), menu);
+        section->setEnabled(false); // Make it non-interactive
+        menu->addAction(section);
 
         if (!tWI->isDisabled())
         {
-            const auto param = i == 0 ? QString() : QString("audio");
-            menu->addAction(tr("Play"), this, [=] {
-                playOrEnqueue("open", currentItem(), param);
-            });
-            menu->addAction(tr("Enqueue"), this, [=] {
-                playOrEnqueue("enqueue", currentItem(), param);
-            });
+            currentParam = (i == 0 ? QString() : QString("audio"));
+            QAction *playAction = menu->addAction(tr("Play"));
+            QAction *enqueueAction = menu->addAction(tr("Enqueue"));
+
+            connect(playAction, SIGNAL(triggered()), this, SLOT(playCurrentItem()));
+            connect(enqueueAction, SIGNAL(triggered()), this, SLOT(enqueueCurrentItem()));
+
             menu->addSeparator();
         }
 
@@ -239,6 +258,18 @@ const QStringList YouTube::getQualityPresets()
     };
 }
 
+void YouTube::onQualityPresetChanged(const QString &preset) {
+    sets().set("YouTube/QualityPreset", preset);
+}
+
+void YouTube::onQualityToggled() {
+    QAction *act = qobject_cast<QAction *>(sender());
+    if (act && act->isChecked()) {
+        int qualityIdx = m_qualityGroup->actions().indexOf(act);
+        setItags(qualityIdx);
+    }
+}
+
 YouTube::YouTube(Module &module) :
     completer(new QCompleter(new QStringListModel(this), this)),
     currPage(1),
@@ -270,9 +301,8 @@ YouTube::YouTube(Module &module) :
     searchB->setAutoRaise(true);
 
     QToolButton *showSettingsB = new QToolButton;
-    connect(showSettingsB, &QToolButton::clicked, this, [] {
-        emit QMPlay2Core.showSettings("Extensions");
-    });
+    connect(showSettingsB, SIGNAL(clicked()), this, SLOT(onShowSettingsClicked()));
+
     showSettingsB->setIcon(QMPlay2Core.getIconFromTheme("configure"));
     showSettingsB->setToolTip(tr("Settings"));
     showSettingsB->setAutoRaise(true);
@@ -283,20 +313,23 @@ YouTube::YouTube(Module &module) :
 
     QMenu *qualityMenu = new QMenu(this);
     int qualityIdx = 0;
-    for (QAction *act : m_qualityGroup->actions())
-    {
-        connect(act, &QAction::triggered, this, [=] {
-            sets().set("YouTube/QualityPreset", act->text());
-        });
-        connect(act, &QAction::toggled, this, [=](bool checked) {
-            if (checked)
-                setItags(qualityIdx);
-        });
+    QSignalMapper *qualitySignalMapper = new QSignalMapper(this);
+
+    for (int i = 0; i < m_qualityGroup->actions().size(); ++i) {
+        QAction *act = m_qualityGroup->actions().at(i);
+
+        qualitySignalMapper->setMapping(act, act->text());
+        connect(act, SIGNAL(triggered()), qualitySignalMapper, SLOT(map()));
+        connect(act, SIGNAL(toggled(bool)), this, SLOT(onQualityToggled()));
+
         act->setCheckable(true);
         qualityMenu->addAction(act);
         ++qualityIdx;
     }
-    qualityMenu->insertSeparator(qualityMenu->actions().at(5));
+    connect(qualitySignalMapper, SIGNAL(mapped(QString)), this, SLOT(onQualityPresetChanged(QString)));
+    if (qualityMenu->actions().size() > 5) {
+        qualityMenu->insertSeparator(qualityMenu->actions().at(5));
+    }
 
     QToolButton *qualityB = new QToolButton;
     qualityB->setPopupMode(QToolButton::InstantPopup);
@@ -312,21 +345,18 @@ YouTube::YouTube(Module &module) :
     m_sortByGroup->addAction(tr("Rating"));
 
     QMenu *sortByMenu = new QMenu(this);
-    int sortByIdx = 0;
-    for (QAction *act : m_sortByGroup->actions())
-    {
-        connect(act, &QAction::triggered, this, [=] {
-            if (m_sortByIdx != sortByIdx)
-            {
-                m_sortByIdx = sortByIdx;
-                sets().set("YouTube/SortBy", m_sortByIdx);
-                search();
-            }
-        });
+    QSignalMapper *sortBySignalMapper = new QSignalMapper(this);
+
+    for (int i = 0; i < m_sortByGroup->actions().size(); ++i) {
+        QAction *act = m_sortByGroup->actions().at(i);
+
+        sortBySignalMapper->setMapping(act, i);
+        connect(act, SIGNAL(triggered()), sortBySignalMapper, SLOT(map()));
+
         act->setCheckable(true);
         sortByMenu->addAction(act);
-        ++sortByIdx;
     }
+    connect(sortBySignalMapper, SIGNAL(mapped(int)), this, SLOT(onSortByChanged(int)));
 
     QToolButton *sortByB = new QToolButton;
     sortByB->setPopupMode(QToolButton::InstantPopup);
@@ -371,6 +401,23 @@ YouTube::~YouTube()
 
 bool YouTube::set()
 {
+    const auto preferredCodec = sets().getString("YouTube/PreferredCodec");
+    const auto oldPpreferredCodec = m_preferredCodec;
+    const auto oldAllowVp9Hdr = m_allowVp9Hdr;
+
+    if (preferredCodec == "H.264")
+        m_preferredCodec = PreferredCodec::H264;
+    else if (preferredCodec == "AV1")
+        m_preferredCodec = PreferredCodec::AV1;
+    else
+        m_preferredCodec = PreferredCodec::VP9;
+
+    m_allowVp9Hdr = sets().getBool("YouTube/AllowVp9HDR");
+
+    auto forceToggledSignal = [&] {
+        return (oldPpreferredCodec != m_preferredCodec || oldAllowVp9Hdr != m_allowVp9Hdr);
+    };
+
     const auto qualityActions = m_qualityGroup->actions();
     const auto qualityText = sets().getString("YouTube/QualityPreset");
     bool qualityActionChecked = false;
@@ -380,6 +427,8 @@ bool YouTube::set()
         {
             if (qualityAction->text() == qualityText)
             {
+                if (forceToggledSignal() && qualityAction->isChecked())
+                    qualityAction->setChecked(false); // Force "toggled" signal
                 qualityAction->setChecked(true);
                 qualityActionChecked = true;
                 break;
@@ -387,7 +436,11 @@ bool YouTube::set()
         }
     }
     if (!qualityActionChecked)
+    {
+        if (forceToggledSignal() && qualityActions[3]->isChecked())
+            qualityActions[3]->setChecked(false); // Force "toggled" signal
         qualityActions[3]->setChecked(true);
+    }
 
     resultsW->setColumnCount(sets().getBool("YouTube/ShowUserName") ? 3 : 2);
     m_allowSubtitles = sets().getBool("YouTube/Subtitles");
@@ -597,118 +650,191 @@ void YouTube::searchMenu()
 
 void YouTube::setItags(int qualityIdx)
 {
-#if 0 // Itag info (incomplete)
-    Video + Audio:
-    43 = 360p WebM (VP8 + Vorbis 128kbps)
-    36 = 180p MP4 (MPEG4 + AAC 32kbps)
-    22 = 720p MP4 (H.264 + AAC 192kbps)
-    18 = 360p MP4 (H.264 + AAC 96kbps)
-     5 = 240p FLV (FLV + MP3 64kbps)
-
-    Video only:
-    247 = Video  720p (VP9)
-    248 = Video 1080p (VP9)
-    271 = Video 1440p (VP9)
-    313 = Video 2160p (VP9)
-    272 = Video 4320p/2160p (VP9)
-
-    302 = Video  720p 60fps (VP9)
-    303 = Video 1080p 60fps (VP9)
-    308 = Video 1440p 60fps (VP9)
-    315 = Video 2160p 60fps (VP9)
-
-    298 = Video  720p 60fps (H.264)
-    299 = Video 1080p 60fps (H.264)
-
-    135 = Video  480p (H.264)
-    136 = Video  720p (H.264)
-    137 = Video 1080p (H.264)
-    264 = Video 1440p (H.264)
-    266 = Video 2160p (H.264)
-
-    170 = Video  480p (VP8)
-    168 = Video  720p (VP8)
-    170 = Video 1080p (VP8)
-
-    Audio only:
-    139 = Audio (AAC 48kbps)
-    140 = Audio (AAC 128kbps)
-    141 = Audio (AAC 256kbps) //?
-
-    171 = Audio (Vorbis 128kbps)
-    172 = Audio (Vorbis 256kbps) //?
-
-    249 = Audio (Opus 50kbps)
-    250 = Audio (Opus 70kbps)
-    251 = Audio (Opus 160kbps)
-#endif
+    // Itag info: https://gist.github.com/AgentOak/34d47c65b1d28829bb17c24c04a0096f
+    enum
+    {
+        // Video
+        H264_144p = 160,
+        H264_240p = 133,
+        H264_360p = 134,
+        H264_480p = 135,
+        H264_720p = 136,
+        H264_1080p = 137,
+        H264_1440p = 264,
+        H264_2160p = 266,
+
+        H264_720p60 = 298,
+        H264_1080p60 = 299,
+        H264_1440p60 = 304,
+        H264_2160p60 = 305,
+
+        VP9_144p = 278,
+        VP9_240p = 242,
+        VP9_360p = 243,
+        VP9_480p = 244,
+        VP9_720p = 247,
+        VP9_1080p = 248,
+        VP9_1440p = 271,
+        VP9_2160p = 313,
+
+        VP9_720p60 = 302,
+        VP9_1080p60 = 303,
+        VP9_1440p60 = 308,
+        VP9_2160p60 = 315,
+        VP9_4320p60 = 272,
+
+        VP9_720p60_HDR = 334,
+        VP9_1080p60_HDR = 335,
+        VP9_1440p60_HDR = 336,
+        VP9_2160p60_HDR = 337,
+
+        AV1_480p = 397,
+        AV1_360p = 396,
+        AV1_240p = 395,
+        AV1_144p = 394,
+
+        AV1_HFR_4320p_1 = 571,
+        AV1_HFR_4320p_2 = 402,
+        AV1_HFR_2160p = 401,
+        AV1_HFR_1440p = 400,
+        AV1_HFR_1080p = 399,
+        AV1_HFR_720p = 398,
+
+        AV1_HFR_HIGH_2160p = 701,
+        AV1_HFR_HIGH_1440p = 700,
+        AV1_HFR_HIGH_1080p = 699,
+        AV1_HFR_HIGH_720p = 698,
+
+        // Live video
+        H264_144p_AAC_48 = 91,
+        H264_240p_AAC_48 = 92,
+        H264_360p_AAC_128 = 93,
+        H264_480p_AAC_128 = 94,
+        H264_720p_AAC_256 = 95,
+        H264_1080p_AAC_256 = 96,
+        H264_720p60_AAC_128 = 300,
+        H264_1080p60_AAC_128 = 301,
+
+        // Audio
+        Opus_160 = 251,
+        AAC_128 = 140,
+
+        // Video + Audio
+        H264_360P_AAC_128 = 18,
+        H264_720P_AAC_128 = 22,
+    };
 
     enum
     {
-        _4320p60,
-        _2160p60,
-        _1440p60,
-        _1080p60,
-        _720p60,
-        _2160p,
-        _1440p,
-        _1080p,
-        _720p,
-        _480p,
-        QualityPresetsCount,
+        Preset_4320p60,
+        Preset_2160p60,
+        Preset_1440p60,
+        Preset_1080p60,
+        Preset_720p60,
+        Preset_2160p,
+        Preset_1440p,
+        Preset_1080p,
+        Preset_720p,
+        Preset_480p,
+
+        PresetCount,
     };
 
-    QList<int> qualityPresets[QualityPresetsCount];
+    QVector<int> qualityPresets[PresetCount];
     {
-        qualityPresets[_720p60]  << 298 << 302;
-        qualityPresets[_1080p60] << 299 << 303 << qualityPresets[_720p60];
-        qualityPresets[_1440p60] << 308 << qualityPresets[_1080p60];
-        qualityPresets[_2160p60] << 315 << qualityPresets[_1440p60];
-        qualityPresets[_4320p60] << 272 << qualityPresets[_2160p60];
-
-        qualityPresets[_480p]  << 135 << 134 << 133;
-        qualityPresets[_720p]  << 136 << 247 << qualityPresets[_480p];
-        qualityPresets[_1080p] << 137 << 248 << qualityPresets[_720p];
-        qualityPresets[_1440p] << 264 << 271 << qualityPresets[_1080p];
-        qualityPresets[_2160p] << 266 << 313 << qualityPresets[_1440p];
+        auto maybeAppendVp9Hdr = [this, &qualityPresets] {
+            if (m_allowVp9Hdr)
+            {
+                qualityPresets[Preset_720p60]  << VP9_720p60_HDR;
+                qualityPresets[Preset_1080p60] << VP9_1080p60_HDR;
+                qualityPresets[Preset_1440p60] << VP9_1440p60_HDR;
+                qualityPresets[Preset_2160p60] << VP9_2160p60_HDR;
+            }
+        };
+        if (m_preferredCodec == PreferredCodec::VP9)
+        {
+            qualityPresets[Preset_480p]  << VP9_480p << H264_480p << VP9_360p << H264_360p << H264_360P_AAC_128 << VP9_240p << H264_240p << VP9_144p << H264_144p;
+            qualityPresets[Preset_720p]  << VP9_720p << H264_720p << H264_720P_AAC_128 << qualityPresets[Preset_480p];
+            qualityPresets[Preset_1080p] << VP9_1080p << H264_1080p << qualityPresets[Preset_720p];
+            qualityPresets[Preset_1440p] << VP9_1440p << H264_1440p << qualityPresets[Preset_1080p];
+            qualityPresets[Preset_2160p] << VP9_2160p << H264_2160p << qualityPresets[Preset_1440p];
+
+            maybeAppendVp9Hdr();
+            qualityPresets[Preset_720p60]  << VP9_720p60 << H264_720p60;
+            qualityPresets[Preset_1080p60] << VP9_1080p60 << H264_1080p60 << qualityPresets[Preset_720p60];
+            qualityPresets[Preset_1440p60] << VP9_1440p60 << H264_1440p60 << qualityPresets[Preset_1080p60];
+            qualityPresets[Preset_2160p60] << VP9_2160p60 << H264_2160p60 << qualityPresets[Preset_1440p60];
+            qualityPresets[Preset_4320p60] << VP9_4320p60 << qualityPresets[Preset_2160p60];
+        }
+        else if (m_preferredCodec == PreferredCodec::H264)
+        {
+            qualityPresets[Preset_480p]  << H264_480p << VP9_480p << H264_360p << VP9_360p << H264_360P_AAC_128 << H264_240p << VP9_240p << H264_144p << VP9_144p;
+            qualityPresets[Preset_720p]  << H264_720p << VP9_720p << H264_720P_AAC_128 << qualityPresets[Preset_480p];
+            qualityPresets[Preset_1080p] << H264_1080p << VP9_1080p << qualityPresets[Preset_720p];
+            qualityPresets[Preset_1440p] << H264_1440p << VP9_1440p << qualityPresets[Preset_1080p];
+            qualityPresets[Preset_2160p] << H264_2160p << VP9_2160p << qualityPresets[Preset_1440p];
+
+            qualityPresets[Preset_720p60]  << H264_720p60 << VP9_720p60;
+            qualityPresets[Preset_1080p60] << H264_1080p60 << VP9_1080p60 << qualityPresets[Preset_720p60];
+            qualityPresets[Preset_1440p60] << H264_1440p60 << VP9_1440p60 << qualityPresets[Preset_1080p60];
+            qualityPresets[Preset_2160p60] << H264_2160p60 << VP9_2160p60 << qualityPresets[Preset_1440p60];
+            qualityPresets[Preset_4320p60] << VP9_4320p60 << qualityPresets[Preset_2160p60];
+        }
+        else if (m_preferredCodec == PreferredCodec::AV1)
+        {
+            qualityPresets[Preset_480p]  << AV1_480p << AV1_360p << VP9_480p << H264_480p << VP9_360p << H264_360p << H264_360P_AAC_128 << AV1_240p << VP9_240p << H264_240p << AV1_144p << VP9_144p << H264_144p;
+            qualityPresets[Preset_720p]  << VP9_720p << H264_720p << H264_720P_AAC_128 << qualityPresets[Preset_480p];
+            qualityPresets[Preset_1080p] << VP9_1080p << H264_1080p << qualityPresets[Preset_720p];
+            qualityPresets[Preset_1440p] << VP9_1440p << H264_1440p << qualityPresets[Preset_1080p];
+            qualityPresets[Preset_2160p] << VP9_2160p << H264_2160p << qualityPresets[Preset_1440p];
+
+            qualityPresets[Preset_720p60]  << AV1_HFR_HIGH_720p << AV1_HFR_720p;
+            qualityPresets[Preset_1080p60] << AV1_HFR_HIGH_1080p << AV1_HFR_1080p;
+            qualityPresets[Preset_1440p60] << AV1_HFR_HIGH_1440p << AV1_HFR_1440p;
+            qualityPresets[Preset_2160p60] << AV1_HFR_HIGH_2160p << AV1_HFR_2160p;
+            qualityPresets[Preset_4320p60] << AV1_HFR_4320p_1 << AV1_HFR_4320p_2;
+            maybeAppendVp9Hdr();
+            qualityPresets[Preset_720p60]  << VP9_720p60 << H264_720p60;
+            qualityPresets[Preset_1080p60] << VP9_1080p60 << H264_1080p60 << qualityPresets[Preset_720p60];
+            qualityPresets[Preset_1440p60] << VP9_1440p60 << H264_1440p60 << qualityPresets[Preset_1080p60];
+            qualityPresets[Preset_2160p60] << VP9_2160p60 << H264_2160p60 << qualityPresets[Preset_1440p60];
+            qualityPresets[Preset_4320p60] << VP9_4320p60 << qualityPresets[Preset_2160p60];
+        }
 
         // Append also non-60 FPS itags to 60 FPS itags
-        qualityPresets[_720p60]  += qualityPresets[_720p];
-        qualityPresets[_1080p60] += qualityPresets[_1080p];
-        qualityPresets[_1440p60] += qualityPresets[_1440p];
-        qualityPresets[_2160p60] += qualityPresets[_2160p];
-        qualityPresets[_4320p60] += qualityPresets[_2160p];
+        qualityPresets[Preset_720p60]  << qualityPresets[Preset_720p];
+        qualityPresets[Preset_1080p60] << qualityPresets[Preset_1080p];
+        qualityPresets[Preset_1440p60] << qualityPresets[Preset_1440p];
+        qualityPresets[Preset_2160p60] << qualityPresets[Preset_2160p];
+        qualityPresets[Preset_4320p60] << qualityPresets[Preset_2160p];
     }
 
-    QList<int> liveQualityPresets[QualityPresetsCount];
+    QVector<int> liveQualityPresets[PresetCount];
     {
-        liveQualityPresets[_720p60]  << 300;
-        liveQualityPresets[_1080p60] << 301 << liveQualityPresets[_720p60];
-        liveQualityPresets[_1440p60] << liveQualityPresets[_1080p60];
-        liveQualityPresets[_2160p60] << liveQualityPresets[_1440p60];
-        liveQualityPresets[_4320p60] << liveQualityPresets[_2160p60];
-
-        liveQualityPresets[_480p]  << 94 << 93 << 92 << 91;
-        liveQualityPresets[_720p]  << 95 << liveQualityPresets[_480p];
-        liveQualityPresets[_1080p] << 96 << liveQualityPresets[_720p];
-        liveQualityPresets[_1440p] << 265 << liveQualityPresets[_1080p];
-        liveQualityPresets[_2160p] << 267 << liveQualityPresets[_1440p];
+        liveQualityPresets[Preset_480p]  << H264_480p_AAC_128 << H264_360p_AAC_128 << H264_240p_AAC_48 << H264_144p_AAC_48;
+        liveQualityPresets[Preset_720p]  << H264_720p_AAC_256 << liveQualityPresets[Preset_480p];
+        liveQualityPresets[Preset_1080p] << H264_1080p_AAC_256 << liveQualityPresets[Preset_720p];
+        liveQualityPresets[Preset_1440p] << liveQualityPresets[Preset_1080p];
+        liveQualityPresets[Preset_2160p] << liveQualityPresets[Preset_1440p];
+
+        liveQualityPresets[Preset_720p60]  << H264_720p60_AAC_128;
+        liveQualityPresets[Preset_1080p60] << H264_1080p60_AAC_128 << liveQualityPresets[Preset_720p60];
+        liveQualityPresets[Preset_1440p60] << liveQualityPresets[Preset_1080p60];
+        liveQualityPresets[Preset_2160p60] << liveQualityPresets[Preset_1440p60];
+        liveQualityPresets[Preset_4320p60] << liveQualityPresets[Preset_2160p60];
 
         // Append also non-60 FPS itags to 60 FPS itags
-        liveQualityPresets[_720p60]  += liveQualityPresets[_720p];
-        liveQualityPresets[_1080p60] += liveQualityPresets[_1080p];
-        liveQualityPresets[_1440p60] += liveQualityPresets[_1440p];
-        liveQualityPresets[_2160p60] += liveQualityPresets[_2160p];
-        liveQualityPresets[_4320p60] += liveQualityPresets[_2160p];
+        liveQualityPresets[Preset_720p60]  += liveQualityPresets[Preset_720p];
+        liveQualityPresets[Preset_1080p60] += liveQualityPresets[Preset_1080p];
+        liveQualityPresets[Preset_1440p60] += liveQualityPresets[Preset_1440p];
+        liveQualityPresets[Preset_2160p60] += liveQualityPresets[Preset_2160p];
+        liveQualityPresets[Preset_4320p60] += liveQualityPresets[Preset_2160p];
     }
 
     QMutexLocker locker(&m_itagsMutex);
     m_videoItags = qualityPresets[qualityIdx];
-    m_audioItags = {251, 171, 140, 250, 249};
+    m_audioItags = {Opus_160, AAC_128};
     m_hlsItags = liveQualityPresets[qualityIdx];
-    m_singleUrlItags = {43, 18};
-    if (qualityIdx != _480p)
-        m_singleUrlItags.prepend(22);
 }
 
 void YouTube::deleteReplies()
@@ -725,13 +851,13 @@ void YouTube::setAutocomplete(const QByteArray &data)
     const QJsonDocument json = QJsonDocument::fromJson(data, &jsonErr);
     if (jsonErr.error != QJsonParseError::NoError)
     {
-        qCWarning(youtube) << "Cannot parse autocomplete JSON:" << jsonErr.errorString();
+        qWarning() << "Cannot parse autocomplete JSON:" << jsonErr.errorString();
         return;
     }
     const QJsonArray mainArr = json.array();
     if (mainArr.count() < 2)
     {
-        qCWarning(youtube) << "Invalid autocomplete JSON array";
+        qWarning() << "Invalid autocomplete JSON array";
         return;
     }
     const QJsonArray arr = mainArr.at(1).toArray();
@@ -745,143 +871,108 @@ void YouTube::setAutocomplete(const QByteArray &data)
     if (searchE->hasFocus())
         completer->complete();
 }
-void YouTube::setSearchResults(QString data)
+
+void YouTube::setSearchResults(const QByteArray &data)
 {
-    /* Usuwanie komentarzy HTML */
-    for (int commentIdx = 0 ;;)
-    {
-        if ((commentIdx = data.indexOf("<!--", commentIdx)) < 0)
-            break;
-        int commentEndIdx = data.indexOf("-->", commentIdx);
-        if (commentEndIdx >= 0) //Jeżeli jest koniec komentarza
-            data.remove(commentIdx, commentEndIdx - commentIdx + 3); //Wyrzuć zakomentowany fragment
-        else
-        {
-            data.remove(commentIdx, data.length() - commentIdx); //Wyrzuć cały tekst do końca
-            break;
-        }
-    }
+    const auto json = getYtInitialData(data);
 
-    int i;
-    const QStringList splitted = data.split("yt-lockup ");
-    for (i = 1; i < splitted.count(); ++i)
+    const auto sectionListRendererContents = json.object()
+        ["contents"].toObject()
+        ["twoColumnSearchResultsRenderer"].toObject()
+        ["primaryContents"].toObject()
+        ["sectionListRenderer"].toObject()
+        ["contents"].toArray()
+    ;
+
+    for (auto &&obj : sectionListRendererContents)
     {
-        QString title, videoInfoLink, duration, image, user;
-        const QString &entry = splitted[i];
-        int idx;
+        const auto contents = obj.toObject()
+            ["itemSectionRenderer"].toObject()
+            ["contents"].toArray()
+        ;
 
-        if (entry.contains("yt-lockup-channel")) //Ignore channels
-            continue;
+        for (auto &&obj : contents)
+        {
+            const auto videoRenderer = obj.toObject()["videoRenderer"].toObject();
+            const auto playlistRenderer = obj.toObject()["playlistRenderer"].toObject();
 
-        const bool isPlaylist = entry.contains("yt-lockup-playlist");
+            const bool isVideo = !videoRenderer.isEmpty() && playlistRenderer.isEmpty();
 
-        if ((idx = entry.indexOf("yt-lockup-title")) > -1)
-        {
-            int urlIdx = entry.indexOf("href=\"", idx);
-            int titleIdx = entry.indexOf("title=\"", idx);
-            if (titleIdx > -1 && urlIdx > -1 && titleIdx > urlIdx)
+            QString title, contentId, length, user, publishTime, viewCount, thumbnail, url;
+
+            if (isVideo)
             {
-                const int endUrlIdx = entry.indexOf("\"", urlIdx += 6);
-                const int endTitleIdx = entry.indexOf("\"", titleIdx += 7);
-                if (endTitleIdx > -1 && endUrlIdx > -1 && endTitleIdx > endUrlIdx)
-                {
-                    videoInfoLink = entry.mid(urlIdx, endUrlIdx - urlIdx).replace("&amp;", "&");
-                    if (!videoInfoLink.isEmpty() && videoInfoLink.startsWith('/'))
-                        videoInfoLink.prepend(YOUTUBE_URL);
-                    title = entry.mid(titleIdx, endTitleIdx - titleIdx);
-                }
+                title = videoRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
+                contentId = videoRenderer["videoId"].toString();
+                if (title.isEmpty() || contentId.isEmpty())
+                    continue;
+
+                length = videoRenderer["lengthText"].toObject()["simpleText"].toString();
+                user = videoRenderer["ownerText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
+                publishTime = videoRenderer["publishedTimeText"].toObject()["simpleText"].toString();
+                viewCount = videoRenderer["shortViewCountText"].toObject()["simpleText"].toString();
+                thumbnail = videoRenderer["thumbnail"].toObject()["thumbnails"].toArray().at(0).toObject()["url"].toString();
+
+                url = YOUTUBE_URL "/watch?v=" + contentId;
             }
-        }
-        if ((idx = entry.indexOf("video-thumb")) > -1)
-        {
-            int skip = 0;
-            int imgIdx = entry.indexOf("data-thumb=\"", idx);
-            if (imgIdx > -1)
-                skip = 12;
             else
             {
-                imgIdx = entry.indexOf("src=\"", idx);
-                skip = 5;
-            }
-            if (imgIdx > -1)
-            {
-                int imgEndIdx = entry.indexOf("\"", imgIdx += skip);
-                if (imgEndIdx > -1)
-                {
-                    image = entry.mid(imgIdx, imgEndIdx - imgIdx);
-                    if (image.endsWith(".gif")) //GIF nie jest miniaturką - jest to pojedynczy piksel :D (very old code, is it still relevant?)
-                        image.clear();
-                    else if (image.startsWith("//"))
-                        image.prepend("https:");
-                    if ((idx = image.indexOf("?")) > 0)
-                        image.truncate(idx);
-                }
-            }
-        }
-        if (!isPlaylist && (idx = entry.indexOf("video-time")) > -1 && (idx = entry.indexOf(">", idx)) > -1)
-        {
-            int endIdx = entry.indexOf("<", idx += 1);
-            if (endIdx > -1)
-            {
-                duration = entry.mid(idx, endIdx - idx);
-                if (!duration.startsWith("0") && duration.indexOf(":") == 1 && duration.count(":") == 1)
-                    duration.prepend("0");
+                title = playlistRenderer["title"].toObject()["simpleText"].toString();
+                contentId = playlistRenderer["playlistId"].toString();
+                if (title.isEmpty() || contentId.isEmpty())
+                    continue;
+
+                user = playlistRenderer["longBylineText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
+                thumbnail = playlistRenderer
+                    ["thumbnailRenderer"].toObject()
+                    ["playlistVideoThumbnailRenderer"].toObject()
+                    ["thumbnail"].toObject()
+                    ["thumbnails"].toArray().at(0).toObject()
+                    ["url"].toString()
+                ;
+
+                url = YOUTUBE_URL "/playlist?list=" + contentId;
             }
-        }
-        if ((idx = entry.indexOf("yt-lockup-byline")) > -1)
-        {
-            int endIdx = entry.indexOf("</a>", idx);
-            if (endIdx > -1 && (idx = entry.lastIndexOf(">", endIdx)) > -1)
-            {
-                ++idx;
 
-                QTextDocument txtDoc;
-                txtDoc.setHtml(entry.mid(idx, endIdx - idx));
-                user = txtDoc.toPlainText();
-            }
-        }
+            auto tWI = new QTreeWidgetItem(resultsW);
 
-        if (!title.isEmpty() && !videoInfoLink.isEmpty())
-        {
-            QTreeWidgetItem *tWI = new QTreeWidgetItem(resultsW);
-
-            QTextDocument txtDoc;
-            txtDoc.setHtml(title);
-
-            tWI->setText(0, txtDoc.toPlainText());
-            tWI->setText(1, !isPlaylist ? duration : tr("Playlist"));
+            tWI->setText(0, title);
+            tWI->setText(1, isVideo ? length : tr("Playlist"));
             tWI->setText(2, user);
 
-            tWI->setToolTip(0, QString("%1: %2\n%3: %4\n%5: %6")
-                .arg(resultsW->headerItem()->text(0), tWI->text(0),
-                !isPlaylist ? resultsW->headerItem()->text(1) : tr("Playlist"),
-                !isPlaylist ? tWI->text(1) : tr("yes"),
-                resultsW->headerItem()->text(2), tWI->text(2))
-            );
+            QString tooltip;
+            tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(0), tWI->text(0));
+            tooltip += QString("%1: %2\n").arg(isVideo ? resultsW->headerItem()->text(1) : tr("Playlist"), isVideo ? tWI->text(1) : tr("yes"));
+            tooltip += QString("%1: %2").arg(resultsW->headerItem()->text(2), tWI->text(2));
+            if (isVideo)
+            {
+                tooltip += QString("\n%1: %2\n").arg(tr("Publish time"), publishTime);
+                tooltip += QString("%1: %2").arg(tr("View count"), viewCount);
+            }
+            tWI->setToolTip(0, tooltip);
 
-            tWI->setData(0, Qt::UserRole, videoInfoLink);
-            tWI->setData(1, Qt::UserRole, isPlaylist);
+            tWI->setData(0, Qt::UserRole, url);
+            tWI->setData(1, Qt::UserRole, !isVideo);
 
-            if (isPlaylist)
+            if (!isVideo)
             {
                 tWI->setDisabled(true);
 
-                NetworkReply *linkReply = net.start(videoInfoLink);
-                linkReply->setProperty("tWI", qVariantFromValue((void *)tWI));
+                auto linkReply = net.start(url);
+                linkReply->setProperty("tWI", QVariant::fromValue((void *)tWI));
                 linkReplies += linkReply;
             }
 
-            NetworkReply *imageReply = net.start(image);
-            imageReply->setProperty("tWI", qVariantFromValue((void *)tWI));
-            imageReplies += imageReply;
+            if (!thumbnail.isEmpty())
+            {
+                auto imageReply = net.start(thumbnail);
+                imageReply->setProperty("tWI", QVariant::fromValue((void *)tWI));
+                imageReplies += imageReply;
+            }
         }
     }
 
-    if (i == 1)
-    {
-        resultsW->clear();
-    }
-    else
+    if (resultsW->topLevelItemCount() > 0)
     {
         pageSwitcher->currPageB->setValue(currPage);
         pageSwitcher->show();
@@ -927,7 +1018,6 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
     const auto videoItags = m_videoItags;
     const auto audioItags = m_audioItags;
     const auto hlsItags = m_hlsItags;
-    const auto singleUrlItags = m_singleUrlItags;
     m_itagsMutex.unlock();
 
     QHash<int, QPair<QString, QString>> itagsData;
@@ -938,11 +1028,11 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
         if (format.isEmpty())
             continue;
 
-        const auto container = format["container"].toString();
-        if (container.contains("dash", Qt::CaseInsensitive))
+        const auto protocol = format["protocol"].toString();
+        if (protocol.contains("dash", Qt::CaseInsensitive))
         {
-            // Skip MP4 DASH, because it doesn't work properly
-            continue;
+            if (format.contains("fragment_base_url"))
+                continue; // Skip DASH, because it doesn't work
         }
 
         const auto itag = format["format_id"].toString().toInt();
@@ -952,11 +1042,11 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
             itagsData[itag] = {url, "." + ext};
     }
 
-    auto appendUrl = [&](const QList<int> &itags) {
+    auto appendUrl = [&](const QVector<int> &itags) {
         for (auto &&itag : itags)
         {
             auto it = itagsData.constFind(itag);
-            if (it != itagsData.cend())
+            if (it != itagsData.constEnd())
             {
                 urls += it->first;
                 exts += it->second;
@@ -965,19 +1055,21 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
         }
     };
 
-    if (!isLive)
+    if (isLive)
     {
+        appendUrl(hlsItags);
+    }
+    else
+    {
+        appendUrl(audioItags);
         if (!audioOnly)
             appendUrl(videoItags);
-        appendUrl(audioItags);
     }
+
     if (urls.count() != 1 + (audioOnly ? 0 : 1))
     {
-        if (!urls.isEmpty())
-            urls.clear();
-        appendUrl(hlsItags);
-        if (urls.isEmpty())
-            appendUrl(singleUrlItags);
+        urls.clear();
+        exts.clear();
     }
 
     if (urls.isEmpty())
@@ -988,6 +1080,8 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
 
     const auto subtitles = o["subtitles"].toObject();
     QString lang = QMPlay2Core.getSettings().getString("SubtitlesLanguage");
+    if (lang.isEmpty()) // Default language
+        lang = QLocale::languageToString(QLocale::system().language());
     if (!audioOnly && m_allowSubtitles && !subtitles.isEmpty() && !lang.isEmpty())
     {
         // Try to convert full language name into short language code
@@ -1007,6 +1101,8 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
         auto subtitlesForLang = subtitles[lang].toArray();
         if (subtitlesForLang.isEmpty())
             subtitlesForLang = subtitles[QMPlay2Core.getLanguage()].toArray();
+        if (subtitlesForLang.isEmpty())
+            subtitlesForLang = subtitles["en"].toArray();
 
         for (auto &&subtitlesFmtVal : asConst(subtitlesForLang))
         {
@@ -1056,44 +1152,78 @@ QStringList YouTube::getYouTubeVideo(const QString &param, const QString &url, I
     return result;
 }
 
-void YouTube::preparePlaylist(const QString &data, QTreeWidgetItem *tWI)
+void YouTube::preparePlaylist(const QByteArray &data, QTreeWidgetItem *tWI)
 {
-    int idx = data.indexOf("playlist-videos-container");
-    if (idx > -1)
+    QStringList playlist;
+
+    const auto json = getYtInitialData(data);
+
+    const auto contents = json.object()
+        ["contents"].toObject()
+        ["twoColumnBrowseResultsRenderer"].toObject()
+        ["tabs"].toArray().at(0).toObject()
+        ["tabRenderer"].toObject()
+        ["content"].toObject()
+        ["sectionListRenderer"].toObject()
+        ["contents"].toArray().at(0).toObject()
+        ["itemSectionRenderer"].toObject()
+        ["contents"].toArray().at(0).toObject()
+        ["playlistVideoListRenderer"].toObject()
+        ["contents"].toArray()
+    ;
+
+    for (auto &&obj : contents)
     {
-        const QString tags[2] = {"video-id", "video-title"};
-        QStringList playlist, entries = data.mid(idx).split("yt-uix-scroller-scroll-unit", QString::SkipEmptyParts);
-        entries.removeFirst();
-        for (const QString &entry : asConst(entries))
-        {
-            QStringList plistEntry;
-            for (int i = 0; i < 2; ++i)
-            {
-                idx = entry.indexOf(tags[i]);
-                if (idx > -1 && (idx = entry.indexOf('"', idx += tags[i].length())) > -1)
-                {
-                    const int endIdx = entry.indexOf('"', idx += 1);
-                    if (endIdx > -1)
-                    {
-                        const QString str = entry.mid(idx, endIdx - idx);
-                        if (!i)
-                            plistEntry += str;
-                        else
-                        {
-                            QTextDocument txtDoc;
-                            txtDoc.setHtml(str);
-                            plistEntry += txtDoc.toPlainText();
-                        }
-                    }
-                }
-            }
-            if (plistEntry.count() == 2)
-                playlist += plistEntry;
-        }
-        if (!playlist.isEmpty())
-        {
-            tWI->setData(0, Qt::UserRole + 1, playlist);
-            tWI->setDisabled(false);
-        }
+        const auto playlistRenderer = obj.toObject()["playlistVideoRenderer"].toObject();
+
+        const auto title = playlistRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
+        const auto videoId = playlistRenderer["videoId"].toString();
+        if (title.isEmpty() || videoId.isEmpty())
+            continue;
+
+        playlist += {
+            videoId,
+            title,
+        };
+    }
+
+    if (!playlist.isEmpty())
+    {
+        tWI->setData(0, Qt::UserRole + 1, playlist);
+        tWI->setDisabled(false);
+    }
+}
+
+void YouTube::onShowSettingsClicked() {
+    emit QMPlay2Core.showSettings("Extensions");
+}
+
+void YouTube::onSortByChanged(int index) {
+    if (m_sortByIdx != index) {
+        m_sortByIdx = index;
+        sets().set("YouTube/SortBy", m_sortByIdx);
+        search();
     }
 }
+
+QJsonDocument YouTube::getYtInitialData(const QByteArray &data)
+{
+    int idx = data.indexOf("ytInitialData");
+    if (idx < 0)
+        return QJsonDocument();
+
+    idx = data.indexOf("{", idx);
+    if (idx < 0)
+        return QJsonDocument();
+
+    QJsonParseError e = {};
+    auto jsonDoc = QJsonDocument::fromJson(data.mid(idx), &e);
+
+    if (Q_UNLIKELY(e.error == QJsonParseError::NoError))
+        return jsonDoc;
+
+    if (e.error == QJsonParseError::GarbageAtEnd && e.offset > 0)
+        return QJsonDocument::fromJson(data.mid(idx, e.offset));
+
+    return QJsonDocument();
+}
diff --git a/src/modules/Extensions/YouTube.hpp b/src/modules/Extensions/YouTube.hpp
index 17350c2d..d853bbb1 100644
--- a/src/modules/Extensions/YouTube.hpp
+++ b/src/modules/Extensions/YouTube.hpp
@@ -26,6 +26,8 @@
 #include <QPointer>
 #include <QMutex>
 
+#include <QJsonDocument.h>
+
 class NetworkReply;
 class QProgressBar;
 class QActionGroup;
@@ -37,18 +39,27 @@ class LineEdit;
 class QLabel;
 class QMenu;
 
+enum class PreferredCodec
+{
+    VP9,
+    H264,
+    AV1,
+};
+
 /**/
 
-class ResultsYoutube final : public QTreeWidget
+class ResultsYoutube : public QTreeWidget
 {
     Q_OBJECT
 public:
     ResultsYoutube();
-    ~ResultsYoutube();
+    ~ResultsYoutube() final;
 
 private:
     void playOrEnqueue(const QString &param, QTreeWidgetItem *tWI, const QString &addrParam = QString());
 
+    QString currentParam;
+
     QMenu *menu;
 
 private slots:
@@ -58,11 +69,14 @@ private slots:
     void copyPageURL();
 
     void contextMenu(const QPoint &p);
+
+    void playCurrentItem();
+    void enqueueCurrentItem();
 };
 
 /**/
 
-class PageSwitcher final : public QWidget
+class PageSwitcher : public QWidget
 {
     Q_OBJECT
 public:
@@ -76,7 +90,7 @@ public:
 
 using ItagNames = QPair<QStringList, QList<int>>;
 
-class YouTube final : public QWidget, public QMPlay2Extensions
+class YouTube : public QWidget, public QMPlay2Extensions
 {
     Q_OBJECT
 
@@ -85,19 +99,19 @@ public:
 
 public:
     YouTube(Module &module);
-    ~YouTube();
+    ~YouTube() final;
 
-    bool set() override;
+    bool set() override final;
 
-    DockWidget *getDockWidget() override;
+    DockWidget *getDockWidget() override final;
 
-    bool canConvertAddress() const override;
+    bool canConvertAddress() const override final;
 
-    QString matchAddress(const QString &url) const override;
-    QList<AddressPrefix> addressPrefixList(bool) const override;
-    void convertAddress(const QString &, const QString &, const QString &, QString *, QString *, QIcon *, QString *, IOController<> *ioCtrl) override;
+    QString matchAddress(const QString &url) const override final;
+    QList<AddressPrefix> addressPrefixList(bool) const override final;
+    void convertAddress(const QString &, const QString &, const QString &, QString *, QString *, QIcon *, QString *, IOController<> *ioCtrl) override final;
 
-    QVector<QAction *> getActions(const QString &, double, const QString &, const QString &, const QString &) override;
+    QVector<QAction *> getActions(const QString &, double, const QString &, const QString &, const QString &) override final;
 
 private slots:
     void next();
@@ -111,18 +125,26 @@ private slots:
 
     void searchMenu();
 
+    void onShowSettingsClicked();
+    void onQualityPresetChanged(const QString &preset);
+    void onQualityToggled();
+    void onSortByChanged(int index);
+
 private:
     void setItags(int qualityIdx);
 
     void deleteReplies();
 
     void setAutocomplete(const QByteArray &data);
-    void setSearchResults(QString data);
+    void setSearchResults(const QByteArray &data);
 
     QStringList getYouTubeVideo(const QString &param, const QString &url, IOController<YouTubeDL> &youTubeDL);
 
-    void preparePlaylist(const QString &data, QTreeWidgetItem *tWI);
+    void preparePlaylist(const QByteArray &data, QTreeWidgetItem *tWI);
 
+    QJsonDocument getYtInitialData(const QByteArray &data);
+
+private:
     DockWidget *dw;
 
     QIcon youtubeIcon, videoIcon;
@@ -148,7 +170,9 @@ private:
     int m_sortByIdx = 0;
 
     QMutex m_itagsMutex;
-    QList<int> m_videoItags, m_audioItags, m_hlsItags, m_singleUrlItags;
+    PreferredCodec m_preferredCodec = PreferredCodec::VP9;
+    bool m_allowVp9Hdr = false;
+    QVector<int> m_videoItags, m_audioItags, m_hlsItags, m_singleUrlItags;
 };
 
 #define YouTubeName "YouTube Browser"
diff --git a/src/modules/FFmpeg/FFDemux.cpp b/src/modules/FFmpeg/FFDemux.cpp
index f8fa33ee..69c5942f 100644
--- a/src/modules/FFmpeg/FFDemux.cpp
+++ b/src/modules/FFmpeg/FFDemux.cpp
@@ -408,15 +408,18 @@ Playlist::Entries FFDemux::fetchTracks(const QString &url, bool &ok)
             {
                 Playlist::Entry &entry = entries[i];
                 const bool lastItem = (i == entries.count() - 1);
-                const double start = indexes.value(i).second;
-                const double end = indexes.value(i + 1, {-1.0, -1.0}).first;
+                const double start = indexes.contains(i) ? indexes.value(i).second : -1.0;
+                double end = -1.0;
+                if (indexes.contains(i + 1))
+                    end = indexes.value(i + 1).first;
+
                 if (entry.url.isEmpty() || start < 0.0 || (end <= 0.0 && !lastItem))
                 {
-                    entries.removeAt(i);
+                    entries.remove(i);
                     continue;
                 }
                 const QString param = QString("CUE:%1:%2").arg(start).arg(end);
-                if (lastItem && end < 0.0) // Last entry doesn't have specified length in CUE file
+                if (lastItem && end < 0.0)
                 {
                     FormatContext *fmtCtx = createFmtCtx();
                     if (fmtCtx->open(entry.url, param))
diff --git a/src/modules/FFmpeg/FFDemux.hpp b/src/modules/FFmpeg/FFDemux.hpp
index 498f4b29..389b3419 100644
--- a/src/modules/FFmpeg/FFDemux.hpp
+++ b/src/modules/FFmpeg/FFDemux.hpp
@@ -22,42 +22,42 @@
 
 class FormatContext;
 
-class FFDemux final : public Demuxer
+class FFDemux : public Demuxer
 {
     Q_DECLARE_TR_FUNCTIONS(FFDemux)
 public:
     FFDemux(Module &);
 private:
-    ~FFDemux();
+    ~FFDemux() final;
 
-    bool set() override;
+    bool set() override final;
 
-    bool metadataChanged() const override;
+    bool metadataChanged() const override final;
 
-    bool isStillImage() const override;
+    bool isStillImage() const override final;
 
-    QList<ProgramInfo> getPrograms() const override;
-    QList<ChapterInfo> getChapters() const override;
+    QList<ProgramInfo> getPrograms() const override final;
+    QList<ChapterInfo> getChapters() const override final;
 
-    QString name() const override;
-    QString title() const override;
-    QList<QMPlay2Tag> tags() const override;
-    bool getReplayGain(bool album, float &gain_db, float &peak) const override;
-    qint64 size() const override;
-    double length() const override;
-    int bitrate() const override;
-    QByteArray image(bool forceCopy) const override;
+    QString name() const override final;
+    QString title() const override final;
+    QList<QMPlay2Tag> tags() const override final;
+    bool getReplayGain(bool album, float &gain_db, float &peak) const override final;
+    qint64 size() const override final;
+    double length() const override final;
+    int bitrate() const override final;
+    QByteArray image(bool forceCopy) const override final;
 
-    bool localStream() const override;
+    bool localStream() const override final;
 
-    bool seek(double pos, bool backward) override;
-    bool read(Packet &encoded, int &idx) override;
-    void pause() override;
-    void abort() override;
+    bool seek(double pos, bool backward) override final;
+    bool read(Packet &encoded, int &idx) override final;
+    void pause() override final;
+    void abort() override final;
 
-    bool open(const QString &entireUrl) override;
+    bool open(const QString &entireUrl) override final;
 
-    Playlist::Entries fetchTracks(const QString &url, bool &ok) override;
+    Playlist::Entries fetchTracks(const QString &url, bool &ok) override final;
 
     /**/
 
diff --git a/src/modules/FFmpeg/FFReader.hpp b/src/modules/FFmpeg/FFReader.hpp
index 75caec11..0cd53f11 100644
--- a/src/modules/FFmpeg/FFReader.hpp
+++ b/src/modules/FFmpeg/FFReader.hpp
@@ -24,25 +24,25 @@
 
 struct AVIOContext;
 
-class FFReader final : public Reader
+class FFReader : public Reader
 {
 public:
     FFReader();
 private:
-    bool readyRead() const override;
-    bool canSeek() const override;
+    bool readyRead() const override final;
+    bool canSeek() const override final;
 
-    bool seek(qint64) override;
-    QByteArray read(qint64) override;
-    void pause() override;
-    bool atEnd() const override;
-    void abort() override;
+    bool seek(qint64) override final;
+    QByteArray read(qint64) override final;
+    void pause() override final;
+    bool atEnd() const override final;
+    void abort() override final;
 
-    qint64 size() const override;
-    qint64 pos() const override;
-    QString name() const override;
+    qint64 size() const override final;
+    qint64 pos() const override final;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
diff --git a/src/modules/FFmpeg/FFmpeg.hpp b/src/modules/FFmpeg/FFmpeg.hpp
index 70e73d4e..0e5ad238 100644
--- a/src/modules/FFmpeg/FFmpeg.hpp
+++ b/src/modules/FFmpeg/FFmpeg.hpp
@@ -24,19 +24,19 @@
 
 class QComboBox;
 
-class FFmpeg final : public Module
+class FFmpeg : public Module
 {
     Q_DECLARE_TR_FUNCTIONS(FFmpeg)
 public:
     FFmpeg();
-    ~FFmpeg();
+    ~FFmpeg() final;
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 
-    void videoDeintSave() override;
+    void videoDeintSave() override final;
 
     /**/
 
@@ -65,7 +65,7 @@ class QGroupBox;
 class QSpinBox;
 class Slider;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
 #ifdef QMPlay2_VDPAU
     Q_OBJECT
@@ -84,7 +84,7 @@ private slots:
     void checkEnables();
 #endif
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     QGroupBox *demuxerB;
     QCheckBox *reconnectStreamedB;
diff --git a/src/modules/FFmpeg/FormatContext.cpp b/src/modules/FFmpeg/FormatContext.cpp
index 0dd0231f..71b2a266 100644
--- a/src/modules/FFmpeg/FormatContext.cpp
+++ b/src/modules/FFmpeg/FormatContext.cpp
@@ -843,6 +843,10 @@ StreamInfo *FormatContext::getStreamInfo(AVStream *stream) const
         if (streamInfo->codec_name.isEmpty())
             streamInfo->codec_name = codecDescr->name;
     }
+    else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+    {
+        streamInfo->must_decode = false;
+    }
 
     streamInfo->bitrate = stream->codecpar->bit_rate;
     streamInfo->bpcs = stream->codecpar->bits_per_coded_sample;
diff --git a/src/modules/FFmpeg/VAAPIWriter.hpp b/src/modules/FFmpeg/VAAPIWriter.hpp
index c36966db..84249e53 100644
--- a/src/modules/FFmpeg/VAAPIWriter.hpp
+++ b/src/modules/FFmpeg/VAAPIWriter.hpp
@@ -24,27 +24,27 @@
 #include <QWidget>
 #include <QTimer>
 
-class VAAPIWriter final : public QWidget, public VideoWriter
+class VAAPIWriter : public QWidget, public VideoWriter
 {
     Q_OBJECT
 public:
     VAAPIWriter(Module &module, VAAPI *vaapi);
-    ~VAAPIWriter();
+    ~VAAPIWriter() final;
 
-    bool set() override;
+    bool set() override final;
 
-    bool readyWrite() const override;
+    bool readyWrite() const override final;
 
-    bool processParams(bool *paramsCorrected) override;
-    void writeVideo(const VideoFrame &videoFrame) override;
-    void writeOSD(const QList<const QMPlay2OSD *> &osd) override;
-    void pause() override;
+    bool processParams(bool *paramsCorrected) override final;
+    void writeVideo(const VideoFrame &videoFrame) override final;
+    void writeOSD(const QList<const QMPlay2OSD *> &osd) override final;
+    void pause() override final;
 
-    bool hwAccelGetImg(const VideoFrame &videoFrame, void *dest, ImgScaler *nv12ToRGB32) const override;
+    bool hwAccelGetImg(const VideoFrame &videoFrame, void *dest, ImgScaler *nv12ToRGB32) const override final;
 
-    QString name() const override;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
@@ -56,11 +56,11 @@ public:
 private:
     void draw(VASurfaceID id = -1, int field = -1);
 
-    void resizeEvent(QResizeEvent *) override;
-    void paintEvent(QPaintEvent *) override;
-    bool event(QEvent *) override;
+    void resizeEvent(QResizeEvent *) override final;
+    void paintEvent(QPaintEvent *) override final;
+    bool event(QEvent *) override final;
 
-    QPaintEngine *paintEngine() const override;
+    QPaintEngine *paintEngine() const override final;
 
     void clearVaImage();
 
diff --git a/src/modules/FFmpeg/VDPAUWriter.hpp b/src/modules/FFmpeg/VDPAUWriter.hpp
index 4c6dc28f..123fb86b 100644
--- a/src/modules/FFmpeg/VDPAUWriter.hpp
+++ b/src/modules/FFmpeg/VDPAUWriter.hpp
@@ -28,27 +28,27 @@
 
 struct _XDisplay;
 
-class VDPAUWriter final : public QWidget, public VideoWriter
+class VDPAUWriter : public QWidget, public VideoWriter
 {
     Q_OBJECT
 public:
     VDPAUWriter(Module &module);
-    ~VDPAUWriter();
+    ~VDPAUWriter() final;
 
-    bool set() override;
+    bool set() override final;
 
-    bool readyWrite() const override;
+    bool readyWrite() const override final;
 
-    bool processParams(bool *paramsCorrected) override;
-    void writeVideo(const VideoFrame &videoFrame) override;
-    void writeOSD(const QList<const QMPlay2OSD *> &osd) override;
-    void pause() override;
+    bool processParams(bool *paramsCorrected) override final;
+    void writeVideo(const VideoFrame &videoFrame) override final;
+    void writeOSD(const QList<const QMPlay2OSD *> &osd) override final;
+    void pause() override final;
 
-    bool hwAccelGetImg(const VideoFrame &videoFrame, void *dest, ImgScaler *nv12ToRGB32) const override;
+    bool hwAccelGetImg(const VideoFrame &videoFrame, void *dest, ImgScaler *nv12ToRGB32) const override final;
 
-    QString name() const override;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
@@ -78,11 +78,11 @@ private:
     Q_SLOT void draw(VdpVideoSurface surface_id = VDP_INVALID_HANDLE);
     void vdpau_display();
 
-    void resizeEvent(QResizeEvent *) override;
-    void paintEvent(QPaintEvent *) override;
-    bool event(QEvent *) override;
+    void resizeEvent(QResizeEvent *) override final;
+    void paintEvent(QPaintEvent *) override final;
+    bool event(QEvent *) override final;
 
-    QPaintEngine *paintEngine() const override;
+    QPaintEngine *paintEngine() const override final;
 
     void destroyOutputSurfaces();
     void clr();
diff --git a/src/modules/Inputs/Inputs.hpp b/src/modules/Inputs/Inputs.hpp
index 9cdbcff3..5484fe53 100644
--- a/src/modules/Inputs/Inputs.hpp
+++ b/src/modules/Inputs/Inputs.hpp
@@ -20,18 +20,18 @@
 
 #include <Module.hpp>
 
-class Inputs final : public Module
+class Inputs : public Module
 {
     Q_OBJECT
 public:
     Inputs();
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    QList<QAction *> getAddActions() override;
+    QList<QAction *> getAddActions() override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 
     QIcon toneIcon, pcmIcon, rayman2Icon;
 private slots:
@@ -43,7 +43,7 @@ private slots:
 #include <QSpinBox>
 #include <QDialog>
 
-class HzW final : public QWidget
+class HzW : public QWidget
 {
 public:
     HzW(int, const QStringList &);
@@ -60,7 +60,7 @@ private:
 
 class QGridLayout;
 
-class AddD final : public QDialog
+class AddD : public QDialog
 {
     Q_OBJECT
 public:
@@ -95,7 +95,7 @@ class QGroupBox;
 class QCheckBox;
 class QLineEdit;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_OBJECT
 public:
@@ -103,7 +103,7 @@ public:
 private slots:
     void applyFreqs();
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     AddD *toneGenerator;
     QGroupBox *pcmB;
diff --git a/src/modules/Inputs/PCM.hpp b/src/modules/Inputs/PCM.hpp
index eb47a911..528cb35a 100644
--- a/src/modules/Inputs/PCM.hpp
+++ b/src/modules/Inputs/PCM.hpp
@@ -24,25 +24,25 @@
 
 class Reader;
 
-class PCM final : public Demuxer
+class PCM : public Demuxer
 {
 public:
     enum FORMAT {PCM_U8, PCM_S8, PCM_S16, PCM_S24, PCM_S32, PCM_FLT, FORMAT_COUNT};
 
     PCM(Module &);
 private:
-    bool set() override;
+    bool set() override final;
 
-    QString name() const override;
-    QString title() const override;
-    double length() const override;
-    int bitrate() const override;
+    QString name() const override final;
+    QString title() const override final;
+    double length() const override final;
+    int bitrate() const override final;
 
-    bool seek(double, bool) override;
-    bool read(Packet &, int &) override;
-    void abort() override;
+    bool seek(double, bool) override final;
+    bool read(Packet &, int &) override final;
+    void abort() override final;
 
-    bool open(const QString &) override;
+    bool open(const QString &) override final;
 
     /**/
 
diff --git a/src/modules/Inputs/Rayman2.hpp b/src/modules/Inputs/Rayman2.hpp
index af623d90..2326a272 100644
--- a/src/modules/Inputs/Rayman2.hpp
+++ b/src/modules/Inputs/Rayman2.hpp
@@ -24,23 +24,23 @@
 
 class Reader;
 
-class Rayman2 final : public Demuxer
+class Rayman2 : public Demuxer
 {
 public:
     Rayman2(Module &);
 private:
-    bool set() override;
+    bool set() override final;
 
-    QString name() const override;
-    QString title() const override;
-    double length() const override;
-    int bitrate() const override;
+    QString name() const override final;
+    QString title() const override final;
+    double length() const override final;
+    int bitrate() const override final;
 
-    bool seek(double pos, bool backward) override;
-    bool read(Packet &, int &) override;
-    void abort() override;
+    bool seek(double pos, bool backward) override final;
+    bool read(Packet &, int &) override final;
+    void abort() override final;
 
-    bool open(const QString &) override;
+    bool open(const QString &) override final;
 
     /**/
 
diff --git a/src/modules/Inputs/ToneGenerator.cpp b/src/modules/Inputs/ToneGenerator.cpp
index 62ff4436..83c1d0ea 100644
--- a/src/modules/Inputs/ToneGenerator.cpp
+++ b/src/modules/Inputs/ToneGenerator.cpp
@@ -21,7 +21,8 @@
 #include <Functions.hpp>
 #include <Packet.hpp>
 
-#include <QUrlQuery>
+#include <QUrl>
+#define QUrlQuery(url) url
 
 #include <cmath>
 
diff --git a/src/modules/Inputs/ToneGenerator.hpp b/src/modules/Inputs/ToneGenerator.hpp
index a84c7fb0..9b29313b 100644
--- a/src/modules/Inputs/ToneGenerator.hpp
+++ b/src/modules/Inputs/ToneGenerator.hpp
@@ -20,28 +20,28 @@
 
 #include <Demuxer.hpp>
 
-class ToneGenerator final : public Demuxer
+class ToneGenerator : public Demuxer
 {
     Q_DECLARE_TR_FUNCTIONS(ToneGenerator)
 public:
     ToneGenerator(Module &);
 
-    bool set() override;
+    bool set() override final;
 private:
-    bool metadataChanged() const override;
+    bool metadataChanged() const override final;
 
-    QString name() const override;
-    QString title() const override;
-    double length() const override;
-    int bitrate() const override;
+    QString name() const override final;
+    QString title() const override final;
+    double length() const override final;
+    int bitrate() const override final;
 
-    bool dontUseBuffer() const override;
+    bool dontUseBuffer() const override final;
 
-    bool seek(double, bool) override;
-    bool read(Packet &, int &) override;
-    void abort() override;
+    bool seek(double, bool) override final;
+    bool read(Packet &, int &) override final;
+    void abort() override final;
 
-    bool open(const QString &) override;
+    bool open(const QString &) override final;
 
     /**/
 
diff --git a/src/modules/OpenGL2/CMakeLists.txt b/src/modules/OpenGL2/CMakeLists.txt
index 9a411b44..84926476 100644
--- a/src/modules/OpenGL2/CMakeLists.txt
+++ b/src/modules/OpenGL2/CMakeLists.txt
@@ -5,7 +5,6 @@ set(OpenGL2_HDR
     OpenGL2.hpp
     OpenGL2Writer.hpp
     OpenGL2Common.hpp
-    OpenGL2Window.hpp
     OpenGL2Widget.hpp
     Sphere.hpp
     Vertices.hpp
@@ -15,7 +14,6 @@ set(OpenGL2_SRC
     OpenGL2.cpp
     OpenGL2Writer.cpp
     OpenGL2Common.cpp
-    OpenGL2Window.cpp
     OpenGL2Widget.cpp
     Sphere.cpp
 )
@@ -24,15 +22,25 @@ set(OpenGL2_RESOURCES
     res.qrc
 )
 
-if(NOT WIN32)
-    if(Qt5Gui_OPENGL_IMPLEMENTATION STREQUAL GLESv2)
-        set(QT_USES_OPENGLES ON)
-        add_definitions(-DOPENGL_ES2)
-    elseif(Qt5Gui_OPENGL_IMPLEMENTATION STREQUAL GLES)
-        message(SEND_ERROR "OpenGL|ES 1.0 is not supported!")
-    endif()
+function(gles1Error)
+    message(SEND_ERROR "OpenGL|ES 1.0 is not supported!")
+endfunction()
+
+find_package(Qt4 REQUIRED QtOpenGL)
+include(${QT_USE_FILE})
+if(${QT_QCONFIG} MATCHES "opengles2")
+    set(QT_USES_OPENGLES ON)
+    add_definitions(-DOPENGL_ES2)
+elseif(${QT_QCONFIG} MATCHES "opengles1")
+    gles1Error()
 endif()
 
+if(NOT QT_USES_OPENGLES)
+    add_definitions(-DVSYNC_SETTINGS)
+endif()
+
+qt4_add_resources(OpenGL2_RESOURCES_RCC ${OpenGL2_RESOURCES})
+
 include_directories(../../qmplay2/headers)
 
 add_library(${PROJECT_NAME} ${QMPLAY2_MODULE}
@@ -41,6 +49,8 @@ add_library(${PROJECT_NAME} ${QMPLAY2_MODULE}
     ${OpenGL2_RESOURCES}
 )
 
+target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui Qt4::QtOpenGL)
+
 target_link_libraries(${PROJECT_NAME}
     libqmplay2
 )
@@ -48,12 +58,6 @@ target_link_libraries(${PROJECT_NAME}
 if(APPLE)
     find_package(OpenGL REQUIRED)
     target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES})
-elseif(WIN32)
-    if(QT_USES_OPENGLES)
-        target_link_libraries(${PROJECT_NAME} GLESv2)
-    else()
-        target_link_libraries(${PROJECT_NAME} opengl32)
-    endif()
 endif()
 
 install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${MODULES_INSTALL_PATH})
diff --git a/src/modules/OpenGL2/OpenGL2.cpp b/src/modules/OpenGL2/OpenGL2.cpp
index b52c76be..a98a6f51 100644
--- a/src/modules/OpenGL2/OpenGL2.cpp
+++ b/src/modules/OpenGL2/OpenGL2.cpp
@@ -19,8 +19,6 @@
 #include <OpenGL2.hpp>
 #include <OpenGL2Writer.hpp>
 
-#include <QGuiApplication>
-
 OpenGL2::OpenGL2() :
     Module("OpenGL2")
 {
@@ -29,12 +27,8 @@ OpenGL2::OpenGL2() :
     init("Enabled", true);
     init("AllowPBO", true);
     init("HQScaling", false);
-    const QString platformName = QGuiApplication::platformName();
-    init("ForceRtt", (platformName == "cocoa" || platformName == "android"));
+#ifdef VSYNC_SETTINGS
     init("VSync", true);
-#ifdef Q_OS_WIN
-    if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0)
-        init("PreventFullScreen", true);
 #endif
 }
 
@@ -45,6 +39,7 @@ QList<OpenGL2::Info> OpenGL2::getModulesInfo(const bool showDisabled) const
         modulesInfo += Info(OpenGL2WriterName, WRITER, QStringList{"video"});
     return modulesInfo;
 }
+
 void *OpenGL2::createInstance(const QString &name)
 {
     if (name == OpenGL2WriterName && getBool("Enabled"))
@@ -76,35 +71,16 @@ ModuleSettingsWidget::ModuleSettingsWidget(Module &module) :
     hqScalingB = new QCheckBox(tr("High quality video scaling"));
     hqScalingB->setToolTip(tr("Trilinear filtering for minification and bicubic filtering for magnification."));
     hqScalingB->setChecked(sets().getBool("HQScaling"));
-
-    forceRttB = new QCheckBox(tr("Force render to texture if possible (not recommended)"));
-    forceRttB->setToolTip(tr("Always enabled on Wayland and Android platforms.\nSet visualizations to OpenGL mode if enabled."));
-    forceRttB->setChecked(sets().getBool("ForceRtt"));
-
+#ifdef VSYNC_SETTINGS
     vsyncB = new QCheckBox(tr("Vertical sync") +  " (VSync)");
     vsyncB->setChecked(sets().getBool("VSync"));
-
-#ifdef Q_OS_WIN
-    if (QSysInfo::windowsVersion() >= QSysInfo::WV_6_0)
-    {
-        preventFullScreenB = new QCheckBox(tr("Try to prevent exclusive full screen"));
-        preventFullScreenB->setChecked(sets().getBool("PreventFullScreen"));
-    }
-    else
-    {
-        preventFullScreenB = nullptr;
-    }
 #endif
-
     QGridLayout *layout = new QGridLayout(this);
     layout->addWidget(enabledB);
     layout->addWidget(allowPboB);
     layout->addWidget(hqScalingB);
-    layout->addWidget(forceRttB);
+#ifdef VSYNC_SETTINGS
     layout->addWidget(vsyncB);
-#ifdef Q_OS_WIN
-    if (preventFullScreenB)
-        layout->addWidget(preventFullScreenB);
 #endif
 }
 
@@ -113,10 +89,7 @@ void ModuleSettingsWidget::saveSettings()
     sets().set("Enabled", enabledB->isChecked());
     sets().set("AllowPBO", allowPboB->isChecked());
     sets().set("HQScaling", hqScalingB->isChecked());
-    sets().set("ForceRtt", forceRttB->isChecked());
+#ifdef VSYNC_SETTINGS
     sets().set("VSync", vsyncB->isChecked());
-#ifdef Q_OS_WIN
-    if (preventFullScreenB)
-        sets().set("PreventFullScreen", preventFullScreenB->isChecked());
 #endif
 }
diff --git a/src/modules/OpenGL2/OpenGL2.hpp b/src/modules/OpenGL2/OpenGL2.hpp
index 89f22d21..83aab459 100644
--- a/src/modules/OpenGL2/OpenGL2.hpp
+++ b/src/modules/OpenGL2/OpenGL2.hpp
@@ -20,15 +20,16 @@
 
 #include <Module.hpp>
 
-class OpenGL2 final : public Module
+class OpenGL2 : public Module
 {
 public:
     OpenGL2();
+
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 };
 
 /**/
@@ -37,18 +38,18 @@ private:
 
 class QCheckBox;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_DECLARE_TR_FUNCTIONS(ModuleSettingsWidget)
+
 public:
     ModuleSettingsWidget(Module &);
+
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     QCheckBox *enabledB, *allowPboB, *hqScalingB;
-    QCheckBox *forceRttB;
+#ifdef VSYNC_SETTINGS
     QCheckBox *vsyncB;
-#ifdef Q_OS_WIN
-    QCheckBox *preventFullScreenB;
 #endif
 };
diff --git a/src/modules/OpenGL2/OpenGL2Common.cpp b/src/modules/OpenGL2/OpenGL2Common.cpp
index a5c451d9..4c518938 100644
--- a/src/modules/OpenGL2/OpenGL2Common.cpp
+++ b/src/modules/OpenGL2/OpenGL2Common.cpp
@@ -26,9 +26,8 @@
 #include <VideoFrame.hpp>
 #include <Functions.hpp>
 
-#include <QOffscreenSurface>
-#include <QOpenGLContext>
-#include <QOpenGLShader>
+#include <QGLContext>
+#include <QGLShader>
 #include <QResizeEvent>
 #include <QMatrix4x4>
 #include <QMatrix3x3>
@@ -38,6 +37,16 @@
 
 #include <cmath>
 
+#define QOpenGLContext QGLContext
+#define QOpenGLShader QGLShader
+
+#ifndef GL_MAJOR_VERSION
+    #define GL_MAJOR_VERSION 0x821B
+#endif
+#ifndef GL_MINOR_VERSION
+    #define GL_MINOR_VERSION 0x821C
+#endif
+
 /* OpenGL|ES 2.0 doesn't have those definitions */
 #ifndef GL_MAP_WRITE_BIT
     #define GL_MAP_WRITE_BIT 0x0002
@@ -88,7 +97,9 @@ OpenGL2Common::OpenGL2Common() :
     supportsShaders(false), canCreateNonPowerOfTwoTextures(false),
     glActiveTexture(nullptr),
 #endif
+#ifdef VSYNC_SETTINGS
     vSync(true),
+#endif
     hwAccellnterface(nullptr),
     shaderProgramVideo(nullptr), shaderProgramOSD(nullptr),
     texCoordYCbCrLoc(-1), positionYCbCrLoc(-1), texCoordOSDLoc(-1), positionOSDLoc(-1),
@@ -96,9 +107,6 @@ OpenGL2Common::OpenGL2Common() :
     target(0),
     Deinterlace(0),
     allowPBO(true), hasPbo(false),
-#ifdef Q_OS_WIN
-    preventFullScreen(false),
-#endif
     isPaused(false), isOK(false), hwAccelError(false), hasImage(false), doReset(true), setMatrix(true), correctLinesize(false), canUseHueSharpness(true),
     subsX(-1), subsY(-1), W(-1), H(-1), subsW(-1), subsH(-1), outW(-1), outH(-1), verticesIdx(0),
     glVer(0),
@@ -118,6 +126,7 @@ OpenGL2Common::OpenGL2Common() :
     rotAnimation.setEasingCurve(QEasingCurve::OutQuint);
     rotAnimation.setDuration(1000.0);
 }
+
 OpenGL2Common::~OpenGL2Common()
 {
     contextAboutToBeDestroyed();
@@ -130,24 +139,11 @@ void OpenGL2Common::deleteMe()
     delete this;
 }
 
-bool OpenGL2Common::testGL()
-{
-    QOpenGLContext glCtx;
-    if ((isOK = glCtx.create()))
-    {
-        QOffscreenSurface offscreenSurface;
-        offscreenSurface.create();
-        if ((isOK = glCtx.makeCurrent(&offscreenSurface)))
-            testGLInternal();
-    }
-    return isOK;
-}
-
 void OpenGL2Common::newSize(const QSize &size)
 {
     const bool canUpdate = !size.isValid();
     const QSize winSize = canUpdate ? widget()->size() : size;
-    const qreal dpr = widget()->devicePixelRatioF();
+    const qreal dpr = 1.0;
     if (!isRotate90())
     {
         Functions::getImageSize(aspectRatio, zoom, winSize.width(), winSize.height(), W, H, &subsX, &subsY);
@@ -167,6 +163,7 @@ void OpenGL2Common::newSize(const QSize &size)
             updateTimer.start(40);
     }
 }
+
 void OpenGL2Common::clearImg()
 {
     hasImage = false;
@@ -319,7 +316,9 @@ void OpenGL2Common::initializeGL()
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     }
 
+#ifdef VSYNC_SETTINGS
     setVSync(vSync);
+#endif
 
     doReset = true;
     resetSphereVbo();
@@ -383,7 +382,7 @@ void OpenGL2Common::paintGL()
                 if (hqScaling)
                 {
                     // Must be set before "HWAccelInterface::init()" and must have "m_textureSize"
-                    maybeSetMipmaps(widget()->devicePixelRatioF());
+                    maybeSetMipmaps(1.0);
                 }
 
                 /* Prepare textures, register GL textures */
@@ -426,7 +425,7 @@ void OpenGL2Common::paintGL()
                 texCoordYCbCr[2] = texCoordYCbCr[6] = (videoFrame.linesize[0] == widths[0]) ? 1.0f : (widths[0] / (videoFrame.linesize[0] + 1.0f));
 
                 if (hqScaling)
-                    maybeSetMipmaps(widget()->devicePixelRatioF());
+                    maybeSetMipmaps(1.0);
             }
             resetDone = true;
             hasImage = false;
@@ -567,7 +566,7 @@ void OpenGL2Common::paintGL()
         }
         if (hqScaling)
         {
-            const qreal dpr = widget()->devicePixelRatioF();
+            const qreal dpr = 1.0;
             if (!resetDone)
                 maybeSetMipmaps(dpr);
             const bool useBicubic = (W * dpr > m_textureSize.width() || H * dpr > m_textureSize.height());
@@ -663,7 +662,7 @@ void OpenGL2Common::paintGL()
                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
         }
 
-        const QSizeF winSizeSubs = winSize * widget()->devicePixelRatioF();
+        const QSizeF winSizeSubs = winSize * 1.0;
         const float left   = (bounds.left() + subsX) * 2.0f / winSizeSubs.width() - osdOffset.x();
         const float right  = (bounds.right() + subsX + 1) * 2.0f / winSizeSubs.width() - osdOffset.x();
         const float top    = (bounds.top() + subsY) * 2.0f / winSizeSubs.height() - osdOffset.y();
@@ -817,36 +816,6 @@ void OpenGL2Common::testGLInternal()
     QWidget *w = widget();
     w->grabGesture(Qt::PinchGesture);
     w->setMouseTracking(true);
-#ifdef Q_OS_WIN
-    /*
-     * This property is read by QMPlay2 and it ensures that toolbar will be visible
-     * on fullscreen in Windows Vista and newer on nVidia and AMD drivers.
-    */
-    const bool canPreventFullScreen = (qstrcmp(w->metaObject()->className(), "QOpenGLWidget") != 0);
-    const QSysInfo::WinVersion winVer = QSysInfo::windowsVersion();
-    if (canPreventFullScreen && winVer >= QSysInfo::WV_6_0)
-    {
-        Qt::CheckState compositionEnabled;
-        if (!preventFullScreen)
-            compositionEnabled = Qt::PartiallyChecked;
-        else
-        {
-            compositionEnabled = Qt::Checked;
-            if (winVer <= QSysInfo::WV_6_1) //Windows 8 and 10 can't disable DWM composition
-            {
-                using DwmIsCompositionEnabledProc = HRESULT (WINAPI *)(BOOL *pfEnabled);
-                DwmIsCompositionEnabledProc DwmIsCompositionEnabled = (DwmIsCompositionEnabledProc)GetProcAddress(GetModuleHandleA("dwmapi.dll"), "DwmIsCompositionEnabled");
-                if (DwmIsCompositionEnabled)
-                {
-                    BOOL enabled = false;
-                    if (DwmIsCompositionEnabled(&enabled) == S_OK && !enabled)
-                        compositionEnabled = Qt::PartiallyChecked;
-                }
-            }
-        }
-        w->setProperty("preventFullScreen", (int)compositionEnabled);
-    }
-#endif
 }
 
 bool OpenGL2Common::initGLProc()
@@ -881,6 +850,7 @@ bool OpenGL2Common::initGLProc()
 
     return true;
 }
+
 #ifndef OPENGL_ES2
 void OpenGL2Common::showOpenGLMissingFeaturesMessage()
 {
@@ -1006,6 +976,7 @@ void OpenGL2Common::mousePress(QMouseEvent *e)
         }
     }
 }
+
 void OpenGL2Common::mouseMove(QMouseEvent *e)
 {
     if ((moveVideo || moveOSD) && (e->buttons() & Qt::LeftButton))
@@ -1027,6 +998,7 @@ void OpenGL2Common::mouseMove(QMouseEvent *e)
         updateGL(true);
     }
 }
+
 void OpenGL2Common::mouseRelease(QMouseEvent *e)
 {
     if ((moveVideo || moveOSD) && e->button() == Qt::LeftButton)
@@ -1051,6 +1023,7 @@ void OpenGL2Common::mousePress360(QMouseEvent *e)
         mousePos = e->pos();
     }
 }
+
 void OpenGL2Common::mouseMove360(QMouseEvent *e)
 {
     if (mouseWrapped)
@@ -1072,7 +1045,6 @@ void OpenGL2Common::mouseMove360(QMouseEvent *e)
         mouseTime = currTime;
 
         mousePos = newMousePos;
-        if (e->source() == Qt::MouseEventNotSynthesized)
         {
             if (canWrapMouse)
                 mouseWrapped = Functions::wrapMouse(widget(), mousePos, 1);
@@ -1084,6 +1056,7 @@ void OpenGL2Common::mouseMove360(QMouseEvent *e)
         updateGL(true);
     }
 }
+
 void OpenGL2Common::mouseRelease360(QMouseEvent *e)
 {
     if (buttonPressed && e->button() == Qt::LeftButton)
@@ -1099,11 +1072,13 @@ void OpenGL2Common::mouseRelease360(QMouseEvent *e)
         buttonPressed = false;
     }
 }
+
 inline void OpenGL2Common::resetSphereVbo()
 {
     memset(sphereVbo, 0, sizeof sphereVbo);
     nIndices = 0;
 }
+
 inline void OpenGL2Common::deleteSphereVbo()
 {
     if (nIndices > 0)
@@ -1112,6 +1087,7 @@ inline void OpenGL2Common::deleteSphereVbo()
         resetSphereVbo();
     }
 }
+
 void OpenGL2Common::loadSphere()
 {
     const quint32 slices = 50;
diff --git a/src/modules/OpenGL2/OpenGL2Common.hpp b/src/modules/OpenGL2/OpenGL2Common.hpp
index 60604799..c1b09d18 100644
--- a/src/modules/OpenGL2/OpenGL2Common.hpp
+++ b/src/modules/OpenGL2/OpenGL2Common.hpp
@@ -21,8 +21,7 @@
 #include <VideoFrame.hpp>
 #include <VideoAdjustment.hpp>
 
-#include <QOpenGLShaderProgram>
-
+#include <QGLShaderProgram>
 #include <QVariantAnimation>
 #include <QCoreApplication>
 #include <QImage>
@@ -33,7 +32,9 @@
     #include <GL/glext.h>
 #endif
 
-#if defined OPENGL_ES2 && !defined APIENTRY
+#define QOpenGLShaderProgram QGLShaderProgram
+
+#if !defined APIENTRY
     #define APIENTRY
 #endif
 
@@ -48,6 +49,7 @@ public:
     inline RotAnimation(OpenGL2Common &glCommon) :
         glCommon(glCommon)
     {}
+
 private:
     void updateCurrentValue(const QVariant &value) override;
 
@@ -70,6 +72,7 @@ class OpenGL2Common
     using GLMapBufferRange = void *(APIENTRY *)(GLenum, GLintptr, GLsizeiptr, GLbitfield);
     using GLMapBuffer      = void *(APIENTRY *)(GLenum, GLbitfield);
     using GLUnmapBuffer    = GLboolean(APIENTRY *)(GLenum);
+
 public:
     OpenGL2Common();
     virtual ~OpenGL2Common();
@@ -78,7 +81,7 @@ public:
 
     virtual QWidget *widget() = 0;
 
-    bool testGL();
+    virtual bool testGL() = 0;
     virtual bool setVSync(bool enable) = 0;
     virtual void updateGL(bool requestDelayed) = 0;
 
@@ -86,6 +89,7 @@ public:
     void clearImg();
 
     void setSpherical(bool spherical);
+
 protected:
     void initializeGL();
     void paintGL();
@@ -110,9 +114,12 @@ protected:
     GLMapBuffer glMapBuffer = nullptr;
     GLUnmapBuffer glUnmapBuffer = nullptr;
 
+#ifdef VSYNC_SETTINGS
     bool vSync;
+#endif
 
     void dispatchEvent(QEvent *e, QObject *p);
+
 private:
     void maybeSetMipmaps(qreal dpr);
 
@@ -133,6 +140,7 @@ private:
     inline void resetSphereVbo();
     inline void deleteSphereVbo();
     void loadSphere();
+
 public:
     HWAccelInterface *hwAccellnterface;
     QStringList videoAdjustmentKeys;
@@ -155,10 +163,6 @@ public:
     quint32 pbo[4];
     bool allowPBO, hasPbo, hqScaling = false;
 
-#ifdef Q_OS_WIN
-    bool preventFullScreen;
-#endif
-
     bool isPaused, isOK, hwAccelError, hasImage, doReset, setMatrix, correctLinesize, canUseHueSharpness, m_useMipmaps = false;
     int subsX, subsY, W, H, subsW, subsH, outW, outH, verticesIdx;
     int glVer;
diff --git a/src/modules/OpenGL2/OpenGL2Widget.cpp b/src/modules/OpenGL2/OpenGL2Widget.cpp
index 11b26fcf..2c1a8172 100644
--- a/src/modules/OpenGL2/OpenGL2Widget.cpp
+++ b/src/modules/OpenGL2/OpenGL2Widget.cpp
@@ -1,6 +1,6 @@
 /*
     QMPlay2 is a video and audio player.
-    Copyright (C) 2010-2019  Błażej Szczygieł
+    Copyright (C) 2010-2017  Błażej Szczygieł
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Lesser General Public License as published
@@ -18,12 +18,13 @@
 
 #include "OpenGL2Widget.hpp"
 
-#include <QOpenGLContext>
+#include <QMPlay2Core.hpp>
 
 OpenGL2Widget::OpenGL2Widget()
 {
-    connect(&updateTimer, SIGNAL(timeout()), this, SLOT(update()));
+    connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateGL())); // updateGL() from the base class
 }
+
 OpenGL2Widget::~OpenGL2Widget()
 {
     makeCurrent();
@@ -34,46 +35,66 @@ QWidget *OpenGL2Widget::widget()
     return this;
 }
 
+bool OpenGL2Widget::testGL()
+{
+    makeCurrent();
+    if ((isOK = isValid()))
+        testGLInternal();
+    doneCurrent();
+    return isOK;
+}
+
 bool OpenGL2Widget::setVSync(bool enable)
 {
-    QSurfaceFormat fmt = format();
-    vSync = enable;
-    if (!isValid())
+#ifdef VSYNC_SETTINGS
+    bool doDoneCurrent = false;
+    if (QGLContext::currentContext() != context())
     {
-        fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer); //Probably it doesn't work
-        fmt.setSwapInterval(enable); //Does it work on QOpenGLWidget?
-        setFormat(fmt);
-        return true;
+        makeCurrent();
+        doDoneCurrent = true;
     }
-    return (fmt.swapInterval() == enable);
+    using SwapInterval = int (APIENTRY *)(int); // BOOL is just normal int in Windows, APIENTRY declares nothing on non-Windows platforms
+    SwapInterval swapInterval = NULL;
+    swapInterval = (SwapInterval)context()->getProcAddress("glXSwapIntervalMESA");
+    if (!swapInterval)
+        swapInterval = (SwapInterval)context()->getProcAddress("glXSwapIntervalSGI");
+    if (swapInterval)
+        swapInterval(enable);
+    if (doDoneCurrent)
+        doneCurrent();
+    vSync = enable;
+#else
+    Q_UNUSED(enable)
+#endif
+    return true;
 }
+
 void OpenGL2Widget::updateGL(bool requestDelayed)
 {
     if (requestDelayed)
-        QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
+        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
     else
-        update();
+        QGLWidget::updateGL();
 }
 
 void OpenGL2Widget::initializeGL()
 {
-    connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(aboutToBeDestroyed()), Qt::DirectConnection);
     OpenGL2Common::initializeGL();
 }
+
 void OpenGL2Widget::paintGL()
 {
+    glClear(GL_COLOR_BUFFER_BIT);
     OpenGL2Common::paintGL();
 }
 
-void OpenGL2Widget::aboutToBeDestroyed()
+void OpenGL2Widget::resizeGL(int w, int h)
 {
-    makeCurrent();
-    contextAboutToBeDestroyed();
-    doneCurrent();
+    glViewport(0, 0, w, h);
 }
 
 bool OpenGL2Widget::event(QEvent *e)
 {
     dispatchEvent(e, parent());
-    return QOpenGLWidget::event(e);
+    return QGLWidget::event(e);
 }
diff --git a/src/modules/OpenGL2/OpenGL2Widget.hpp b/src/modules/OpenGL2/OpenGL2Widget.hpp
index 3e8bc14e..3a5c3729 100644
--- a/src/modules/OpenGL2/OpenGL2Widget.hpp
+++ b/src/modules/OpenGL2/OpenGL2Widget.hpp
@@ -1,6 +1,6 @@
 /*
     QMPlay2 is a video and audio player.
-    Copyright (C) 2010-2019  Błażej Szczygieł
+    Copyright (C) 2010-2017  Błażej Szczygieł
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU Lesser General Public License as published
@@ -16,29 +16,34 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#pragma once
+#ifndef OPENGL2WIDGET_HPP
+#define OPENGL2WIDGET_HPP
 
 #include <OpenGL2Common.hpp>
 
-#include <QSurfaceFormat>
-#include <QOpenGLWidget>
+#include <QGLWidget>
 
-class OpenGL2Widget final : public QOpenGLWidget, public OpenGL2Common
+class OpenGL2Widget : public QGLWidget, public OpenGL2Common
 {
     Q_OBJECT
+
 public:
     OpenGL2Widget();
-    ~OpenGL2Widget();
+        ~OpenGL2Widget() final;
+
+    QWidget *widget() override final;
 
-    QWidget *widget() override;
+    bool testGL() override final;
+    bool setVSync(bool enable) override final;
+    void updateGL(bool requestDelayed) override final;
 
-    bool setVSync(bool enable) override;
-    void updateGL(bool requestDelayed) override;
+    void initializeGL() override final;
+    void paintGL() override final;
 
-    void initializeGL() override;
-    void paintGL() override;
-private slots:
-    void aboutToBeDestroyed();
 private:
-    bool event(QEvent *e) override;
+    void resizeGL(int w, int h) override final;
+
+    bool event(QEvent *e) override final;
 };
+
+#endif // OPENGL2WIDGET_HPP
diff --git a/src/modules/OpenGL2/OpenGL2Writer.cpp b/src/modules/OpenGL2/OpenGL2Writer.cpp
index 72fe4f70..ebdeefa0 100644
--- a/src/modules/OpenGL2/OpenGL2Writer.cpp
+++ b/src/modules/OpenGL2/OpenGL2Writer.cpp
@@ -18,18 +18,14 @@
 
 #include <OpenGL2Writer.hpp>
 
-#include <OpenGL2Window.hpp>
 #include <OpenGL2Widget.hpp>
 
 #include <HWAccelInterface.hpp>
 #include <VideoFrame.hpp>
 
-#include <QGuiApplication>
-
 OpenGL2Writer::OpenGL2Writer(Module &module)
     : drawable(nullptr)
     , allowPBO(true)
-    , forceRtt(false)
 {
     addParam("W");
     addParam("H");
@@ -42,6 +38,7 @@ OpenGL2Writer::OpenGL2Writer(Module &module)
 
     SetModule(module);
 }
+
 OpenGL2Writer::~OpenGL2Writer()
 {
     if (drawable)
@@ -65,21 +62,10 @@ bool OpenGL2Writer::set()
         m_hqScaling = newHqScaling;
         doReset = true;
     }
-
+#ifdef VSYNC_SETTINGS
     vSync = sets().getBool("VSync");
     if (drawable && !drawable->setVSync(vSync))
         doReset = true;
-
-    const bool newForceRtt = sets().getBool("ForceRtt");
-    if (forceRtt != newForceRtt)
-        doReset = true;
-    forceRtt = newForceRtt;
-
-#ifdef Q_OS_WIN
-    bool newPreventFullScreen = sets().getBool("PreventFullScreen");
-    if (preventFullScreen != newPreventFullScreen)
-        doReset = true;
-    preventFullScreen = newPreventFullScreen;
 #endif
 
     return !doReset && sets().getBool("Enabled");
@@ -182,6 +168,7 @@ void OpenGL2Writer::writeVideo(const VideoFrame &videoFrame)
     }
     drawable->updateGL(drawable->sphericalView);
 }
+
 void OpenGL2Writer::writeOSD(const QList<const QMPlay2OSD *> &osds)
 {
     QMutexLocker mL(&drawable->osdMutex);
@@ -205,8 +192,6 @@ QString OpenGL2Writer::name() const
     QString glStr = drawable->glVer ? QString("%1.%2").arg(drawable->glVer / 10).arg(drawable->glVer % 10) : "2";
     if (drawable->hwAccellnterface)
         glStr += " " + drawable->hwAccellnterface->name();
-    if (useRtt)
-        glStr += " (render-to-texture)";
 #ifdef OPENGL_ES2
     return "OpenGL|ES " + glStr;
 #else
@@ -216,27 +201,15 @@ QString OpenGL2Writer::name() const
 
 bool OpenGL2Writer::open()
 {
-    static const QString platformName = QGuiApplication::platformName();
-    useRtt = platformName.startsWith("wayland") || platformName == "android" || forceRtt;
-    if (useRtt)
-    {
-        //Don't use rtt when videoDock has native window
-        const QWidget *videoDock = QMPlay2Core.getVideoDock();
-        useRtt = !videoDock->internalWinId() || (videoDock == videoDock->window());
-    }
-    if (useRtt)
-        drawable = new OpenGL2Widget;
-    else
-        drawable = new OpenGL2Window;
+    drawable = new OpenGL2Widget;
     drawable->hwAccellnterface = m_hwAccelInterface;
-#ifdef Q_OS_WIN
-    drawable->preventFullScreen = preventFullScreen;
-#endif
     drawable->allowPBO = allowPBO;
     drawable->hqScaling = m_hqScaling;
     if (drawable->testGL())
     {
+#ifdef VSYNC_SETTINGS
         drawable->setVSync(vSync);
+#endif
         bool hasBrightness = false, hasContrast = false, hasSharpness = false;
         if (!drawable->videoAdjustmentKeys.isEmpty())
         {
diff --git a/src/modules/OpenGL2/OpenGL2Writer.hpp b/src/modules/OpenGL2/OpenGL2Writer.hpp
index 7881da78..501ddcaa 100644
--- a/src/modules/OpenGL2/OpenGL2Writer.hpp
+++ b/src/modules/OpenGL2/OpenGL2Writer.hpp
@@ -27,8 +27,10 @@ class OpenGL2Common;
 class OpenGL2Writer final : public VideoWriter
 {
     Q_DECLARE_TR_FUNCTIONS(OpenGL2Writer)
+
 public:
     OpenGL2Writer(Module &);
+
 private:
     ~OpenGL2Writer();
 
@@ -58,10 +60,8 @@ private:
     OpenGL2Common *drawable;
     bool allowPBO;
     bool m_hqScaling = false;
-    bool forceRtt, useRtt;
+#ifdef VSYNC_SETTINGS
     bool vSync;
-#ifdef Q_OS_WIN
-    bool preventFullScreen;
 #endif
 };
 
diff --git a/src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h b/src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
index 8ddf0b10..515006f0 100644
--- a/src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
+++ b/src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
@@ -51,17 +51,12 @@
 
 #include <CoreServices/CoreServices.h>
 #include <CoreAudio/CoreAudio.h>
+#include <AvailabilityMacros.h>
 
 #include <qglobal.h>
 
 #ifndef DEPRECATED_LISTENER_API
-#    if !defined(__MAC_10_11)
-#        define __MAC_10_11 101100
-#    endif
-#    ifndef QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE
-#        define QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE
-#    endif
-#    if !QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_11)
+#    if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
 #        define DEPRECATED_LISTENER_API
 #        warning "Using the deprecated PropertyListener API; at least it works"
 #    endif
diff --git a/src/modules/PortAudio/PortAudioWriter.cpp b/src/modules/PortAudio/PortAudioWriter.cpp
index aead169e..736945e7 100644
--- a/src/modules/PortAudio/PortAudioWriter.cpp
+++ b/src/modules/PortAudio/PortAudioWriter.cpp
@@ -23,6 +23,10 @@
     #define MMSYSERR_NODRIVER 6
 #endif
 
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+    #define QStringLiteral QString::fromUtf8
+#endif
+
 #ifdef Q_OS_MACOS
     #include "3rdparty/CoreAudio/AudioDeviceList.h"
     #include "3rdparty/CoreAudio/AudioDevice.h"
@@ -47,6 +51,7 @@ PortAudioWriter::PortAudioWriter(Module &module) :
 
     SetModule(module);
 }
+
 PortAudioWriter::~PortAudioWriter()
 {
 #ifdef Q_OS_MACOS
@@ -130,6 +135,7 @@ bool PortAudioWriter::processParams(bool *paramsCorrected)
 
     return readyWrite();
 }
+
 qint64 PortAudioWriter::write(const QByteArray &arr)
 {
     if (!readyWrite())
@@ -192,6 +198,7 @@ qint64 PortAudioWriter::write(const QByteArray &arr)
 
     return arr.size();
 }
+
 void PortAudioWriter::pause()
 {
     if (readyWrite())
@@ -253,6 +260,7 @@ bool PortAudioWriter::openStream()
     }
     return false;
 }
+
 bool PortAudioWriter::startStream()
 {
     const PaError e = Pa_StartStream(stream);
@@ -266,6 +274,7 @@ bool PortAudioWriter::startStream()
     }
     return true;
 }
+
 inline bool PortAudioWriter::writeStream(const QByteArray &arr)
 {
     const PaError e = Pa_WriteStream(stream, arr.constData(), arr.size() / outputParameters.channelCount / sizeof(float));
@@ -288,6 +297,7 @@ inline bool PortAudioWriter::writeStream(const QByteArray &arr)
     }
     return (e != paUnanticipatedHostError);
 }
+
 qint64 PortAudioWriter::playbackError()
 {
     QMPlay2Core.logError("PortAudio :: " + tr("Playback error"));
@@ -302,6 +312,7 @@ bool PortAudioWriter::isNoDriverError() const
     return errorInfo && errorInfo->hostApiType == paMME && errorInfo->errorCode == MMSYSERR_NODRIVER;
 }
 #endif
+
 bool PortAudioWriter::reopenStream()
 {
     Pa_CloseStream(stream);
diff --git a/src/modules/PortAudio/PortAudioWriter.hpp b/src/modules/PortAudio/PortAudioWriter.hpp
index 20ff55d8..b458e9ca 100644
--- a/src/modules/PortAudio/PortAudioWriter.hpp
+++ b/src/modules/PortAudio/PortAudioWriter.hpp
@@ -29,25 +29,25 @@
 class AudioDevice;
 #endif
 
-class PortAudioWriter final : public Writer
+class PortAudioWriter : public Writer
 {
     Q_DECLARE_TR_FUNCTIONS(PortAudioWriter)
 public:
     PortAudioWriter(Module &);
 private:
-    ~PortAudioWriter();
+    ~PortAudioWriter() final;
 
-    bool set() override;
+    bool set() override final;
 
-    bool readyWrite() const override;
+    bool readyWrite() const override final;
 
-    bool processParams(bool *paramsCorrected) override;
-    qint64 write(const QByteArray &) override;
-    void pause() override;
+    bool processParams(bool *paramsCorrected) override final;
+    qint64 write(const QByteArray &) override final;
+    void pause() override final;
 
-    QString name() const override;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
diff --git a/src/modules/PulseAudio/PulseAudio.hpp b/src/modules/PulseAudio/PulseAudio.hpp
index 006f749d..e59f7046 100644
--- a/src/modules/PulseAudio/PulseAudio.hpp
+++ b/src/modules/PulseAudio/PulseAudio.hpp
@@ -20,15 +20,15 @@
 
 #include <Module.hpp>
 
-class PulseAudio final : public Module
+class PulseAudio : public Module
 {
 public:
     PulseAudio();
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 };
 
 /**/
@@ -38,13 +38,13 @@ private:
 class QDoubleSpinBox;
 class QCheckBox;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_DECLARE_TR_FUNCTIONS(ModuleSettingsWidget)
 public:
     ModuleSettingsWidget(Module &);
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     QCheckBox *enabledB;
     QDoubleSpinBox *delayB;
diff --git a/src/modules/PulseAudio/PulseAudioWriter.hpp b/src/modules/PulseAudio/PulseAudioWriter.hpp
index 8ba6eb64..4d816b29 100644
--- a/src/modules/PulseAudio/PulseAudioWriter.hpp
+++ b/src/modules/PulseAudio/PulseAudioWriter.hpp
@@ -23,23 +23,23 @@
 
 #include <QCoreApplication>
 
-class PulseAudioWriter final : public Writer
+class PulseAudioWriter : public Writer
 {
     Q_DECLARE_TR_FUNCTIONS(PulseAudioWriter)
 public:
     PulseAudioWriter(Module &);
-    ~PulseAudioWriter();
+    ~PulseAudioWriter() final;
 private:
-    bool set() override;
+    bool set() override final;
 
-    bool readyWrite() const override;
+    bool readyWrite() const override final;
 
-    bool processParams(bool *paramsCorrected) override;
-    qint64 write(const QByteArray &) override;
+    bool processParams(bool *paramsCorrected) override final;
+    qint64 write(const QByteArray &) override final;
 
-    QString name() const override;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
diff --git a/src/modules/QPainter/QPainter.hpp b/src/modules/QPainter/QPainter.hpp
index 7257c675..b142be81 100644
--- a/src/modules/QPainter/QPainter.hpp
+++ b/src/modules/QPainter/QPainter.hpp
@@ -20,15 +20,15 @@
 
 #include <Module.hpp>
 
-class QPainterSW final : public Module
+class QPainterSW : public Module
 {
 public:
     QPainterSW();
 private:
-    QList<Info> getModulesInfo(const bool) const override;
-    void *createInstance(const QString &) override;
+    QList<Info> getModulesInfo(const bool) const override final;
+    void *createInstance(const QString &) override final;
 
-    SettingsWidget *getSettingsWidget() override;
+    SettingsWidget *getSettingsWidget() override final;
 };
 
 /**/
@@ -37,13 +37,13 @@ private:
 
 class QCheckBox;
 
-class ModuleSettingsWidget final : public Module::SettingsWidget
+class ModuleSettingsWidget : public Module::SettingsWidget
 {
     Q_DECLARE_TR_FUNCTIONS(ModuleSettingsWidget)
 public:
     ModuleSettingsWidget(Module &);
 private:
-    void saveSettings() override;
+    void saveSettings() override final;
 
     QCheckBox *enabledB;
 };
diff --git a/src/modules/QPainter/QPainterWriter.cpp b/src/modules/QPainter/QPainterWriter.cpp
index 92a3225f..fcb11587 100644
--- a/src/modules/QPainter/QPainterWriter.cpp
+++ b/src/modules/QPainter/QPainterWriter.cpp
@@ -72,9 +72,9 @@ void Drawable::draw(const VideoFrame &newVideoFrame, bool canRepaint, bool entir
 
 void Drawable::resizeEvent(QResizeEvent *e)
 {
-    const qreal dpr = devicePixelRatioF();
+    const qreal scale = QMPlay2Core.getVideoDevicePixelRatio();
     Functions::getImageSize(writer.aspect_ratio, writer.zoom, width(), height(), W, H, &X, &Y);
-    Functions::getImageSize(writer.aspect_ratio, writer.zoom, width() * dpr, height() * dpr, imgW, imgH);
+    Functions::getImageSize(writer.aspect_ratio, writer.zoom, width() * scale, height() * scale, imgW, imgH);
     imgW = Functions::aligned(imgW, 8);
 
     imgScaler.destroy();
@@ -82,6 +82,7 @@ void Drawable::resizeEvent(QResizeEvent *e)
 
     draw(VideoFrame(), e ? false : true, true);
 }
+
 void Drawable::paintEvent(QPaintEvent *)
 {
     QPainter p(this);
@@ -94,14 +95,15 @@ void Drawable::paintEvent(QPaintEvent *)
     osd_mutex.lock();
     if (!osd_list.isEmpty())
     {
-        const qreal dpr = devicePixelRatioF();
-        if (!qFuzzyCompare(dpr, 1.0))
-            p.scale(1.0 / dpr, 1.0 / dpr);
+        const qreal scale = QMPlay2Core.getVideoDevicePixelRatio();
+        if (!qFuzzyCompare(scale, 1.0))
+            p.scale(1.0 / scale, 1.0 / scale);
         p.setClipRect(0, 0, imgW, imgH);
-        Functions::paintOSD(true, osd_list, W * dpr / writer.outW, H * dpr / writer.outH, p);
+        Functions::paintOSD(true, osd_list, (qreal)W / writer.outW, (qreal)H / writer.outH, p);
     }
     osd_mutex.unlock();
 }
+
 bool Drawable::event(QEvent *e)
 {
     /* Pass gesture and touch event to the parent */
diff --git a/src/modules/QPainter/QPainterWriter.hpp b/src/modules/QPainter/QPainterWriter.hpp
index f5e0a680..61991e4e 100644
--- a/src/modules/QPainter/QPainterWriter.hpp
+++ b/src/modules/QPainter/QPainterWriter.hpp
@@ -27,23 +27,23 @@
 class QPainterWriter;
 class QMPlay2OSD;
 
-class Drawable final : public QWidget
+class Drawable : public QWidget
 {
 public:
     Drawable(class QPainterWriter &);
-    ~Drawable();
+    ~Drawable() final;
 
     void draw(const VideoFrame &newVideoFrame, bool, bool);
 
-    void resizeEvent(QResizeEvent *) override;
+    void resizeEvent(QResizeEvent *) override final;
 
     VideoFrame videoFrame;
     QList<const QMPlay2OSD *> osd_list;
     int Brightness, Contrast;
     QMutex osd_mutex;
 private:
-    void paintEvent(QPaintEvent *) override;
-    bool event(QEvent *) override;
+    void paintEvent(QPaintEvent *) override final;
+    bool event(QEvent *) override final;
 
     int X, Y, W, H, imgW, imgH;
     QPainterWriter &writer;
@@ -54,28 +54,28 @@ private:
 
 /**/
 
-class QPainterWriter final : public VideoWriter
+class QPainterWriter : public VideoWriter
 {
     friend class Drawable;
 public:
     QPainterWriter(Module &);
 private:
-    ~QPainterWriter();
+    ~QPainterWriter() final;
 
-    bool set() override;
+    bool set() override final;
 
-    bool readyWrite() const override;
+    bool readyWrite() const override final;
 
-    bool processParams(bool *paramsCorrected) override;
+    bool processParams(bool *paramsCorrected) override final;
 
-    QMPlay2PixelFormats supportedPixelFormats() const override;
+    QMPlay2PixelFormats supportedPixelFormats() const override final;
 
-    void writeVideo(const VideoFrame &videoFrame) override;
-    void writeOSD(const QList<const QMPlay2OSD *> &) override;
+    void writeVideo(const VideoFrame &videoFrame) override final;
+    void writeOSD(const QList<const QMPlay2OSD *> &) override final;
 
-    QString name() const override;
+    QString name() const override final;
 
-    bool open() override;
+    bool open() override final;
 
     /**/
 
diff --git a/src/modules/Subtitles/Classic.cpp b/src/modules/Subtitles/Classic.cpp
index d5c4d003..cb8f8bab 100644
--- a/src/modules/Subtitles/Classic.cpp
+++ b/src/modules/Subtitles/Classic.cpp
@@ -167,7 +167,7 @@ bool Classic::toASS(const QByteArray &txt, LibASS *ass, double fps)
                 if (use_mDVD_FPS && (s == 0 || s == 1))
                 {
                     use_mDVD_FPS = false;
-                    const double newFPS = sub.midRef(0, 6).toDouble();
+                    const double newFPS = sub.mid(0, 6).toDouble();
                     if (newFPS > 0.0 && newFPS < 100.0)
                     {
                         fps = newFPS;
diff --git a/src/modules/Subtitles/Classic.hpp b/src/modules/Subtitles/Classic.hpp
index b4270f7f..db15b93d 100644
--- a/src/modules/Subtitles/Classic.hpp
+++ b/src/modules/Subtitles/Classic.hpp
@@ -20,12 +20,12 @@
 
 #include <SubsDec.hpp>
 
-class Classic final : public SubsDec
+class Classic : public SubsDec
 {
 public:
     Classic(bool, double);
 private:
-    bool toASS(const QByteArray &, class LibASS *, double) override;
+    bool toASS(const QByteArray &, class LibASS *, double) override final;
 
     /**/
 
diff --git a/src/qmplay2/CMakeLists.txt b/src/qmplay2/CMakeLists.txt
index c70bd00c..d7509e9a 100644
--- a/src/qmplay2/CMakeLists.txt
+++ b/src/qmplay2/CMakeLists.txt
@@ -112,7 +112,7 @@ if(USE_FREEDESKTOP_NOTIFICATIONS)
     qt5_add_dbus_interface(QMPLAY2_SRC org.freedesktop.Notifications.xml notifications_interface)
     add_definitions(-DNOTIFIES_FREEDESKTOP)
     set(DBUS Qt5::DBus)
-elseif(APPLE)
+elseif(APPLE AND NOT USE_QT4)
     list(APPEND QMPLAY2_HDR headers/NotifiesMacOS.hpp)
     list(APPEND QMPLAY2_SRC NotifiesMacOS.mm)
     find_package(Qt5MacExtras REQUIRED)
@@ -195,11 +195,18 @@ add_library(${PROJECT_NAME} SHARED
 set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
 
 if(APPLE)
-    target_link_libraries(${PROJECT_NAME}
-        PRIVATE
-        Qt5::MacExtras
-        ${APPKIT_LIBRARY}
+    if(USE_QT4)
+        target_link_libraries(${PROJECT_NAME}
+            PRIVATE
+            ${APPKIT_LIBRARY}
     )
+    else()
+        target_link_libraries(${PROJECT_NAME}
+            PRIVATE
+            Qt5::MacExtras
+            ${APPKIT_LIBRARY}
+    )
+    endif()
 endif()
 
 if(WIN32)
@@ -229,14 +236,30 @@ else()
     endif()
 endif()
 
+if(USE_QT4)
+    add_definitions(-I@prefix@/include/QJson4)
+    target_link_libraries(${PROJECT_NAME}
+        PUBLIC
+        Qt4::QtCore
+        Qt4::QtGui
+        Qt4::QtSvg
+        QJson4
+    )
+else()
+    target_link_libraries(${PROJECT_NAME}
+        PUBLIC
+        Qt5::Core
+        Qt5::Gui
+        Qt5::Widgets
+        Qt5::Svg
+        PRIVATE
+        ${QML}
+    )
+endif()
+
 target_link_libraries(${PROJECT_NAME}
-    PUBLIC
-    Qt5::Core
-    Qt5::Gui
-    Qt5::Widgets
     PRIVATE
     ${DBUS}
-    ${QML}
     ${LIBQMPLAY2_LIBS}
 )
 
diff --git a/src/qmplay2/Functions.cpp b/src/qmplay2/Functions.cpp
index 572952e2..554b0100 100644
--- a/src/qmplay2/Functions.cpp
+++ b/src/qmplay2/Functions.cpp
@@ -36,12 +36,16 @@
 #include <QDir>
 #include <QUrl>
 #include <QRegExp>
-#include <QWindow>
+#include <QWidget>
 #include <QLibrary>
 #include <QTextCodec>
 #include <QMessageBox>
 #include <QStyleOption>
 
+#ifdef Q_WS_X11
+#include <QX11Info>
+#endif
+
 extern "C"
 {
     #include <libavformat/version.h>
@@ -58,14 +62,16 @@ static inline void swapArray(quint8 *a, quint8 *b, int size)
     memcpy(b, t, size);
 }
 
-static inline QWindow *getNativeWindow(const QWidget *w)
+static inline WId getNativeWindow(const QWidget *w)
 {
     if (w)
     {
-        if (QWidget *winW = w->window())
-            return winW->windowHandle();
+        if (const QWidget *winW = w->window())
+        {
+            return winW->winId();
+        }
     }
-    return nullptr;
+    return 0;
 }
 
 /**/
@@ -301,7 +307,8 @@ QPixmap Functions::getPixmapFromIcon(const QIcon &icon, QSize size, QWidget *w)
         imgSize.scale(size, size.isEmpty() ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio);
     }
 
-    return icon.pixmap(getNativeWindow(w), imgSize);
+    Q_UNUSED(w)
+    return icon.pixmap(imgSize);
 }
 void Functions::drawPixmap(QPainter &p, const QPixmap &pixmap, const QWidget *w, Qt::TransformationMode transformationMode, Qt::AspectRatioMode aRatioMode, QSize size, qreal scale)
 {
@@ -347,14 +354,13 @@ void Functions::drawPixmap(QPainter &p, const QPixmap &pixmap, const QWidget *w,
         pixmapSize = pixmap.size();
     }
 
-    const qreal dpr = w->devicePixelRatioF();
+    qreal devicePixelRatio = QMPlay2Core.getVideoDevicePixelRatio();
 
-    pixmapToDraw = pixmapToDraw.scaled(pixmapSize * dpr, Qt::IgnoreAspectRatio, transformationMode);
-    pixmapToDraw.setDevicePixelRatio(dpr);
+    pixmapToDraw = pixmapToDraw.scaled(pixmapSize * devicePixelRatio, Qt::IgnoreAspectRatio, transformationMode);
 
     const QPoint pixmapPos {
-        size.width()  / 2 - int(pixmapToDraw.width()  / (dpr * 2)),
-        size.height() / 2 - int(pixmapToDraw.height() / (dpr * 2))
+        size.width()  / 2 - int(pixmapToDraw.width()  / (devicePixelRatio * 2)),
+        size.height() / 2 - int(pixmapToDraw.height() / (devicePixelRatio * 2))
     };
 
     p.drawPixmap(pixmapPos, pixmapToDraw);
@@ -405,8 +411,8 @@ void Functions::paintOSD(bool rgbSwapped, const QList<const QMPlay2OSD *> &osd_l
         for (int j = 0; j < osd->imageCount(); j++)
         {
             const QMPlay2OSD::Image &img = osd->getImage(j);
-            const QImage qImg = QImage((const uchar *)img.data.constData(), img.rect.width(), img.rect.height(), rgbSwapped ? QImage::Format_RGBA8888 : QImage::Format_ARGB32);
-            painter.drawImage(img.rect.topLeft(), qImg);
+            const QImage qImg = QImage((uchar *)img.data.data(), img.rect.width(), img.rect.height(), QImage::Format_ARGB32);
+            painter.drawImage(img.rect.topLeft(), rgbSwapped ? qImg.rgbSwapped() : qImg);
         }
         if (osd->needsRescale())
             painter.restore();
diff --git a/src/qmplay2/LineEdit.cpp b/src/qmplay2/LineEdit.cpp
index 6f94853d..72176e1b 100644
--- a/src/qmplay2/LineEdit.cpp
+++ b/src/qmplay2/LineEdit.cpp
@@ -19,19 +19,58 @@
 #include <LineEdit.hpp>
 
 #include <QMPlay2Core.hpp>
+#include <Functions.hpp>
 
-#include <QAction>
+#include <QResizeEvent>
+
+LineEditButton::LineEditButton()
+{
+    const QSize iconSize(16, 16);
+    setToolTip(tr("Clear"));
+    setPixmap(Functions::getPixmapFromIcon(QMPlay2Core.getIconFromTheme("edit-clear"), iconSize, this));
+    resize(iconSize);
+    setCursor(Qt::ArrowCursor);
+}
+
+void LineEditButton::mousePressEvent(QMouseEvent *e)
+{
+    if (e->button() & Qt::LeftButton)
+        emit clicked();
+}
+
+/**/
 
 LineEdit::LineEdit(QWidget *parent)
     : QLineEdit(parent)
 {
-    QAction *clearAct = addAction(QMPlay2Core.getIconFromTheme("edit-clear"), TrailingPosition);
-    connect(clearAct, &QAction::triggered, this, &LineEdit::clearText);
-    connect(this, &LineEdit::textChanged, this, [=](const QString &text) {
-        clearAct->setVisible(!text.isEmpty());
-    });
-    clearAct->setToolTip(tr("Clear"));
-    clearAct->setVisible(false);
+    connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(textChangedSlot(const QString &)));
+    connect(&b, SIGNAL(clicked()), this, SLOT(clearText()));
+    setMinimumWidth(b.width() * 2.5);
+    setTextMargins(0, 0, b.width() * 1.5, 0);
+    b.setParent(this);
+    b.hide();
+}
+
+void LineEdit::resizeEvent(QResizeEvent *e)
+{
+    b.move(e->size().width() - b.width() * 1.5, e->size().height() / 2 - b.height() / 2);
+}
+
+void LineEdit::mousePressEvent(QMouseEvent *e)
+{
+    if (!b.underMouse())
+        QLineEdit::mousePressEvent(e);
+}
+
+void LineEdit::mouseMoveEvent(QMouseEvent *e)
+{
+    if (!b.underMouse())
+        QLineEdit::mouseMoveEvent(e);
+}
+
+void LineEdit::textChangedSlot(const QString &str)
+{
+    b.setVisible(!str.isEmpty());
 }
 
 void LineEdit::clearText()
diff --git a/src/qmplay2/QMPlay2Core.cpp b/src/qmplay2/QMPlay2Core.cpp
index 55f775da..56a04b28 100644
--- a/src/qmplay2/QMPlay2Core.cpp
+++ b/src/qmplay2/QMPlay2Core.cpp
@@ -28,7 +28,6 @@
 #include <Version.hpp>
 #include <Module.hpp>
 
-#include <QLoggingCategory>
 #include <QApplication>
 #include <QLibraryInfo>
 #include <QTranslator>
@@ -36,19 +35,19 @@
 #include <QLibrary>
 #include <QPointer>
 #include <QLocale>
-#include <QWindow>
 #include <QFile>
 #include <QDir>
 #if defined Q_OS_WIN
     #include <windows.h>
     #include <powrprof.h>
-#elif defined Q_OS_MACOS
-    #include <QStandardPaths>
 #endif
 
 #include <cstdarg>
 #include <cstdio>
 
+#define QT_VERSION_MAJOR 4
+#define QT_VERSION_MINOR 8 // Qt 4.8.x is the oldest supported Qt version
+
 extern "C"
 {
     #include <libavformat/avformat.h>
@@ -58,19 +57,8 @@ extern "C"
 
 /**/
 
-Q_LOGGING_CATEGORY(ffmpeglog, "FFmpegLog")
-
 static void avQMPlay2LogHandler(void *avcl, int level, const char *fmt, va_list vl)
 {
-    if (level <= AV_LOG_FATAL)
-    {
-        const QByteArray msg = QString::vasprintf(fmt, vl).trimmed().toUtf8();
-        qCCritical(ffmpeglog) << msg.constData();
-    }
-    else
-    {
-        av_log_default_callback(avcl, level, fmt, vl);
-    }
 }
 
 /**/
@@ -109,6 +97,8 @@ QMPlay2CoreClass::QMPlay2CoreClass()
 {
     qmplay2Core = this;
 
+    videoDevicePixelRatio = 1.0;
+
     QFile f(":/Languages.txt");
     if (f.open(QFile::ReadOnly))
     {
@@ -203,8 +193,6 @@ void QMPlay2CoreClass::init(bool loadModules, bool modulesInSubdirs, const QStri
     {
 #if defined(Q_OS_WIN)
         settingsDir = QFileInfo(QSettings(QSettings::IniFormat, QSettings::UserScope, QString()).fileName()).absolutePath() + "/QMPlay2/";
-#elif defined(Q_OS_MACOS)
-        settingsDir = Functions::cleanPath(QStandardPaths::standardLocations(QStandardPaths::DataLocation).value(0, settingsDir));
 #else
         settingsDir = QDir::homePath() + "/.qmplay2/";
 #endif
@@ -414,9 +402,8 @@ QStringList QMPlay2CoreClass::getModules(const QString &type, int typeLen) const
     return modules + availableModules;
 }
 
-qreal QMPlay2CoreClass::getVideoDevicePixelRatio() const
+void QMPlay2CoreClass::setVideoDevicePixelRatio()
 {
-    return getVideoDock()->devicePixelRatioF();
 }
 
 QIcon QMPlay2CoreClass::getIconFromTheme(const QString &iconName, const QIcon &fallback)
diff --git a/src/qmplay2/QMPlay2OSD.cpp b/src/qmplay2/QMPlay2OSD.cpp
index d77bc2ce..7c0ff030 100644
--- a/src/qmplay2/QMPlay2OSD.cpp
+++ b/src/qmplay2/QMPlay2OSD.cpp
@@ -17,13 +17,13 @@
 */
 
 #include <QMPlay2OSD.hpp>
+#include <QAtomicInt>
 
-#include <QAtomicInteger>
-static QAtomicInteger<quint64> g_id;
+static QAtomicInt g_id;
 
 void QMPlay2OSD::genId()
 {
-    m_id = ++g_id;
+    m_id = g_id.fetchAndAddOrdered(1) + 1;
 }
 
 void QMPlay2OSD::clear(bool all)
diff --git a/src/qmplay2/Writer.cpp b/src/qmplay2/Writer.cpp
index 5eb05afd..a09bda50 100644
--- a/src/qmplay2/Writer.cpp
+++ b/src/qmplay2/Writer.cpp
@@ -20,7 +20,7 @@
 
 #include <Functions.hpp>
 
-#include <QSaveFile>
+#include <QFile>
 #include <QBuffer>
 
 #include <memory>
@@ -52,8 +52,10 @@ class QMPlay2FileWriter : public IODeviceWriter
 {
     ~QMPlay2FileWriter()
     {
-        if (auto f = static_cast<QSaveFile *>(m_io.get()))
-            f->commit();
+        if (auto f = static_cast<QFile *>(m_io.get()))
+        {
+            f->close();
+        }
     }
 
     QString name() const override final
@@ -63,7 +65,7 @@ class QMPlay2FileWriter : public IODeviceWriter
 
     bool open() override final
     {
-        m_io.reset(new QSaveFile(getUrl().mid(7)));
+        m_io.reset(new QFile(getUrl().mid(7)));
         return IODeviceWriter::open();
     }
 };
diff --git a/src/qmplay2/YouTubeDL.cpp b/src/qmplay2/YouTubeDL.cpp
index f05e615b..8e3e757d 100644
--- a/src/qmplay2/YouTubeDL.cpp
+++ b/src/qmplay2/YouTubeDL.cpp
@@ -23,26 +23,33 @@
 #include <Functions.hpp>
 #include <CppUtils.hpp>
 
-#include <QStandardPaths>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
+#include <QRegExp>
 #include <QFileInfo>
 #include <QMutex>
 #include <QFile>
 
+#include <QJsonArray.h>
+#include <QJsonDocument.h>
+#include <QJsonObject.h>
+
+/* Avoid downloading yt-dlp, it fails to work correctly. */
+#ifndef BUNDLED_YTDLP
+#define BUNDLED_YTDLP 0
+#endif
+
 constexpr const char *g_name = "YouTubeDL";
 static bool g_mustUpdate = true;
 static QMutex g_mutex(QMutex::Recursive);
 
 QString YouTubeDL::getFilePath()
 {
-    return QMPlay2Core.getSettingsDir() + "youtube-dl"
+    return QMPlay2Core.getSettingsDir() + "yt-dlp"
 #ifdef Q_OS_WIN
     ".exe"
 #endif
     ;
 }
+
 QStringList YouTubeDL::getCommonArgs()
 {
     QStringList commonArgs {
@@ -164,7 +171,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
         startProcess(processArgs);
     }
 
-    if (!m_process.waitForFinished() || m_aborted)
+    if (!m_process.waitForFinished(-1) || m_aborted)
         return {};
 
     QStringList result;
@@ -181,7 +188,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
         }
         else
         {
-            result = result.constFirst().split('\n', QString::SkipEmptyParts);
+            result = result[0].split('\n', QString::SkipEmptyParts);
 
             // Verify if URLs has printable characters, because sometimes we
             // can get binary garbage at output (especially on Openload).
@@ -267,7 +274,7 @@ bool YouTubeDL::prepare()
         if (m_aborted)
             return false;
     }
-
+#if BUNDLED_YTDLP
     if (!QFileInfo(m_ytDlPath).exists())
     {
         if (!download())
@@ -297,7 +304,7 @@ bool YouTubeDL::prepare()
     }
 
     ensureExecutable();
-
+#endif
     g_mutex.unlock();
     return true;
 }
@@ -305,8 +312,8 @@ bool YouTubeDL::prepare()
 bool YouTubeDL::download()
 {
     // Mutex must be locked here
-
-    const QString downloadUrl = "https://yt-dl.org/downloads/latest/youtube-dl"
+#if BUNDLED_YTDLP
+    const QString downloadUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"
 #ifdef Q_OS_WIN
     ".exe"
 #endif
@@ -354,11 +361,13 @@ bool YouTubeDL::download()
 
     QMPlay2Core.setWorking(false);
     return false;
+#endif
 }
+
 bool YouTubeDL::update()
 {
     // Mutex must be locked here
-
+#if BUNDLED_YTDLP
     qDebug() << "\"youtube-dl\" updates will be checked";
     QMPlay2Core.setWorking(true);
 
@@ -390,29 +399,11 @@ bool YouTubeDL::update()
         {
             qCritical() << "youtube-dl update failed:" << updateOutput;
         }
-        else if (m_process.exitCode() == 0 && !updateOutput.contains("up-to-date"))
+        else if (m_process.exitCode() == 0 && !updateOutput.contains(QRegExp(R"(up\Wto\Wdate)")))
         {
-#ifdef Q_OS_WIN
-            const QString updatedFile = m_ytDlPath + ".new";
-            QFile::remove(Functions::filePath(m_ytDlPath) + "youtube-dl-updater.bat");
-            if (QFile::exists(updatedFile))
-            {
-                Functions::s_wait(0.2); // Wait 200 ms to be sure that file is closed
-                QFile::remove(m_ytDlPath);
-                if (QFile::rename(updatedFile, m_ytDlPath))
-                {
-#endif
-                    QMPlay2Core.setWorking(false);
-                    emit QMPlay2Core.sendMessage(tr("\"youtube-dl\" has been successfully updated!"), g_name);
-                    return true;
-#ifdef Q_OS_WIN
-                }
-            }
-            else
-            {
-                qDebug() << "Updated youtube-dl file:" + updatedFile + "not found!";
-            }
-#endif
+                QMPlay2Core.setWorking(false);
+                emit QMPlay2Core.sendMessage(tr("\"youtube-dl\" has been successfully updated!"), g_name);
+                return {};
         }
     }
     else if (updating && m_aborted)
@@ -421,6 +412,7 @@ bool YouTubeDL::update()
     }
 
     QMPlay2Core.setWorking(false);
+#endif
     return true;
 }
 
@@ -460,24 +452,7 @@ void YouTubeDL::startProcess(QStringList args)
         if (shebang.startsWith("#!") && idx > -1)
         {
             const auto pythonCmd = shebang.mid(idx);
-            if (!QStandardPaths::findExecutable(pythonCmd).endsWith(pythonCmd))
-            {
-                QStringList pythonCmdsToCheck {
-                    "python",
-                    "python2",
-                    "python3",
-                };
-                pythonCmdsToCheck.removeOne(pythonCmd);
-                for (auto &&pythonCmd : asConst(pythonCmdsToCheck))
-                {
-                    if (QStandardPaths::findExecutable(pythonCmd).endsWith(pythonCmd))
-                    {
-                        args.prepend(program);
-                        program = pythonCmd;
-                        break;
-                    }
-                }
-            }
+            // We do not want to fetch yt-dlp anyway.
         }
         ytDlFile.close();
     }
diff --git a/src/qmplay2/headers/ColorButton.hpp b/src/qmplay2/headers/ColorButton.hpp
index 75aa784a..a9b8c5e0 100644
--- a/src/qmplay2/headers/ColorButton.hpp
+++ b/src/qmplay2/headers/ColorButton.hpp
@@ -22,7 +22,7 @@
 
 #include <QPushButton>
 
-class QMPLAY2SHAREDLIB_EXPORT ColorButton final : public QPushButton
+class ColorButton : public QPushButton
 {
     Q_OBJECT
 public:
@@ -34,7 +34,7 @@ public:
         return m_color;
     }
 protected:
-    void paintEvent(QPaintEvent *) override;
+    void paintEvent(QPaintEvent *) override final;
 private:
     QColor m_color;
     bool m_alphaChannel;
diff --git a/src/qmplay2/headers/IPC.hpp b/src/qmplay2/headers/IPC.hpp
index 4766b238..8ebd4b3c 100644
--- a/src/qmplay2/headers/IPC.hpp
+++ b/src/qmplay2/headers/IPC.hpp
@@ -31,18 +31,18 @@ class IPCServerPriv;
 
 class QSocketNotifier;
 
-class QMPLAY2SHAREDLIB_EXPORT IPCSocket final : public QIODevice
+class IPCSocket : public QIODevice
 {
     Q_OBJECT
     friend class IPCServer;
 public:
     IPCSocket(const QString &fileName, QObject *parent = nullptr);
-    ~IPCSocket();
+    ~IPCSocket() final;
 
     bool isConnected() const;
 
-    bool open(OpenMode mode) override;
-    void close() override;
+    bool open(OpenMode mode) override final;
+    void close() override final;
 
 private slots:
     void socketReadActive();
@@ -54,20 +54,20 @@ private:
     IPCSocket(int socket, QObject *parent);
 #endif
 
-    qint64 readData(char *data, qint64 maxSize) override;
-    qint64 writeData(const char *data, qint64 maxSize) override;
+    qint64 readData(char *data, qint64 maxSize) override final;
+    qint64 writeData(const char *data, qint64 maxSize) override final;
 
     IPCSocketPriv *m_priv;
 };
 
 /**/
 
-class QMPLAY2SHAREDLIB_EXPORT IPCServer final : public QObject
+class IPCServer : public QObject
 {
     Q_OBJECT
 public:
     IPCServer(const QString &fileName, QObject *parent = nullptr);
-    ~IPCServer();
+    ~IPCServer() final;
 
     bool listen();
     void close();
diff --git a/src/qmplay2/headers/InDockW.hpp b/src/qmplay2/headers/InDockW.hpp
index d4b51c50..67504fc5 100644
--- a/src/qmplay2/headers/InDockW.hpp
+++ b/src/qmplay2/headers/InDockW.hpp
@@ -23,7 +23,7 @@
 #include <QPointer>
 #include <QWidget>
 
-class QMPLAY2SHAREDLIB_EXPORT InDockW final : public QWidget
+class InDockW : public QWidget
 {
     Q_OBJECT
 public:
@@ -41,12 +41,12 @@ private:
     QPointer<QWidget> w;
 private slots:
     void wallpaperChanged(bool hasWallpaper, double alpha);
-public:
+public slots:
     void setWidget(QWidget *newW);
 protected:
-    void resizeEvent(QResizeEvent *) override;
-    void paintEvent(QPaintEvent *) override;
-    bool event(QEvent *) override;
+    void resizeEvent(QResizeEvent *) override final;
+    void paintEvent(QPaintEvent *) override final;
+    bool event(QEvent *) override final;
 signals:
     void resized(int, int);
     void itemDropped(const QString &);
diff --git a/src/qmplay2/headers/LineEdit.hpp b/src/qmplay2/headers/LineEdit.hpp
index 676be13e..b7606e40 100644
--- a/src/qmplay2/headers/LineEdit.hpp
+++ b/src/qmplay2/headers/LineEdit.hpp
@@ -21,16 +21,36 @@
 #include <QMPlay2Lib.hpp>
 
 #include <QLineEdit>
+#include <QLabel>
 
-class QMPLAY2SHAREDLIB_EXPORT LineEdit final : public QLineEdit
+class LineEditButton : public QLabel
 {
     Q_OBJECT
+public:
+    LineEditButton();
+private:
+    void mousePressEvent(QMouseEvent *) override final;
+signals:
+    void clicked();
+};
+
+/**/
 
+class LineEdit : public QLineEdit
+{
+    Q_OBJECT
 public:
     LineEdit(QWidget *parent = nullptr);
 
-    void clearText();
+private:
+    void resizeEvent(QResizeEvent *) override final;
+    void mousePressEvent(QMouseEvent *) override final;
+    void mouseMoveEvent(QMouseEvent *) override final;
 
+    LineEditButton b;
+private slots:
+    void textChangedSlot(const QString &);
+    void clearText();
 signals:
     void clearButtonClicked();
 };
diff --git a/src/qmplay2/headers/MkvMuxer.hpp b/src/qmplay2/headers/MkvMuxer.hpp
index f0f71a96..a49f5da0 100644
--- a/src/qmplay2/headers/MkvMuxer.hpp
+++ b/src/qmplay2/headers/MkvMuxer.hpp
@@ -26,7 +26,7 @@ struct AVFormatContext;
 class StreamInfo;
 struct Packet;
 
-class QMPLAY2SHAREDLIB_EXPORT MkvMuxer
+class MkvMuxer
 {
     MkvMuxer(const MkvMuxer &) = delete;
     MkvMuxer &operator =(const MkvMuxer &) = delete;
diff --git a/src/qmplay2/headers/Module.hpp b/src/qmplay2/headers/Module.hpp
index 5fee6266..442ef070 100644
--- a/src/qmplay2/headers/Module.hpp
+++ b/src/qmplay2/headers/Module.hpp
@@ -27,7 +27,7 @@
 
 class ModuleCommon;
 
-class QMPLAY2SHAREDLIB_EXPORT Module : public Settings
+class Module : public Settings
 {
     friend class ModuleCommon;
 public:
diff --git a/src/qmplay2/headers/ModuleCommon.hpp b/src/qmplay2/headers/ModuleCommon.hpp
index 89ea8782..42bc0c40 100644
--- a/src/qmplay2/headers/ModuleCommon.hpp
+++ b/src/qmplay2/headers/ModuleCommon.hpp
@@ -20,7 +20,7 @@
 
 #include <Module.hpp>
 
-class QMPLAY2SHAREDLIB_EXPORT ModuleCommon
+class ModuleCommon
 {
 public:
     virtual bool set();
diff --git a/src/qmplay2/headers/ModuleParams.hpp b/src/qmplay2/headers/ModuleParams.hpp
index d351c08e..ebc69315 100644
--- a/src/qmplay2/headers/ModuleParams.hpp
+++ b/src/qmplay2/headers/ModuleParams.hpp
@@ -24,7 +24,7 @@
 #include <QString>
 #include <QVariant>
 
-class QMPLAY2SHAREDLIB_EXPORT ModuleParams
+class ModuleParams
 {
     Q_DISABLE_COPY(ModuleParams)
 public:
diff --git a/src/qmplay2/headers/NetworkAccess.hpp b/src/qmplay2/headers/NetworkAccess.hpp
index fef728fb..1da4950c 100644
--- a/src/qmplay2/headers/NetworkAccess.hpp
+++ b/src/qmplay2/headers/NetworkAccess.hpp
@@ -26,7 +26,7 @@
 class NetworkReplyPriv;
 struct NetworkAccessParams;
 
-class QMPLAY2SHAREDLIB_EXPORT NetworkReply final : public QObject, public BasicIO
+class NetworkReply : public QObject, public BasicIO
 {
     Q_OBJECT
 
@@ -49,7 +49,6 @@ public:
         Download,
         Aborted
     };
-    Q_ENUM(Error)
 
     enum class Wait
     {
@@ -57,13 +56,12 @@ public:
         Timeout,
         Error
     };
-    Q_ENUM(Wait)
 
-    ~NetworkReply();
+    ~NetworkReply() final;
 
     QString url() const;
 
-    void abort() override;
+    void abort() override final;
 
     bool hasError() const;
     Error error() const;
@@ -86,7 +84,7 @@ private:
 
 /**/
 
-class QMPLAY2SHAREDLIB_EXPORT NetworkAccess : public QObject
+class NetworkAccess : public QObject
 {
     Q_OBJECT
 
diff --git a/src/qmplay2/headers/NetworkAccessJS.hpp b/src/qmplay2/headers/NetworkAccessJS.hpp
index 8cc77097..dd20c4e0 100644
--- a/src/qmplay2/headers/NetworkAccessJS.hpp
+++ b/src/qmplay2/headers/NetworkAccessJS.hpp
@@ -8,7 +8,7 @@
 
 class NetworkAccess;
 
-class QMPLAY2SHAREDLIB_EXPORT NetworkAccessJS : public QObject
+class NetworkAccessJS : public QObject
 {
     Q_OBJECT
 
diff --git a/src/qmplay2/headers/PixelFormats.hpp b/src/qmplay2/headers/PixelFormats.hpp
index 52e312db..64d4fe66 100644
--- a/src/qmplay2/headers/PixelFormats.hpp
+++ b/src/qmplay2/headers/PixelFormats.hpp
@@ -22,22 +22,27 @@
 
 #include <QVector>
 
+extern "C"
+{
+    #include <libavutil/pixfmt.h>
+}
+
 enum class QMPlay2PixelFormat
 {
     None = -1,
 
-    YUV420P,
-    YUVJ420P,
-    YUV422P,
-    YUVJ422P,
-    YUV444P,
-    YUVJ444P,
+    YUV420P = AV_PIX_FMT_YUV420P,
+    YUVJ420P = AV_PIX_FMT_YUVJ420P,
+    YUV422P = AV_PIX_FMT_YUV422P,
+    YUVJ422P = AV_PIX_FMT_YUVJ422P,
+    YUV444P = AV_PIX_FMT_YUV444P,
+    YUVJ444P = AV_PIX_FMT_YUVJ444P,
 
-    YUV410P,
-    YUV411P,
-    YUVJ411P,
-    YUV440P,
-    YUVJ440P,
+    YUV410P = AV_PIX_FMT_YUV410P,
+    YUV411P = AV_PIX_FMT_YUV411P,
+    YUVJ411P = AV_PIX_FMT_YUVJ411P,
+    YUV440P = AV_PIX_FMT_YUV440P,
+    YUVJ440P = AV_PIX_FMT_YUVJ440P,
 
     Count,
 };
@@ -53,6 +58,7 @@ enum class QMPlay2ColorSpace
     SMPTE240M,
     BT2020,
 };
+
 struct LumaCoefficients
 {
     float cR, cG, cB;
diff --git a/src/qmplay2/headers/QMPlay2Core.hpp b/src/qmplay2/headers/QMPlay2Core.hpp
index 7c73b00c..5cdf4815 100644
--- a/src/qmplay2/headers/QMPlay2Core.hpp
+++ b/src/qmplay2/headers/QMPlay2Core.hpp
@@ -94,7 +94,11 @@ public:
         return *settings;
     }
 
-    qreal getVideoDevicePixelRatio() const;
+    inline qreal getVideoDevicePixelRatio() const
+    {
+        return videoDevicePixelRatio;
+    }
+    void setVideoDevicePixelRatio();
 
     QIcon getIconFromTheme(const QString &iconName, const QIcon &fallback = QIcon());
 
@@ -211,6 +215,7 @@ private:
     QStringList logs;
     QMap<QString, QString> languages;
     QList<QPointer<QWidget>> videoFilters;
+    qreal videoDevicePixelRatio;
     QString lang;
 
     struct
diff --git a/src/qmplay2/headers/QMPlay2Lib.hpp b/src/qmplay2/headers/QMPlay2Lib.hpp
index 0d1f1016..8bc2a569 100644
--- a/src/qmplay2/headers/QMPlay2Lib.hpp
+++ b/src/qmplay2/headers/QMPlay2Lib.hpp
@@ -20,8 +20,13 @@
 
 #include <QtGlobal>
 
-#if defined(QMPLAY2SHAREDLIB_LIBRARY)
-    #define QMPLAY2SHAREDLIB_EXPORT Q_DECL_EXPORT
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+    // Define to nothing, it breaks linking otherwise
+    #define QMPLAY2SHAREDLIB_EXPORT
 #else
-    #define QMPLAY2SHAREDLIB_EXPORT Q_DECL_IMPORT
+    #if defined(QMPLAY2SHAREDLIB_LIBRARY)
+        #define QMPLAY2SHAREDLIB_EXPORT Q_DECL_EXPORT
+    #else
+        #define QMPLAY2SHAREDLIB_EXPORT Q_DECL_IMPORT
+    #endif
 #endif
diff --git a/src/qmplay2/headers/Slider.hpp b/src/qmplay2/headers/Slider.hpp
index 52acb3b0..d8d49ee9 100644
--- a/src/qmplay2/headers/Slider.hpp
+++ b/src/qmplay2/headers/Slider.hpp
@@ -22,7 +22,7 @@
 
 #include <QSlider>
 
-class QMPLAY2SHAREDLIB_EXPORT Slider final : public QSlider
+class Slider : public QSlider
 {
     Q_OBJECT
 public:
@@ -45,12 +45,12 @@ public slots:
     }
     void drawRange(int first, int second);
 protected:
-    void paintEvent(QPaintEvent *) override;
-    void mousePressEvent(QMouseEvent *) override;
-    void mouseReleaseEvent(QMouseEvent *) override;
-    void mouseMoveEvent(QMouseEvent *) override;
-    void wheelEvent(QWheelEvent *) override;
-    void enterEvent(QEvent *) override;
+    void paintEvent(QPaintEvent *) override final;
+    void mousePressEvent(QMouseEvent *) override final;
+    void mouseReleaseEvent(QMouseEvent *) override final;
+    void mouseMoveEvent(QMouseEvent *) override final;
+    void wheelEvent(QWheelEvent *) override final;
+    void enterEvent(QEvent *) override final;
 private:
     int getMousePos(const QPoint &pos);
 
diff --git a/src/qmplay2/headers/StreamInfo.hpp b/src/qmplay2/headers/StreamInfo.hpp
index 6a8951ac..ec830b88 100644
--- a/src/qmplay2/headers/StreamInfo.hpp
+++ b/src/qmplay2/headers/StreamInfo.hpp
@@ -26,6 +26,8 @@
 #include <QVector>
 #include <QPair>
 
+#include <qnumeric.h> // qQNaN
+
 using QMPlay2Tag = QPair<QString, QString>;
 
 enum QMPlay2MediaType
diff --git a/src/qmplay2/headers/YouTubeDL.hpp b/src/qmplay2/headers/YouTubeDL.hpp
index 76e0d2ce..beb55ced 100644
--- a/src/qmplay2/headers/YouTubeDL.hpp
+++ b/src/qmplay2/headers/YouTubeDL.hpp
@@ -26,7 +26,7 @@
 
 class NetworkReply;
 
-class QMPLAY2SHAREDLIB_EXPORT YouTubeDL final : public BasicIO
+class YouTubeDL : public BasicIO
 {
     Q_DECLARE_TR_FUNCTIONS(YouTubeDL)
     Q_DISABLE_COPY(YouTubeDL)
@@ -38,14 +38,14 @@ public:
     static bool fixUrl(const QString &url, QString &outUrl, IOController<> *ioCtrl, QString *name, QString *extension, QString *error);
 
     YouTubeDL();
-    ~YouTubeDL();
+    ~YouTubeDL() final;
 
     void addr(const QString &url, const QString &param, QString *streamUrl, QString *name, QString *extension, QString *err = nullptr);
 
     QStringList exec(const QString &url, const QStringList &args, QString *silentErr = nullptr, bool rawOutput = false);
 
 private:
-    void abort() override;
+    void abort() override final;
 
 private:
     bool prepare();
