Import old content, load from disk
This commit is contained in:
@ -1,21 +1,71 @@
|
||||
import Foundation
|
||||
|
||||
struct ImportedContent {
|
||||
enum FileType {
|
||||
case image
|
||||
case file
|
||||
case video
|
||||
case resource
|
||||
|
||||
let posts: [Post]
|
||||
|
||||
let categories: [Tag]
|
||||
init(fileExtension: String) {
|
||||
switch fileExtension.lowercased() {
|
||||
case "jpg", "jpeg", "png", "gif":
|
||||
self = .image
|
||||
case "html", "stl", "f3d", "step", "f3z", "zip", "json", "conf", "css", "js", "cpp", "cddx", "svg", "glb", "mp3", "pdf", "swift":
|
||||
self = .file
|
||||
case "mp4":
|
||||
self = .video
|
||||
case "key", "psd":
|
||||
self = .resource
|
||||
default:
|
||||
print("Unhandled file type: \(fileExtension)")
|
||||
self = .resource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ImportedPage {
|
||||
|
||||
let page: PageFile
|
||||
|
||||
let deContentUrl: URL
|
||||
|
||||
let enContentUrl: URL
|
||||
}
|
||||
|
||||
|
||||
struct FileResource {
|
||||
|
||||
let type: FileType
|
||||
|
||||
let url: URL
|
||||
|
||||
let name: String
|
||||
|
||||
init(image: String, url: URL) {
|
||||
self.type = .image
|
||||
self.url = url
|
||||
self.name = image
|
||||
}
|
||||
|
||||
init(type: FileType, url: URL, name: String) {
|
||||
self.type = type
|
||||
self.url = url
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
final class Importer {
|
||||
|
||||
var posts: [Post] = []
|
||||
var posts: [String : PostFile] = [:]
|
||||
|
||||
var pages: [Page] = []
|
||||
var pages: [String : ImportedPage] = [:]
|
||||
|
||||
var tags: [Tag] = []
|
||||
var tags: [String : TagFile] = [:]
|
||||
|
||||
var images: [ImageResource] = []
|
||||
var files: [String : FileResource] = [:]
|
||||
|
||||
var ignoredFiles: [URL] = []
|
||||
|
||||
var foldersToSearch: [(path: String, tag: String)] = [
|
||||
("/Users/ch/Downloads/Website/projects/electronics", "electronics"),
|
||||
@ -30,93 +80,250 @@ final class Importer {
|
||||
("/Users/ch/Downloads/Website/travel", "travel")
|
||||
]
|
||||
|
||||
func importOldContent() throws {
|
||||
for (folder, tagName) in foldersToSearch {
|
||||
let url = URL(filePath: folder)
|
||||
let tag = try importTag(name: tagName, folder: url)
|
||||
try importEntries(in: url, tag: tag)
|
||||
tags.append(tag)
|
||||
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)
|
||||
}
|
||||
}
|
||||
posts.sort { $0.startDate > $1.startDate }
|
||||
//pages.sort { $0.startDate > $1.startDate }
|
||||
tags.sort()
|
||||
}
|
||||
|
||||
private func importTag(name: String, folder: URL) throws -> 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)
|
||||
|
||||
return .init(
|
||||
en: meta.info(for: .english)!.title,
|
||||
de: meta.info(for: .german)!.title)
|
||||
let thumbnailUrl = folder.appending(path: "thumbnail.jpg", directoryHint: .notDirectory)
|
||||
var thumbnail: FileResource? = nil
|
||||
if FileManager.default.fileExists(atPath: thumbnailUrl.path()) {
|
||||
thumbnail = FileResource(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,
|
||||
german: deTag,
|
||||
english: enTag)
|
||||
tags[tagId] = tag
|
||||
return tagId
|
||||
}
|
||||
|
||||
private func importEntries(in folder: URL, tag: Tag) throws {
|
||||
private func findPageFolders(in folder: URL) throws -> [URL] {
|
||||
try FileManager.default
|
||||
.contentsOfDirectory(at: folder, includingPropertiesForKeys: [.isDirectoryKey])
|
||||
.filter { $0.hasDirectoryPath }
|
||||
.forEach { try importEntry(at: $0, tag: tag) }
|
||||
}
|
||||
|
||||
private func importEntry(at url: URL, tag: Tag) throws {
|
||||
private func findResources(in folder: URL, pageId: String) throws -> [FileResource] {
|
||||
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 FileResource(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())")
|
||||
print("No entry at \(url.path())")
|
||||
return
|
||||
}
|
||||
let data = try Data(contentsOf: metadataUrl)
|
||||
let meta = try JSONDecoder().decode(GenericMetadata.self, from: data)
|
||||
|
||||
let page = Page(
|
||||
id: meta.customId ?? url.lastPathComponent,
|
||||
isDraft: meta.state == "draft",
|
||||
metadata: meta.languages!.map(convertPageContent),
|
||||
externalFiles: meta.externalFiles ?? [],
|
||||
requiredFiles: meta.requiredFiles ?? [],
|
||||
images: meta.images ?? [])
|
||||
pages.append(page)
|
||||
let pageId = meta.customId ?? url.lastPathComponent
|
||||
|
||||
let de = meta.languages!.first { $0.language == "de" }!
|
||||
let en = meta.languages!.first { $0.language == "en" }!
|
||||
let resources = try findResources(in: url, pageId: pageId)
|
||||
|
||||
let thumbnailImageName = meta.thumbnailPath ?? "thumbnail.jpg"
|
||||
let thumbnailImageUrl = url.appending(path: thumbnailImageName, directoryHint: .notDirectory)
|
||||
var images: [ImageResource] = []
|
||||
if tag.id != "articles" {
|
||||
if FileManager.default.fileExists(atPath: thumbnailImageUrl.path()) {
|
||||
let thumbnail = ImageResource(
|
||||
uniqueId: meta.customId ?? url.lastPathComponent,
|
||||
altText: .init(en: "An image about \(en.title!)", de: "Ein Bild zu \(de.title!)"),
|
||||
fileUrl: thumbnailImageUrl)
|
||||
images.append(thumbnail)
|
||||
self.images.append(thumbnail)
|
||||
} else {
|
||||
print("Thumbnail \(thumbnailImageUrl.path()) not found")
|
||||
}
|
||||
guard let languages = meta.languages else {
|
||||
print("No languages for \(url.path())")
|
||||
return
|
||||
}
|
||||
|
||||
let lastPostId = posts.last?.id ?? 0
|
||||
let externalFiles = meta.externalFiles ?? []
|
||||
let requiredFiles = meta.requiredFiles ?? []
|
||||
|
||||
let post = Post(
|
||||
id: lastPostId + 1,
|
||||
isDraft: meta.state == "draft" || meta.state == "hidden",
|
||||
startDate: meta.date!.toDate(),
|
||||
endDate: meta.endDate?.toDate(),
|
||||
title: .init(en: en.linkPreviewTitle ?? en.title!,
|
||||
de: de.linkPreviewTitle ?? de.title!),
|
||||
text: .init(en: en.linkPreviewDescription ?? en.description ?? "No description",
|
||||
de: de.linkPreviewDescription ?? de.description ?? "Keine Beschreibung"),
|
||||
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,
|
||||
externalFiles: externalFiles,
|
||||
requiredFiles: requiredFiles,
|
||||
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],
|
||||
images: images)
|
||||
createdDate: date,
|
||||
startDate: date,
|
||||
endDate: endDate,
|
||||
german: dePage,
|
||||
english: enPage)
|
||||
|
||||
posts.append(post)
|
||||
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 convertPageContent(_ meta: GenericMetadata.LocalizedMetadata) -> LocalizedPage {
|
||||
.init(language: ContentLanguage(rawValue: meta.language!)!,
|
||||
urlString: nil,
|
||||
headline: meta.title!)
|
||||
private func add(resource: FileResource) {
|
||||
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: [FileResource], folder: URL, customPath: String?, pageId: String, language: String) throws -> FileResource? {
|
||||
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 -> FileResource? {
|
||||
guard let thumbnailUrl = findThumbnailUrl(in: folder, customPath: customPath, language: language) else {
|
||||
return nil
|
||||
}
|
||||
let id = pageId + "-" + thumbnailUrl.lastPathComponent
|
||||
return FileResource(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: Set(images),
|
||||
title: page.linkPreviewTitle ?? page.title,
|
||||
content: content,
|
||||
lastModifiedDate: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user