Simplify images, tag overview

This commit is contained in:
Christoph Hagen
2025-01-04 08:44:26 +01:00
parent 4d4275e072
commit 22e7d9a05a
49 changed files with 603 additions and 509 deletions

View File

@ -15,20 +15,33 @@ final class ImageGenerator {
self.storage = storage
self.settings = settings
self.generatedImages = storage.loadListOfGeneratedImages() ?? [:]
print("ImageGenerator: Loaded list of \(totalImageCount) already generated images")
}
private var outputFolder: String {
settings.paths.imagesOutputFolderPath
}
private var totalImageCount: Int {
generatedImages.values.reduce(0) { $0 + $1.count }
}
@discardableResult
func save() -> Bool {
guard storage.save(listOfGeneratedImages: generatedImages) else {
print("Failed to save list of generated images")
print("ImageGenerator: Failed to save list of generated images")
return false
}
print("ImageGenerator: Saved list of \(totalImageCount) images")
return true
}
private var avifCommands: Set<String> = []
func printAvifCommands() {
avifCommands.sorted().forEach { print($0) }
}
/**
Remove all versions of an image, so that they will be recreated on the next run.
@ -44,26 +57,27 @@ final class ImageGenerator {
print("Image generator: \(generatedImages.count)/\(images.count) images (\(versionCount) versions)")
}
private func needsToGenerate(version: String, for image: String) -> Bool {
if exists(version) {
private func hasPreviouslyGenerated(_ version: ImageVersion) -> Bool {
guard let versions = generatedImages[version.image.id] else {
return false
}
guard let versions = generatedImages[image] else {
return true
}
guard versions.contains(version) else {
return true
}
return !exists(version)
return versions.contains(version.versionId)
}
private func hasNowGenerated(version: String, for image: String) {
guard var versions = generatedImages[image] else {
generatedImages[image] = [version]
return
private func needsToGenerate(_ version: ImageVersion) -> Bool {
if hasPreviouslyGenerated(version) {
return false
}
versions.insert(version)
generatedImages[image] = versions
if exists(version) {
// Mark as already generated
hasNowGenerated(version)
return false
}
return true
}
private func hasNowGenerated(_ version: ImageVersion) {
generatedImages[version.image.id, default: []].insert(version.versionId)
}
private func removeVersions(for image: String) {
@ -72,53 +86,59 @@ final class ImageGenerator {
// MARK: Files
private func exists(_ image: String) -> Bool {
storage.hasFileInOutputFolder(relativePath(for: image))
private func exists(_ version: ImageVersion) -> Bool {
storage.hasFileInOutputFolder(version.outputPath)
}
private func relativePath(for image: String) -> String {
outputFolder + "/" + image
}
private func write(imageData data: Data, version: String) -> Bool {
return storage.write(data, to: relativePath(for: version))
private func write(imageData data: Data, of version: ImageVersion) -> Bool {
return storage.write(data, to: version.outputPath)
}
// MARK: Image operations
func generate(job: ImageGenerationJob) -> Bool {
guard needsToGenerate(version: job.version, for: job.image) else {
func generate(version: ImageVersion) -> Bool {
guard needsToGenerate(version) else {
return true
}
guard let data = storage.fileData(for: job.image) else {
print("Failed to load image \(job.image)")
guard let data = version.image.dataContent() else {
print("ImageGenerator: Failed to load data for image \(version.image.id)")
return false
}
guard let originalImage = NSImage(data: data) else {
print("Failed to load image")
print("ImageGenerator: Failed to load image \(version.image.id)")
return false
}
let representation = create(image: originalImage, width: CGFloat(job.maximumWidth), height: CGFloat(job.maximumHeight))
let representation = create(image: originalImage, width: CGFloat(version.maximumWidth), height: CGFloat(version.maximumHeight))
guard let data = create(image: representation, type: job.type, quality: job.quality) else {
print("Failed to get data for type \(job.type)")
guard let data = create(image: representation, type: version.type, quality: version.quality) else {
print("ImageGenerator: Failed to get data for type \(version.type) of image \(version.image.id)")
return false
}
if job.type == .avif {
let input = job.version.fileNameAndExtension.fileName + "." + job.image.fileExtension!
print("avifenc -q 70 \(input) \(job.version)")
hasNowGenerated(version: job.version, for: job.image)
if version.type == .avif {
// AVIF conversion is very slow, so we save bash commands
// for the conversion instead
let baseVersion = ImageVersion(
image: version.image,
type: version.image.type,
maximumWidth: version.maximumWidth,
maximumHeight: version.maximumHeight)
let originalImagePath = storage.outputPath(to: baseVersion.outputPath)!.path()
let generatedImagePath = storage.outputPath(to: version.outputPath)!.path()
let quality = Int(version.quality * 100)
avifCommands.insert("avifenc -q \(quality) '\(originalImagePath)' '\(generatedImagePath)'")
// hasNowGenerated(version)
return true
}
guard write(imageData: data, version: job.version) else {
guard write(imageData: data, of: version) else {
return false
}
hasNowGenerated(version: job.version, for: job.image)
hasNowGenerated(version)
return true
}