ref: URL utils class

This commit is contained in:
relikd
2025-11-05 03:44:49 +01:00
parent 33cec015ab
commit fb8fa41dd0
4 changed files with 52 additions and 23 deletions

View File

@@ -37,6 +37,8 @@
547F52F72EB2CAC7002B6D5F /* Preview+Footer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F62EB2CAC7002B6D5F /* Preview+Footer.swift */; }; 547F52F72EB2CAC7002B6D5F /* Preview+Footer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F62EB2CAC7002B6D5F /* Preview+Footer.swift */; };
547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F82EB2CBAB002B6D5F /* Date+Format.swift */; }; 547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F52F82EB2CBAB002B6D5F /* Date+Format.swift */; };
549E3B9F2EBA9D2500ADFF56 /* QLAppBundle Preview Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 54442C202E378BAF008A870E /* QLAppBundle Preview Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 549E3B9F2EBA9D2500ADFF56 /* QLAppBundle Preview Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 54442C202E378BAF008A870E /* QLAppBundle Preview Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
549E3BA12EBAE7D300ADFF56 /* URL+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E3BA02EBAE7D300ADFF56 /* URL+File.swift */; };
549E3BA22EBAECD400ADFF56 /* URL+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E3BA02EBAE7D300ADFF56 /* URL+File.swift */; };
54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */; }; 54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */; };
54B6FFEE2EB6A847007397C0 /* AssetCarReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B6FFEC2EB6A847007397C0 /* AssetCarReader.swift */; }; 54B6FFEE2EB6A847007397C0 /* AssetCarReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B6FFEC2EB6A847007397C0 /* AssetCarReader.swift */; };
54B6FFEF2EB6A8E0007397C0 /* CoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */; }; 54B6FFEF2EB6A8E0007397C0 /* CoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D3A6F52EA4610B001EF4F6 /* CoreUI.framework */; };
@@ -152,6 +154,7 @@
547F52FB2EB37F10002B6D5F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 547F52FB2EB37F10002B6D5F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
547F52FC2EB37F3A002B6D5F /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; }; 547F52FC2EB37F3A002B6D5F /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
549E3B9E2EBA8FDA00ADFF56 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; }; 549E3B9E2EBA8FDA00ADFF56 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
549E3BA02EBAE7D300ADFF56 /* URL+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+File.swift"; sourceTree = "<group>"; };
54AE5BFB2EB3DB1000B4CFC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 54AE5BFB2EB3DB1000B4CFC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
54AE5BFC2EB3DB1000B4CFC7 /* QLThumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLThumbnail.entitlements; sourceTree = "<group>"; }; 54AE5BFC2EB3DB1000B4CFC7 /* QLThumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QLThumbnail.entitlements; sourceTree = "<group>"; };
54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = "<group>"; }; 54AE5BFD2EB3DB1000B4CFC7 /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = "<group>"; };
@@ -222,6 +225,7 @@
547F52E92EB2C672002B6D5F /* Preview+FileInfo.swift */, 547F52E92EB2C672002B6D5F /* Preview+FileInfo.swift */,
547F52F62EB2CAC7002B6D5F /* Preview+Footer.swift */, 547F52F62EB2CAC7002B6D5F /* Preview+Footer.swift */,
5405CF642EA1376B00613856 /* Zip.swift */, 5405CF642EA1376B00613856 /* Zip.swift */,
549E3BA02EBAE7D300ADFF56 /* URL+File.swift */,
54D3A6EF2EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift */, 54D3A6EF2EA3F49F001EF4F6 /* NSBezierPath+RoundedRect.swift */,
547F52F82EB2CBAB002B6D5F /* Date+Format.swift */, 547F52F82EB2CBAB002B6D5F /* Date+Format.swift */,
); );
@@ -540,6 +544,7 @@
547F52E82EB2C41C002B6D5F /* PreviewGenerator.swift in Sources */, 547F52E82EB2C41C002B6D5F /* PreviewGenerator.swift in Sources */,
547F52EB2EB2C672002B6D5F /* Preview+FileInfo.swift in Sources */, 547F52EB2EB2C672002B6D5F /* Preview+FileInfo.swift in Sources */,
547F52ED2EB2C822002B6D5F /* Preview+AppInfo.swift in Sources */, 547F52ED2EB2C822002B6D5F /* Preview+AppInfo.swift in Sources */,
549E3BA12EBAE7D300ADFF56 /* URL+File.swift in Sources */,
547F52F42EB2CA05002B6D5F /* Preview+Entitlements.swift in Sources */, 547F52F42EB2CA05002B6D5F /* Preview+Entitlements.swift in Sources */,
5405CF5E2EA1199B00613856 /* MetaInfo.swift in Sources */, 5405CF5E2EA1199B00613856 /* MetaInfo.swift in Sources */,
547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */, 547F52F92EB2CBAB002B6D5F /* Date+Format.swift in Sources */,
@@ -556,6 +561,7 @@
547899722EB38F3D00F96B80 /* MetaInfo.swift in Sources */, 547899722EB38F3D00F96B80 /* MetaInfo.swift in Sources */,
547899732EB38F3D00F96B80 /* NSBezierPath+RoundedRect.swift in Sources */, 547899732EB38F3D00F96B80 /* NSBezierPath+RoundedRect.swift in Sources */,
54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */, 54AE5BFF2EB3DB1000B4CFC7 /* ThumbnailProvider.swift in Sources */,
549E3BA22EBAECD400ADFF56 /* URL+File.swift in Sources */,
547899752EB38F3D00F96B80 /* AppIcon.swift in Sources */, 547899752EB38F3D00F96B80 /* AppIcon.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@@ -13,12 +13,9 @@ class PreviewViewController: NSViewController, QLPreviewingController {
/// Load resource file either from user documents dir (if exists) or app bundle (default). /// Load resource file either from user documents dir (if exists) or app bundle (default).
func bundleFile(filename: String, ext: String) throws -> String { func bundleFile(filename: String, ext: String) throws -> String {
if let appSupport = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { if let userFile = URL.UserModDir?.appendingPathComponent(filename + "." + ext, isDirectory: false), userFile.exists() {
let override = appSupport.appendingPathComponent(filename + "." + ext) return try String(contentsOf: userFile, encoding: .utf8)
if FileManager.default.fileExists(atPath: override.path) { // else: do NOT copy! Breaks on future updates
return try String(contentsOfFile: override.path, encoding: .utf8)
}
// else: do NOT copy! Breaks on future updates
} }
// else, load bundle file // else, load bundle file
let path = Bundle.main.url(forResource: filename, withExtension: ext) let path = Bundle.main.url(forResource: filename, withExtension: ext)

View File

@@ -1,27 +1,12 @@
import Foundation import Foundation
extension PreviewGenerator { extension PreviewGenerator {
/// Calculate file / folder size.
private func getFileSize(_ path: String) -> Int64 {
var isDir: ObjCBool = false
FileManager.default.fileExists(atPath: path, isDirectory: &isDir)
if !isDir.boolValue {
return try! FileManager.default.attributesOfItem(atPath: path)[.size] as! Int64
}
var fileSize: Int64 = 0
for child in try! FileManager.default.subpathsOfDirectory(atPath: path) {
fileSize += try! FileManager.default.attributesOfItem(atPath: path + "/" + child)[.size] as! Int64
}
return fileSize
}
/// Process meta information about the file itself. Like file size and last modification. /// Process meta information about the file itself. Like file size and last modification.
mutating func procFileInfo(_ url: URL) { mutating func procFileInfo(_ url: URL) {
let attrs = try? FileManager.default.attributesOfItem(atPath: url.path)
self.apply([ self.apply([
"FileName": escapeXML(url.lastPathComponent), "FileName": escapeXML(url.lastPathComponent),
"FileSize": ByteCountFormatter.string(fromByteCount: getFileSize(url.path), countStyle: .file), "FileSize": url.fileSizeHuman(),
"FileModified": (attrs?[.modificationDate] as? Date)?.mediumFormat() ?? "", "FileModified": url.modificationDate()?.mediumFormat() ?? "",
]) ])
} }
} }
@@ -36,3 +21,32 @@ private func escapeXML(_ stringToEscape: String) -> String {
.replacingOccurrences(of: "<", with: "&lt;") .replacingOccurrences(of: "<", with: "&lt;")
.replacingOccurrences(of: ">", with: "&gt;") .replacingOccurrences(of: ">", with: "&gt;")
} }
extension URL {
/// Last modification date of file (or folder)
@inlinable func modificationDate() -> Date? {
(try? FileManager.default.attributesOfItem(atPath: self.path))?[.modificationDate] as? Date
}
/// Calls `fileSize()`. Will convert `Int` to human readable `String`.
func fileSizeHuman() -> String {
ByteCountFormatter.string(fromByteCount: self.fileSize(), countStyle: .file)
}
// MARK: - private methods
/// Calculate file or folder size.
private func fileSize() -> Int64 {
var isDir: ObjCBool = false
FileManager.default.fileExists(atPath: self.path, isDirectory: &isDir)
if !isDir.boolValue {
return try! FileManager.default.attributesOfItem(atPath: self.path)[.size] as! Int64
}
var fileSize: Int64 = 0
for child in try! FileManager.default.subpathsOfDirectory(atPath: self.path) {
fileSize += try! FileManager.default.attributesOfItem(atPath: self.path + "/" + child)[.size] as! Int64
}
return fileSize
}
}

12
src/URL+File.swift Normal file
View File

@@ -0,0 +1,12 @@
import Foundation
extension URL {
/// Folder where user can mofifications to html template
static let UserModDir: URL? =
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
/// Returns `true` if file or folder exists.
@inlinable func exists() -> Bool {
FileManager.default.fileExists(atPath: self.path)
}
}