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.
81 lines
2.9 KiB
Swift
81 lines
2.9 KiB
Swift
import AppKit
|
|
import Foundation
|
|
|
|
/// Headless conversion, used for verification and scripting:
|
|
///
|
|
/// Konverter --convert a.png b.pdf --to jpg
|
|
/// Konverter --convert clip.mov --tool trimVideo --param start=0:02 --param end=0:05
|
|
enum CLI {
|
|
static func run() -> Int32 {
|
|
let arguments = Array(CommandLine.arguments.dropFirst())
|
|
|
|
guard let convertIndex = arguments.firstIndex(of: "--convert") else {
|
|
FileHandle.standardError.write(Data("usage: Konverter --convert <files…> --to <ext>\n".utf8))
|
|
return 2
|
|
}
|
|
|
|
var files: [URL] = []
|
|
var target: Format?
|
|
var tool: ToolID?
|
|
var params = ToolParams()
|
|
|
|
var index = convertIndex + 1
|
|
while index < arguments.count {
|
|
let argument = arguments[index]
|
|
switch argument {
|
|
case "--to":
|
|
index += 1
|
|
if index < arguments.count { target = Format(rawValue: arguments[index]) }
|
|
case "--tool":
|
|
index += 1
|
|
if index < arguments.count { tool = ToolID(rawValue: arguments[index]) }
|
|
case "--param":
|
|
index += 1
|
|
if index < arguments.count {
|
|
let pair = arguments[index].split(separator: "=", maxSplits: 1).map(String.init)
|
|
if pair.count == 2 { params[pair[0]] = pair[1] }
|
|
}
|
|
default:
|
|
if !argument.hasPrefix("--") {
|
|
files.append(URL(fileURLWithPath: argument))
|
|
}
|
|
}
|
|
index += 1
|
|
}
|
|
|
|
guard !files.isEmpty else {
|
|
FileHandle.standardError.write(Data("no input files\n".utf8))
|
|
return 2
|
|
}
|
|
|
|
do {
|
|
if let tool {
|
|
let outcome = try ToolRunner.run(tool: tool, inputs: files, params: params)
|
|
if let message = outcome.message {
|
|
print(message)
|
|
}
|
|
for output in outcome.outputs { print(output.path) }
|
|
return outcome.outputs.isEmpty && outcome.message == nil ? 1 : 0
|
|
}
|
|
|
|
guard let target else {
|
|
FileHandle.standardError.write(Data("--to is required\n".utf8))
|
|
return 2
|
|
}
|
|
|
|
var written: [URL] = []
|
|
for file in files {
|
|
let destination = OutputNaming.destination(for: file, format: target)
|
|
let outputs = try Converter.convert(source: file, to: target, at: destination)
|
|
written.append(contentsOf: outputs)
|
|
}
|
|
for output in written { print(output.path) }
|
|
return written.isEmpty ? 1 : 0
|
|
} catch {
|
|
let message = (error as? LocalizedError)?.errorDescription ?? error.localizedDescription
|
|
FileHandle.standardError.write(Data((message + "\n").utf8))
|
|
return 1
|
|
}
|
|
}
|
|
}
|