Full generation, file type cleanup
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user