291 lines
11 KiB
Swift
291 lines
11 KiB
Swift
import Foundation
|
|
|
|
final class Importer {
|
|
|
|
var posts: [String : PostFile] = [:]
|
|
|
|
var pages: [String : PageOnDisk] = [:]
|
|
|
|
var tags: [String : TagFile] = [:]
|
|
|
|
var files: [String : FileOnDisk] = [:]
|
|
|
|
var ignoredFiles: [URL] = []
|
|
|
|
var foldersToSearch: [(path: String, tag: String)] = [
|
|
("/Users/ch/Downloads/Website/projects/electronics", "electronics"),
|
|
("/Users/ch/Downloads/Website/projects/endeavor", "endeavor"),
|
|
("/Users/ch/Downloads/Website/projects/furniture", "furniture"),
|
|
("/Users/ch/Downloads/Website/projects/lighting", "lighting"),
|
|
("/Users/ch/Downloads/Website/projects/other", "other"),
|
|
("/Users/ch/Downloads/Website/projects/sewing", "sewing"),
|
|
("/Users/ch/Downloads/Website/projects/software", "software"),
|
|
("/Users/ch/Downloads/Website/articles", "articles"),
|
|
("/Users/ch/Downloads/Website/photography", "photography"),
|
|
("/Users/ch/Downloads/Website/travel", "travel")
|
|
]
|
|
|
|
func importContent() throws {
|
|
for (path, name) in foldersToSearch {
|
|
let folder = URL(filePath: path)
|
|
let pageFolders = try findPageFolders(in: folder)
|
|
|
|
let tag = try importTag(name: name, folder: folder)
|
|
|
|
for pageFolder in pageFolders {
|
|
try importEntry(at: pageFolder, tag: tag)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func importTag(name: String, folder: URL) throws -> String {
|
|
let metadataUrl = folder.appending(path: "metadata.json", directoryHint: .notDirectory)
|
|
let data = try Data(contentsOf: metadataUrl)
|
|
let meta = try JSONDecoder().decode(ImportableTag.self, from: data)
|
|
|
|
let thumbnailUrl = folder.appending(path: "thumbnail.jpg", directoryHint: .notDirectory)
|
|
var thumbnail: FileOnDisk? = nil
|
|
if FileManager.default.fileExists(atPath: thumbnailUrl.path()) {
|
|
thumbnail = FileOnDisk(type: .image, url: thumbnailUrl, name: "\(name)-thumbnail.jpg")
|
|
add(resource: thumbnail!)
|
|
}
|
|
|
|
func makeTag(metadata: TagLanguage) throws -> LocalizedTagFile {
|
|
let language = ContentLanguage(rawValue: metadata.language)!
|
|
let originalUrl = folder
|
|
.appendingPathComponent("\(language.rawValue).html", isDirectory: false)
|
|
.path()
|
|
.replacingOccurrences(of: "/Users/ch/Downloads/Website", with: "")
|
|
|
|
return LocalizedTagFile(
|
|
urlComponent: metadata.title.lowercased().replacingOccurrences(of: " ", with: "-"),
|
|
name: metadata.title,
|
|
subtitle: metadata.subtitle,
|
|
description: metadata.description,
|
|
thumbnail: thumbnail?.name,
|
|
originalURL: originalUrl)
|
|
}
|
|
|
|
let en = meta.info(for: .english)!
|
|
let de = meta.info(for: .german)!
|
|
|
|
let tagId = en.title.lowercased().replacingOccurrences(of: " ", with: "-")
|
|
|
|
let enTag = try makeTag(metadata: en)
|
|
let deTag = try makeTag(metadata: de)
|
|
|
|
let tag = TagFile(
|
|
id: enTag.urlComponent,
|
|
isVisible: true,
|
|
german: deTag,
|
|
english: enTag)
|
|
tags[tagId] = tag
|
|
return tagId
|
|
}
|
|
|
|
private func findPageFolders(in folder: URL) throws -> [URL] {
|
|
try FileManager.default
|
|
.contentsOfDirectory(at: folder, includingPropertiesForKeys: [.isDirectoryKey])
|
|
.filter { $0.hasDirectoryPath }
|
|
}
|
|
|
|
private func findResources(in folder: URL, pageId: String) throws -> [FileOnDisk] {
|
|
try FileManager.default
|
|
.contentsOfDirectory(at: folder, includingPropertiesForKeys: [.isDirectoryKey])
|
|
.filter { !$0.hasDirectoryPath }
|
|
.compactMap { url in
|
|
let fileName = url.lastPathComponent
|
|
let fileExtension = url.pathExtension
|
|
|
|
guard fileName != "metadata.json",
|
|
fileName != "de.md",
|
|
fileName != "en.md" else {
|
|
return nil
|
|
}
|
|
|
|
let type = FileType(fileExtension: fileExtension)
|
|
guard type != .resource else {
|
|
self.ignoredFiles.append(url)
|
|
return nil
|
|
}
|
|
|
|
let name = pageId + "-" + fileName
|
|
|
|
return FileOnDisk(type: type, url: url, name: name)
|
|
}
|
|
}
|
|
|
|
private func importEntry(at url: URL, tag: String) throws {
|
|
let metadataUrl = url.appending(path: "metadata.json", directoryHint: .notDirectory)
|
|
guard FileManager.default.fileExists(atPath: metadataUrl.path()) else {
|
|
print("No entry at \(url.path())")
|
|
return
|
|
}
|
|
let data = try Data(contentsOf: metadataUrl)
|
|
let meta = try JSONDecoder().decode(GenericMetadata.self, from: data)
|
|
|
|
let pageId = meta.customId ?? url.lastPathComponent
|
|
|
|
let resources = try findResources(in: url, pageId: pageId)
|
|
|
|
guard let languages = meta.languages else {
|
|
print("No languages for \(url.path())")
|
|
return
|
|
}
|
|
|
|
let externalFiles = meta.externalFiles ?? []
|
|
let requiredFiles = meta.requiredFiles ?? []
|
|
|
|
let date = meta.date!.toDate()
|
|
let endDate = meta.endDate?.toDate()
|
|
|
|
let de = languages.first { $0.language == "de" }!
|
|
let en = languages.first { $0.language == "en" }!
|
|
|
|
@discardableResult
|
|
func makePage(_ content: GenericMetadata.LocalizedMetadata) throws -> (LocalizedPageFile, URL, LocalizedPostFile) {
|
|
let language = ContentLanguage(rawValue: content.language!)!
|
|
|
|
let id: String
|
|
if language == .english {
|
|
id = pageId
|
|
} else {
|
|
id = pageId + "-" + language.rawValue
|
|
}
|
|
|
|
let originalUrl = url
|
|
.appendingPathComponent("\(language.rawValue).html", isDirectory: false)
|
|
.path()
|
|
.replacingOccurrences(of: "/Users/ch/Downloads/Website", with: "")
|
|
|
|
var pageFiles = Set(resources.map { $0.name })
|
|
let thumbnail = try determineThumbnail(in: resources, folder: url, customPath: meta.thumbnailPath, pageId: id, language: language.rawValue)
|
|
if let thumbnail {
|
|
pageFiles.insert(thumbnail.name)
|
|
}
|
|
let page = LocalizedPageFile(
|
|
url: id,
|
|
files: pageFiles.sorted(),
|
|
externalFiles: externalFiles.sorted(),
|
|
requiredFiles: requiredFiles.sorted(),
|
|
title: content.title!,
|
|
linkPreviewImage: thumbnail?.name,
|
|
linkPreviewTitle: content.linkPreviewTitle,
|
|
linkPreviewDescription: content.linkPreviewDescription,
|
|
lastModifiedDate: nil,
|
|
originalURL: originalUrl)
|
|
let contentUrl = url.appendingPathComponent("\(content.language!).md", isDirectory: false)
|
|
|
|
let postContent = content.linkPreviewDescription ?? content.description ?? ""
|
|
|
|
let post = createPost(page: page, content: postContent)
|
|
|
|
return (page, contentUrl, post)
|
|
}
|
|
let (dePage, deUrl, dePost) = try makePage(de)
|
|
let (enPage, enUrl, enPost) = try makePage(en)
|
|
|
|
let page = PageFile(
|
|
isDraft: meta.state == "draft",
|
|
tags: [tag],
|
|
createdDate: date,
|
|
startDate: date,
|
|
endDate: endDate,
|
|
german: dePage,
|
|
english: enPage)
|
|
|
|
if pages[pageId] != nil {
|
|
print("Conflicting page id \(pageId)")
|
|
}
|
|
|
|
pages[pageId] = .init(page: page, deContentUrl: deUrl, enContentUrl: enUrl)
|
|
|
|
|
|
for resource in resources {
|
|
add(resource: resource)
|
|
}
|
|
|
|
let post = PostFile(
|
|
isDraft: page.isDraft || meta.state == "hidden",
|
|
createdDate: page.createdDate,
|
|
startDate: page.startDate,
|
|
endDate: page.endDate,
|
|
tags: page.tags,
|
|
german: dePost,
|
|
english: enPost,
|
|
linkedPageId: pageId)
|
|
|
|
posts[pageId] = post
|
|
}
|
|
|
|
private func add(resource: FileOnDisk) {
|
|
guard let existingFile = files[resource.name] else {
|
|
files[resource.name] = resource
|
|
return
|
|
}
|
|
|
|
guard existingFile.url != resource.url else {
|
|
return
|
|
}
|
|
print("Conflicting name for file \(resource.name)")
|
|
}
|
|
|
|
private func determineThumbnail(in resources: [FileOnDisk], folder: URL, customPath: String?, pageId: String, language: String) throws -> FileOnDisk? {
|
|
guard let thumbnailUrl = findThumbnailUrl(in: folder, customPath: customPath, language: language) else {
|
|
return nil
|
|
}
|
|
return resources.first { $0.url == thumbnailUrl }
|
|
}
|
|
|
|
private func determineThumbnail(in folder: URL, customPath: String?, pageId: String, language: String) throws -> FileOnDisk? {
|
|
guard let thumbnailUrl = findThumbnailUrl(in: folder, customPath: customPath, language: language) else {
|
|
return nil
|
|
}
|
|
let id = pageId + "-" + thumbnailUrl.lastPathComponent
|
|
return FileOnDisk(image: id, url: thumbnailUrl)
|
|
}
|
|
|
|
private func findThumbnailUrl(in folder: URL, customPath: String?, language: String) -> URL? {
|
|
if let customPath {
|
|
return folder.appending(path: customPath, directoryHint: .notDirectory)
|
|
}
|
|
let thumbnailImageUrl = folder.appending(path: "thumbnail.jpg", directoryHint: .notDirectory)
|
|
if FileManager.default.fileExists(atPath: thumbnailImageUrl.path()) {
|
|
return thumbnailImageUrl
|
|
}
|
|
|
|
let localizedThumbnail = folder.appending(path: "thumbnail-\(language).jpg", directoryHint: .notDirectory)
|
|
if FileManager.default.fileExists(atPath: localizedThumbnail.path()) {
|
|
return localizedThumbnail
|
|
}
|
|
print("No thumbnail found in \(folder.path())")
|
|
return nil
|
|
}
|
|
|
|
private func createPost(page: LocalizedPageFile, content: String) -> LocalizedPostFile {
|
|
let images = page.linkPreviewImage.map { [$0] } ?? []
|
|
|
|
return LocalizedPostFile(
|
|
images: images.sorted(),
|
|
title: page.linkPreviewTitle ?? page.title,
|
|
content: content,
|
|
lastModifiedDate: nil,
|
|
linkPreviewImage: nil,
|
|
linkPreviewTitle: nil,
|
|
linkPreviewDescription: nil)
|
|
}
|
|
}
|
|
|
|
private extension String {
|
|
|
|
private static let metadataDate: DateFormatter = {
|
|
let df = DateFormatter()
|
|
df.dateFormat = "dd.MM.yy"
|
|
return df
|
|
}()
|
|
|
|
func toDate() -> Date {
|
|
String.metadataDate.date(from: self)!
|
|
}
|
|
}
|