ChWebsiteApp/CHDataManagement/Model/Content+Generation.swift
2024-12-17 23:05:45 +01:00

154 lines
4.5 KiB
Swift

import Foundation
extension Content {
func generatePage(_ page: Page) -> Bool {
guard startGenerating() else { return false }
defer { endGenerating() }
for language in ContentLanguage.allCases {
guard generateInternal(page, in: language) else {
return false
}
}
return true
}
func generatePage(_ page: Page, in language: ContentLanguage) -> Bool {
guard startGenerating() else { return false }
defer { endGenerating() }
return generateInternal(page, in: language)
}
// MARK: Paths to items
private func makeCleanAbsolutePath(_ path: String) -> String {
("/" + path).replacingOccurrences(of: "//", with: "/")
}
func absoluteUrlPrefixForTag(_ tag: Tag, language: ContentLanguage) -> String {
makeCleanAbsolutePath(settings.paths.tagsOutputFolderPath + "/" + tag.localized(in: language).urlComponent)
}
func absoluteUrlToTag(_ tag: Tag, language: ContentLanguage) -> String {
absoluteUrlPrefixForTag(tag, language: language) + ".html"
}
// MARK: Find items by id
func page(_ pageId: String) -> Page? {
pages.first { $0.id == pageId }
}
func image(_ imageId: String) -> FileResource? {
files.first { $0.id == imageId && $0.type.isImage }
}
func video(_ videoId: String) -> FileResource? {
files.first { $0.id == videoId && $0.type.isVideo }
}
func file(_ fileId: String) -> FileResource? {
files.first { $0.id == fileId }
}
func tag(_ tagId: String) -> Tag? {
tags.first { $0.id == tagId }
}
// MARK: Generation input
func navigationBar(in language: ContentLanguage) -> [NavigationBar.Link] {
settings.navigationItems.map {
.init(text: $0.title(in: language),
url: $0.absoluteUrl(in: language))
}
}
var defaultPageHeaders: Set<HeaderElement> {
var result: Set<HeaderElement> = [.charset, .viewport]
if let defaultCss = settings.posts.defaultCssFile {
result.insert(.css(file: defaultCss, order: HeaderElement.defaultCssFileOrder))
}
return result
}
// MARK: Generation
private func startGenerating() -> Bool {
guard !isGeneratingWebsite else {
return false
}
// TODO: Fix bug where multiple generating operations can be started
// due to dispatch of locking property on main queue
self.set(isGenerating: true)
return true
}
private func endGenerating() {
set(isGenerating: false)
}
private func generateInternal(_ page: Page, in language: ContentLanguage) -> Bool {
let pagesFolder = settings.paths.pagesOutputFolderPath
guard storage.create(folder: pagesFolder, in: .outputPath) else {
print("Failed to generate output folder")
return false
}
let imageGenerator = ImageGenerator(
storage: storage,
settings: settings)
let pageGenerator = PageGenerator(
content: self,
imageGenerator: imageGenerator)
let content: String
let results: PageGenerationResults
do {
(content, results) = try pageGenerator.generate(page: page, language: language)
} catch {
print("Failed to generate page \(page.id) in language \(language): \(error)")
return false
}
guard !content.trimmed.isEmpty else {
#warning("Generate page with placeholder content")
return true
}
let path = page.absoluteUrl(in: language) + ".html"
do {
try storage.write(content: content, to: path)
} catch {
print("Failed to save page \(page.id): \(error)")
return false
}
guard imageGenerator.runJobs(callback: { _ in }) else {
return false
}
guard copy(requiredFiles: results.files) else {
return false
}
return true
return true
}
private func copy(requiredFiles: Set<FileResource>) -> Bool {
//print("Copying \(requiredVideoFiles.count) files...")
for file in requiredFiles {
guard !file.isExternallyStored else {
continue
}
do {
try storage.copy(file: file.id, to: file.absoluteUrl)
} catch {
print("Failed to copy file \(file.id): \(error)")
return false
}
}
return true
}
}