import Foundation /// Optional external helpers. Konverter works without them — they only widen /// the conversion matrix (formats Apple's frameworks cannot write) and are /// detected once at launch. enum Toolchain { private static let homebrewPaths = ["/opt/homebrew/bin", "/usr/local/bin"] /// ffmpeg/ffprobe: MKV, WebM, AVI, WMV, MP3, FLAC, OGG, Opus, WMA. static let ffmpeg: String? = Shell.find("ffmpeg", extraPaths: homebrewPaths) static let ffprobe: String? = Shell.find("ffprobe", extraPaths: homebrewPaths) /// LibreOffice: PPT/PPTX rendering, XLS/XLSX/ODS, PAGES, Keynote. static let soffice: String? = { let candidates = [ "/Applications/LibreOffice.app/Contents/MacOS/soffice", "/Applications/LibreOffice.app/Contents/MacOS/soffice.bin" ] for candidate in candidates where FileManager.default.isExecutableFile(atPath: candidate) { return candidate } return Shell.find("soffice", extraPaths: homebrewPaths) }() static var hasFFmpeg: Bool { ffmpeg != nil } static var hasLibreOffice: Bool { soffice != nil } /// `curl`-free, sandbox-free best-effort image rasteriser for SVG and other /// formats ImageIO refuses to decode but Quick Look understands. static let qlmanage: String? = Shell.find("qlmanage", extraPaths: ["/usr/bin"]) static let ditto = "/usr/bin/ditto" static let tar = "/usr/bin/tar" static let gzip = "/usr/bin/gzip" static let gunzip = "/usr/bin/gunzip" static let textutil = "/usr/bin/textutil" static let afconvert = "/usr/bin/afconvert" static let zip = "/usr/bin/zip" static let unzip = "/usr/bin/unzip" /// Human readable summary for the About/Settings pane. static var summary: String { var parts: [String] = ["Core (ImageIO, PDFKit, AVFoundation)"] if hasFFmpeg { parts.append("ffmpeg") } if hasLibreOffice { parts.append("LibreOffice") } return parts.joined(separator: " · ") } static var missingToolAdvice: String? { var advice: [String] = [] if !hasFFmpeg { advice.append("brew install ffmpeg") } if !hasLibreOffice { advice.append("brew install --cask libreoffice") } return advice.isEmpty ? nil : advice.joined(separator: " · ") } }