File indexing completed on 2024-04-14 04:46:58
0001 /* 0002 SPDX-FileCopyrightText: 2019 Vincent Pinon <vpinon@kde.org> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 #include "otioconvertions.h" 0007 0008 #include "core.h" 0009 #include "doc/kdenlivedoc.h" 0010 #include "mainwindow.h" 0011 #include "project/projectmanager.h" 0012 0013 #include <KLocalizedString> 0014 #include <KMessageBox> 0015 #include <QFileDialog> 0016 #include <QPlainTextEdit> 0017 #include <QStandardPaths> 0018 #include <QVBoxLayout> 0019 0020 OtioConvertions::OtioConvertions() 0021 : AbstractPythonInterface() 0022 { 0023 addDependency(QStringLiteral("opentimelineio"), i18n("OpenTimelineIO conversion")); 0024 addScript(QStringLiteral("otiointerface.py")); 0025 connect(this, &OtioConvertions::dependenciesAvailable, this, [&]() { 0026 if (QStandardPaths::findExecutable(QStringLiteral("otioconvert")).isEmpty()) { 0027 Q_EMIT setupError(i18n("Could not find \"otioconvert\" script although it is installed through pip3.\n" 0028 "Please check the otio scripts are installed in a directory " 0029 "listed in PATH environment variable")); 0030 return; 0031 } 0032 m_importAdapters = runScript(QStringLiteral("otiointerface.py"), {"--import-suffixes"}); 0033 qInfo() << "OTIO import adapters:" << m_importAdapters; 0034 if (!m_importAdapters.isEmpty()) { 0035 // no error occured so we can check export adapters as well 0036 m_exportAdapters = runScript(QStringLiteral("otiointerface.py"), {"--export-suffixes"}); 0037 qInfo() << "OTIO export adapters:" << m_exportAdapters; 0038 } 0039 if (m_importAdapters.isEmpty() || m_exportAdapters.isEmpty()) { 0040 // something is wrong. Maybe it is related to an old version? 0041 proposeMaybeUpdate("opentimelineio", "0.14.0"); 0042 // versions < 0.14.0 do not work on windows properly 0043 // see https://github.com/PixarAnimationStudios/OpenTimelineIO/issues/813 0044 return; 0045 } 0046 if (!(m_exportAdapters.contains("kdenlive") && m_importAdapters.contains("kdenlive"))) { 0047 Q_EMIT setupError(i18n("Your OpenTimelineIO module does not include Kdenlive adapter.\n" 0048 "Please install version >= 0.12\n")); 0049 } 0050 }); 0051 } 0052 0053 bool OtioConvertions::wellConfigured() 0054 { 0055 checkDependencies(false, false); 0056 return checkSetup() && missingDependencies().isEmpty() && !m_importAdapters.isEmpty() && m_importAdapters.contains("kdenlive") && 0057 !m_exportAdapters.isEmpty() && m_exportAdapters.contains("kdenlive"); 0058 } 0059 0060 bool OtioConvertions::configureSetup() 0061 { 0062 QDialog *d = new QDialog(pCore->window()); 0063 QVBoxLayout *l = new QVBoxLayout(); 0064 QLabel *label = new QLabel(i18n("Configure your OpenTimelineIO setup")); 0065 QHBoxLayout *h = new QHBoxLayout(); 0066 PythonDependencyMessage *msg = new PythonDependencyMessage(d, this); 0067 msg->setCloseButtonVisible(false); 0068 QToolButton *refresh = new QToolButton(d); 0069 refresh->setText(i18n("Check again")); 0070 refresh->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 0071 connect(refresh, &QToolButton::clicked, this, [&]() { 0072 if (wellConfigured()) { 0073 checkVersions(); 0074 } 0075 }); 0076 h->addWidget(msg); 0077 h->addWidget(refresh); 0078 QPlainTextEdit *textOutput = new QPlainTextEdit(d); 0079 textOutput->setReadOnly(true); 0080 textOutput->setFrameShape(QFrame::NoFrame); 0081 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0082 connect(buttonBox, &QDialogButtonBox::rejected, d, &QDialog::reject); 0083 l->addWidget(label); 0084 l->addLayout(h); 0085 l->addWidget(textOutput); 0086 l->addWidget(buttonBox); 0087 d->setLayout(l); 0088 0089 connect(this, &OtioConvertions::scriptStarted, [textOutput]() { QMetaObject::invokeMethod(textOutput, "clear"); }); 0090 connect(this, &OtioConvertions::installFeedback, 0091 [textOutput](const QString jobData) { QMetaObject::invokeMethod(textOutput, "appendPlainText", Q_ARG(QString, jobData)); }); 0092 connect(this, &OtioConvertions::scriptFinished, [msg]() { QMetaObject::invokeMethod(msg, "checkAfterInstall", Qt::QueuedConnection); }); 0093 0094 if (!wellConfigured()) { 0095 d->show(); 0096 return true; 0097 } 0098 d->close(); 0099 return false; 0100 } 0101 0102 bool OtioConvertions::runOtioconvert(const QString &inputFile, const QString &outputFile) 0103 { 0104 QProcess convert; 0105 QString otioBinary = QStandardPaths::findExecutable(QStringLiteral("otioconvert")); 0106 if (otioBinary.isEmpty()) { 0107 KMessageBox::error(pCore->window(), i18n("OpenTimelineIO Application otioconvert not found")); 0108 return false; 0109 } 0110 convert.start(otioBinary, {"-i", inputFile, "-o", outputFile}); 0111 convert.waitForFinished(); 0112 if (convert.exitStatus() != QProcess::NormalExit || convert.exitCode() != 0) { 0113 KMessageBox::detailedError(pCore->window(), i18n("OpenTimelineIO Project conversion failed"), QString(convert.readAllStandardError())); 0114 return false; 0115 } 0116 pCore->displayMessage(i18n("Project conversion complete"), InformationMessage); 0117 return true; 0118 } 0119 0120 void OtioConvertions::slotExportProject() 0121 { 0122 if (configureSetup()) { 0123 return; 0124 } 0125 QString exportFile = QFileDialog::getSaveFileName(pCore->window(), i18n("Export Project"), pCore->currentDoc()->projectDataFolder(), 0126 i18n("OpenTimelineIO adapters (%1)(%1)", m_exportAdapters)); 0127 if (exportFile.isNull()) { 0128 return; 0129 } 0130 QByteArray xml = pCore->projectManager()->projectSceneList("").toUtf8(); 0131 if (xml.isNull()) { 0132 KMessageBox::error(pCore->window(), i18n("Project file could not be saved for export.")); 0133 return; 0134 } 0135 QTemporaryFile tmp; 0136 tmp.setFileTemplate(QStringLiteral("XXXXXX.kdenlive")); 0137 if (!tmp.open() || !(tmp.write(xml) > 0)) { 0138 KMessageBox::error(pCore->window(), i18n("Unable to write to temporary kdenlive file for export: %1", tmp.fileName())); 0139 return; 0140 } else { 0141 tmp.close(); 0142 } 0143 runOtioconvert(tmp.fileName(), exportFile); 0144 tmp.remove(); 0145 } 0146 0147 void OtioConvertions::slotImportProject() 0148 { 0149 if (configureSetup()) { 0150 return; 0151 } 0152 // Select foreign project to import 0153 QString importFile = QFileDialog::getOpenFileName(pCore->window(), i18n("Project to import"), pCore->currentDoc()->projectDataFolder(), 0154 i18n("OpenTimelineIO adapters (%1)(%1)", m_importAdapters)); 0155 if (importFile.isNull() || !QFile::exists(importFile)) { 0156 return; 0157 } 0158 // Select converted project file 0159 QString importedFile = QFileDialog::getSaveFileName(pCore->window(), i18n("Imported Project"), pCore->currentDoc()->projectDataFolder(), 0160 i18n("Kdenlive project (*.kdenlive)")); 0161 if (importedFile.isNull()) { 0162 return; 0163 } 0164 if (!runOtioconvert(importFile, importedFile)) { 0165 return; 0166 } 0167 // Verify current project can be closed 0168 if (pCore->currentDoc()->isModified() && 0169 KMessageBox::warningContinueCancel(pCore->window(), i18n("The current project has not been saved\n" 0170 "Do you want to load imported project abandoning latest changes?")) != KMessageBox::Continue) { 0171 return; 0172 } 0173 pCore->projectManager()->openFile(QUrl::fromLocalFile(importedFile)); 0174 }