Add videos, delete unused files
This commit is contained in:
parent
f4b81e9823
commit
3c950d47a2
@ -19,6 +19,9 @@ final class Content: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
var images: [ImageResource]
|
var images: [ImageResource]
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var videos: [String]
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var files: [FileResource]
|
var files: [FileResource]
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ final class Content: ObservableObject {
|
|||||||
tags: [Tag],
|
tags: [Tag],
|
||||||
images: [ImageResource],
|
images: [ImageResource],
|
||||||
files: [FileResource],
|
files: [FileResource],
|
||||||
|
videos: [String],
|
||||||
storedContentPath: String) {
|
storedContentPath: String) {
|
||||||
self.websiteData = websiteData
|
self.websiteData = websiteData
|
||||||
self.posts = posts
|
self.posts = posts
|
||||||
@ -49,6 +53,7 @@ final class Content: ObservableObject {
|
|||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.images = images
|
self.images = images
|
||||||
self.files = files
|
self.files = files
|
||||||
|
self.videos = videos
|
||||||
self.storedContentPath = storedContentPath
|
self.storedContentPath = storedContentPath
|
||||||
self.contentPath = storedContentPath
|
self.contentPath = storedContentPath
|
||||||
self.storage = Storage(baseFolder: URL(filePath: storedContentPath))
|
self.storage = Storage(baseFolder: URL(filePath: storedContentPath))
|
||||||
@ -70,6 +75,7 @@ final class Content: ObservableObject {
|
|||||||
self.tags = []
|
self.tags = []
|
||||||
self.images = []
|
self.images = []
|
||||||
self.files = []
|
self.files = []
|
||||||
|
self.videos = []
|
||||||
|
|
||||||
contentPath = storedContentPath
|
contentPath = storedContentPath
|
||||||
do {
|
do {
|
||||||
@ -142,21 +148,23 @@ final class Content: ObservableObject {
|
|||||||
let postsData = try storage.loadAllPosts()
|
let postsData = try storage.loadAllPosts()
|
||||||
let filesData = try storage.loadAllFiles()
|
let filesData = try storage.loadAllFiles()
|
||||||
|
|
||||||
let images: [String : ImageResource] = filesData.reduce(into: [:]) { dict, item in
|
var images: [String : ImageResource] = [:]
|
||||||
let (file, url) = item
|
var files: [FileResource] = []
|
||||||
let ext = file.components(separatedBy: ".").last!.lowercased()
|
var videos: [String] = []
|
||||||
let type = FileType(fileExtension: ext)
|
|
||||||
guard type == .image else { return }
|
|
||||||
dict[file] = ImageResource(uniqueId: file, altText: .init(en: "", de: ""), fileUrl: url)
|
|
||||||
}
|
|
||||||
|
|
||||||
let files: [FileResource] = filesData.compactMap { file, url in
|
for (file, url) in filesData {
|
||||||
let ext = file.components(separatedBy: ".").last!.lowercased()
|
let ext = file.components(separatedBy: ".").last!.lowercased()
|
||||||
let type = FileType(fileExtension: ext)
|
let type = FileType(fileExtension: ext)
|
||||||
guard type == .file else {
|
switch type {
|
||||||
return nil
|
case .image:
|
||||||
|
images[file] = ImageResource(uniqueId: file, altText: .init(en: "", de: ""), fileUrl: url)
|
||||||
|
case .file:
|
||||||
|
files.append(FileResource(uniqueId: file, description: ""))
|
||||||
|
case .video:
|
||||||
|
videos.append(file)
|
||||||
|
case .resource:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return FileResource(uniqueId: file, description: "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tags = tagData.reduce(into: [:]) { (tags, data) in
|
let tags = tagData.reduce(into: [:]) { (tags, data) in
|
||||||
@ -189,6 +197,7 @@ final class Content: ObservableObject {
|
|||||||
self.pages = pages.values.sorted(ascending: false) { $0.startDate }
|
self.pages = pages.values.sorted(ascending: false) { $0.startDate }
|
||||||
self.files = files.sorted { $0.uniqueId }
|
self.files = files.sorted { $0.uniqueId }
|
||||||
self.images = images.values.sorted { $0.id }
|
self.images = images.values.sorted { $0.id }
|
||||||
|
self.videos = videos
|
||||||
self.posts = posts.sorted(ascending: false) { $0.startDate }
|
self.posts = posts.sorted(ascending: false) { $0.startDate }
|
||||||
self.websiteData = WebsiteData(
|
self.websiteData = WebsiteData(
|
||||||
navigationTags: websiteData.navigationTags.map { tags[$0]! },
|
navigationTags: websiteData.navigationTags.map { tags[$0]! },
|
||||||
@ -228,6 +237,15 @@ final class Content: ObservableObject {
|
|||||||
}
|
}
|
||||||
storage.save(websiteData: websiteData.dataFile)
|
storage.save(websiteData: websiteData.dataFile)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try storage.deletePostFiles(notIn: posts.map { $0.id })
|
||||||
|
try storage.deletePageFiles(notIn: pages.map { $0.id })
|
||||||
|
try storage.deleteTagFiles(notIn: tags.map { $0.id })
|
||||||
|
let allFiles = files.map { $0.uniqueId } + images.map { $0.id } + videos
|
||||||
|
try storage.deleteFiles(notIn: allFiles)
|
||||||
|
} catch {
|
||||||
|
print("Failed to remove unused files: \(error)")
|
||||||
|
}
|
||||||
// TODO: Remove all files that are no longer in use (they belong to deleted items)
|
// TODO: Remove all files that are no longer in use (they belong to deleted items)
|
||||||
//print("Finished save")
|
//print("Finished save")
|
||||||
}
|
}
|
||||||
|
@ -10,3 +10,7 @@ enum ContentLanguage: String {
|
|||||||
extension ContentLanguage: Codable {
|
extension ContentLanguage: Codable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ContentLanguage: CaseIterable {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -21,5 +21,6 @@ extension Content {
|
|||||||
tags: [.hiking, .mountains, .nature, .sports],
|
tags: [.hiking, .mountains, .nature, .sports],
|
||||||
images: [],
|
images: [],
|
||||||
files: [],
|
files: [],
|
||||||
|
videos: [],
|
||||||
storedContentPath: dbPath)
|
storedContentPath: dbPath)
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,7 @@ final class Storage {
|
|||||||
|
|
||||||
func createFolderStructure() throws {
|
func createFolderStructure() throws {
|
||||||
try create(folder: pagesFolder)
|
try create(folder: pagesFolder)
|
||||||
try create(folder: imagesFolder)
|
|
||||||
try create(folder: filesFolder)
|
try create(folder: filesFolder)
|
||||||
try create(folder: videosFolder)
|
|
||||||
try create(folder: postsFolder)
|
try create(folder: postsFolder)
|
||||||
try create(folder: tagsFolder)
|
try create(folder: tagsFolder)
|
||||||
}
|
}
|
||||||
@ -88,16 +86,20 @@ final class Storage {
|
|||||||
/// The folder path where the markdown and metadata files of the pages are stored (by their id/url component)
|
/// The folder path where the markdown and metadata files of the pages are stored (by their id/url component)
|
||||||
private var pagesFolder: URL { subFolder("pages") }
|
private var pagesFolder: URL { subFolder("pages") }
|
||||||
|
|
||||||
private func pageFileUrl(pageId: String) -> URL {
|
private func pageContentFileName(_ id: String, _ language: ContentLanguage) -> String {
|
||||||
pagesFolder.appending(path: pageId, directoryHint: .notDirectory)
|
"\(id)-\(language.rawValue).md"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func pageFileName(_ id: String) -> String {
|
||||||
|
id + ".json"
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pageContentUrl(pageId: String, language: ContentLanguage) -> URL {
|
private func pageContentUrl(pageId: String, language: ContentLanguage) -> URL {
|
||||||
pagesFolder.appending(path: "\(pageId)-\(language.rawValue).md", directoryHint: .notDirectory)
|
pagesFolder.appending(path: pageContentFileName(pageId, language), directoryHint: .notDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pageMetadataUrl(pageId: String) -> URL {
|
private func pageMetadataUrl(pageId: String) -> URL {
|
||||||
pagesFolder.appending(path: pageId + ".json", directoryHint: .notDirectory)
|
pagesFolder.appending(path: pageFileName(pageId), directoryHint: .notDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
@ -136,6 +138,14 @@ final class Storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deletePageFiles(notIn pages: [String]) throws {
|
||||||
|
var files = Set(pages.map(pageFileName))
|
||||||
|
for language in ContentLanguage.allCases {
|
||||||
|
files.formUnion(pages.map { pageContentFileName($0, language) })
|
||||||
|
}
|
||||||
|
try deleteFiles(in: pagesFolder, notIn: files)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Posts
|
// MARK: Posts
|
||||||
|
|
||||||
/// The folder path where the markdown files of the posts are stored (by their unique id/url component)
|
/// The folder path where the markdown files of the posts are stored (by their unique id/url component)
|
||||||
@ -164,6 +174,11 @@ final class Storage {
|
|||||||
return try post(at: url)
|
return try post(at: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deletePostFiles(notIn posts: [String]) throws {
|
||||||
|
let files = Set(posts.map { $0 + ".json" })
|
||||||
|
try deleteFiles(in: postsFolder, notIn: files)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Tags
|
// MARK: Tags
|
||||||
|
|
||||||
/// The folder path where the source images are stored (by their unique name)
|
/// The folder path where the source images are stored (by their unique name)
|
||||||
@ -187,19 +202,9 @@ final class Storage {
|
|||||||
try loadAll(in: tagsFolder)
|
try loadAll(in: tagsFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Images
|
func deleteTagFiles(notIn tags: [String]) throws {
|
||||||
|
let files = Set(tags.map { $0 + ".json" })
|
||||||
/// The folder path where the source images are stored (by their unique name)
|
try deleteFiles(in: tagsFolder, notIn: files)
|
||||||
private var imagesFolder: URL { subFolder("images") }
|
|
||||||
|
|
||||||
private func imageUrl(image: String) -> URL {
|
|
||||||
imagesFolder.appending(path: image, directoryHint: .notDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func copyImage(at url: URL, imageId: String) -> Bool {
|
|
||||||
let contentUrl = imageUrl(image: imageId)
|
|
||||||
return copy(file: url, to: contentUrl, type: "image", id: imageId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Files
|
// MARK: Files
|
||||||
@ -223,23 +228,8 @@ final class Storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Videos
|
func deleteFiles(notIn fileSet: [String]) throws {
|
||||||
|
try deleteFiles(in: filesFolder, notIn: Set(fileSet))
|
||||||
/// The folder path where source videos are stored (by their unique name)
|
|
||||||
private var videosFolder: URL { subFolder("videos") }
|
|
||||||
|
|
||||||
private func videoUrl(video: String) -> URL {
|
|
||||||
videosFolder.appending(path: video, directoryHint: .notDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func copyVideo(at url: URL, videoId: String) -> Bool {
|
|
||||||
let contentUrl = videoUrl(video: videoId)
|
|
||||||
return copy(file: url, to: contentUrl, type: "video", id: videoId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadAllVideos() throws -> [String] {
|
|
||||||
try fileNames(in: videosFolder)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Website data
|
// MARK: Website data
|
||||||
@ -259,6 +249,16 @@ final class Storage {
|
|||||||
|
|
||||||
// MARK: Writing files
|
// MARK: Writing files
|
||||||
|
|
||||||
|
private func deleteFiles(in folder: URL, notIn fileSet: Set<String>) throws {
|
||||||
|
let filesToDelete = try files(in: folder)
|
||||||
|
.filter { !fileSet.contains($0.lastPathComponent) }
|
||||||
|
|
||||||
|
for file in filesToDelete {
|
||||||
|
try fm.removeItem(at: file)
|
||||||
|
print("Deleted \(file.path())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Encode a value and write it to a file, if the content changed
|
Encode a value and write it to a file, if the content changed
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user