Check dependencies and outputs

This commit is contained in:
Christoph Hagen 2022-12-05 11:43:30 +01:00
parent a8b328efce
commit f52c3bc8b9
3 changed files with 113 additions and 12 deletions

View File

@ -0,0 +1,77 @@
import Foundation
func checkDependencies() -> Bool {
print("--- DEPENDENCIES -----------------------------------")
print(" ")
let result = checkImageOptimAvailability() && checkMagickAvailability() && checkCwebpAvailability() && checkAvifAvailability()
print(" Complete: \(result ? "Yes" : "No")")
print(" ")
return result
}
private func checkImageOptimAvailability() -> Bool {
do {
let output = try safeShell("imageoptim --version")
let version = output.components(separatedBy: ".").compactMap { Int($0.trimmed) }
if version.count > 1 {
print(" ImageOptim: \(version.map { "\($0)" }.joined(separator: "."))")
} else {
print(" ImageOptim: Not found")
return false
}
} catch {
print(" ImageOptim: Failed to get version (\(error))")
return false
}
return true
}
private func checkMagickAvailability() -> Bool {
do {
let output = try safeShell("magick --version")
guard let version = output.components(separatedBy: "ImageMagick ").dropFirst().first?
.components(separatedBy: " ").first else {
print(" Magick: Not found")
return false
}
print(" Magick: \(version)")
} catch {
print(" Magick: Failed to get version (\(error))")
return false
}
return true
}
private func checkCwebpAvailability() -> Bool {
do {
let output = try safeShell("cwebp -version")
let version = output.components(separatedBy: ".").compactMap { Int($0.trimmed) }
if version.count > 1 {
print(" cwebp: \(version.map { "\($0)" }.joined(separator: "."))")
} else {
print(" cwebp: Not found")
return false
}
} catch {
print(" cwebp: Failed to get version (\(error))")
return false
}
return true
}
private func checkAvifAvailability() -> Bool {
do {
let output = try safeShell("npx avif --version")
let version = output.components(separatedBy: ".").compactMap { Int($0.trimmed) }
if version.count > 1 {
print(" avif: \(version.map { "\($0)" }.joined(separator: "."))")
} else {
print(" avif: Not found")
return false
}
} catch {
print(" avif: Failed to get version (\(error))")
return false
}
return true
}

View File

