From 494b04c3d454b6503dd31e0af6394fb3c2c93cb5 Mon Sep 17 00:00:00 2001 From: Bionus Date: Sat, 3 Feb 2024 12:48:32 +0100 Subject: [PATCH] feat: add wrapper for ImageMagick --- src/lib/src/external/image-magick.cpp | 110 ++++++++++++++++++++++++++ src/lib/src/external/image-magick.h | 33 ++++++++ 2 files changed, 143 insertions(+) create mode 100644 src/lib/src/external/image-magick.cpp create mode 100644 src/lib/src/external/image-magick.h diff --git a/src/lib/src/external/image-magick.cpp b/src/lib/src/external/image-magick.cpp new file mode 100644 index 000000000..c65618fa2 --- /dev/null +++ b/src/lib/src/external/image-magick.cpp @@ -0,0 +1,110 @@ +#include "image-magick.h" +#include +#include +#include +#include +#include "functions.h" +#include "logger.h" + + +QString ImageMagick::version(int msecs) +{ + QProcess process; + process.start("magick", { "-version" }); + + if (!process.waitForStarted(msecs)) { + return ""; + } + if (!process.waitForFinished(msecs)) { + process.kill(); + return ""; + } + if (process.exitCode() != 0) { + return ""; + } + + const QString output = QString::fromLocal8Bit(process.readAllStandardOutput()); + QString line = output.split("\n").first().trimmed(); + + if (line.startsWith("Version: ImageMagick ")) { + line = line.mid(21); + } + + const qsizetype index = line.indexOf("https://"); + if (index != -1) { + return line.left(index).trimmed(); + } + + return line.trimmed(); +} + + +QString ImageMagick::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 convert file that does not exist: `%1`").arg(file), Logger::Error); + return file; + } + if (QFile::exists(destination)) { + 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 = { file, 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); + } + + return file; + } + + // Copy file creation information + setFileCreationDate(destination, info.lastModified()); + + // On success, delete the original file if requested + if (deleteOriginal) { + QFile::remove(file); + } + + return destination; +} + + +bool ImageMagick::execute(const QStringList ¶ms, int msecs) +{ + QProcess process; + process.start("magick", params); + + // Ensure the process started successfully + if (!process.waitForStarted(msecs)) { + log(QStringLiteral("Could not start ImageMagick")); + return false; + } + + // Wait for FFmpeg to finish + bool finishedOk = process.waitForFinished(msecs); + bool didntCrash = process.exitStatus() == QProcess::NormalExit; + bool exitCodeOk = process.exitCode() == 0; + bool ok = finishedOk && didntCrash && exitCodeOk; + + // Print stdout and stderr to the log + const QString standardOutput = QString::fromLocal8Bit(process.readAllStandardOutput()).trimmed(); + if (!standardOutput.isEmpty()) { + log(QString("[ImageMagick] %1").arg(standardOutput), Logger::Debug); + } + const QString standardError = QString::fromLocal8Bit(process.readAllStandardError()).trimmed(); + if (!standardError.isEmpty()) { + log(QString("[ImageMagick] %1").arg(standardError), Logger::Error); + } + + return ok; +} diff --git a/src/lib/src/external/image-magick.h b/src/lib/src/external/image-magick.h new file mode 100644 index 000000000..e624a7fc1 --- /dev/null +++ b/src/lib/src/external/image-magick.h @@ -0,0 +1,33 @@ +#ifndef IMAGE_MAGICK_H +#define IMAGE_MAGICK_H + +#include + + +class ImageMagick +{ + public: + /** + * Get the version of ImageMagick. + * + * @param msecs The duration to wait in milliseconds for the version command to run. + * @return The version number found (ex: "7.0.10"). + */ + static QString version(int msecs = 30000); + + /** + * Convert a file to a different format. + * + * @param file The file to convert. + * @param extension The target extension (ex: "jpg"). + * @param deleteOriginal Whether to delete the original file on success. + * @param msecs The duration to wait in milliseconds for the command to run. + * @return The destination file path on success, the original file path on error. + */ + static QString convert(const QString &file, const QString &extension, bool deleteOriginal = true, int msecs = 30000); + + protected: + static bool execute(const QStringList ¶ms, int msecs = 30000); +}; + +#endif // IMAGE_MAGICK_H