diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@
*.swp
*_manifest.*
*.embed.manifest
+bin/
bin/autotests
.clang_complete
bin/falkon.app
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -10,6 +10,7 @@
add_subdirectory(StatusBarIcons)
add_subdirectory(TabManager)
add_subdirectory(VerticalTabs)
+add_subdirectory(YoutubeDownload)
if (GNOME_KEYRING_FOUND)
add_subdirectory(GnomeKeyringPasswords)
diff --git a/src/plugins/YoutubeDownload/CMakeLists.txt b/src/plugins/YoutubeDownload/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/CMakeLists.txt
@@ -0,0 +1,20 @@
+set( YoutubeDownload_SRCS
+ yticon.cpp
+ ytinterface.cpp
+ ytsettings.cpp
+ ytprocess.cpp
+)
+
+set( YoutubeDownload_UIS
+ ytsettings.ui
+)
+qt5_wrap_ui(UIS ${YoutubeDownload_UIS})
+
+set( YoutubeDownload_RSCS
+ yt.qrc
+)
+qt5_add_resources(RSCS ${YoutubeDownload_RSCS})
+
+add_library(YoutubeDownload MODULE ${YoutubeDownload_SRCS} ${UIS} ${RSCS})
+install(TARGETS YoutubeDownload DESTINATION ${FALKON_INSTALL_PLUGINDIR})
+target_link_libraries(YoutubeDownload FalkonPrivate)
diff --git a/src/plugins/YoutubeDownload/data/icon-white.svg b/src/plugins/YoutubeDownload/data/icon-white.svg
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/data/icon-white.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/src/plugins/YoutubeDownload/data/icon.svg b/src/plugins/YoutubeDownload/data/icon.svg
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/data/icon.svg
@@ -0,0 +1,14 @@
+
diff --git a/src/plugins/YoutubeDownload/metadata.desktop b/src/plugins/YoutubeDownload/metadata.desktop
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/metadata.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=Youtube Download
+Comment=Adds ability to download youtube videos
+
+Icon=:ytdownload/data/icon.svg
+Type=Service
+
+X-Falkon-Author=Luca Gasperini
+X-Falkon-Email=info@xsoftware.eu
+X-Falkon-Version=0.1.0
+X-Falkon-Settings=true
diff --git a/src/plugins/YoutubeDownload/yt.qrc b/src/plugins/YoutubeDownload/yt.qrc
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/yt.qrc
@@ -0,0 +1,7 @@
+
+
+ metadata.desktop
+ data/icon.svg
+ data/icon-white.svg
+
+
diff --git a/src/plugins/YoutubeDownload/yticon.h b/src/plugins/YoutubeDownload/yticon.h
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/yticon.h
@@ -0,0 +1,21 @@
+#ifndef YTICON_H
+#define YTICON_H
+
+#include
+
+class YtIcon : public AbstractButtonInterface
+{
+ Q_OBJECT
+
+public:
+ explicit YtIcon(QObject *parent = nullptr);
+
+ QString id() const override;
+ QString name() const override;
+
+private:
+ void updateState();
+ void clicked(ClickController *controller);
+};
+
+#endif //YTICON_H
diff --git a/src/plugins/YoutubeDownload/yticon.cpp b/src/plugins/YoutubeDownload/yticon.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/yticon.cpp
@@ -0,0 +1,41 @@
+#include "yticon.h"
+
+YtIcon::YtIcon(QObject *parent)
+ : AbstractButtonInterface(parent)
+{
+ setIcon(QIcon::fromTheme(QSL("im-youtube"), QIcon(QSL(":ytdownload/data/icon.svg"))));
+ setTitle(tr("Download video"));
+ setToolTip(tr("Download video"));
+
+ connect(this, &AbstractButtonInterface::clicked, this, &YtIcon::clicked);
+
+ updateState();
+}
+
+QString YtIcon::id() const
+{
+ return QSL("youtube-download");
+}
+
+QString YtIcon::name() const
+{
+ return tr("Download Youtube");
+}
+
+void YtIcon::updateState()
+{
+/* setVisible(m_manager->downloadsCount() > 0);
+ const int count = m_manager->activeDownloadsCount();
+ if (count > 0) {
+ setBadgeText(QString::number(count));
+ } else {
+ setBadgeText(QString());
+ }*/
+}
+
+void YtIcon::clicked(ClickController *controller)
+{
+ Q_UNUSED(controller)
+
+ //mApp->downloadManager()->show();
+}
diff --git a/src/plugins/YoutubeDownload/ytinterface.h b/src/plugins/YoutubeDownload/ytinterface.h
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytinterface.h
@@ -0,0 +1,61 @@
+#ifndef YTINTERFACE_H
+#define YTINTERFACE_H
+
+// Include plugininterface.h for your version of Falkon
+#include "plugininterface.h"
+#include "yticon.h"
+#include "browserwindow.h"
+
+#include
+#include
+#include
+
+class YtInterface : public QObject, public PluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID "Falkon.Browser.plugin.YtInterface")
+
+public:
+ explicit YtInterface();
+
+ DesktopFile metaData() const override;
+ void init(InitState state, const QString &settingsPath) override;
+ void unload() override;
+ bool testPlugin() override;
+ void showSettings(QWidget *parent) override;
+ void saveSettings();
+ void loadSettings();
+
+ QString getOutputFile(const QString &url);
+
+ bool s_debug;
+ bool s_askalways;
+ bool s_metadata;
+ bool s_subtitle;
+ bool s_thumbnail;
+ bool s_extractaudio;
+ bool s_useproxy;
+ bool s_askalwaysfile;
+ QString s_formataudio;
+ QString s_formatvideo;
+ QString s_defaultdir;
+
+ int s_audioquality;
+
+ QString s_executable;
+
+private slots:
+ void actionSlot();
+ void downloadFinished(const QString &file);
+ void windowCreated(BrowserWindow* window);
+
+private:
+
+ int dialogSettings(QWidget* parent = nullptr);
+ YtIcon* m_download;
+ WebView* m_view;
+ QString m_settingsPath;
+};
+
+#endif // YTINTERFACE_H
diff --git a/src/plugins/YoutubeDownload/ytinterface.cpp b/src/plugins/YoutubeDownload/ytinterface.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytinterface.cpp
@@ -0,0 +1,189 @@
+
+#include "ytinterface.h"
+#include "ytsettings.h"
+#include "ytprocess.h"
+#include "browserwindow.h"
+#include "webview.h"
+#include "pluginproxy.h"
+#include "mainapplication.h"
+#include "sidebar.h"
+#include "webhittestresult.h"
+#include "../config.h"
+#include "desktopfile.h"
+#include "ytsettings.h"
+#include "tabwidget.h"
+#include "tabbar.h"
+#include "desktopnotificationsfactory.h"
+#include "networkmanager.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+YtInterface::YtInterface()
+ : QObject()
+{
+ // Don't do anything expensive in constructor!
+ // It will be called even if user doesn't have the plugin allowed
+}
+
+DesktopFile YtInterface::metaData() const
+{
+ return DesktopFile(QSL(":ytdownload/metadata.desktop"));
+}
+
+void YtInterface::init(InitState state, const QString &settingsPath)
+{
+ Q_UNUSED(state)
+
+ m_download = new YtIcon();
+ m_download->setIcon(QIcon(QL1S(":ytdownload/data/icon.svg")));
+ connect(m_download, SIGNAL(clicked(ClickController*)), this, SLOT(actionSlot()));
+ connect(mApp->plugins(), SIGNAL(mainWindowCreated(BrowserWindow*)), this, SLOT(windowCreated(BrowserWindow*)));
+
+ m_settingsPath = settingsPath + QL1S("/ytdownload/ytdownload.ini");
+ loadSettings();
+}
+
+void YtInterface::unload()
+{
+ mApp->getWindow()->navigationBar()->removeToolButton(m_download);
+}
+
+bool YtInterface::testPlugin()
+{
+ // This function is called right after init()
+ // There should be some testing if plugin is loaded correctly
+ // If this function returns false, plugin is automatically unloaded
+
+ return (Qz::VERSION == QLatin1String(FALKON_VERSION));
+}
+
+int YtInterface::dialogSettings(QWidget* parent)
+{
+ YtSettings* settings = new YtSettings(this, parent);
+ return settings->exec();
+}
+
+void YtInterface::showSettings(QWidget* parent)
+{
+ dialogSettings(parent);
+}
+
+void YtInterface::windowCreated(BrowserWindow *window)
+{
+ window->navigationBar()->addToolButton(m_download);
+}
+
+void YtInterface::actionSlot()
+{
+ YtProcess* exe = new YtProcess(nullptr, s_debug);
+ QStringList args;
+ QString outputfile;
+ QUrl url = mApp->getWindow()->tabWidget()->webTab(mApp->getWindow()->tabWidget()->currentIndex())->url();
+
+ connect(exe, SIGNAL(downloadFinished(QString)), this, SLOT(downloadFinished(QString)));
+
+ if(s_askalways)
+ if(dialogSettings() == QDialog::Rejected)
+ return;
+ if(s_askalwaysfile)
+ outputfile = QFileDialog::getSaveFileName(nullptr, "Save Video", s_defaultdir + "/" + YtProcess::getVideoTitle(s_executable, url.toString()));
+ else
+ outputfile = s_defaultdir + "/" + YtProcess::getVideoTitle(s_executable, url.toString());
+
+ if(outputfile.isEmpty())
+ return;
+
+ if(s_useproxy)
+ args << "--proxy" << "HTTP://" + mApp->networkManager()->proxy().hostName() + ":" + QString::number(mApp->networkManager()->proxy().port());
+ if(s_debug)
+ args.append("--verbose");
+ if(s_metadata)
+ args.append(QL1S("--add-metadata"));
+ if(s_subtitle)
+ args.append(QL1S("--write-sub"));
+ if(s_thumbnail)
+ args.append(QL1S("--embed-thumbnail"));
+ if(s_extractaudio)
+ args.append(QL1S("--extract-audio"));
+
+ args << "--audio-format" << s_formataudio;
+ args << "--audio-quality" << QString::number(s_audioquality);
+ if(!s_extractaudio)
+ args << "--recode-video" << s_formatvideo;
+
+ exe->setExecutable(s_executable);
+ exe->setArguments(args);
+ exe->setUrl(url);
+ exe->setOutputFile(outputfile);
+ exe->start();
+}
+
+void YtInterface::saveSettings()
+{
+ QSettings settings(m_settingsPath, QSettings::IniFormat);
+ settings.beginGroup("General");
+ settings.setValue("Debug", s_debug);
+ settings.setValue("AskFilename", s_askalwaysfile);
+ settings.setValue("AskAlways", s_askalways);
+ settings.setValue("ExecutablePath", s_executable);
+ settings.endGroup();
+ settings.beginGroup("Output");
+ settings.setValue("DefaultDir", s_defaultdir);
+ settings.setValue("ExtractAudio", s_extractaudio);
+ settings.endGroup();
+ settings.beginGroup("Format");
+ settings.setValue("Video", s_formatvideo);
+ settings.setValue("Audio", s_formataudio);
+ settings.setValue("AudioQuality", s_audioquality);
+ settings.endGroup();
+ settings.beginGroup("Other");
+ settings.setValue("Metadata", s_metadata);
+ settings.setValue("Subtitle", s_subtitle);
+ settings.setValue("Thumbnail", s_thumbnail);
+ settings.endGroup();
+
+}
+
+void YtInterface::loadSettings()
+{
+ QSettings settings(m_settingsPath, QSettings::IniFormat);
+ settings.beginGroup("General");
+ s_debug = settings.value("Debug", false).toBool();
+ s_askalwaysfile = settings.value("AskFilename", true).toBool();
+ s_askalways = settings.value("AskAlways", false).toBool();
+ s_executable = settings.value("ExecutablePath", "/bin/youtube-dl").toString();
+ settings.endGroup();
+ settings.beginGroup("Output");
+ s_defaultdir = settings.value("DefaultDir", QDir::homePath()).toString();
+ s_extractaudio = settings.value("ExtractAudio", false).toBool();
+ settings.endGroup();
+ settings.beginGroup("Format");
+ s_formatvideo = settings.value("Video", "mp4").toString();
+ s_formataudio = settings.value("Audio", "m4a").toString();
+ s_audioquality = settings.value("AudioQuality", 5).toInt();
+ settings.endGroup();
+ settings.beginGroup("Other");
+ s_metadata = settings.value("Metadata", false).toBool();
+ s_subtitle = settings.value("Subtitle", false).toBool();
+ s_thumbnail = settings.value("Thumbnail", false).toBool();
+ settings.endGroup();
+}
+
+void YtInterface::downloadFinished(const QString &file)
+{
+ DesktopNotificationsFactory* notify = new DesktopNotificationsFactory();
+ QString f = file + "." + (s_extractaudio ? s_formataudio : s_formatvideo);
+ if(QFile::exists(f))
+ notify->showNotification(QPixmap(QL1S(":ytdownload/data/icon-white.svg")),"Youtube video downloaded", "The youtube video has been downloaded!");
+ else
+ notify->showNotification(QPixmap(QL1S(":ytdownload/data/icon-white.svg")),"Download failed", "The youtube video cannot be downloaded!");
+}
diff --git a/src/plugins/YoutubeDownload/ytprocess.h b/src/plugins/YoutubeDownload/ytprocess.h
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytprocess.h
@@ -0,0 +1,41 @@
+#ifndef YTPROCESS_H
+#define YTPROCESS_H
+
+#include
+#include
+#include
+
+class YtProcess : public QThread
+{
+ Q_OBJECT
+public:
+ YtProcess(QObject* parent = nullptr, bool debug = false);
+ ~YtProcess();
+
+ void run();
+ static bool test(const QString& e);
+
+ static QString getVideoTitle(const QString& _exe, const QString& _url);
+
+ void setExecutable(const QString& _exe);
+ void setArguments(const QStringList& _args);
+ void setUrl(const QUrl &_url);
+ void setOutputFile(const QString &out);
+
+ QString executable();
+ QStringList arguments();
+ QUrl url();
+ QString outputfile();
+
+signals:
+ void readOutput(const QString& out);
+ void downloadFinished(const QString& file);
+private:
+ QProcess* proc;
+ QString exe;
+ QStringList args;
+ QUrl u;
+ QString of;
+};
+
+#endif //YTPROCESS_H
diff --git a/src/plugins/YoutubeDownload/ytprocess.cpp b/src/plugins/YoutubeDownload/ytprocess.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytprocess.cpp
@@ -0,0 +1,82 @@
+#include "ytprocess.h"
+#include
+
+YtProcess::YtProcess(QObject* parent, bool debug) : QThread(parent)
+{
+ proc = new QProcess;
+ if(debug)
+ proc->setProcessChannelMode(QProcess::ForwardedChannels);
+}
+
+YtProcess::~YtProcess()
+{
+}
+
+void YtProcess::run()
+{
+ if(exe.isEmpty() || args.isEmpty() || u.isEmpty() || of.isEmpty())
+ {
+ qWarning() << "[YoutubeDownload][FAIL] program or arguments are not found";
+ return;
+ }
+
+ QStringList temp = args;
+ temp << "-o" << of + ".%(ext)s" << u.toString();
+
+ proc->start(exe, temp, QIODevice::ReadOnly);
+ if(!proc->waitForStarted())
+ return;
+
+ proc->waitForReadyRead();
+ while(proc->bytesAvailable())
+ {
+ emit readOutput(proc->readLine());
+ }
+ proc->waitForFinished();
+ emit downloadFinished(of);
+
+}
+
+QString YtProcess::getVideoTitle(const QString& _exe, const QString &_url)
+{
+ QProcess* p = new QProcess();
+ QStringList a;
+ QString offset;
+ a << "--get-filename" << "-o" << "%(title)s" << _url;
+
+ p->start(_exe, a, QIODevice::ReadOnly);
+ if(!p->waitForStarted())
+ return "";
+
+
+ p->waitForReadyRead();
+ offset = p->readAll();
+ return offset;
+}
+
+bool YtProcess::test(const QString &e)
+{
+ QProcess* p = new QProcess();
+ QStringList a;
+ a << "--get-filename" << "--restrict-filenames" << "-o" << "%(title)s" << "BaW_jenozKc";
+
+ p->start(e, a, QIODevice::ReadOnly);
+ if(!p->waitForStarted())
+ return false;
+
+ QString b;
+ while(p->waitForReadyRead())
+ b.append(p->readAll());
+
+ return b == "youtube-dl_test_video_a\n";
+}
+
+void YtProcess::setExecutable(const QString &_exe) { exe = _exe; }
+void YtProcess::setArguments(const QStringList& _args) { args = _args; }
+void YtProcess::setUrl(const QUrl &_url) { u = _url; }
+void YtProcess::setOutputFile(const QString &out) { of = out; }
+
+QString YtProcess::executable() { return exe; }
+QStringList YtProcess::arguments() { return args; }
+QUrl YtProcess::url() { return u; }
+QString YtProcess::outputfile() { return of; }
diff --git a/src/plugins/YoutubeDownload/ytsettings.h b/src/plugins/YoutubeDownload/ytsettings.h
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytsettings.h
@@ -0,0 +1,36 @@
+#ifndef YTSETTINGS_H
+#define YTSETTINGS_H
+
+#include
+#include "ytinterface.h"
+#include "ytprocess.h"
+
+namespace Ui {
+class YtSettings;
+}
+
+class YtSettings : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit YtSettings(YtInterface *plugin, QWidget *parent = nullptr);
+ ~YtSettings();
+
+private Q_SLOTS:
+
+ void on_buttonBox_accepted();
+
+ void on_buttonBox_rejected();
+
+ void selectFile();
+ void selectDir();
+ void changeExec(const QString& file);
+
+private:
+
+ Ui::YtSettings *ui;
+ YtInterface *m_plugin;
+};
+
+#endif
diff --git a/src/plugins/YoutubeDownload/ytsettings.cpp b/src/plugins/YoutubeDownload/ytsettings.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytsettings.cpp
@@ -0,0 +1,87 @@
+#include "ui_ytsettings.h"
+#include "ytsettings.h"
+#include
+#include
+#include
+#include
+#include
+
+YtSettings::YtSettings(YtInterface *plugin, QWidget *parent) : QDialog(parent), ui(new Ui::YtSettings), m_plugin(plugin)
+{
+ setAttribute(Qt::WA_DeleteOnClose);
+ ui->setupUi(this);
+
+ m_plugin->loadSettings();
+
+ ui->comboAudioExtention->setCurrentText(m_plugin->s_formataudio);
+ ui->comboVideoExtention->setCurrentText(m_plugin->s_formatvideo);
+ ui->lineExec->setText(m_plugin->s_executable);
+ ui->lineDefaultDir->setText(m_plugin->s_defaultdir);
+ ui->checkMetadata->setChecked(m_plugin->s_metadata);
+ ui->checkSubtitle->setChecked(m_plugin->s_subtitle);
+ ui->checkThumbnail->setChecked(m_plugin->s_thumbnail);
+ ui->checkAskAlways->setChecked(m_plugin->s_askalways);
+ ui->checkMetadata->setChecked(m_plugin->s_metadata);
+ ui->checkDebug->setChecked(m_plugin->s_debug);
+ ui->checkExtract->setChecked(m_plugin->s_extractaudio);
+ ui->checkAskAlwaysFile->setChecked(m_plugin->s_askalwaysfile);
+ ui->checkProxy->setChecked(m_plugin->s_useproxy);
+ ui->sliderQuality->setValue(m_plugin->s_audioquality);
+
+ connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
+ connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(on_buttonBox_rejected()));
+ connect(ui->toolExecutable, SIGNAL(clicked(bool)), this, SLOT(selectFile()));
+ connect(ui->toolDir, SIGNAL(clicked(bool)), this, SLOT(selectDir()));
+ connect(ui->lineExec, SIGNAL(textChanged(QString)), this, SLOT(changeExec(QString)));
+}
+
+YtSettings::~YtSettings()
+{
+ delete ui;
+}
+
+void YtSettings::on_buttonBox_accepted()
+{
+ if(!ui->lineExec->text().isEmpty())
+ m_plugin->s_executable = ui->lineExec->text();
+
+ m_plugin->s_metadata = ui->checkMetadata->isChecked();
+ m_plugin->s_subtitle = ui->checkSubtitle->isChecked();
+ m_plugin->s_thumbnail = ui->checkThumbnail->isChecked();
+ m_plugin->s_askalways = ui->checkAskAlways->isChecked();
+ m_plugin->s_debug = ui->checkDebug->isChecked();
+ m_plugin->s_extractaudio = ui->checkExtract->isChecked();
+ m_plugin->s_askalwaysfile = ui->checkAskAlwaysFile->isChecked();
+ m_plugin->s_useproxy = ui->checkProxy->isChecked();
+
+ m_plugin->s_formatvideo = ui->comboVideoExtention->currentText();
+ m_plugin->s_formataudio = ui->comboAudioExtention->currentText();
+ m_plugin->s_defaultdir = ui->lineDefaultDir->text();
+ m_plugin->s_audioquality = ui->sliderQuality->value();
+
+ m_plugin->saveSettings();
+ accept();
+}
+
+void YtSettings::on_buttonBox_rejected()
+{
+ reject();
+}
+
+void YtSettings::selectFile()
+{
+ ui->lineExec->setText(QFileDialog::getOpenFileName(this, "Select youtube-dl executable file.", m_plugin->s_executable));
+}
+
+void YtSettings::selectDir()
+{
+ ui->lineDefaultDir->setText(QFileDialog::getExistingDirectory(this, "Select download directory.", m_plugin->s_defaultdir));
+}
+
+void YtSettings::changeExec(const QString& file)
+{
+ if(!YtProcess::test(file))
+ ui->lineExec->setStyleSheet("QLineEdit { color : red; }");
+ else
+ ui->lineExec->setStyleSheet("");
+}
diff --git a/src/plugins/YoutubeDownload/ytsettings.ui b/src/plugins/YoutubeDownload/ytsettings.ui
new file mode 100644
--- /dev/null
+++ b/src/plugins/YoutubeDownload/ytsettings.ui
@@ -0,0 +1,260 @@
+
+
+ YtSettings
+
+
+
+ 0
+ 0
+ 460
+ 344
+
+
+
+ Youtube Download Settings
+
+
+ -
+
+
-
+
+
-
+
+ m4a
+
+
+ -
+
+ aac
+
+
+ -
+
+ flac
+
+
+ -
+
+ mp3
+
+
+ -
+
+ opus
+
+
+ -
+
+ vorbis
+
+
+ -
+
+ wav
+
+
+
+
+ -
+
+
+ 10
+
+
+ 5
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+ mp4
+
+
+ -
+
+ avi
+
+
+ -
+
+ flv
+
+
+ -
+
+ ogg
+
+
+ -
+
+ webm
+
+
+ -
+
+ mkv
+
+
+
+
+ -
+
+
+ Audio Quality
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Audio Format
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Video Format
+
+
+ Qt::AlignCenter
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
-
+
+
+ /bin/youtube-dl
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Youtube-dl executable path
+
+
+ Qt::AlignCenter
+
+
+
+
+
+ -
+
+
-
+
+
+ Add Metadata
+
+
+
+ -
+
+
+ Enable Debug
+
+
+
+ -
+
+
+ Add Thumbnail
+
+
+
+ -
+
+
+ Ask always settings
+
+
+
+ -
+
+
+ Add Subtitle (ENG)
+
+
+
+ -
+
+
+ Extract Audio
+
+
+
+ -
+
+
+ Ask always output filename
+
+
+
+ -
+
+
+ Use Proxy
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Default download location
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
+
+