Skip to content

Commit

Permalink
feat: add support for image conversion (fix #3091)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bionus committed Feb 3, 2024
1 parent 494b04c commit 2c12890
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 42 deletions.
77 changes: 74 additions & 3 deletions src/gui/src/settings/options-window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "custom-buttons.h"
#include "external/exiftool.h"
#include "external/ffmpeg.h"
#include "external/image-magick.h"
#include "functions.h"
#include "filename/conditional-filename.h"
#include "helpers.h"
Expand All @@ -44,7 +45,7 @@
#include "viewer/viewer-window-buttons.h"


void disableItem(QComboBox *combo, const int index, const QString &toolTip) {
void disableItem(QComboBox *combo, const int index, const QString &toolTip = {}) {
auto *model = qobject_cast<QStandardItemModel*>(combo->model());
QStandardItem *item = model->item(index);
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
Expand Down Expand Up @@ -139,20 +140,59 @@ OptionsWindow::OptionsWindow(Profile *profile, ThemeLoader *themeLoader, QWidget
ui->keyAcceptDialogue->setKeySequence(getKeySequence(settings, "keyAcceptDialog", Qt::CTRL | Qt::Key_Y));
ui->keyDeclineDialogue->setKeySequence(getKeySequence(settings, "keyDeclineDialog", Qt::CTRL | Qt::Key_N));

// FFmpeg format conversion
// FFmpeg version
QFuture<QString> ffmpegVersion = QtConcurrent::run([=]() {
return FFmpeg::version();
});
auto *ffmpegVersionWatcher = new QFutureWatcher<QString>(this);
connect(ffmpegVersionWatcher, &QFutureWatcher<QString>::finished, [=]() {
const QString &version = ffmpegVersion.result();
ui->labelConversionFFmpegVersion->setText(version.isEmpty() ? tr("ffmpeg not found") : version);
ui->labelConversionFFmpegVersion->setText(version.isEmpty() ? tr("FFmpeg not found") : version);
if (version.isEmpty()) {
ui->labelConversionFFmpegVersion->setStyleSheet("color: red");
disableItem(ui->comboConversionImageBackend, 1);
}
});
ffmpegVersionWatcher->setFuture(ffmpegVersion);

// ImageMagick version
QFuture<QString> imageMagickVersion = QtConcurrent::run([=]() {
return ImageMagick::version();
});
auto *imageMagickVersionWatcher = new QFutureWatcher<QString>(this);
connect(imageMagickVersionWatcher, &QFutureWatcher<QString>::finished, [=]() {
const QString &version = imageMagickVersion.result();
ui->labelConversionImageMagickVersion->setText(version.isEmpty() ? tr("ImageMagick not found") : version);
if (version.isEmpty()) {
ui->labelConversionImageMagickVersion->setStyleSheet("color: red");
disableItem(ui->comboConversionImageBackend, 0);
}
});
imageMagickVersionWatcher->setFuture(imageMagickVersion);

// Video conversion
ui->checkConversionFFmpegRemuxWebmToMp4->setChecked(settings->value("Save/FFmpegRemuxWebmToMp4", false).toBool());

// Image conversion
ui->comboConversionImageBackend->setCurrentText(settings->value("Save/ImageConversionBackend", "ImageMagick").toString());
settings->beginGroup("Save/ImageConversion");
for (const QString &from : settings->childGroups()) {
settings->beginGroup(from);
const QString to = settings->value("to").toString();
settings->endGroup();

auto *leFrom = new QLineEdit(from, this);
auto *leTo = new QLineEdit(to, this);
m_imageConversion.append(QPair<QLineEdit*, QLineEdit*> { leFrom, leTo });

auto *layout = new QHBoxLayout(this);
layout->addWidget(leFrom);
layout->addWidget(leTo);
ui->layoutConversionImageList->addLayout(layout);
}
settings->endGroup();

// Ugoira conversion
ui->checkConversionUgoiraEnabled->setChecked(settings->value("Save/ConvertUgoira", false).toBool());
ui->comboConversionUgoiraTargetExtension->setCurrentText(settings->value("Save/ConvertUgoiraFormat", "gif").toString().toUpper());
ui->checkConversionUgoiraDelete->setChecked(settings->value("Save/ConvertUgoiraDeleteOriginal", false).toBool());
Expand Down Expand Up @@ -601,6 +641,18 @@ void OptionsWindow::on_buttonMetadataExiftoolAdd_clicked()
m_metadataExiftool.append(QPair<QLineEdit*, QLineEdit*> { leKey, leValue });
}

void OptionsWindow::on_buttonConversionImageListAdd_clicked()
{
auto *leFrom = new QLineEdit(this);
auto *leTo = new QLineEdit(this);
m_imageConversion.append(QPair<QLineEdit*, QLineEdit*> { leFrom, leTo });

auto *layout = new QHBoxLayout(this);
layout->addWidget(leFrom);
layout->addWidget(leTo);
ui->layoutConversionImageList->addLayout(layout);
}


void OptionsWindow::reloadSourceRegistries()
{
Expand Down Expand Up @@ -1288,7 +1340,26 @@ void OptionsWindow::save()
tokenSettings->save();
}

// Video conversion
settings->setValue("FFmpegRemuxWebmToMp4", ui->checkConversionFFmpegRemuxWebmToMp4->isChecked());

// Image conversion
settings->setValue("ImageConversionBackend", ui->comboConversionImageBackend->currentText());
settings->beginGroup("ImageConversion");
settings->remove("");
for (int i = 0, j = 0; i < m_imageConversion.count(); ++i) {
const QString &from = m_imageConversion[i].first->text();
const QString &to = m_imageConversion[i].second->text();
if (!from.isEmpty() && !to.isEmpty()) {
settings->beginGroup(from.toUpper());
settings->setValue("to", to.toUpper());
settings->endGroup();
++j;
}
}
settings->endGroup();

// Ugoira conversion
settings->setValue("ConvertUgoira", ui->checkConversionUgoiraEnabled->isChecked());
settings->setValue("ConvertUgoiraFormat", ui->comboConversionUgoiraTargetExtension->currentText().toLower());
settings->setValue("ConvertUgoiraDeleteOriginal", ui->checkConversionUgoiraDelete->isChecked());
Expand Down
2 changes: 2 additions & 0 deletions src/gui/src/settings/options-window.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class OptionsWindow : public QDialog
void addFilename(const QString &, const QString &, const QString &);
void on_buttonMetadataPropsysAdd_clicked();
void on_buttonMetadataExiftoolAdd_clicked();
void on_buttonConversionImageListAdd_clicked();

// Custom image window buttons
void initButtonSettingPairs();
Expand Down Expand Up @@ -137,6 +138,7 @@ class OptionsWindow : public QDialog
QList<TokenSettingsWidget*> m_tokenSettings;
QList<QPair<QLineEdit*, QLineEdit*>> m_metadataPropsys, m_metadataExiftool;
QList<QPair<QCheckBox*, QSpinBox*>> m_buttonSettingPairs;
QList<QPair<QLineEdit*, QLineEdit*>> m_imageConversion;
};

#endif // OPTIONS_WINDOW_H
88 changes: 71 additions & 17 deletions src/gui/src/settings/options-window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@
<x>0</x>
<y>0</y>
<width>476</width>
<height>493</height>
<height>475</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaWidgetLayout">
Expand Down Expand Up @@ -1437,14 +1437,35 @@
<widget class="QWidget" name="pageFormatConversion">
<layout class="QFormLayout" name="formLayout_28">
<item row="0" column="0">
<widget class="QLabel" name="label_22">
<widget class="QLabel" name="labelConversionFFmpegVersionLabel">
<property name="text">
<string>FFmpeg version</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupConversionFFmpeg">
<item row="0" column="1">
<widget class="QLabel" name="labelConversionFFmpegVersion">
<property name="text">
<string>Loading...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelConversionImageMagickVersionLabel">
<property name="text">
<string>ImageMagick version</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelConversionImageMagickVersion">
<property name="text">
<string>Loading...</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupConversionVideo">
<property name="title">
<string>Video conversion</string>
</property>
Expand All @@ -1459,15 +1480,48 @@
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelConversionFFmpegVersion">
<property name="text">
<string>Loading...</string>
<item row="3" column="0" colspan="2">
<widget class="QGroupBox" name="groupConversionImage">
<property name="title">
<string>Image conversion</string>
</property>
<layout class="QFormLayout" name="formLayout_30">
<item row="0" column="0">
<widget class="QLabel" name="labelConversionImageBackend">
<property name="text">
<string>Back-end</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboConversionImageBackend">
<item>
<property name="text">
<string notr="true">ImageMagick</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">FFmpeg</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QVBoxLayout" name="layoutConversionImageList"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="buttonConversionImageListAdd">
<property name="text">
<string>Add new image conversion</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="groupConversionUgoira">
<property name="title">
<string>Ugoira (ZIP) conversion</string>
</property>
Expand Down Expand Up @@ -1518,7 +1572,7 @@
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkConversionUgoiraDelete">
<property name="text">
<string>Delete original ugoira ZIP files</string>
<string>Delete original file on success</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -1977,8 +2031,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>261</width>
<height>622</height>
<width>323</width>
<height>682</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
Expand Down Expand Up @@ -2687,8 +2741,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>329</width>
<height>660</height>
<width>388</width>
<height>710</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
Expand Down Expand Up @@ -4137,8 +4191,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>194</width>
<height>765</height>
<width>235</width>
<height>840</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
Expand Down
64 changes: 44 additions & 20 deletions src/lib/src/external/ffmpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,42 +41,51 @@ QString FFmpeg::version(int msecs)
}


QString FFmpeg::remux(const QString &file, const QString &extension, bool deleteOriginal, int msecs)
QString FFmpeg::convert(const QString &file, const QString &extension, bool deleteOriginal, int msecs)
{
// Since the method takes an extension, build an absolute path to the input file with that extension
const QFileInfo info(file);
QString destination = info.path() + QDir::separator() + info.completeBaseName() + "." + extension;

// Ensure the operation is safe to do
if (!QFile::exists(file)) {
log(QStringLiteral("Cannot remux file that does not exist: `%1`").arg(file), Logger::Error);
log(QStringLiteral("Cannot convert file that does not exist: `%1`").arg(file), Logger::Error);
return file;
}
if (QFile::exists(destination)) {
log(QStringLiteral("Remuxing the file `%1` would overwrite another file: `%2`").arg(file, destination), Logger::Error);
log(QStringLiteral("Converting the file `%1` would overwrite another file: `%2`").arg(file, destination), Logger::Error);
return file;
}

// Execute the conversion command
const QStringList params = { "-n", "-loglevel", "error", "-i", file, "-c", "copy", destination };
if (!execute(params, msecs)) {
// Clean-up failed conversions
if (QFile::exists(destination)) {
log(QStringLiteral("Cleaning up failed conversion target file: `%1`").arg(destination), Logger::Warning);
QFile::remove(destination);
}

const QStringList params = { "-n", "-loglevel", "error", "-i", file, destination };
if (!executeConvert(file, destination, deleteOriginal, params, msecs)) {
return file;
}
return destination;
}

// Copy file creation information
setFileCreationDate(destination, info.lastModified());
QString FFmpeg::remux(const QString &file, const QString &extension, bool deleteOriginal, int msecs)
{
// Since the method takes an extension, build an absolute path to the input file with that extension
const QFileInfo info(file);
QString destination = info.path() + QDir::separator() + info.completeBaseName() + "." + extension;

// On success, delete the original file if requested
if (deleteOriginal) {
QFile::remove(file);
// Ensure the operation is safe to do
if (!QFile::exists(file)) {
log(QStringLiteral("Cannot remux file that does not exist: `%1`").arg(file), Logger::Error);
return file;
}
if (QFile::exists(destination)) {
log(QStringLiteral("Remuxing the file `%1` would overwrite another file: `%2`").arg(file, destination), Logger::Error);
return file;
}

// Execute the conversion command
const QStringList params = { "-n", "-loglevel", "error", "-i", file, "-c", "copy", destination };
if (!executeConvert(file, destination, deleteOriginal, params, msecs)) {
return file;
}
return destination;
}

Expand Down Expand Up @@ -145,22 +154,37 @@ QString FFmpeg::convertUgoira(const QString &file, const QList<QPair<QString, in
params.append(destination);

// Execute the conversion command
if (!execute(params, msecs)) {
if (!executeConvert(file, destination, deleteOriginal, params, msecs)) {
return file;
}
return destination;
}


bool FFmpeg::executeConvert(const QString &file, const QString &destination, bool deleteOriginal, const QStringList &params, int msecs)
{
// Execute the command
if (!execute(params, msecs)) {
// Clean-up failed conversions
if (QFile::exists(destination)) {
log(QStringLiteral("Cleaning up failed conversion target file: `%1`").arg(destination), Logger::Warning);
QFile::remove(destination);
}

return false;
}

// Copy file creation information
setFileCreationDate(destination, info.lastModified());
setFileCreationDate(destination, QFileInfo(file).lastModified());

// On success, delete the original file if requested
if (deleteOriginal) {
QFile::remove(file);
}

return destination;
return true;
}


bool FFmpeg::execute(const QStringList &params, int msecs)
{
QProcess process;
Expand Down
Loading

0 comments on commit 2c12890

Please # to comment.