feat: load optional XDG TOML configuration with session overrides
This commit is contained in:
@@ -2,12 +2,12 @@ import Foundation
|
||||
|
||||
enum PlayerCommand {
|
||||
case help
|
||||
case launch(URL?, newInstance: Bool)
|
||||
case launch(URL?, newInstance: Bool, noConfig: Bool)
|
||||
|
||||
static let usage = """
|
||||
Usage: terebi [--new-instance] [FILE | YOUTUBE_URL]
|
||||
terebi [--new-instance] --url YOUTUBE_URL
|
||||
terebi [--new-instance] -- FILE
|
||||
Usage: terebi [--new-instance] [--no-config] [FILE | YOUTUBE_URL]
|
||||
terebi [--new-instance] [--no-config] --url YOUTUBE_URL
|
||||
terebi [--new-instance] [--no-config] -- FILE
|
||||
terebi --help
|
||||
|
||||
Open one local media file or YouTube video and start playback, resuming saved
|
||||
@@ -15,6 +15,7 @@ enum PlayerCommand {
|
||||
Quote paths and URLs containing spaces or shell metacharacters.
|
||||
|
||||
--new-instance Launch a separate player, leaving existing playback untouched.
|
||||
--no-config Use built-in settings instead of config.toml on a fresh launch.
|
||||
--url URL Open a YouTube video URL.
|
||||
-- Treat the following argument as a filename, even with a leading '-'.
|
||||
-h, --help Show this help without launching the player.
|
||||
@@ -26,13 +27,23 @@ enum PlayerCommand {
|
||||
|
||||
static func parse(_ arguments: [String], directory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)) throws -> PlayerCommand {
|
||||
var inputs = arguments
|
||||
let newInstance = inputs.first == "--new-instance"
|
||||
if newInstance { inputs.removeFirst() }
|
||||
if inputs.isEmpty { return .launch(nil, newInstance: newInstance) }
|
||||
var newInstance = false
|
||||
var noConfig = false
|
||||
while let option = inputs.first, option == "--new-instance" || option == "--no-config" {
|
||||
if option == "--new-instance" {
|
||||
guard !newInstance else { throw ArgumentError("--new-instance may only be supplied once.") }
|
||||
newInstance = true
|
||||
} else {
|
||||
guard !noConfig else { throw ArgumentError("--no-config may only be supplied once.") }
|
||||
noConfig = true
|
||||
}
|
||||
inputs.removeFirst()
|
||||
}
|
||||
if inputs.isEmpty { return .launch(nil, newInstance: newInstance, noConfig: noConfig) }
|
||||
if inputs == ["--help"] || inputs == ["-h"] { return .help }
|
||||
if inputs.first == "--url" {
|
||||
guard inputs.count == 2 else { throw ArgumentError("--url requires exactly one YouTube URL.") }
|
||||
return .launch(try YouTubeResolver.validateURL(inputs[1]), newInstance: newInstance)
|
||||
return .launch(try YouTubeResolver.validateURL(inputs[1]), newInstance: newInstance, noConfig: noConfig)
|
||||
}
|
||||
let filenameOnly = inputs.first == "--"
|
||||
if filenameOnly {
|
||||
@@ -52,7 +63,7 @@ enum PlayerCommand {
|
||||
}
|
||||
file = url
|
||||
} else {
|
||||
return .launch(try YouTubeResolver.validateURL(input), newInstance: newInstance)
|
||||
return .launch(try YouTubeResolver.validateURL(input), newInstance: newInstance, noConfig: noConfig)
|
||||
}
|
||||
} else {
|
||||
let base = directory.hasDirectoryPath ? directory : URL(fileURLWithPath: directory.path, isDirectory: true)
|
||||
@@ -62,7 +73,7 @@ enum PlayerCommand {
|
||||
FileManager.default.isReadableFile(atPath: file.path) else {
|
||||
throw ArgumentError("File does not exist or is not a readable regular file.")
|
||||
}
|
||||
return .launch(file.standardizedFileURL, newInstance: newInstance)
|
||||
return .launch(file.standardizedFileURL, newInstance: newInstance, noConfig: noConfig)
|
||||
}
|
||||
|
||||
private struct ArgumentError: LocalizedError {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import AppKit
|
||||
import Darwin
|
||||
import TerebiConfiguration
|
||||
|
||||
@main
|
||||
@MainActor
|
||||
@@ -14,7 +15,16 @@ enum TerebiCLI {
|
||||
switch command {
|
||||
case .help:
|
||||
print(PlayerCommand.usage)
|
||||
case .launch(let url, let newInstance):
|
||||
case .launch(let url, let newInstance, let noConfig):
|
||||
let configURL = PlayerConfiguration.fileURL(environment: ProcessInfo.processInfo.environment,
|
||||
homeDirectory: FileManager.default.homeDirectoryForCurrentUser)
|
||||
if !noConfig {
|
||||
do {
|
||||
_ = try PlayerConfiguration.load(from: configURL)
|
||||
} catch {
|
||||
fail(error.localizedDescription, code: 78)
|
||||
}
|
||||
}
|
||||
guard let executable = Bundle.main.executableURL else {
|
||||
fail("Could not locate the CLI executable.", code: 69)
|
||||
}
|
||||
@@ -25,8 +35,10 @@ enum TerebiCLI {
|
||||
}
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
configuration.createsNewApplicationInstance = newInstance
|
||||
// LaunchServices does not inherit the invoking shell's XDG environment.
|
||||
configuration.arguments = noConfig ? ["--no-config"] : ["--config-file", configURL.absoluteString]
|
||||
if newInstance, let url {
|
||||
configuration.arguments = ["--open-media", url.absoluteString]
|
||||
configuration.arguments += ["--open-media", url.absoluteString]
|
||||
}
|
||||
do {
|
||||
// Launch an application, not a document, so SwiftUI creates its singleton window.
|
||||
|
||||
Reference in New Issue
Block a user