diff --git a/app/cli/cliexport/cliexportmanager.cpp b/app/cli/cliexport/cliexportmanager.cpp
new file mode 100644
index 000000000..de0145729
--- /dev/null
+++ b/app/cli/cliexport/cliexportmanager.cpp
@@ -0,0 +1,30 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ 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 "cliexportmanager.h"
+
+OLIVE_NAMESPACE_ENTER
+
+CLIExportManager::CLIExportManager()
+{
+
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/cli/cliexport/cliexportmanager.h b/app/cli/cliexport/cliexportmanager.h
new file mode 100644
index 000000000..6d3fc346b
--- /dev/null
+++ b/app/cli/cliexport/cliexportmanager.h
@@ -0,0 +1,36 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ 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 CLIEXPORTMANAGER_H
+#define CLIEXPORTMANAGER_H
+
+#include "task/export/export.h"
+
+OLIVE_NAMESPACE_ENTER
+
+class CLIExportManager : public QObject
+{
+public:
+ CLIExportManager();
+};
+
+OLIVE_NAMESPACE_EXIT
+
+#endif // CLIEXPORTMANAGER_H
diff --git a/app/cli/cliprogress/cliprogressdialog.cpp b/app/cli/cliprogress/cliprogressdialog.cpp
index fa846c1ff..88c0cfd57 100644
--- a/app/cli/cliprogress/cliprogressdialog.cpp
+++ b/app/cli/cliprogress/cliprogressdialog.cpp
@@ -27,9 +27,10 @@ OLIVE_NAMESPACE_ENTER
CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) :
QObject(parent),
title_(title),
- progress_(0),
+ progress_(-1),
drawn_(false)
{
+ SetProgress(0);
}
void CLIProgressDialog::Update()
@@ -68,7 +69,7 @@ void CLIProgressDialog::Update()
std::cout << "[";
// Get UI bar progress
- int bar_prog = qRound(progress_ * 0.01 * progress_bar_columns);
+ int bar_prog = qRound(progress_ * progress_bar_columns);
// Draw filled in bar
for (int i=0;iGetTitle(), parent)
+ CLIProgressDialog(task->GetTitle(), parent),
+ task_(task)
{
- // FIXME: Still developing this, don't try to use
+ connect(task_, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress);
+}
+
+bool CLITaskDialog::Run()
+{
+ return task_->Start();
}
OLIVE_NAMESPACE_EXIT
diff --git a/app/cli/clitask/clitaskdialog.h b/app/cli/clitask/clitaskdialog.h
index 6408b238e..c574b6011 100644
--- a/app/cli/clitask/clitaskdialog.h
+++ b/app/cli/clitask/clitaskdialog.h
@@ -28,9 +28,15 @@ OLIVE_NAMESPACE_ENTER
class CLITaskDialog : public CLIProgressDialog
{
+ Q_OBJECT
public:
CLITaskDialog(Task *task, QObject* parent = nullptr);
+ bool Run();
+
+private:
+ Task* task_;
+
};
OLIVE_NAMESPACE_EXIT
diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp
index 43e0ca1a6..388278525 100644
--- a/app/codec/encoder.cpp
+++ b/app/codec/encoder.cpp
@@ -158,6 +158,63 @@ void EncodingParams::SetExportLength(const rational &export_length)
export_length_ = export_length;
}
+void EncodingParams::Save(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("encode"));
+
+ writer->writeTextElement(QStringLiteral("filename"), filename_);
+
+ writer->writeStartElement(QStringLiteral("video"));
+
+ writer->writeAttribute(QStringLiteral("enabled"), QString::number(video_enabled_));
+
+ if (video_enabled_) {
+ writer->writeTextElement(QStringLiteral("codec"), QString::number(video_codec_));
+ writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width()));
+ writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height()));
+ writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format()));
+ writer->writeTextElement(QStringLiteral("timebase"), video_params_.time_base().toString());
+ writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider()));
+ writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_));
+ writer->writeTextElement(QStringLiteral("maxbitrate"), QString::number(video_max_bit_rate_));
+ writer->writeTextElement(QStringLiteral("bufsize"), QString::number(video_buffer_size_));
+ writer->writeTextElement(QStringLiteral("threads"), QString::number(video_threads_));
+
+ if (!video_opts_.isEmpty()) {
+ writer->writeStartElement(QStringLiteral("opts"));
+
+ QHash::const_iterator i;
+ for (i=video_opts_.constBegin(); i!=video_opts_.constEnd(); i++) {
+ writer->writeStartElement(QStringLiteral("entry"));
+
+ writer->writeTextElement(QStringLiteral("key"), i.key());
+ writer->writeTextElement(QStringLiteral("value"), i.value());
+
+ writer->writeEndElement(); // entry
+ }
+
+ writer->writeEndElement(); // opts
+ }
+ }
+
+ writer->writeEndElement(); // video
+
+ writer->writeStartElement(QStringLiteral("audio"));
+
+ writer->writeAttribute(QStringLiteral("enabled"), QString::number(audio_enabled_));
+
+ if (audio_enabled_) {
+ writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_));
+ writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate()));
+ writer->writeTextElement(QStringLiteral("channellayout"), QString::number(audio_params_.channel_layout()));
+ writer->writeTextElement(QStringLiteral("format"), QString::number(audio_params_.format()));
+ }
+
+ writer->writeEndElement(); // audio
+
+ writer->writeEndElement(); // encode
+}
+
Encoder* Encoder::CreateFromID(const QString &id, const EncodingParams& params)
{
Q_UNUSED(id)
diff --git a/app/codec/encoder.h b/app/codec/encoder.h
index 8a60a75cc..260c735a3 100644
--- a/app/codec/encoder.h
+++ b/app/codec/encoder.h
@@ -23,6 +23,7 @@
#include
#include
+#include
#include "codec/exportcodec.h"
#include "codec/exportformat.h"
@@ -69,6 +70,8 @@ public:
const rational& GetExportLength() const;
void SetExportLength(const rational& GetExportLength);
+ virtual void Save(QXmlStreamWriter* writer) const;
+
private:
QString filename_;
diff --git a/app/core.cpp b/app/core.cpp
index 70fb71f6c..d737a0563 100644
--- a/app/core.cpp
+++ b/app/core.cpp
@@ -81,18 +81,17 @@ Core *Core::instance()
return &instance_;
}
-bool Core::Start()
+int Core::execute(QCoreApplication* a)
{
- // Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes
- // the fact that some of the config paths set by default rely on the app name having been set (in main())
- Config::Current().SetDefaults();
+ int exit_code = 1;
+
+ // Start core
+ OLIVE_NAMESPACE::Core::instance()->Start();
//
// Parse command line arguments
//
- QCoreApplication* app = QCoreApplication::instance();
-
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
@@ -111,7 +110,7 @@ bool Core::Start()
parser.addOption(headless_export_option);
// Parse options
- parser.process(*app);
+ parser.process(*a);
QStringList args = parser.positionalArguments();
@@ -120,6 +119,44 @@ bool Core::Start()
startup_project_ = args.first();
}
+ gui_active_ = !parser.isSet(headless_export_option);
+
+ if (gui_active_) {
+
+ // Start GUI
+ StartGUI(parser.isSet(fullscreen_option));
+
+ // If we have a startup
+ QMetaObject::invokeMethod(this, "OpenStartupProject", Qt::QueuedConnection);
+
+ // Run application loop and receive exit code
+ exit_code = a->exec();
+
+ } else {
+
+ if (parser.isSet(headless_export_option)) {
+ // Start a headless export
+ if (StartHeadlessExport()) {
+ exit_code = 0;
+ }
+ }
+
+ }
+
+
+
+ // Clear core memory
+ OLIVE_NAMESPACE::Core::instance()->Stop();
+
+ return exit_code;
+}
+
+void Core::Start()
+{
+ // Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes
+ // the fact that some of the config paths set by default rely on the app name having been set (in main())
+ Config::Current().SetDefaults();
+
// Declare custom types for Qt signal/slot system
DeclareTypesForQt();
@@ -141,53 +178,6 @@ bool Core::Start()
//
qInfo() << "Using Qt version:" << qVersion();
-
- gui_active_ = !parser.isSet(headless_export_option);
-
- if (gui_active_) {
-
- // Start GUI
- StartGUI(parser.isSet(fullscreen_option));
-
- // Load startup project
- if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) {
- QMessageBox::warning(main_window(),
- tr("Failed to open startup file"),
- tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_),
- QMessageBox::Ok);
-
- startup_project_.clear();
- }
-
- if (startup_project_.isEmpty()) {
- // If no load project is set, create a new one on open
- CreateNewProject();
- } else {
- OpenProjectInternal(startup_project_);
- }
-
- return true;
-
- } else {
-
- if (parser.isSet(headless_export_option)) {
-
- if (startup_project_.isEmpty()) {
- qCritical().noquote() << tr("You must specify a project file to export");
- } else {
- OpenProjectInternal(startup_project_);
-
- qDebug() << "Ready for exporting!";
-
- return true;
- }
-
- }
-
- // Error fallback
- return false;
-
- }
}
void Core::Stop()
@@ -539,6 +529,106 @@ void Core::ProjectWasModified(bool e)
}
}
+bool Core::StartHeadlessExport()
+{
+ if (startup_project_.isEmpty()) {
+ qCritical().noquote() << tr("You must specify a project file to export");
+ return false;
+ }
+
+ if (!QFileInfo::exists(startup_project_)) {
+ qCritical().noquote() << tr("Specified project does not exist");
+ return false;
+ }
+
+ // Start a load task and try running it
+ ProjectLoadTask plm(startup_project_);
+ CLITaskDialog task_dialog(&plm);
+
+ if (task_dialog.Run()) {
+ ProjectPtr p = plm.GetLoadedProjects().first();
+ QList items = p->get_items_of_type(Item::kSequence);
+
+ // Check if this project contains sequences
+ if (items.isEmpty()) {
+ qCritical().noquote() << tr("Project contains no sequences, nothing to export");
+ return false;
+ }
+
+ SequencePtr sequence = nullptr;
+
+ // Check if this project contains multiple sequences
+ if (items.size() > 1) {
+ qInfo().noquote() << tr("This project has multiple sequences. Which do you wish to export?");
+ for (int i=0;iname().toStdString();
+ }
+
+ QTextStream stream(stdin);
+ QString sequence_read;
+ int sequence_index = -1;
+ QString quit_code = QStringLiteral("q");
+ std::string prompt = tr("Enter number (or %1 to cancel): ").arg(quit_code).toStdString();
+ forever {
+ std::cout << prompt;
+
+ stream.readLineInto(&sequence_read);
+
+ if (!QString::compare(sequence_read, quit_code, Qt::CaseInsensitive)) {
+ return false;
+ }
+
+ bool ok;
+ sequence_index = sequence_read.toInt(&ok);
+
+ if (ok && sequence_index >= 0 && sequence_index < items.size()) {
+ break;
+ } else {
+ qCritical().noquote() << tr("Invalid sequence number");
+ }
+ }
+
+ sequence = std::static_pointer_cast(items.at(sequence_index));
+ } else {
+ sequence = std::static_pointer_cast(items.first());
+ }
+
+ ExportParams params;
+ ExportTask export_task(sequence->viewer_output(), p->color_manager(), params);
+ CLITaskDialog export_dialog(&export_task);
+ if (export_dialog.Run()) {
+ qInfo().noquote() << tr("Export succeeded");
+ return true;
+ } else {
+ qInfo().noquote() << tr("Export failed: %1").arg(export_task.GetError());
+ return false;
+ }
+ } else {
+ qCritical().noquote() << tr("Project failed to load: %1").arg(plm.GetError());
+ return false;
+ }
+}
+
+void Core::OpenStartupProject()
+{
+ // Load startup project
+ if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) {
+ QMessageBox::warning(main_window_,
+ tr("Failed to open startup file"),
+ tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_),
+ QMessageBox::Ok);
+
+ startup_project_.clear();
+ }
+
+ if (startup_project_.isEmpty()) {
+ // If no load project is set, create a new one on open
+ CreateNewProject();
+ } else {
+ OpenProjectInternal(startup_project_);
+ }
+}
+
void Core::DeclareTypesForQt()
{
qRegisterMetaType();
@@ -559,6 +649,7 @@ void Core::DeclareTypesForQt()
qRegisterMetaType();
qRegisterMetaType();
qRegisterMetaType();
+ qRegisterMetaType();
}
void Core::StartGUI(bool full_screen)
@@ -877,6 +968,11 @@ bool Core::SaveProjectAs(ProjectPtr p)
GetProjectFilter());
if (!fn.isEmpty()) {
+ QString extension(QStringLiteral(".ove"));
+ if (!fn.endsWith(extension, Qt::CaseInsensitive)) {
+ fn.append(extension);
+ }
+
p->set_filename(fn);
SaveProjectInternal(p);
@@ -927,7 +1023,7 @@ void Core::OpenProjectInternal(const QString &filename)
//connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
- CLITaskDialog task_dialog(plm);
+
}
}
diff --git a/app/core.h b/app/core.h
index 07aeccb08..9b3d25728 100644
--- a/app/core.h
+++ b/app/core.h
@@ -66,12 +66,14 @@ public:
*/
static Core* instance();
+ int execute(QCoreApplication *a);
+
/**
* @brief Start Olive Core
*
* Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode).
*/
- bool Start();
+ void Start();
/**
* @brief Stop Olive Core
@@ -405,11 +407,6 @@ private:
*/
void PushRecentlyOpenedProject(const QString &s);
- /**
- * @brief Internal project open
- */
- void OpenProjectInternal(const QString& filename);
-
/**
* @brief Declare custom types/classes for Qt's signal/slot system
*
@@ -507,6 +504,15 @@ private slots:
void ProjectWasModified(bool e);
+ bool StartHeadlessExport();
+
+ void OpenStartupProject();
+
+ /**
+ * @brief Internal project open
+ */
+ void OpenProjectInternal(const QString& filename);
+
};
OLIVE_NAMESPACE_EXIT
diff --git a/app/main.cpp b/app/main.cpp
index e87f1fd34..0026395f0 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -86,23 +86,5 @@ int main(int argc, char *argv[]) {
avfilter_register_all();
#endif
- int exit_code;
-
- // Start core
- if (OLIVE_NAMESPACE::Core::instance()->Start()) {
-
- // Run application loop and receive exit code
- exit_code = a.exec();
-
- } else {
-
- // Core failed to start, exit now
- exit_code = 1;
-
- }
-
- // Clear core memory
- OLIVE_NAMESPACE::Core::instance()->Stop();
-
- return exit_code;
+ return OLIVE_NAMESPACE::Core::instance()->execute(&a);
}
diff --git a/app/task/export/exportparams.cpp b/app/task/export/exportparams.cpp
index a603735ee..299c1fca3 100644
--- a/app/task/export/exportparams.cpp
+++ b/app/task/export/exportparams.cpp
@@ -100,4 +100,26 @@ QMatrix4x4 ExportParams::GenerateMatrix(ExportParams::VideoScalingMethod method,
return preview_matrix;
}
+void ExportParams::Save(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("export"));
+
+ writer->writeTextElement(QStringLiteral("encoder"), encoder_id_);
+
+ writer->writeTextElement(QStringLiteral("vscale"), QString::number(video_scaling_method_));
+
+ writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_));
+
+ writer->writeTextElement(QStringLiteral("customrangein"), custom_range_.in().toString());
+
+ writer->writeTextElement(QStringLiteral("customrangeout"), custom_range_.out().toString());
+
+ // FIXME: Change this when color chains are implemented
+ writer->writeTextElement(QStringLiteral("color"), color_transform_.output());
+
+ EncodingParams::Save(writer);
+
+ writer->writeEndElement(); // export
+}
+
OLIVE_NAMESPACE_EXIT
diff --git a/app/task/export/exportparams.h b/app/task/export/exportparams.h
index a74ea3c5d..ed6106d67 100644
--- a/app/task/export/exportparams.h
+++ b/app/task/export/exportparams.h
@@ -56,6 +56,8 @@ public:
int source_width, int source_height,
int dest_width, int dest_height);
+ virtual void Save(QXmlStreamWriter* writer) const override;
+
private:
QString encoder_id_;
diff --git a/app/task/project/load/load.cpp b/app/task/project/load/load.cpp
index 20d05ce7d..932a94c74 100644
--- a/app/task/project/load/load.cpp
+++ b/app/task/project/load/load.cpp
@@ -70,6 +70,8 @@ bool ProjectLoadTask::Run()
project_file.close();
+ emit ProgressChanged(1);
+
if (reader.hasError()) {
SetError(reader.errorString());
return false;