Generate first tag pages

This commit is contained in:
Christoph Hagen
2024-12-09 17:47:03 +01:00
parent 4f08526978
commit 8183bc4903
35 changed files with 719 additions and 1105 deletions

View File

@ -33,7 +33,12 @@ final class ImageGenerator {
init(storage: Storage, relativeImageOutputPath: String) {
self.storage = storage
self.relativeImageOutputPath = relativeImageOutputPath
self.generatedImages = storage.loadListOfGeneratedImages()
do {
self.generatedImages = try storage.loadListOfGeneratedImages()
} catch {
print("Failed to load list of previously generated images: \(error)")
self.generatedImages = [:]
}
}
func prepareForGeneration() -> Bool {
@ -60,7 +65,13 @@ final class ImageGenerator {
}
func save() -> Bool {
storage.save(listOfGeneratedImages: generatedImages)
do {
try storage.save(listOfGeneratedImages: generatedImages)
return true
} catch {
print("Failed to save list of generated images: \(error)")
return false
}
}
private func versionFileName(image: String, type: ImageFileType, width: CGFloat, height: CGFloat) -> String {

View File

@ -14,7 +14,7 @@ final class PageGenerator {
self.navigationBarData = navigationBarData
}
func generate(page: Page, language: ContentLanguage) -> String {
func generate(page: Page, language: ContentLanguage) throws -> String {
let contentGenerator = PageContentParser(
page: page,
content: content,
@ -22,22 +22,26 @@ final class PageGenerator {
results: results,
imageGenerator: imageGenerator)
let rawPageContent = content.storage.pageContent(for: page.id, language: language)
let rawPageContent = try content.storage.pageContent(for: page.id, language: language)
let pageContent = contentGenerator.generatePage(from: rawPageContent)
let localized = page.localized(in: language)
let tags: [FeedEntryData.Tag] = page.tags.map { tag in
.init(name: tag.localized(in: language).name,
url: content.tagLink(tag, language: language))
}
return ContentPage(
language: language,
dateString: page.dateText(in: language),
title: localized.title,
tags: page.tags.map { $0.data(in: language) },
tags: tags,
linkTitle: localized.linkPreviewTitle ?? localized.title,
description: localized.linkPreviewDescription ?? "",
navigationBarData: navigationBarData,
pageContent: pageContent)
.content
}
}

View File

@ -0,0 +1,118 @@
import Foundation
final class PostListPageGenerator {
private let language: ContentLanguage
private let content: Content
private let imageGenerator: ImageGenerator
private let navigationBarData: NavigationBarData
private let showTitle: Bool
private let pageTitle: String
private let pageDescription: String
/// The url of the page, excluding the extension
private let pageUrlPrefix: String
init(language: ContentLanguage, content: Content, imageGenerator: ImageGenerator, navigationBarData: NavigationBarData, showTitle: Bool, pageTitle: String, pageDescription: String, pageUrlPrefix: String) {
self.language = language
self.content = content
self.imageGenerator = imageGenerator
self.navigationBarData = navigationBarData
self.showTitle = showTitle
self.pageTitle = pageTitle
self.pageDescription = pageDescription
self.pageUrlPrefix = pageUrlPrefix
}
private var mainContentMaximumWidth: CGFloat {
CGFloat(content.settings.posts.contentWidth)
}
private var postsPerPage: Int {
content.settings.posts.postsPerPage
}
func createPages(for posts: [Post]) -> Bool {
let totalCount = posts.count
guard totalCount > 0 else {
return true
}
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
for pageIndex in 1...numberOfPages {
let startIndex = (pageIndex - 1) * postsPerPage
let endIndex = min(pageIndex * postsPerPage, totalCount)
let postsOnPage = posts[startIndex..<endIndex]
guard createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage, bar: navigationBarData) else {
return false
}
}
return true
}
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>, bar: NavigationBarData) -> Bool {
let posts: [FeedEntryData] = posts.map { post in
let localized: LocalizedPost = post.localized(in: language)
let linkUrl = post.linkedPage.map {
FeedEntryData.Link(
url: content.pageLink($0, language: language),
text: language == .english ? "View" : "Anzeigen") // TODO: Add to settings
}
let tags: [FeedEntryData.Tag] = post.tags.map { tag in
.init(name: tag.localized(in: language).name,
url: content.tagLink(tag, language: language))
}
return FeedEntryData(
entryId: "\(post.id)",
title: localized.title,
textAboveTitle: post.dateText(in: language),
link: linkUrl,
tags: tags,
text: [localized.content], // TODO: Convert from markdown to html
images: localized.images.map(createImageSet))
}
let feed = PageInFeed(
language: language,
title: pageTitle,
showTitle: showTitle,
description: pageDescription,
navigationBarData: bar,
pageNumber: pageIndex,
totalPages: pageCount,
posts: posts)
let fileContent = feed.content
if pageIndex == 1 {
return save(fileContent, to: "\(pageUrlPrefix).html")
} else {
return save(fileContent, to: "\(pageUrlPrefix)-\(pageIndex).html")
}
}
private func createImageSet(for image: FileResource) -> FeedEntryData.Image {
imageGenerator.generateImageSet(
for: image.id,
maxWidth: mainContentMaximumWidth,
maxHeight: mainContentMaximumWidth,
altText: image.getDescription(for: language))
}
private func save(_ content: String, to relativePath: String) -> Bool {
do {
try self.content.storage.write(content: content, to: relativePath)
return true
} catch {
print("Failed to write page \(relativePath)")
return false
}
}
}

View File

@ -14,18 +14,6 @@ final class WebsiteGenerator {
content.settings.posts.postsPerPage
}
private var postFeedTitle: String {
localizedSettings.posts.title
}
private var postFeedDescription: String {
localizedSettings.posts.description
}
private var postFeedUrlPrefix: String {
localizedSettings.posts.feedUrlPrefix
}
private var navigationIconPath: String {
content.settings.navigationBar.iconPath
}
@ -57,7 +45,10 @@ final class WebsiteGenerator {
guard imageGenerator.prepareForGeneration() else {
return false
}
guard createPostFeedPages() else {
guard createMainPostFeedPages() else {
return false
}
guard generateTagPages() else {
return false
}
guard imageGenerator.runJobs(callback: callback) else {
@ -66,18 +57,37 @@ final class WebsiteGenerator {
return imageGenerator.save()
}
private func createPostFeedPages() -> Bool {
let totalCount = content.posts.count
guard totalCount > 0 else {
return true
}
private func createMainPostFeedPages() -> Bool {
let generator = PostListPageGenerator(
language: language,
content: content,
imageGenerator: imageGenerator,
navigationBarData: navigationBarData,
showTitle: false,
pageTitle: localizedSettings.posts.title,
pageDescription: localizedSettings.posts.description,
pageUrlPrefix: localizedSettings.posts.feedUrlPrefix)
return generator.createPages(for: content.posts)
}
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
for pageIndex in 1...numberOfPages {
let startIndex = (pageIndex - 1) * postsPerPage
let endIndex = min(pageIndex * postsPerPage, totalCount)
let postsOnPage = content.posts[startIndex..<endIndex]
guard createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage, bar: navigationBarData) else {
private func generateTagPages() -> Bool {
for tag in content.tags {
let posts = content.posts.filter { $0.tags.contains(tag) }
guard posts.count > 0 else { continue }
let localized = tag.localized(in: language)
#warning("Get tag url prefix from settings")
let generator = PostListPageGenerator(
language: language,
content: content,
imageGenerator: imageGenerator,
navigationBarData: navigationBarData,
showTitle: true,
pageTitle: localized.name,
pageDescription: localized.description ?? "",
pageUrlPrefix: "tags/\(localized.urlComponent)")
guard generator.createPages(for: posts) else {
return false
}
}
@ -95,50 +105,6 @@ final class WebsiteGenerator {
navigationItems: navigationItems)
}
private func createImageSet(for image: FileResource) -> FeedEntryData.Image {
imageGenerator.generateImageSet(
for: image.id,
maxWidth: mainContentMaximumWidth,
maxHeight: mainContentMaximumWidth,
altText: image.getDescription(for: language))
}
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>, bar: NavigationBarData) -> Bool {
let posts: [FeedEntryData] = posts.map { post in
let localized: LocalizedPost = post.localized(in: language)
let linkUrl = post.linkedPage.map {
FeedEntryData.Link(
url: content.pageLink($0, language: language),
text: language == .english ? "View" : "Anzeigen") // TODO: Add to settings
}
return FeedEntryData(
entryId: "\(post.id)",
title: localized.title,
textAboveTitle: post.dateText(in: language),
link: linkUrl,
tags: post.tags.map { $0.data(in: language) },
text: [localized.content], // TODO: Convert from markdown to html
images: localized.images.map(createImageSet))
}
let feed = PageInFeed(
language: language,
title: postFeedTitle,
description: postFeedDescription,
navigationBarData: bar,
pageNumber: pageIndex,
totalPages: pageCount,
posts: posts)
let fileContent = feed.content
if pageIndex == 1 {
return save(fileContent, to: "\(postFeedUrlPrefix).html")
} else {
return save(fileContent, to: "\(postFeedUrlPrefix)-\(pageIndex).html")
}
}
private func generatePagesFolderIfNeeded() -> Bool {
let relativePath = content.settings.pages.pageUrlPrefix
@ -159,7 +125,14 @@ final class WebsiteGenerator {
return false
}
let pageGenerator = PageGenerator(content: content, imageGenerator: imageGenerator, navigationBarData: navigationBarData)
let content = pageGenerator.generate(page: page, language: language)
let content: String
do {
content = try pageGenerator.generate(page: page, language: language)
} catch {
print("Failed to generate page \(page.id) in language \(language): \(error)")
return false
}
let path = self.content.pageLink(page, language: language) + ".html"
guard save(content, to: path) else {
@ -181,8 +154,10 @@ final class WebsiteGenerator {
guard let outputPath = content.pathToFile(fileId) else {
return false
}
guard content.storage.copy(file: fileId, to: outputPath) else {
print("Failed to copy video file to output folder")
do {
try content.storage.copy(file: fileId, to: outputPath)
} catch {
print("Failed to copy video file: \(error)")
return false
}
}
@ -190,23 +165,12 @@ final class WebsiteGenerator {
}
private func save(_ content: String, to relativePath: String) -> Bool {
guard let data = content.data(using: .utf8) else {
print("Failed to create data for \(relativePath)")
do {
try self.content.storage.write(content: content, to: relativePath)
return true
} catch {
print("Failed to write page \(relativePath)")
return false
}
return save(data, to: relativePath)
}
private func save(_ data: Data, to relativePath: String) -> Bool {
self.content.storage.write(in: .outputPath) { folder in
let outputFile = folder.appendingPathComponent(relativePath, isDirectory: false)
do {
try data.write(to: outputFile)
return true
} catch {
print("Failed to save \(outputFile.path()): \(error)")
return false
}
}
}
}