Generate AVIF images, fix result display
This commit is contained in:
parent
1bc40bfb47
commit
6e161bf6b5
@ -19,27 +19,9 @@ final class ImageGenerator {
|
||||
settings.paths.imagesOutputFolderPath
|
||||
}
|
||||
|
||||
private var avifCommands: Set<String> = []
|
||||
|
||||
/**
|
||||
Write a file to the output folder containing a script to generate all missing AVIF images.
|
||||
|
||||
- Note: AVIF images could be generated internally, but the process is very slow.
|
||||
*/
|
||||
func writeAvifCommandScript() {
|
||||
guard !avifCommands.isEmpty else {
|
||||
if storage.hasFileInOutputFolder("generate-images.sh") {
|
||||
storage.deleteInOutputFolder("generate-images.sh")
|
||||
}
|
||||
return
|
||||
}
|
||||
let content = avifCommands.sorted().joined(separator: "\n")
|
||||
storage.write(content, to: "generate-images.sh")
|
||||
}
|
||||
|
||||
private func needsToGenerate(_ version: ImageVersion) -> Bool {
|
||||
if version.wasPreviouslyGenerated {
|
||||
return false
|
||||
return !exists(version)
|
||||
}
|
||||
if exists(version) {
|
||||
// Mark as already generated
|
||||
@ -71,20 +53,12 @@ final class ImageGenerator {
|
||||
// Skip GIFs, since they can't be converted by avifenc
|
||||
return true
|
||||
}
|
||||
// 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)'")
|
||||
version.wasNowGenerated()
|
||||
return true
|
||||
if createAvifUsingBash(version: version) {
|
||||
version.wasNowGenerated()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
guard let data = version.image.dataContent() else {
|
||||
@ -168,6 +142,38 @@ final class ImageGenerator {
|
||||
return SDImageAVIFCoder.shared.encodedData(with: newImage, format: .AVIF, options: [.encodeCompressionQuality: quality])
|
||||
}
|
||||
|
||||
private func createAvifUsingBash(version: ImageVersion) -> Bool {
|
||||
|
||||
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)
|
||||
|
||||
let process = Process()
|
||||
process.launchPath = "/opt/homebrew/bin/avifenc" // Adjust based on installation
|
||||
process.arguments = ["-q", "\(quality)", originalImagePath, generatedImagePath]
|
||||
|
||||
let pipe = Pipe()
|
||||
process.standardOutput = pipe
|
||||
process.standardError = pipe
|
||||
|
||||
process.launch()
|
||||
process.waitUntilExit()
|
||||
|
||||
if process.terminationStatus != 0 {
|
||||
print("ImageGenerator: Failed to create AVIF image \(version.image.id)")
|
||||
let outputData = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let outputString = String(data: outputData, encoding: .utf8) ?? ""
|
||||
print(outputString)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func createWebp(image: NSBitmapImageRep, quality: CGFloat) -> Data? {
|
||||
let newImage = NSImage(size: image.size)
|
||||
newImage.addRepresentation(image)
|
||||
|
@ -70,7 +70,7 @@ final class GenerationResults: ObservableObject {
|
||||
let general = PageGenerationResults(itemId: id, delegate: self)
|
||||
self.general = general
|
||||
cache[id] = general
|
||||
self.resultCount = 1
|
||||
self.resultCount = 0
|
||||
}
|
||||
|
||||
func makeResults(_ itemId: LocalizedItemId) -> PageGenerationResults {
|
||||
@ -98,6 +98,28 @@ final class GenerationResults: ObservableObject {
|
||||
return makeResults(itemId)
|
||||
}
|
||||
|
||||
func reset() {
|
||||
update {
|
||||
self.inaccessibleFiles = []
|
||||
self.unparsableFiles = []
|
||||
self.missingFiles = []
|
||||
self.missingTags = []
|
||||
self.missingPages = []
|
||||
self.externalLinks = []
|
||||
self.requiredFiles = []
|
||||
self.imagesToGenerate = []
|
||||
self.invalidCommands = []
|
||||
self.invalidBlocks = []
|
||||
self.warnings = []
|
||||
self.unsavedOutputFiles = []
|
||||
self.emptyPages = []
|
||||
self.redirects = [:]
|
||||
}
|
||||
for result in cache.values {
|
||||
result.reset()
|
||||
}
|
||||
}
|
||||
|
||||
func recalculate() {
|
||||
let inaccessibleFiles = cache.values.map { $0.inaccessibleFiles }.union()
|
||||
update { self.inaccessibleFiles = inaccessibleFiles }
|
||||
|
@ -4,6 +4,7 @@ extension Content {
|
||||
|
||||
func generateWebsiteInAllLanguages() {
|
||||
performGenerationIfIdle {
|
||||
self.results.reset()
|
||||
self.generatePagesInternal()
|
||||
self.generatePostFeedPagesInternal()
|
||||
self.generateTagPagesInternal()
|
||||
@ -80,8 +81,6 @@ extension Content {
|
||||
}
|
||||
results.failed(image: image)
|
||||
}
|
||||
|
||||
imageGenerator.writeAvifCommandScript()
|
||||
}
|
||||
|
||||
func generateAllPages() {
|
||||
|
@ -181,7 +181,9 @@ final class Content: ObservableObject {
|
||||
private(set) var lastModification: Date = .now
|
||||
|
||||
func update(saveState: SaveState) {
|
||||
self.saveState = saveState
|
||||
DispatchQueue.main.async {
|
||||
self.saveState = saveState
|
||||
}
|
||||
}
|
||||
|
||||
func setModificationTimestamp() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user