Add more file properties, organize storage, add video block
This commit is contained in:
36
CHDataManagement/Storage/Model/FileResourceFile.swift
Normal file
36
CHDataManagement/Storage/Model/FileResourceFile.swift
Normal 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 {
|
||||
|
||||
}
|
@ -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:
|
||||
|
@ -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) }
|
||||
|
Reference in New Issue
Block a user