diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index 7d3f9b9..f60852e 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ E25DA59B2D024A2B00AEF16D /* DateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA59A2D024A2900AEF16D /* DateItem.swift */; }; E26C300F2E634B3A00FEB26D /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26C300E2E634B3A00FEB26D /* TimeInterval+Extensions.swift */; }; E2720B882DF38BB700FDB543 /* Insert+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2720B872DF38BB200FDB543 /* Insert+Video.swift */; }; + E29A577E2E9E444800B19DA3 /* ToolSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29A577D2E9E444000B19DA3 /* ToolSettings.swift */; }; E29D31202D0320E70051B7F4 /* ContentLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D311F2D0320E20051B7F4 /* ContentLabels.swift */; }; E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31212D0363FA0051B7F4 /* ContentButtons.swift */; }; E29D31242D0366860051B7F4 /* TagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31232D0366820051B7F4 /* TagList.swift */; }; @@ -415,6 +416,7 @@ E25DA59A2D024A2900AEF16D /* DateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateItem.swift; sourceTree = ""; }; E26C300E2E634B3A00FEB26D /* TimeInterval+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Extensions.swift"; sourceTree = ""; }; E2720B872DF38BB200FDB543 /* Insert+Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Insert+Video.swift"; sourceTree = ""; }; + E29A577D2E9E444000B19DA3 /* ToolSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolSettings.swift; sourceTree = ""; }; E29D311F2D0320E20051B7F4 /* ContentLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentLabels.swift; sourceTree = ""; }; E29D31212D0363FA0051B7F4 /* ContentButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentButtons.swift; sourceTree = ""; }; E29D31232D0366820051B7F4 /* TagList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagList.swift; sourceTree = ""; }; @@ -781,6 +783,7 @@ E25DA53B2D0042EA00AEF16D /* Settings */ = { isa = PBXGroup; children = ( + E29A577D2E9E444000B19DA3 /* ToolSettings.swift */, E2FD1D2D2D37180600B48627 /* GeneralSettings.swift */, E2FE0F392D2B3E4E002963B7 /* AudioPlayerSettings.swift */, E2FE0F6D2D2D3685002963B7 /* LocalizedAudioPlayerSettings.swift */, @@ -1389,7 +1392,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1540; - LastUpgradeCheck = 1630; + LastUpgradeCheck = 2600; TargetAttributes = { E2DD046F2C276F31003BFF1F = { CreatedOnToolsVersion = 15.4; @@ -1663,6 +1666,7 @@ E2F3B3982DC54F9400CFA712 /* ChangeObservingItem.swift in Sources */, E21850372CFCA55F0090B18B /* LocalizedPostSettings.swift in Sources */, E2FD1D642D47EF4200B48627 /* DetailListItem.swift in Sources */, + E29A577E2E9E444800B19DA3 /* ToolSettings.swift in Sources */, E2FE0F0B2D2689FF002963B7 /* FeedGeneratorSource.swift in Sources */, E2DD04742C276F31003BFF1F /* MainView.swift in Sources */, E20BCCAD2D53F48100B8DBEB /* IssueStatus.swift in Sources */, @@ -1811,6 +1815,7 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -1867,6 +1872,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; @@ -1899,7 +1905,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 1.5; + MARKETING_VERSION = 1.6; PRODUCT_BUNDLE_IDENTIFIER = de.christophhagen.CHDataManagement; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; @@ -1938,7 +1944,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 1.5; + MARKETING_VERSION = 1.6; PRODUCT_BUNDLE_IDENTIFIER = de.christophhagen.CHDataManagement; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; diff --git a/CHDataManagement.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CHDataManagement.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 94fc30c..b2c7f7b 100644 --- a/CHDataManagement.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CHDataManagement.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "6a373ae0a2cc4ad97293e2b13e76aa783451436d6a17beb2295cd5e9b2067122", + "originHash" : "f8a1ac1b6fd2d65b9edf0e288c06780ac6a71414f18592b869bb082fb8c7690d", "pins" : [ { "identity" : "async-http-client", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/christophhagen/BinaryCodable", "state" : { - "revision" : "4febea33ee5d813fd9c94c9158be6c85472480d2", - "version" : "3.1.0" + "revision" : "53f057050f3c78a1997ed0218337fd92d2eba2b5", + "version" : "3.1.1" } }, { diff --git a/CHDataManagement.xcodeproj/xcshareddata/xcschemes/CHDataManagement.xcscheme b/CHDataManagement.xcodeproj/xcshareddata/xcschemes/CHDataManagement.xcscheme index 9df1e76..ee03c8c 100644 --- a/CHDataManagement.xcodeproj/xcshareddata/xcschemes/CHDataManagement.xcscheme +++ b/CHDataManagement.xcodeproj/xcshareddata/xcschemes/CHDataManagement.xcscheme @@ -1,6 +1,6 @@ Double { - let factor = _math.pow(10.0, Double(decimals)) + let factor = Double.pow(10.0, Double(decimals)) return (self * factor).rounded() / factor } } diff --git a/CHDataManagement/Generator/ImageGenerator.swift b/CHDataManagement/Generator/ImageGenerator.swift index abf8983..e1d8816 100644 --- a/CHDataManagement/Generator/ImageGenerator.swift +++ b/CHDataManagement/Generator/ImageGenerator.swift @@ -188,8 +188,7 @@ final class ImageGenerator { // TODO: Run in security scope let process = Process() - #warning("TODO: Move avifenc path to settings") - process.launchPath = "/opt/homebrew/bin/avifenc" // Adjust based on installation + process.launchPath = settings.tools.avifencPath process.arguments = ["-q", "\(quality)", imagePath, generatedImagePath] let pipe = Pipe() diff --git a/CHDataManagement/Model/ContentLanguage.swift b/CHDataManagement/Model/ContentLanguage.swift index 9f22fa8..db49afc 100644 --- a/CHDataManagement/Model/ContentLanguage.swift +++ b/CHDataManagement/Model/ContentLanguage.swift @@ -33,6 +33,12 @@ extension ContentLanguage: Comparable { } } +extension ContentLanguage: CustomStringConvertible { + + var description: String { + rawValue + } +} extension ContentLanguage { diff --git a/CHDataManagement/Model/FileResource.swift b/CHDataManagement/Model/FileResource.swift index 2d57e2b..0732307 100644 --- a/CHDataManagement/Model/FileResource.swift +++ b/CHDataManagement/Model/FileResource.swift @@ -354,7 +354,7 @@ final class FileResource: Item, LocalizedItem { } private func determineVideoType() -> String? { - #warning("TODO: Move ffmpeg path to settings") + let ffmpegPath = content.settings.tools.ffprobePath switch type { case .webm: return "video/webm" @@ -371,7 +371,7 @@ final class FileResource: Item, LocalizedItem { let process = Process() let arguments = "-v error -select_streams v:0 -show_entries stream=codec_tag_string -of default=noprint_wrappers=1:nokey=1 \(path.path())" .components(separatedBy: " ") - process.launchPath = "/opt/homebrew/bin/ffprobe" + process.launchPath = ffmpegPath process.arguments = Array(arguments) let pipe = Pipe() diff --git a/CHDataManagement/Model/Settings/Settings.swift b/CHDataManagement/Model/Settings/Settings.swift index 3945c5e..fcf355c 100644 --- a/CHDataManagement/Model/Settings/Settings.swift +++ b/CHDataManagement/Model/Settings/Settings.swift @@ -22,6 +22,9 @@ final class Settings: ChangeObservableItem { @Published var audioPlayer: AudioPlayerSettings + @Published + var tools: ToolSettings + weak var content: Content? var cancellables: Set = [] @@ -31,13 +34,15 @@ final class Settings: ChangeObservableItem { navigation: NavigationSettings, posts: PostSettings, pages: PageSettings, - audioPlayer: AudioPlayerSettings) { + audioPlayer: AudioPlayerSettings, + tools: ToolSettings) { self.general = general self.paths = paths self.navigation = navigation self.posts = posts self.pages = pages self.audioPlayer = audioPlayer + self.tools = tools observeChildChanges() } @@ -58,6 +63,7 @@ final class Settings: ChangeObservableItem { observe(posts) observe(pages) observe(audioPlayer) + observe(tools) } } @@ -72,7 +78,8 @@ extension Settings { navigation: .init(context: context, data: data.navigation), posts: .init(context: context, data: data.posts), pages: .init(context: context, data: data.pages), - audioPlayer: .init(context: context, data: data.audioPlayer)) + audioPlayer: .init(context: context, data: data.audioPlayer), + tools: .init(context: context, data: data.tools)) content = context.content } @@ -84,7 +91,8 @@ extension Settings { posts: posts.data, pages: pages.data, audioPlayer: audioPlayer.data, - tagOverview: tagOverview?.data) + tagOverview: tagOverview?.data, + tools: tools.data) } struct Data: Codable, Equatable { @@ -95,6 +103,7 @@ extension Settings { let pages: PageSettings.Data let audioPlayer: AudioPlayerSettings.Data let tagOverview: Tag.Data? + let tools: ToolSettings.Data } func saveToDisk(_ data: Data) -> Bool { @@ -110,7 +119,8 @@ extension Settings { navigation: .default, posts: .default, pages: .default, - audioPlayer: .default) + audioPlayer: .default, + tools: .default) } extension GeneralSettings { @@ -195,3 +205,11 @@ extension PageSettings { emptyPageText: "This page is empty")) } } + +extension ToolSettings { + + static var `default`: ToolSettings { + .init(ffprobePath: "/opt/homebrew/bin/ffprobe", + avifencPath: "/opt/homebrew/bin/avifenc") + } +} diff --git a/CHDataManagement/Model/Settings/ToolSettings.swift b/CHDataManagement/Model/Settings/ToolSettings.swift new file mode 100644 index 0000000..d7dac60 --- /dev/null +++ b/CHDataManagement/Model/Settings/ToolSettings.swift @@ -0,0 +1,39 @@ +import Foundation + +final class ToolSettings: ObservableObject { + + /// The items to show in the navigation bar + @Published + var ffprobePath: String + + @Published + var avifencPath: String + + init(ffprobePath: String, + avifencPath: String) { + self.ffprobePath = ffprobePath + self.avifencPath = avifencPath + } +} + +// MARK: Storage + +extension ToolSettings { + + convenience init(context: LoadingContext, data: ToolSettings.Data) { + self.init( + ffprobePath: data.ffprobePath, + avifencPath: data.avifencPath) + } + + struct Data: Codable, Equatable { + let ffprobePath: String + let avifencPath: String + } + + var data: Data { + .init( + ffprobePath: ffprobePath, + avifencPath: avifencPath) + } +} diff --git a/CHDataManagement/Page Elements/ContentElements/Icons/GeneralIcons.swift b/CHDataManagement/Page Elements/ContentElements/Icons/GeneralIcons.swift index 9d286b2..a02ec3b 100644 --- a/CHDataManagement/Page Elements/ContentElements/Icons/GeneralIcons.swift +++ b/CHDataManagement/Page Elements/ContentElements/Icons/GeneralIcons.swift @@ -133,3 +133,33 @@ extension Icon { """ } } + +extension Icon { + + struct Pencil: ContentIcon { + + static let id = "icon-pencil" + + static let attributes = "viewBox='0 0 16 16' fill='currentColor'" + + static let content = + """ + + """ + } +} + +extension Icon { + + struct PersonPlus: ContentIcon { + + static let id = "person-plus" + + static let attributes = "viewBox='0 0 16 16' fill='currentColor'" + + static let content = + """ + + """ + } +} diff --git a/CHDataManagement/Page Elements/ContentElements/Icons/PageIcon.swift b/CHDataManagement/Page Elements/ContentElements/Icons/PageIcon.swift index d1bf110..7c40452 100644 --- a/CHDataManagement/Page Elements/ContentElements/Icons/PageIcon.swift +++ b/CHDataManagement/Page Elements/ContentElements/Icons/PageIcon.swift @@ -21,6 +21,10 @@ enum PageIcon: String, CaseIterable { case bellSlash = "bell-slash" + case pencil + + case personPlus = "person-plus" + // MARK: Statistics case statisticsTime = "time" @@ -97,6 +101,8 @@ enum PageIcon: String, CaseIterable { case .leftRightArrow: Icon.LeftRightArrow.self case .bell: Icon.Bell.self case .bellSlash: Icon.BellSlash.self + case .pencil: Icon.Pencil.self + case .personPlus: Icon.PersonPlus.self } } @@ -111,6 +117,8 @@ enum PageIcon: String, CaseIterable { case .video: "Video" case .bell: "Bell" case .bellSlash: "Bell With Slash" + case .pencil: "Pencil" + case .personPlus: "Person Plus" case .leftRightArrow: "LeftRightArrow" case .buttonExternalLink: "Button: External Link" case .buttonGitLink: "Button: Git Link" diff --git a/CHDataManagement/Storage/SaveState.swift b/CHDataManagement/Storage/SaveState.swift index cb5cb54..14645d2 100644 --- a/CHDataManagement/Storage/SaveState.swift +++ b/CHDataManagement/Storage/SaveState.swift @@ -1,5 +1,5 @@ import SFSafeSymbols -import SwiftUICore +import SwiftUI enum SaveState { case storageNotInitialized diff --git a/CHDataManagement/Storage/SecurityBookmark.swift b/CHDataManagement/Storage/SecurityBookmark.swift index 23bb1b0..6291157 100644 --- a/CHDataManagement/Storage/SecurityBookmark.swift +++ b/CHDataManagement/Storage/SecurityBookmark.swift @@ -183,7 +183,7 @@ struct SecurityBookmark { with(relativePath: relativeSource) { source in if !exists(source) { if !failIfMissing { return true } - reportError("Failed to move \(relativeSource): File does not exist") + reportError("Failed to move \(relativeSource): File \(source) does not exist") return false } diff --git a/CHDataManagement/Views/Generation/GenerationContentView.swift b/CHDataManagement/Views/Generation/GenerationContentView.swift index 0944fdd..42cd1f6 100644 --- a/CHDataManagement/Views/Generation/GenerationContentView.swift +++ b/CHDataManagement/Views/Generation/GenerationContentView.swift @@ -82,7 +82,7 @@ struct GenerationContentView: View { statusWhenNonEmpty: .warning, items: $content.results.emptyPages) { pageId in HStack { - Text("\(pageId.pageId) (\(pageId.language))") + Text("\(pageId.pageId) (\(pageId.language.description))") Spacer() Button("Show") { show(page: pageId.pageId, diff --git a/CHDataManagement/Views/Generation/IssueStatus.swift b/CHDataManagement/Views/Generation/IssueStatus.swift index 4e551c9..f6175f8 100644 --- a/CHDataManagement/Views/Generation/IssueStatus.swift +++ b/CHDataManagement/Views/Generation/IssueStatus.swift @@ -1,5 +1,5 @@ import SFSafeSymbols -import SwiftUICore +import SwiftUI enum IssueStatus { case nominal