diff --git a/src/kdenliveui.rc b/src/kdenliveui.rc index 1e178ac9ed2c565be04c39000798e28c1e347aae..be08b1102184f8d601af8771f10eaac74bbc0441 100644 --- a/src/kdenliveui.rc +++ b/src/kdenliveui.rc @@ -4,6 +4,8 @@ + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0e464c2f2069cd3de88033c3b211bd4be5e3a41e..6a213865392a4957a174846d9aa0b6952e0c4980 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -64,6 +64,7 @@ #include "transitions/transitionsrepository.hpp" #include "utils/resourcewidget.h" #include "utils/thememanager.h" +#include "utils/otioconvertions.h" #include "profiles/profilerepository.hpp" #include "widgets/progressbutton.h" @@ -428,6 +429,7 @@ void MainWindow::init() new HideTitleBars(this); m_extraFactory = new KXMLGUIClient(this); buildDynamicActions(); + m_otioConvertions.getOtioConverters(); // Create Effect Basket (dropdown list of favorites) m_effectBasket = new EffectBasket(this); @@ -1291,6 +1293,15 @@ void MainWindow::setupActions() addAction(QStringLiteral("dvd_wizard"), i18n("DVD Wizard"), this, SLOT(slotDvdWizard()), QIcon::fromTheme(QStringLiteral("media-optical"))); addAction(QStringLiteral("transcode_clip"), i18n("Transcode Clips"), this, SLOT(slotTranscodeClip()), QIcon::fromTheme(QStringLiteral("edit-copy"))); + QAction *exportAction = new QAction(QIcon::fromTheme(QStringLiteral("document-export")), i18n("E&xport project"), this); + connect(exportAction, &QAction::triggered, &m_otioConvertions, &OtioConvertions::slotExportProject); + exportAction->setEnabled(false); + addAction(QStringLiteral("export_project"), exportAction); + QAction *importAction = new QAction(QIcon::fromTheme(QStringLiteral("document-import")), i18n("&Import project"), this); + connect(importAction, &QAction::triggered, &m_otioConvertions, &OtioConvertions::slotImportProject); + importAction->setEnabled(false); + addAction(QStringLiteral("import_project"), importAction); + addAction(QStringLiteral("archive_project"), i18n("Archive Project"), this, SLOT(slotArchiveProject()), QIcon::fromTheme(QStringLiteral("document-save-all"))); addAction(QStringLiteral("switch_monitor"), i18n("Switch monitor"), this, SLOT(slotSwitchMonitors()), QIcon(), Qt::Key_T); diff --git a/src/mainwindow.h b/src/mainwindow.h index 171a11e83d86b20b07578b4ceceb836cafd087dc..cf5071fbf1c2f813665698a530e2aa8527be2cd2 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -44,6 +44,7 @@ #include "kdenlive_debug.h" #include "kdenlivecore_export.h" #include "statusbarmessagelabel.h" +#include "utils/otioconvertions.h" class AssetPanel; class AudioGraphSpectrum; @@ -153,6 +154,7 @@ private: /** @brief Sets up all the actions and attaches them to the collection. */ void setupActions(); + OtioConvertions m_otioConvertions; KColorSchemeManager *m_colorschemes; QDockWidget *m_projectBinDock; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index bd48be1743334151f5d0413e0c6d0612e4a84446..bbe08c33639384e05275bf50e1212c6cfa42dffc 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -7,6 +7,7 @@ set(kdenlive_SRCS utils/flowlayout.cpp utils/freesound.cpp utils/openclipart.cpp + utils/otioconvertions.cpp utils/resourcewidget.cpp utils/thememanager.cpp utils/thumbnailcache.cpp diff --git a/src/utils/otioconvertions.cpp b/src/utils/otioconvertions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd1320cdb2934df20ae10e259c5f40fe272082a4 --- /dev/null +++ b/src/utils/otioconvertions.cpp @@ -0,0 +1,145 @@ +/* + * this file is part of Kdenlive, the Libre Video Editor by KDE + * Copyright 2019 Vincent Pinon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "otioconvertions.h" + +#include "mainwindow.h" +#include "core.h" +#include "doc/kdenlivedoc.h" +#include "project/projectmanager.h" + +#include + +OtioConvertions::OtioConvertions() +{ +} + +void OtioConvertions::getOtioConverters() +{ + connect(&m_otioProcess, static_cast(&QProcess::finished), this, &OtioConvertions::slotGotOtioConverters); + m_otioProcess.start(QStringLiteral("python3")); + m_otioProcess.write(QStringLiteral( +"from opentimelineio import *\n" +"for a in plugins.ActiveManifest().adapters:\n" +" if a.has_feature('read') and a.has_feature('write'):\n" +" print('*.'+' *.'.join(a.suffixes), end=' ')" + ).toUtf8()); + m_otioProcess.closeWriteChannel(); +} + +void OtioConvertions::slotGotOtioConverters(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_otioProcess.disconnect(); + if(exitStatus != QProcess::NormalExit || exitCode != 0) { + qInfo("OpenTimelineIO python module is not available"); + return; + } + m_adapters = m_otioProcess.readAll(); + if (!m_adapters.contains("kdenlive")) { + qInfo() << "Kdenlive not supported by OTIO: " << m_adapters; + return; + } + QAction *exportAction = pCore->window()->actionCollection()->action("export_project"); + QAction *importAction = pCore->window()->actionCollection()->action("import_project"); + exportAction->setEnabled(true); + importAction->setEnabled(true); +} + +void OtioConvertions::slotExportProject() +{ + auto exportFile = QFileDialog::getSaveFileName( + pCore->window(), i18n("Export Project"), + pCore->currentDoc()->projectDataFolder(), + i18n("OpenTimelineIO adapters (%1)", m_adapters)); + if (exportFile.isNull()) { + return; + } + QByteArray xml = pCore->projectManager()->projectSceneList("").toUtf8(); + if (xml.isNull()) { + KMessageBox::error(pCore->window(), i18n("Project file could not be saved for export.")); + return; + } + m_otioTempFile.setFileTemplate(QStringLiteral("XXXXXX.kdenlive")); + if (!m_otioTempFile.open() || !(m_otioTempFile.write(xml) > 0)) { + pCore->displayMessage(i18n("Unable to write to temporary kdenlive file for export: %1", + m_otioTempFile.fileName()), ErrorMessage); + return; + } + connect(&m_otioProcess, static_cast(&QProcess::finished), + this, &OtioConvertions::slotProjectExported); + m_otioProcess.start(QStringLiteral("otioconvert"), {"-i", m_otioTempFile.fileName(), "-o", exportFile}); +} + +void OtioConvertions::slotImportProject() +{ + // Select foreign project to import + auto importFile = QFileDialog::getOpenFileName( + pCore->window(), i18n("Project to import"), + pCore->currentDoc()->projectDataFolder(), + i18n("OpenTimelineIO adapters (%1)", m_adapters)); + if (importFile.isNull() || !QFile::exists(importFile)) { + return; + } + // Select converted project file + m_importedFile = QFileDialog::getSaveFileName( + pCore->window(), i18n("Imported Project"), + pCore->currentDoc()->projectDataFolder(), + i18n("Kdenlive project (*.kdenlive)")); + if (m_importedFile.isNull()) { + return; + } + // Start convertion process + connect(&m_otioProcess, static_cast(&QProcess::finished), + this, &OtioConvertions::slotProjectImported); + m_otioProcess.start(QStringLiteral("otioconvert"), {"-i", importFile, "-o", m_importedFile}); +} + +void OtioConvertions::slotProjectExported(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_otioProcess.disconnect(); + m_otioTempFile.remove(); + if(exitStatus != QProcess::NormalExit || exitCode != 0) { + pCore->displayMessage(i18n("Project convertion failed"), ErrorMessage); + qWarning() << exitCode << exitStatus << QString(m_otioProcess.readAllStandardError()); + return; + } + pCore->displayMessage(i18n("Project convertion complete"), InformationMessage); +} + +void OtioConvertions::slotProjectImported(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_otioProcess.disconnect(); + if(exitStatus != QProcess::NormalExit || exitCode != 0 || !QFile::exists(m_importedFile)) { + pCore->displayMessage(i18n("Project convertion failed"), ErrorMessage); + qWarning() << exitCode << exitStatus << QString(m_otioProcess.readAllStandardError()); + return; + } + pCore->displayMessage(i18n("Project convertion complete"), InformationMessage); + // Verify current project can be closed + if (pCore->currentDoc()->isModified() && + KMessageBox::warningContinueCancel(pCore->window(), i18n( + "The current project has not been saved\n" + "Do you want to load imported project abandoning latest changes?") + ) != KMessageBox::Continue) { + return; + } + pCore->projectManager()->openFile(QUrl::fromLocalFile(m_importedFile)); +} diff --git a/src/utils/otioconvertions.h b/src/utils/otioconvertions.h new file mode 100644 index 0000000000000000000000000000000000000000..63ed5569e540429f07e4721a712f57f405109ea7 --- /dev/null +++ b/src/utils/otioconvertions.h @@ -0,0 +1,52 @@ +/* + * this file is part of Kdenlive, the Libre Video Editor by KDE + * Copyright 2019 Vincent Pinon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OTIOCONVERTIONS_H +#define OTIOCONVERTIONS_H + +#include +#include +#include + +class OtioConvertions: public QObject +{ + Q_OBJECT +public: + OtioConvertions(); + void getOtioConverters(); + +private: + QProcess m_otioProcess; + QString m_adapters; + QTemporaryFile m_otioTempFile; + QString m_importedFile; + +public slots: + void slotExportProject(); + void slotImportProject(); + +private slots: + void slotGotOtioConverters(int exitCode, QProcess::ExitStatus exitStatus); + void slotProjectExported(int exitCode, QProcess::ExitStatus exitStatus); + void slotProjectImported(int exitCode, QProcess::ExitStatus exitStatus); +}; + +#endif // OTIOCONVERTIONS_H