Add Konverter: a local-only menu bar file converter
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.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
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: " · ")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user