diff --git a/Sources/App/CapServer.swift b/Sources/App/CapServer.swift index a88f4a7..64382ed 100644 --- a/Sources/App/CapServer.swift +++ b/Sources/App/CapServer.swift @@ -4,6 +4,8 @@ import Clairvoyant final class CapServer { + private let imageSize = 360 + // MARK: Paths private let imageFolder: URL @@ -115,6 +117,7 @@ final class CapServer { updateGridCapCount() try ensureExistenceOfChangedImagesFile() organizeImages() + shrinkImages() isOperational = true } @@ -611,4 +614,87 @@ final class CapServer { private let imageCountMetric: Metric private let classifierMetric: Metric + + // MARK: Maintenance + + private func getMagickVersion() -> SemanticVersion? { + do { + let command = "convert -version" + let (code, output) = try safeShell(command) + guard code == 0, + let line = output.components(separatedBy: "\n").first, + line.hasPrefix("Version: ImageMagick ") else { + log("Missing dependency ImageMagick: " + output) + return nil + } + guard let versionString = line + .replacingOccurrences(of: "Version: ImageMagick ", with: "") + .components(separatedBy: "-").first else { + log("Invalid ImageMagick version: " + output) + return nil + } + guard let version = SemanticVersion(rawValue: versionString) else { + log("Invalid ImageMagick version: " + output) + return nil + } + return version + } catch { + log("Failed to check dependency ImageMagick: \(error)") + return nil + } + } + + func shrinkImages() { + guard let version = getMagickVersion() else { + log("Failed to shrink images, missing dependency") + return + } + log("Shrinking images using ImageMagick \(version.rawValue)") + + let imageFolders: [URL] + do { + imageFolders = try fm.contentsOfDirectory(at: imageFolder, includingPropertiesForKeys: nil) + } catch { + log("Failed to get all image folders") + return + } + for folder in imageFolders { + guard let images = try? self.images(in: folder) else { + continue + } + for imageUrl in images { + shrink(imageAt: imageUrl) + } + } + } + + private func shrink(imageAt url: URL) { + do { + let command = "convert \(url.path) -resize '\(imageSize)x\(imageSize)>' \(url.path)" + let (code, output) = try safeShell(command) + if code != 0 { + print("Failed to shrink image: " + output) + } + } catch { + print("Failed to shrink image: \(error)") + } + } + + private func safeShell(_ command: String) throws -> (code: Int32, output: String) { + let task = Process() + let pipe = Pipe() + + task.standardOutput = pipe + task.standardError = pipe + task.arguments = ["-cl", command] + task.executableURL = URL(fileURLWithPath: "/bin/bash") + task.standardInput = nil + + try task.run() + task.waitUntilExit() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: data, encoding: .utf8)! + return (task.terminationStatus, output) + } }