Drag files in Finder, hold a modifier, and drop on a wheel of outputs. Conversions run entirely on-device through ImageIO, PDFKit, AVFoundation, the bundled CLI tools, and optional ffmpeg/LibreOffice. Includes a --self-test suite (41 checks) and a --convert CLI mode so the conversion matrix can be verified without the UI.
56 lines
2.3 KiB
Swift
56 lines
2.3 KiB
Swift
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: " · ")
|
|
}
|
|
}
|