import Foundation final class LocalizedWebsiteGenerator { let language: ContentLanguage private let localizedPostSettings: LocalizedPostSettings private var outputDirectory: URL { content.settings.outputDirectory } private var postsPerPage: Int { content.settings.posts.postsPerPage } private var mainContentMaximumWidth: CGFloat { CGFloat(content.settings.posts.contentWidth) } private let content: Content private let imageGenerator: ImageGenerator private var navigationBarLinks: [NavigationBar.Link] { content.settings.navigationTags.map { let localized = $0.localized(in: language) return .init(text: localized.name, url: content.absoluteUrlToTag($0, language: language)) } } init(content: Content, language: ContentLanguage) { self.language = language self.content = content self.localizedPostSettings = content.settings.localized(in: language) self.imageGenerator = ImageGenerator( storage: content.storage, relativeImageOutputPath: content.settings.paths.imagesOutputFolderPath) } func generateWebsite(callback: (String) -> Void) -> Bool { guard imageGenerator.prepareForGeneration() else { return false } guard createMainPostFeedPages() else { return false } #warning("Generate content pages") guard generateTagPages() else { return false } guard imageGenerator.runJobs(callback: callback) else { return false } return imageGenerator.save() } private func createMainPostFeedPages() -> Bool { let generator = PostListPageGenerator( language: language, content: content, imageGenerator: imageGenerator, navigationBarLinks: navigationBarLinks, showTitle: false, pageTitle: localizedPostSettings.title, pageDescription: localizedPostSettings.description, pageUrlPrefix: localizedPostSettings.feedUrlPrefix) return generator.createPages(for: content.posts) } 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) let urlPrefix = content.absoluteUrlPrefixForTag(tag, language: language) let generator = PostListPageGenerator( language: language, content: content, imageGenerator: imageGenerator, navigationBarLinks: navigationBarLinks, showTitle: true, pageTitle: localized.name, pageDescription: localized.description ?? "", pageUrlPrefix: urlPrefix) guard generator.createPages(for: posts) else { return false } } return true } private func generatePagesFolderIfNeeded() -> Bool { let relativePath = content.settings.pages.pageUrlPrefix return content.storage.write(in: .outputPath) { folder in let outputFile = folder.appendingPathComponent(relativePath, isDirectory: true) do { try outputFile.ensureFolderExistence() return true } catch { return false } } } func generate(page: Page) -> Bool { guard generatePagesFolderIfNeeded() else { print("Failed to generate output folder") return false } let pageGenerator = PageGenerator( content: content, imageGenerator: imageGenerator, navigationBarLinks: navigationBarLinks) 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(for: language) + ".html" guard save(content, to: path) else { print("Failed to save page") return false } guard imageGenerator.runJobs(callback: { _ in }) else { return false } guard copy(requiredFiles: results.files) else { return false } return true } private func copy(requiredFiles: Set) -> Bool { //print("Copying \(requiredVideoFiles.count) files...") for file in requiredFiles { guard !file.isExternallyStored else { continue } do { try content.storage.copy(file: file.id, to: file.absoluteUrl) } catch { print("Failed to copy file \(file.id): \(error)") return false } } return true } 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 } } }