Add more file properties, organize storage, add video block

This commit is contained in:
Christoph Hagen
2025-01-06 15:31:19 +01:00
parent 96c0a75c2f
commit 6cf310d849
25 changed files with 712 additions and 219 deletions

View File

@ -0,0 +1,36 @@
import Foundation
/**
This struct holds metadata about a file resource that is stored in the content folder.
*/
struct FileResourceFile {
/// The file/image description in German
let englishDescription: String?
/// The file/image description in English
let germanDescription: String?
/// The list of generated image versions for this image
let generatedImages: [String]?
/// A custom file path in the output folder where this file is located
let customOutputPath: String?
/// A version string of this resource, mostly for assets
let version: String?
/// A URL where the resource was copied/downloaded from
let sourceUrl: String?
/// The date when the file was added
let addedDate: Date
/// The date when the file was last modified
let modifiedDate: Date
}
extension FileResourceFile: Codable {
}

View File

@ -162,13 +162,17 @@ struct SecurityBookmark {
ifFileExists overwrite: OverwriteBehaviour = .fail) -> Bool {
with(relativePath: relativeSource) { source in
if !exists(source) {
return failIfMissing
if !failIfMissing { return true }
print("ContentScope: Could not find file \(relativeSource) to move")
return false
}
let destination = url.appending(path: relativeDestination)
if exists(destination) {
switch overwrite {
case .fail: return false
case .fail:
print("ContentScope: Could not move file \(relativeSource), file exists")
return false
case .skip: return true
case .write: break
case .writeIfChanged:

View File

@ -18,18 +18,14 @@ final class Storage: ObservableObject {
private let filesFolderName = "files"
private let fileInfoFolderName = "file metadata"
private let pagesFolderName = "pages"
private let postsFolderName = "posts"
private let tagsFolderName = "tags"
private let externalFileListName = "external-files.json"
private let fileDescriptionFilename = "file-descriptions.json"
private let generatedImagesListName = "generated-images.json"
private let outputPathFileName = "outputPath.bin"
private let settingsDataFileName = "settings.json"
@ -118,7 +114,8 @@ final class Storage: ObservableObject {
func move(page pageId: String, to newId: String) -> Bool {
guard let contentScope else { return false }
guard contentScope.move(pageFileName(pageId), to: pageFileName(newId)) else {
guard contentScope.move(pageMetadataPath(page: pageId), to: pageMetadataPath(page: newId)) else {
print("Failed to move page file \(pageId)")
return false
}
// Move the existing content files
@ -127,9 +124,10 @@ final class Storage: ObservableObject {
// Copy as many files as possible, since metadata was already moved
// Don't fail early
if !contentScope.move(
pageContentFileName(pageId, language),
to: pageContentFileName(newId, language),
pageContentPath(page: pageId, language: language),
to: pageContentPath(page: newId, language: language),
failIfMissing: false) {
print("Failed to move content file \(language) of page \(pageId)")
result = false
}
}
@ -214,17 +212,6 @@ final class Storage: ObservableObject {
return contentScope.move(tagFilePath(tag: tagId), to: tagFilePath(tag: newId))
}
// MARK: File descriptions
func loadFileDescriptions() -> [FileDescriptions]? {
contentScope?.decode(at: fileDescriptionFilename)
}
func save(fileDescriptions: [FileDescriptions]) -> Bool {
guard let contentScope else { return false }
return contentScope.encode(fileDescriptions, to: fileDescriptionFilename)
}
// MARK: Files
func size(of file: String) -> Int? {
@ -241,9 +228,22 @@ final class Storage: ObservableObject {
}
/**
Delete a file resource from the content folder
Completely delete a file resource from the content folder
*/
func delete(file fileId: String) -> Bool {
guard let contentScope else {
return false
}
guard contentScope.deleteFile(at: filePath(file: fileId)) else {
return false
}
return contentScope.deleteFile(at: fileInfoPath(file: fileId))
}
/**
Delete a file resource from the content folder, making it an external file
*/
func removeFileContent(file fileId: String) -> Bool {
guard let contentScope else {
return false
}
@ -266,6 +266,11 @@ final class Storage: ObservableObject {
filesFolderName + "/" + fileId
}
/// The path to a metadata file for a file resource
private func fileInfoPath(file fileId: String) -> String {
fileInfoFolderName + "/" + fileId + ".json"
}
@discardableResult
func deleteInOutputFolder(_ relativePath: String) -> Bool {
guard let outputScope else { return false }
@ -298,8 +303,30 @@ final class Storage: ObservableObject {
to: relativeOutputPath, of: outputScope)
}
func loadAllFiles() -> [String]? {
contentScope?.fileNames(inRelativeFolder: filesFolderName)
/**
Load the file metadata from the content folder.
- Returns: A dictionary with the file ids as keys and the metadata file as a value.
*/
func loadAllFiles() -> [String : (data: FileResourceFile, isExternal: Bool)]? {
guard let contentScope else { return nil }
guard let list: [String : FileResourceFile] = contentScope.decodeJsonFiles(in: fileInfoFolderName) else {
return nil
}
guard let existingFiles = contentScope.fileNames(inRelativeFolder: filesFolderName).map(Set.init) else {
return nil
}
return Dictionary(uniqueKeysWithValues: list.map { fileId, data in
let isExternal = !existingFiles.contains(fileId)
return (fileId, (data: data, isExternal: isExternal))
})
}
@discardableResult
func save(fileInfo: FileResourceFile, for fileId: String) -> Bool {
guard let contentScope else { return false }
let path = fileInfoPath(file: fileId)
return contentScope.encode(fileInfo, to: path)
}
/**
@ -327,18 +354,6 @@ final class Storage: ObservableObject {
return contentScope.readData(at: path)
}
// MARK: External file list
func loadExternalFileList() -> [String]? {
guard let contentScope else { return nil }
return contentScope.decode(at: externalFileListName)
}
func save(externalFileList: [String]) -> Bool {
guard let contentScope else { return false }
return contentScope.encode(externalFileList.sorted(), to: externalFileListName)
}
// MARK: Settings
func loadSettings() -> SettingsFile? {
@ -353,17 +368,8 @@ final class Storage: ObservableObject {
// MARK: Image generation data
func loadListOfGeneratedImages() -> [String : Set<String>]? {
guard let contentScope else { return nil }
return contentScope.decode(at: generatedImagesListName)
}
func save(listOfGeneratedImages: [String : Set<String>]) -> Bool {
guard let contentScope else { return false }
return contentScope.encode(listOfGeneratedImages, to: generatedImagesListName)
}
func calculateImages(generatedBy imageSet: Set<String>, in folder: String) -> [String : Set<String>] {
#warning("TODO: Move to file resource")
guard let outputScope else { return [:] }
guard let allImages = outputScope.fileNames(inRelativeFolder: folder) else {
print("Failed to get list of generated images in output folder")
@ -375,7 +381,6 @@ final class Storage: ObservableObject {
}
print("Found \(allImages.count) generated images")
let images = Set(allImages)
#warning("TODO: Fix counting generated images")
return imageSet.reduce(into: [:]) { result, imageName in
let prefix = imageName.fileNameWithoutExtension + "@"
let versions = images.filter { $0.hasPrefix(prefix) }