feat: add a native CLI launcher for local and YouTube playback
Build .build/terebi beside the app. Parse a single local file or YouTube URL, --url, --, and help; reject invalid commands before GUI launch. Resolve the adjacent app through symlinks and launch through NSWorkspace, reusing its process/window and saved progress. Launch the app without media before delivering URL events. Raw media argv suppress SwiftUI's initial singleton window. Own warm document and internet Apple events because SwiftUI's default external-event routing closes that window; retain initial Finder document delivery to create it. Verified actual CLI 0/64/69 exit behavior, PATH/symlink discovery from another directory, cold local/YouTube playback, warm local/YouTube replacement in the same native window/process, resume, Finder-style cold launch, and make all check.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import AppKit
|
||||
import Darwin
|
||||
|
||||
@main
|
||||
@MainActor
|
||||
enum TerebiCLI {
|
||||
static func main() async {
|
||||
let command: PlayerCommand
|
||||
do {
|
||||
command = try PlayerCommand.parse(Array(CommandLine.arguments.dropFirst()))
|
||||
} catch {
|
||||
fail(error.localizedDescription, code: 64)
|
||||
}
|
||||
switch command {
|
||||
case .help:
|
||||
print(PlayerCommand.usage)
|
||||
case .launch(let url):
|
||||
guard let executable = Bundle.main.executableURL else {
|
||||
fail("Could not locate the CLI executable.", code: 69)
|
||||
}
|
||||
let app = executable.resolvingSymlinksInPath().deletingLastPathComponent()
|
||||
.appendingPathComponent("terebi.app", isDirectory: true)
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
do {
|
||||
// Launch without media first so SwiftUI creates its singleton window.
|
||||
_ = try await NSWorkspace.shared.openApplication(at: app, configuration: configuration)
|
||||
if let url {
|
||||
_ = try await NSWorkspace.shared.open([url], withApplicationAt: app, configuration: configuration)
|
||||
}
|
||||
} catch {
|
||||
fail("Could not launch the player: \(error.localizedDescription)", code: 69)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func fail(_ message: String, code: Int32) -> Never {
|
||||
FileHandle.standardError.write(Data("terebi: \(message)\n".utf8))
|
||||
exit(code)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user