File indexing completed on 2022-11-22 14:07:17

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "wizard.h"
0007 #include "effects/effectsrepository.hpp"
0008 #include "kdenlivesettings.h"
0009 #include "profiles/profilemodel.hpp"
0010 #include "profiles/profilerepository.hpp"
0011 #include "profilesdialog.h"
0012 
0013 #include "utils/thememanager.h"
0014 #include "core.h"
0015 #include <config-kdenlive.h>
0016 
0017 #include <framework/mlt_version.h>
0018 #include <mlt++/Mlt.h>
0019 
0020 #include <KLocalizedString>
0021 #include <KMessageWidget>
0022 #include <KProcess>
0023 #include <KRun>
0024 
0025 #include "kdenlive_debug.h"
0026 #include <QApplication>
0027 #include <QCheckBox>
0028 #include <QFile>
0029 #include <QLabel>
0030 #include <QListWidget>
0031 #include <QMimeDatabase>
0032 #include <QMimeType>
0033 #include <QPushButton>
0034 #include <QStandardPaths>
0035 #include <QTemporaryFile>
0036 #include <QTimer>
0037 #include <QXmlStreamWriter>
0038 
0039 #include <KIO/OpenUrlJob>
0040 #include <kio_version.h>
0041 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0)
0042 #include <KIO/JobUiDelegateFactory>
0043 #else
0044 #include <KIO/JobUiDelegate>
0045 #endif
0046 
0047 // Recommended MLT version
0048 MyWizardPage::MyWizardPage(QWidget *parent)
0049     : QWizardPage(parent)
0050 
0051 {
0052 }
0053 
0054 void MyWizardPage::setComplete(bool complete)
0055 {
0056     m_isComplete = complete;
0057 }
0058 
0059 bool MyWizardPage::isComplete() const
0060 {
0061     return m_isComplete;
0062 }
0063 
0064 Wizard::Wizard(bool autoClose, QWidget *parent)
0065     : QWizard(parent)
0066     , m_systemCheckIsOk(false)
0067     , m_brokenModule(false)
0068 {
0069     setWindowTitle(i18nc("@title:window", "Welcome to Kdenlive"));
0070     int logoHeight = int(fontMetrics().height() * 2.5);
0071     setWizardStyle(QWizard::ModernStyle);
0072     setOption(QWizard::NoBackButtonOnLastPage, true);
0073     // setOption(QWizard::ExtendedWatermarkPixmap, false);
0074     m_page = new MyWizardPage(this);
0075     m_page->setTitle(i18n("Welcome to Kdenlive %1", QString(KDENLIVE_VERSION)));
0076     m_page->setSubTitle(i18n("Using MLT %1", mlt_version_get_string()));
0077     setPixmap(QWizard::LogoPixmap, QIcon::fromTheme(QStringLiteral(":/pics/kdenlive.png")).pixmap(logoHeight, logoHeight));
0078     m_startLayout = new QVBoxLayout;
0079     m_errorWidget = new KMessageWidget(this);
0080     m_startLayout->addWidget(m_errorWidget);
0081     m_errorWidget->setCloseButtonVisible(false);
0082     m_errorWidget->hide();
0083     m_page->setLayout(m_startLayout);
0084     addPage(m_page);
0085 
0086     setButtonText(QWizard::CancelButton, i18n("Abort"));
0087     setButtonText(QWizard::FinishButton, i18n("OK"));
0088     slotCheckMlt();
0089     if (autoClose) {
0090         // This is a first run instance, check HW encoders
0091         testHwEncoders();
0092     } else {
0093         QPair<QStringList, QStringList> conversion = EffectsRepository::get()->fixDeprecatedEffects();
0094         if (conversion.first.count() > 0) {
0095             QLabel *lab = new QLabel(this);
0096             lab->setText(i18n("Converting old custom effects successful:"));
0097             m_startLayout->addWidget(lab);
0098             auto *list = new QListWidget(this);
0099             m_startLayout->addWidget(list);
0100             list->addItems(conversion.first);
0101         }
0102         if (conversion.second.count() > 0) {
0103             QLabel *lab = new QLabel(this);
0104             lab->setText(i18n("Converting old custom effects failed:"));
0105             m_startLayout->addWidget(lab);
0106             auto *list = new QListWidget(this);
0107             m_startLayout->addWidget(list);
0108             list->addItems(conversion.second);
0109         }
0110     }
0111     if (!m_errors.isEmpty() || !m_warnings.isEmpty() || (!m_infos.isEmpty())) {
0112         QLabel *lab = new QLabel(this);
0113         lab->setText(i18n("Startup error or warning, check our <a href='#'>online manual</a>."));
0114         connect(lab, &QLabel::linkActivated, this, &Wizard::slotOpenManual);
0115         m_startLayout->addWidget(lab);
0116     }
0117     if (!m_infos.isEmpty()) {
0118         auto *errorLabel = new KMessageWidget(this);
0119         errorLabel->setText(QStringLiteral("<ul>") + m_infos + QStringLiteral("</ul>"));
0120         errorLabel->setMessageType(KMessageWidget::Information);
0121         errorLabel->setWordWrap(true);
0122         errorLabel->setCloseButtonVisible(false);
0123         m_startLayout->addWidget(errorLabel);
0124         errorLabel->show();
0125     }
0126     if (m_errors.isEmpty() && m_warnings.isEmpty()) {
0127         // Everything is ok only some info message, show codec status
0128         m_page->setComplete(true);
0129         if (autoClose) {
0130             QTimer::singleShot(0, this, &QDialog::accept);
0131             return;
0132         }
0133         auto *lab = new KMessageWidget(this);
0134         lab->setText(i18n("Codecs have been updated, everything seems fine."));
0135         lab->setMessageType(KMessageWidget::Positive);
0136         lab->setCloseButtonVisible(false);
0137         m_startLayout->addWidget(lab);
0138         // HW accel
0139         QCheckBox *cb = new QCheckBox(i18n("VAAPI hardware acceleration"), this);
0140         m_startLayout->addWidget(cb);
0141         cb->setChecked(KdenliveSettings::vaapiEnabled());
0142         QCheckBox *cbn = new QCheckBox(i18n("NVIDIA hardware acceleration"), this);
0143         m_startLayout->addWidget(cbn);
0144         cbn->setChecked(KdenliveSettings::nvencEnabled());
0145         QPushButton *pb = new QPushButton(i18n("Check hardware acceleration"), this);
0146         connect(pb, &QPushButton::clicked, this, [&, cb, cbn, pb]() {
0147             testHwEncoders();
0148             pb->setEnabled(false);
0149             cb->setChecked(KdenliveSettings::vaapiEnabled());
0150             cbn->setChecked(KdenliveSettings::nvencEnabled());
0151             updateHwStatus();
0152             pb->setEnabled(true);
0153         });
0154         m_startLayout->addWidget(pb);
0155         setOption(QWizard::NoCancelButton, true);
0156         return;
0157     }
0158     if (!m_errors.isEmpty()) {
0159         auto *errorLabel = new KMessageWidget(this);
0160         errorLabel->setText(QStringLiteral("<ul>") + m_errors + QStringLiteral("</ul>"));
0161         errorLabel->setMessageType(KMessageWidget::Error);
0162         errorLabel->setWordWrap(true);
0163         errorLabel->setCloseButtonVisible(false);
0164         m_startLayout->addWidget(errorLabel);
0165         m_page->setComplete(false);
0166         errorLabel->show();
0167         if (!autoClose) {
0168             setButtonText(QWizard::CancelButton, i18n("Close"));
0169         }
0170     } else {
0171         m_page->setComplete(true);
0172         if (!autoClose) {
0173             setOption(QWizard::NoCancelButton, true);
0174         }
0175     }
0176     if (!m_warnings.isEmpty()) {
0177         auto *errorLabel = new KMessageWidget(this);
0178         errorLabel->setText(QStringLiteral("<ul>") + m_warnings + QStringLiteral("</ul>"));
0179         errorLabel->setMessageType(KMessageWidget::Warning);
0180         errorLabel->setWordWrap(true);
0181         errorLabel->setCloseButtonVisible(false);
0182         m_startLayout->addWidget(errorLabel);
0183         errorLabel->show();
0184     }
0185 
0186 }
0187 
0188 void Wizard::checkMltComponents()
0189 {
0190     m_brokenModule = false;
0191     if (!pCore->getMltRepository()) {
0192         m_errors.append(i18n("<li>Cannot start MLT backend, check your installation</li>"));
0193         m_systemCheckIsOk = false;
0194     } else {
0195         int mltVersion = QT_VERSION_CHECK(MLT_MIN_MAJOR_VERSION, MLT_MIN_MINOR_VERSION, MLT_MIN_PATCH_VERSION);
0196         int runningVersion = mlt_version_get_int();
0197         if (runningVersion < mltVersion) {
0198             m_errors.append(i18n("<li>Unsupported MLT version<br/>Please <b>upgrade</b> to %1.%2.%3</li>", MLT_MIN_MAJOR_VERSION, MLT_MIN_MINOR_VERSION,
0199                                  MLT_MIN_PATCH_VERSION));
0200             m_systemCheckIsOk = false;
0201         }
0202         // Retrieve the list of available transitions.
0203         Mlt::Properties *producers = pCore->getMltRepository()->producers();
0204         QStringList producersItemList;
0205         producersItemList.reserve(producers->count());
0206         for (int i = 0; i < producers->count(); ++i) {
0207             producersItemList << producers->get_name(i);
0208         }
0209         delete producers;
0210 
0211         // Check that we have the frei0r effects installed
0212         Mlt::Properties *filters = pCore->getMltRepository()->filters();
0213         bool hasFrei0r = false;
0214         for (int i = 0; i < filters->count(); ++i) {
0215             QString filterName = filters->get_name(i);
0216             if (filterName.startsWith(QStringLiteral("frei0r."))) {
0217                 hasFrei0r = true;
0218                 break;
0219             }
0220         }
0221         if (!hasFrei0r) {
0222             // Frei0r effects not found
0223             qDebug() << "Missing Frei0r module";
0224             m_warnings.append(
0225                 i18n("<li>Missing package: <b>Frei0r</b> effects (frei0r-plugins)<br/>provides many effects and transitions. Install recommended</li>"));
0226         }
0227 
0228         // Check that we have the avfilter effects installed
0229         bool hasAvfilter = false;
0230         for (int i = 0; i < filters->count(); ++i) {
0231             QString filterName = filters->get_name(i);
0232             if (filterName.startsWith(QStringLiteral("avfilter."))) {
0233                 hasAvfilter = true;
0234                 break;
0235             }
0236         }
0237         if (!hasAvfilter) {
0238             // AVFilter effects not found
0239             qDebug() << "Missing AVFilter module";
0240             m_warnings.append(i18n("<li>Missing package: <b>AVFilter</b><br/>provides many effects. Install recommended</li>"));
0241         } else {
0242             // Check that we have the avfilter.subtitles effects installed
0243             bool hasSubtitle = false;
0244             for (int i = 0; i < filters->count(); ++i) {
0245                 QString filterName = filters->get_name(i);
0246                 if (filterName == QStringLiteral("avfilter.subtitles")) {
0247                     hasSubtitle = true;
0248                     break;
0249                 }
0250             }
0251             if (!hasSubtitle) {
0252                 // avfilter.subtitles effect not found
0253                 qDebug() << "Missing avfilter.subtitles module";
0254                 m_warnings.append(i18n("<li>Missing filter: <b>avfilter.subtitles</b><br/>required for subtitle feature. Install recommended</li>"));
0255             }
0256         }
0257         delete filters;
0258 
0259 #if (!(defined(Q_OS_WIN) || defined(Q_OS_MAC)))
0260         // Check that we have the breeze icon theme installed
0261         const QStringList iconPaths = QIcon::themeSearchPaths();
0262         bool hasBreeze = false;
0263         for (const QString &path : iconPaths) {
0264             QDir dir(path);
0265             if (dir.exists(QStringLiteral("breeze"))) {
0266                 hasBreeze = true;
0267                 break;
0268             }
0269         }
0270         if (!hasBreeze) {
0271             // Breeze icons not found
0272             qDebug() << "Missing Breeze icons";
0273             m_warnings.append(
0274                 i18n("<li>Missing package: <b>Breeze</b> icons (breeze-icon-theme)<br/>provides many icons used in Kdenlive. Install recommended</li>"));
0275         }
0276 #endif
0277 
0278         Mlt::Properties *consumers = pCore->getMltRepository()->consumers();
0279         QStringList consumersItemList;
0280         consumersItemList.reserve(consumers->count());
0281         for (int i = 0; i < consumers->count(); ++i) {
0282             consumersItemList << consumers->get_name(i);
0283         }
0284         delete consumers;
0285         KdenliveSettings::setConsumerslist(consumersItemList);
0286         if (consumersItemList.contains(QStringLiteral("sdl2_audio"))) {
0287             // MLT >= 6.6.0 and SDL2 module
0288             KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl2_audio"));
0289             KdenliveSettings::setAudiobackend(QStringLiteral("sdl2_audio"));
0290 #if defined(Q_OS_WIN)
0291             // Use wasapi by default on Windows
0292             KdenliveSettings::setAudiodrivername(QStringLiteral("wasapi"));
0293 #endif
0294         } else if (consumersItemList.contains(QStringLiteral("sdl_audio"))) {
0295             // MLT < 6.6.0
0296             KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl_audio"));
0297             KdenliveSettings::setAudiobackend(QStringLiteral("sdl_audio"));
0298         } else if (consumersItemList.contains(QStringLiteral("rtaudio"))) {
0299             KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl2_audio"));
0300             KdenliveSettings::setAudiobackend(QStringLiteral("rtaudio"));
0301         } else {
0302             // SDL module
0303             m_errors.append(i18n("<li>Missing MLT module: <b>sdl</b> or <b>rtaudio</b><br/>required for audio output</li>"));
0304             m_systemCheckIsOk = false;
0305         }
0306 
0307         Mlt::Consumer *consumer = nullptr;
0308         Mlt::Profile p;
0309         // XML module
0310         if (consumersItemList.contains(QStringLiteral("xml"))) {
0311             consumer = new Mlt::Consumer(p, "xml");
0312         }
0313         if (consumer == nullptr || !consumer->is_valid()) {
0314             qDebug() << "Missing XML MLT module";
0315             m_errors.append(i18n("<li>Missing MLT module: <b>xml</b> <br/>required for audio/video</li>"));
0316             m_systemCheckIsOk = true;
0317         }
0318         // AVformat module
0319         consumer = nullptr;
0320         if (consumersItemList.contains(QStringLiteral("avformat"))) {
0321             consumer = new Mlt::Consumer(p, "avformat");
0322         }
0323         if (consumer == nullptr || !consumer->is_valid()) {
0324             qDebug() << "Missing AVFORMAT MLT module";
0325             m_warnings.append(i18n("<li>Missing MLT module: <b>avformat</b> (FFmpeg)<br/>required for audio/video</li>"));
0326             m_brokenModule = true;
0327         } else {
0328             delete consumer;
0329         }
0330 
0331         // Image module
0332         if (!producersItemList.contains(QStringLiteral("qimage")) && !producersItemList.contains(QStringLiteral("pixbuf"))) {
0333             qDebug() << "Missing image MLT module";
0334             m_warnings.append(i18n("<li>Missing MLT module: <b>qimage</b> or <b>pixbuf</b><br/>required for images and titles</li>"));
0335             m_brokenModule = true;
0336         }
0337 
0338         // Titler module
0339         if (!producersItemList.contains(QStringLiteral("kdenlivetitle"))) {
0340             qDebug() << "Missing TITLER MLT module";
0341             m_warnings.append(i18n("<li>Missing MLT module: <b>kdenlivetitle</b><br/>required to create titles</li>"));
0342             m_brokenModule = true;
0343         }
0344         // Animation module
0345         if (!producersItemList.contains(QStringLiteral("glaxnimate"))) {
0346             qDebug() << "Missing Glaxnimate MLT module";
0347             m_warnings.append(i18n("<li>Missing MLT module: <b>glaxnimate</b><br/>required to load Lottie animations</li>"));
0348             m_brokenModule = true;
0349         }
0350     }
0351     if (m_systemCheckIsOk && !m_brokenModule) {
0352         // everything is ok
0353         return;
0354     }
0355     if (!m_systemCheckIsOk || m_brokenModule) {
0356         // Something is wrong with install
0357         if (!m_systemCheckIsOk) {
0358             // WARN
0359         }
0360     } else {
0361         // OK
0362     }
0363 }
0364 
0365 void Wizard::slotCheckPrograms(QString &infos, QString &warnings)
0366 {
0367     // Check first in same folder as melt exec
0368     const QStringList mltpath({QFileInfo(KdenliveSettings::rendererpath()).canonicalPath(), qApp->applicationDirPath()});
0369     QString exepath;
0370     if (KdenliveSettings::ffmpegpath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffmpegpath())) {
0371         exepath = QStandardPaths::findExecutable(QString("ffmpeg%1").arg(FFMPEG_SUFFIX), mltpath);
0372         if (exepath.isEmpty()) {
0373             exepath = QStandardPaths::findExecutable(QString("ffmpeg%1").arg(FFMPEG_SUFFIX));
0374         }
0375         qDebug() << "Found FFMpeg binary: " << exepath;
0376         if (exepath.isEmpty()) {
0377             // Check for libav version
0378             exepath = QStandardPaths::findExecutable(QStringLiteral("avconv"));
0379             if (exepath.isEmpty()) {
0380                 warnings.append(i18n("<li>Missing app: <b>ffmpeg</b><br/>required for proxy clips and transcoding</li>"));
0381             }
0382         }
0383     }
0384     QString playpath;
0385     if (KdenliveSettings::ffplaypath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffplaypath())) {
0386         playpath = QStandardPaths::findExecutable(QStringLiteral("ffplay%1").arg(FFMPEG_SUFFIX), mltpath);
0387         if (playpath.isEmpty()) {
0388             playpath = QStandardPaths::findExecutable(QStringLiteral("ffplay%1").arg(FFMPEG_SUFFIX));
0389         }
0390         if (playpath.isEmpty()) {
0391             // Check for libav version
0392             playpath = QStandardPaths::findExecutable(QStringLiteral("avplay"));
0393             if (playpath.isEmpty()) {
0394                 infos.append(i18n("<li>Missing app: <b>ffplay</b><br/>recommended for some preview jobs</li>"));
0395             }
0396         }
0397     }
0398     QString probepath;
0399     if (KdenliveSettings::ffprobepath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffprobepath())) {
0400         probepath = QStandardPaths::findExecutable(QStringLiteral("ffprobe%1").arg(FFMPEG_SUFFIX), mltpath);
0401         if (probepath.isEmpty()) {
0402             probepath = QStandardPaths::findExecutable(QStringLiteral("ffprobe%1").arg(FFMPEG_SUFFIX));
0403         }
0404         if (probepath.isEmpty()) {
0405             // Check for libav version
0406             probepath = QStandardPaths::findExecutable(QStringLiteral("avprobe"));
0407             if (probepath.isEmpty()) {
0408                 infos.append(i18n("<li>Missing app: <b>ffprobe</b><br/>recommended for extra clip analysis</li>"));
0409             }
0410         }
0411     }
0412 
0413     if (!exepath.isEmpty()) {
0414         KdenliveSettings::setFfmpegpath(exepath);
0415     }
0416     if (!playpath.isEmpty()) {
0417         KdenliveSettings::setFfplaypath(playpath);
0418     }
0419     if (!probepath.isEmpty()) {
0420         KdenliveSettings::setFfprobepath(probepath);
0421     }
0422 
0423     // set up some default applications
0424     QString program;
0425     if (KdenliveSettings::defaultimageapp().isEmpty()) {
0426         QString imageBinary = QStandardPaths::findExecutable(QStringLiteral("gimp"));
0427 #ifdef Q_OS_WIN
0428         if (imageBinary.isEmpty()) {
0429             imageBinary = QStandardPaths::findExecutable(QStringLiteral("gimp"), {"C:/Program Files/Gimp", "C:/Program Files (x86)/Gimp"});
0430         }
0431 #endif
0432         if (imageBinary.isEmpty()) {
0433             imageBinary = QStandardPaths::findExecutable(QStringLiteral("krita"));
0434         }
0435 #ifdef Q_OS_WIN
0436         if (imageBinary.isEmpty()) {
0437             imageBinary = QStandardPaths::findExecutable(QStringLiteral("krita"), {"C:/Program Files/Krita", "C:/Program Files (x86)/Krita"});
0438         }
0439 #endif
0440         if (!imageBinary.isEmpty()) {
0441             KdenliveSettings::setDefaultimageapp(imageBinary);
0442         }
0443     }
0444     if (KdenliveSettings::defaultaudioapp().isEmpty()) {
0445         QString audioBinary = QStandardPaths::findExecutable(QStringLiteral("audacity"));
0446 #ifdef Q_OS_WIN
0447         if (audioBinary.isEmpty()) {
0448             audioBinary = QStandardPaths::findExecutable(QStringLiteral("audacity"), {"C:/Program Files/Audacity", "C:/Program Files (x86)/Audacity"});
0449         }
0450 #endif
0451         if (audioBinary.isEmpty()) {
0452             audioBinary = QStandardPaths::findExecutable(QStringLiteral("traverso"));
0453         }
0454         if (!audioBinary.isEmpty()) {
0455             KdenliveSettings::setDefaultaudioapp(audioBinary);
0456         }
0457     }
0458     if (KdenliveSettings::glaxnimatePath().isEmpty()) {
0459         QString animBinary = QStandardPaths::findExecutable(QStringLiteral("glaxnimate"));
0460 #ifdef Q_OS_WIN
0461         if (animBinary.isEmpty()) {
0462             animBinary = QStandardPaths::findExecutable(QStringLiteral("glaxnimate"), {"C:/Program Files/Glaxnimate", "C:/Program Files (x86)/Glaxnimate"});
0463         }
0464 #endif
0465         if (!animBinary.isEmpty()) {
0466             KdenliveSettings::setGlaxnimatePath(animBinary);
0467         }
0468     }
0469 
0470     if (KdenliveSettings::mediainfopath().isEmpty() || !QFileInfo::exists(KdenliveSettings::mediainfopath())) {
0471         program = QStandardPaths::findExecutable(QStringLiteral("mediainfo"));
0472 #ifdef Q_OS_WIN
0473         if (program.isEmpty()) {
0474             program = QStandardPaths::findExecutable(QStringLiteral("mediainfo"), {"C:/Program Files/MediaInfo", "C:/Program Files (x86)/MediaInfo"});
0475         }
0476 #endif
0477         if (program.isEmpty()) {
0478             infos.append(i18n("<li>Missing app: <b>mediainfo</b><br/>optional for technical clip information</li>"));
0479         } else {
0480             KdenliveSettings::setMediainfopath(program);
0481         }
0482     }
0483 }
0484 
0485 void Wizard::installExtraMimes(const QString &baseName, const QStringList &globs)
0486 {
0487     QMimeDatabase db;
0488     QString mimefile = baseName;
0489     mimefile.replace('/', '-');
0490     QMimeType mime = db.mimeTypeForName(baseName);
0491     QStringList missingGlobs;
0492 
0493     for (const QString &glob : globs) {
0494         QMimeType type = db.mimeTypeForFile(glob, QMimeDatabase::MatchExtension);
0495         QString mimeName = type.name();
0496         if (!mimeName.contains(QStringLiteral("audio")) && !mimeName.contains(QStringLiteral("video"))) {
0497             missingGlobs << glob;
0498         }
0499     }
0500     if (missingGlobs.isEmpty()) {
0501         return;
0502     }
0503     if (!mime.isValid() || mime.isDefault()) {
0504         qCDebug(KDENLIVE_LOG) << "MIME type " << baseName << " not found";
0505     } else {
0506         QStringList extensions = mime.globPatterns();
0507         QString comment = mime.comment();
0508         for (const QString &glob : qAsConst(missingGlobs)) {
0509             if (!extensions.contains(glob)) {
0510                 extensions << glob;
0511             }
0512         }
0513         // qCDebug(KDENLIVE_LOG) << "EXTS: " << extensions;
0514         QDir mimeDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/mime/packages/"));
0515         if (!mimeDir.exists()) {
0516             mimeDir.mkpath(QStringLiteral("."));
0517         }
0518         QString packageFileName = mimeDir.absoluteFilePath(mimefile + QStringLiteral(".xml"));
0519         // qCDebug(KDENLIVE_LOG) << "INSTALLING NEW MIME TO: " << packageFileName;
0520         QFile packageFile(packageFileName);
0521         if (!packageFile.open(QIODevice::WriteOnly)) {
0522             qCCritical(KDENLIVE_LOG) << "Couldn't open" << packageFileName << "for writing";
0523             return;
0524         }
0525         QXmlStreamWriter writer(&packageFile);
0526         writer.setAutoFormatting(true);
0527         writer.writeStartDocument();
0528 
0529         const QString nsUri = QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info");
0530         writer.writeDefaultNamespace(nsUri);
0531         writer.writeStartElement(QStringLiteral("mime-info"));
0532         writer.writeStartElement(nsUri, QStringLiteral("mime-type"));
0533         writer.writeAttribute(QStringLiteral("type"), baseName);
0534 
0535         if (!comment.isEmpty()) {
0536             writer.writeStartElement(nsUri, QStringLiteral("comment"));
0537             writer.writeCharacters(comment);
0538             writer.writeEndElement(); // comment
0539         }
0540 
0541         for (const QString &pattern : qAsConst(extensions)) {
0542             writer.writeStartElement(nsUri, QStringLiteral("glob"));
0543             writer.writeAttribute(QStringLiteral("pattern"), pattern);
0544             writer.writeEndElement(); // glob
0545         }
0546 
0547         writer.writeEndElement(); // mime-info
0548         writer.writeEndElement(); // mime-type
0549         writer.writeEndDocument();
0550     }
0551 }
0552 
0553 void Wizard::runUpdateMimeDatabase()
0554 {
0555     const QString localPackageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/mime/");
0556     // Q_ASSERT(!localPackageDir.isEmpty());
0557     KProcess proc;
0558     proc << QStringLiteral("update-mime-database");
0559     proc << localPackageDir;
0560     const int exitCode = proc.execute();
0561     if (exitCode != 0) {
0562         qCWarning(KDENLIVE_LOG) << proc.program() << "exited with error code" << exitCode;
0563     }
0564 }
0565 
0566 void Wizard::adjustSettings()
0567 {
0568     QStringList globs;
0569 
0570     globs << QStringLiteral("*.mts") << QStringLiteral("*.m2t") << QStringLiteral("*.mod") << QStringLiteral("*.ts") << QStringLiteral("*.m2ts")
0571           << QStringLiteral("*.m2v");
0572     installExtraMimes(QStringLiteral("video/mpeg"), globs);
0573     globs.clear();
0574     globs << QStringLiteral("*.dv");
0575     installExtraMimes(QStringLiteral("video/dv"), globs);
0576     runUpdateMimeDatabase();
0577 }
0578 
0579 void Wizard::slotCheckMlt()
0580 {
0581     QString errorMessage;
0582     if (KdenliveSettings::rendererpath().isEmpty()) {
0583         errorMessage.append(i18n("Your MLT installation cannot be found. Install MLT and restart Kdenlive.\n"));
0584     }
0585 
0586     if (!errorMessage.isEmpty()) {
0587         errorMessage.prepend(QStringLiteral("<b>%1</b><br />").arg(i18n("Fatal Error")));
0588         QLabel *pix = new QLabel();
0589         pix->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(30));
0590         QLabel *label = new QLabel(errorMessage);
0591         label->setWordWrap(true);
0592         m_startLayout->addSpacing(40);
0593         m_startLayout->addWidget(pix);
0594         m_startLayout->addWidget(label);
0595         m_systemCheckIsOk = false;
0596         // Warn
0597     } else {
0598         m_systemCheckIsOk = true;
0599     }
0600 
0601     if (m_systemCheckIsOk) {
0602         checkMltComponents();
0603     }
0604     slotCheckPrograms(m_infos, m_warnings);
0605 }
0606 
0607 bool Wizard::isOk() const
0608 {
0609     return m_systemCheckIsOk;
0610 }
0611 
0612 void Wizard::slotOpenManual()
0613 {
0614     auto *job = new KIO::OpenUrlJob(QUrl(QStringLiteral("https://docs.kdenlive.org/troubleshooting/installation_troubleshooting.html")));
0615 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0)
0616     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0617 #else
0618     job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0619 #endif
0620     // methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections
0621     // exist in both classes
0622     job->start();
0623     //KIO::OpenUrlJob(QUrl(QStringLiteral("https://docs.kdenlive.org/troubleshooting/installation_troubleshooting.html")), QStringLiteral("text/html"));
0624 }
0625 
0626 void Wizard::testHwEncoders()
0627 {
0628     QProcess hwEncoders;
0629     // Testing vaapi support
0630     QTemporaryFile tmp(QDir::temp().absoluteFilePath(QStringLiteral("XXXXXX.mp4")));
0631     if (!tmp.open()) {
0632         // Something went wrong
0633         return;
0634     }
0635     tmp.close();
0636 
0637     // VAAPI testing
0638     QStringList args{"-hide_banner",
0639                      "-y",
0640                      "-vaapi_device",
0641                      "/dev/dri/renderD128",
0642                      "-f",
0643                      "lavfi",
0644                      "-i",
0645                      "smptebars=duration=5:size=1280x720:rate=25",
0646                      "-vf",
0647                      "format=nv12,hwupload",
0648                      "-c:v",
0649                      "h264_vaapi",
0650                      "-an",
0651                      "-f",
0652                      "mp4",
0653                      tmp.fileName()};
0654     qDebug() << "// FFMPEG ARGS: " << args;
0655     hwEncoders.start(KdenliveSettings::ffmpegpath(), args);
0656     bool vaapiSupported = false;
0657     if (hwEncoders.waitForFinished()) {
0658         if (hwEncoders.exitStatus() == QProcess::CrashExit) {
0659             qDebug() << "/// ++ VAAPI NOT SUPPORTED";
0660         } else {
0661             if (tmp.exists() && tmp.size() > 0) {
0662                 qDebug() << "/// ++ VAAPI YES SUPPORTED ::::::";
0663                 // vaapi support enabled
0664                 vaapiSupported = true;
0665             } else {
0666                 qDebug() << "/// ++ VAAPI FAILED ::::::";
0667                 // vaapi support not enabled
0668             }
0669         }
0670     }
0671     KdenliveSettings::setVaapiEnabled(vaapiSupported);
0672 
0673     // VAAPI with scaling support
0674     QStringList scaleargs{"-hide_banner",
0675                           "-y",
0676                           "-hwaccel",
0677                           "vaapi",
0678                           "-hwaccel_output_format",
0679                           "vaapi",
0680                           "/dev/dri/renderD128",
0681                           "-f",
0682                           "lavfi",
0683                           "-i",
0684                           "smptebars=duration=5:size=1280x720:rate=25",
0685                           "-vf",
0686                           "scale_vaapi=w=640:h=-2:format=nv12,hwupload",
0687                           "-c:v",
0688                           "h264_vaapi",
0689                           "-an",
0690                           "-f",
0691                           "mp4",
0692                           tmp.fileName()};
0693     qDebug() << "// FFMPEG ARGS: " << scaleargs;
0694     hwEncoders.start(KdenliveSettings::ffmpegpath(), scaleargs);
0695     bool vaapiScalingSupported = false;
0696     if (hwEncoders.waitForFinished()) {
0697         if (hwEncoders.exitStatus() == QProcess::CrashExit) {
0698             qDebug() << "/// ++ VAAPI NOT SUPPORTED";
0699         } else {
0700             if (tmp.exists() && tmp.size() > 0) {
0701                 qDebug() << "/// ++ VAAPI YES SUPPORTED ::::::";
0702                 // vaapi support enabled
0703                 vaapiScalingSupported = true;
0704             } else {
0705                 qDebug() << "/// ++ VAAPI FAILED ::::::";
0706                 // vaapi support not enabled
0707             }
0708         }
0709     }
0710     KdenliveSettings::setVaapiScalingEnabled(vaapiScalingSupported);
0711     // NVIDIA testing
0712     QTemporaryFile tmp2(QDir::temp().absoluteFilePath(QStringLiteral("XXXXXX.mp4")));
0713     if (!tmp2.open()) {
0714         // Something went wrong
0715         return;
0716     }
0717     tmp2.close();
0718     QStringList args2{"-hide_banner", "-y",         "-hwaccel", "cuvid", "-f",  "lavfi",        "-i", "smptebars=duration=5:size=1280x720:rate=25",
0719                       "-c:v",         "h264_nvenc", "-an",      "-f",    "mp4", tmp2.fileName()};
0720     qDebug() << "// FFMPEG ARGS: " << args2;
0721     hwEncoders.start(KdenliveSettings::ffmpegpath(), args2);
0722     bool nvencSupported = false;
0723     if (hwEncoders.waitForFinished()) {
0724         if (hwEncoders.exitStatus() == QProcess::CrashExit) {
0725             qDebug() << "/// ++ NVENC NOT SUPPORTED";
0726         } else {
0727             if (tmp2.exists() && tmp2.size() > 0) {
0728                 qDebug() << "/// ++ NVENC YES SUPPORTED ::::::";
0729                 // vaapi support enabled
0730                 nvencSupported = true;
0731             } else {
0732                 qDebug() << "/// ++ NVENC FAILED ::::::";
0733                 // vaapi support not enabled
0734             }
0735         }
0736     }
0737     KdenliveSettings::setNvencEnabled(nvencSupported);
0738 
0739     // Testing NVIDIA SCALER
0740     QStringList args3{"-hide_banner", "-filters"};
0741     qDebug() << "// FFMPEG ARGS: " << args3;
0742     hwEncoders.start(KdenliveSettings::ffmpegpath(), args3);
0743     bool nvScalingSupported = false;
0744     if (hwEncoders.waitForFinished()) {
0745         QByteArray output = hwEncoders.readAll();
0746         hwEncoders.close();
0747         if (output.contains(QByteArray("scale_npp"))) {
0748             qDebug() << "/// ++ SCALE_NPP YES SUPPORTED ::::::";
0749             nvScalingSupported = true;
0750         } else {
0751             qDebug() << "/// ++ SCALE_NPP NOT SUPPORTED";
0752         }
0753     }
0754     KdenliveSettings::setNvScalingEnabled(nvScalingSupported);
0755 }
0756 
0757 void Wizard::updateHwStatus()
0758 {
0759     auto *statusLabel = new KMessageWidget(this);
0760     bool hwEnabled = KdenliveSettings::vaapiEnabled() || KdenliveSettings::nvencEnabled();
0761     statusLabel->setMessageType(hwEnabled ? KMessageWidget::Positive : KMessageWidget::Information);
0762     statusLabel->setWordWrap(true);
0763     QString statusMessage;
0764     if (!hwEnabled) {
0765         statusMessage = i18n("No hardware encoders found.");
0766     } else {
0767         if (KdenliveSettings::nvencEnabled()) {
0768             statusMessage += i18n("NVIDIA hardware encoders found and enabled.");
0769         }
0770         if (KdenliveSettings::vaapiEnabled()) {
0771             statusMessage += i18n("VAAPI hardware encoders found and enabled.");
0772         }
0773     }
0774     statusLabel->setText(statusMessage);
0775     statusLabel->setCloseButtonVisible(false);
0776     // errorLabel->setTimeout();
0777     m_startLayout->addWidget(statusLabel);
0778     statusLabel->animatedShow();
0779     QTimer::singleShot(3000, statusLabel, &KMessageWidget::animatedHide);
0780 }