@ -46,7 +46,7 @@ final class ImageGenerator {
private let numberOfTotalImages: Int private let numberOfTotalImages: Int
private lazy var numberImagesToCreate = jobs.reduce(0) { $0 + $1.images.count } + multiJobs.count * 2 private lazy var numberOfImagesToCreate = jobs.reduce(0) { $0 + $1.images.count } + multiJobs.count * 2
private var numberOfImagesToOptimize = 0 private var numberOfImagesToOptimize = 0
@ -100,7 +100,7 @@ final class ImageGenerator {
for (baseImage, source) in multiJobs { for (baseImage, source) in multiJobs {
createMultiImages(from: baseImage, path: source) createMultiImages(from: baseImage, path: source)
} }
print(" Generated images: \(numberOfGeneratedImages)/\(numberImagesToCreate)") print(" Generated images: \(numberOfGeneratedImages)/\(numberOfImagesToCreate)")
optimizeImages() optimizeImages()
print(" Optimized images: \(numberOfOptimizedImages)/\(numberOfImagesToOptimize)") print(" Optimized images: \(numberOfOptimizedImages)/\(numberOfImagesToOptimize)")
@ -219,14 +219,18 @@ final class ImageGenerator {
generatedImages.insert(webpPath) generatedImages.insert(webpPath)
didGenerateImage() didGenerateImage()
compress(at: source) compress(at: sourcePath)
} }
private func createAVIF(at destination: String, from source: String, quality: Int = 55, effort: Int = 5) { private func createAVIF(at destination: String, from source: String, quality: Int = 55, effort: Int = 5) {
let folder = destination.dropAfterLast("/") let folder = destination.dropAfterLast("/")
let command = "npx avif --input=\(source) --quality=\(quality) --effort=\(effort) --output=\(folder) --overwrite" let command = "npx avif --input=\(source) --quality=\(quality) --effort=\(effort) --output=\(folder) --overwrite"
do { do {
_ = try safeShell(command) let output = try safeShell(command)
if output == "" {
return
}
markImageAsFailed(destination, error: "Failed to create AVIF image: \(output)")
} catch { } catch {
markImageAsFailed(destination, error: "Failed to create AVIF image") markImageAsFailed(destination, error: "Failed to create AVIF image")
} }
@ -235,18 +239,26 @@ final class ImageGenerator {
private func createWEBP(at destination: String, from source: String, quality: Int = 75) { private func createWEBP(at destination: String, from source: String, quality: Int = 75) {
let command = "cwebp \(source) -q \(quality) -o \(destination)" let command = "cwebp \(source) -q \(quality) -o \(destination)"
do { do {
_ = try safeShell(command) let output = try safeShell(command)
if !output.contains("Error") {
return
}
markImageAsFailed(destination, error: "Failed to create WEBP image: \(output)")
} catch { } catch {
markImageAsFailed(destination, error: "Failed to create WEBP image") markImageAsFailed(destination, error: "Failed to create WEBP image: \(error)")
} }
} }
private func compress(at destination: String, quality: Int = 70) { private func compress(at destination: String, quality: Int = 70) {
let command = "magick convert \(destination) -quality \(quality)% \(destination)" let command = "magick convert \(destination) -quality \(quality)% \(destination)"
do { do {
_ = try safeShell(command) let output = try safeShell(command)
if output == "" {
return
}
markImageAsFailed(destination, error: "Failed to compress image: \(output)")
} catch { } catch {
markImageAsFailed(destination, error: "Failed to compress image") markImageAsFailed(destination, error: "Failed to compress image: \(error)")
} }
} }
@ -267,11 +279,18 @@ final class ImageGenerator {
private func optimizeImageBatch(_ batch: ArraySlice<String>) -> Bool { private func optimizeImageBatch(_ batch: ArraySlice<String>) -> Bool {
let command = "imageoptim " + batch.joined(separator: " ") let command = "imageoptim " + batch.joined(separator: " ")
do { do {
_ = try safeShell(command) let output = try safeShell(command)
if output.contains("Finished") {
return true
}
for image in batch {
markImageAsFailed(image, error: "Failed to optimize image: \(output)")
}
return true return true
} catch { } catch {
for image in batch { for image in batch {
markImageAsFailed(image, error: "Failed to optimize image") markImageAsFailed(image, error: "Failed to optimize image: \(error)")
} }
return false return false
} }
@ -281,7 +300,7 @@ final class ImageGenerator {
private func didGenerateImage(count: Int = 1) { private func didGenerateImage(count: Int = 1) {
numberOfGeneratedImages += count numberOfGeneratedImages += count
print(" Generated images: \(numberOfGeneratedImages)/\(numberImagesToCreate) \r", terminator: "") print(" Generated images: \(numberOfGeneratedImages)/\(numberOfImagesToCreate) \r", terminator: "")
fflush(stdout) fflush(stdout)
} }

View File

@ -14,7 +14,6 @@ struct CHGenerator: ParsableCommand {
} }
private func loadConfiguration(at configPath: String) -> Configuration? { private func loadConfiguration(at configPath: String) -> Configuration? {
print(" ")
print("--- CONFIGURATION ----------------------------------") print("--- CONFIGURATION ----------------------------------")
print(" ") print(" ")
print(" Configuration file: \(configPath)") print(" Configuration file: \(configPath)")
@ -128,6 +127,12 @@ private func finish(start: Date) {
private func generate(configPath: String) throws { private func generate(configPath: String) throws {
let start = Date() let start = Date()
print(" ")
guard checkDependencies() else {
return
}
// 1. Load configuration // 1. Load configuration
guard let configuration = loadConfiguration(at: configPath) else { guard let configuration = loadConfiguration(at: configPath) else {
return return