Full generation, file type cleanup

This commit is contained in:
Christoph Hagen
2024-12-25 18:06:05 +01:00
parent 41887a1401
commit 1e4682dad1
56 changed files with 1577 additions and 1103 deletions

View File

@@ -2,70 +2,97 @@ import Foundation
extension Content {
func generateFeed() -> Bool {
#warning("Implement feed generation")
return false
func generateWebsiteInAllLanguages() {
performGenerationIfIdle {
self.generatePagesInternal()
self.generatePostFeedPagesInternal()
self.generateTagPagesInternal()
self.generateTagOverviewPagesInternal()
self.copyRequiredFiles()
self.generateRequiredImages()
self.status("Generation completed")
}
}
func generateAllPages() -> Bool {
guard startGenerating() else { return false }
defer { endGenerating() }
func generatePostFeedPages() {
performGenerationIfIdle {
self.generatePostFeedPagesInternal()
}
}
for page in pages {
func check(content: String, of page: Page, for language: ContentLanguage, onComplete: @escaping (PageGenerationResults) -> Void) {
performGenerationIfIdle {
let results = self.results.makeResults(for: page, in: language)
let generator = PageContentParser(content: page.content, language: language, results: results)
_ = generator.generatePage(from: content)
DispatchQueue.main.async {
onComplete(results)
}
}
}
private func copyRequiredFiles() {
let count = results.requiredFiles.count
var completed = 0
for file in results.requiredFiles {
defer {
completed += 1
status("Copying required files: \(completed) / \(count)")
}
guard !file.isExternallyStored else {
continue
}
let path = file.absoluteUrl
if !storage.copy(file: file.id, to: path) {
results.general.unsavedOutput(path, source: .general)
}
}
}
private func generateRequiredImages() {
let imageGenerator = ImageGenerator(
storage: storage,
settings: settings)
let images = results.imagesToGenerate.sorted()
let count = images.count
var completed = 0
for image in images {
defer {
completed += 1
status("Generating required images: \(completed) / \(count)")
}
if imageGenerator.generate(job: image) {
continue
}
results.failed(image: image)
}
//let images = Set(self.images.map { $0.id })
//imageGenerator.recalculateGeneratedImages(by: images)
}
func generateAllPages() {
performGenerationIfIdle {
self.generatePagesInternal()
}
}
func generatePage(_ page: Page) {
performGenerationIfIdle {
for language in ContentLanguage.allCases {
guard generateInternal(page, in: language) else {
return false
}
self.generateInternal(page, in: language)
}
self.copyRequiredFiles()
self.generateRequiredImages()
}
let failedAssetCopies = results.values
.reduce(Set()) { $0.union($1.assets) }
.filter { !$0.isExternallyStored }
.filter { !storage.copy(file: $0.id, to: $0.assetUrl) }
let failedFileCopies = results.values
.reduce(Set()) { $0.union($1.files) }
.filter { !$0.isExternallyStored }
.filter { !storage.copy(file: $0.id, to: $0.absoluteUrl) }
guard imageGenerator.runJobs(callback: { _ in }) else {
return false
}
return true
}
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
}
func generatePage(_ page: Page, in language: ContentLanguage) {
performGenerationIfIdle {
self.generateInternal(page, in: language)
}
guard imageGenerator.runJobs(callback: { _ in }) else {
return false
}
let failedAssetCopies = results.values
.reduce(Set()) { $0.union($1.assets) }
.filter { !$0.isExternallyStored }
.filter { !storage.copy(file: $0.id, to: $0.assetUrl) }
let failedFileCopies = results.values
.reduce(Set()) { $0.union($1.files) }
.filter { !$0.isExternallyStored }
.filter { !storage.copy(file: $0.id, to: $0.absoluteUrl) }
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
@@ -121,60 +148,134 @@ extension Content {
return result
}
// MARK: Images
func recalculateGeneratedImages() {
let images = Set(self.images.map { $0.id })
imageGenerator.recalculateGeneratedImages(by: images)
}
// 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 pageGenerator = PageGenerator(
content: self,
imageGenerator: imageGenerator)
guard let (content, results) = pageGenerator.generate(page: page, language: language) else {
print("Failed to generate page \(page.id) in language \(language)")
return false
}
private func performGenerationIfIdle(_ operation: @escaping () -> ()) {
DispatchQueue.main.async {
let id = ItemId(itemId: page.id, language: language, itemType: .page)
self.results[id] = results
guard !self.isGeneratingWebsite else {
return
}
self.set(isGenerating: true)
DispatchQueue.global(qos: .userInitiated).async {
operation()
DispatchQueue.main.async {
self.set(isGenerating: false)
}
}
}
}
private func status(_ message: String) {
DispatchQueue.main.async {
self.generationStatus = message
}
}
/**
- Note: Run on background thread
*/
private func generatePagesInternal() {
let count = pages.count
for index in pages.indices {
let page = pages[index]
status("Generating pages: \(index) / \(count)")
guard !page.isExternalUrl else {
continue
}
for language in ContentLanguage.allCases {
guard page.hasContent(in: language) else {
continue
}
generateInternal(page, in: language)
}
}
}
/**
- Note: Run on background thread
*/
private func generatePostFeedPagesInternal() {
status("Generating post feed")
for language in ContentLanguage.allCases {
let results = results.makeResults(for: .feed, in: language)
let generator = PostListPageGenerator(
language: language,
content: self,
results: results,
showTitle: false,
pageTitle: settings.localized(in: language).title,
pageDescription: settings.localized(in: language).description,
pageUrlPrefix: settings.localized(in: language).feedUrlPrefix)
generator.createPages(for: posts)
}
}
/**
- Note: Run on background thread
*/
private func generateTagPagesInternal() {
let count = tags.count
for index in tags.indices {
let tag = tags[index]
status("Generating tag pages: \(index) / \(count)")
generatePagesInternal(for: tag)
}
}
/**
- Note: Run on background thread
*/
private func generatePagesInternal(for tag: Tag) {
for language in ContentLanguage.allCases {
let results = results.makeResults(for: tag, in: language)
let posts = posts.filter { $0.tags.contains(tag) }
guard posts.count > 0 else { continue }
let localized = tag.localized(in: language)
let urlPrefix = absoluteUrlPrefixForTag(tag, language: language)
let generator = PostListPageGenerator(
language: language,
content: self,
results: results,
showTitle: true,
pageTitle: localized.name,
pageDescription: localized.description ?? "",
pageUrlPrefix: urlPrefix)
generator.createPages(for: posts)
}
}
/**
- Note: Run on background thread
*/
private func generateTagOverviewPagesInternal() {
status("Generating tag overview page")
for language in ContentLanguage.allCases {
let results = results.makeResults(for: .tagOverview, in: language)
#warning("Create layout for tag overview page")
}
}
/**
- Note: Run on background thread
*/
private func generateInternal(_ page: Page, in language: ContentLanguage) {
let results = results.makeResults(for: page, in: language)
let pageGenerator = PageGenerator(content: self)
results.require(files: page.requiredFiles)
guard let content = pageGenerator.generate(page: page, language: language, results: results) else {
print("Failed to generate page \(page.id) in language \(language)")
return
}
let path = page.absoluteUrl(in: language) + ".html"
guard storage.write(content, to: path) else {
print("Failed to save page \(page.id)")
return false
return
}
return true
}
}
prefix operator ~>
prefix func ~> (operation: () throws -> Void) -> Bool {
do {
try operation()
return true
} catch {
return false
}
}