feat: report authoritative playback progress from shared backend
This commit is contained in:
@@ -3,11 +3,12 @@ import Foundation
|
||||
enum PlayerCommand {
|
||||
case help
|
||||
case printDefaultConfig
|
||||
case launch(URL?, newInstance: Bool, noConfig: Bool)
|
||||
case launch(URL?, newInstance: Bool, noConfig: Bool, progressFile: URL?)
|
||||
|
||||
static let usage = """
|
||||
Usage: terebi [--new-instance] [--no-config] [FILE | YOUTUBE_URL]
|
||||
terebi [--new-instance] [--no-config] --url YOUTUBE_URL
|
||||
terebi --new-instance --url YOUTUBE_URL --progress-file PATH
|
||||
terebi [--new-instance] [--no-config] -- FILE
|
||||
terebi --help
|
||||
terebi --default-config
|
||||
@@ -19,6 +20,8 @@ enum PlayerCommand {
|
||||
--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.
|
||||
--progress-file PATH Write JSONL playback facts to a new private file; requires
|
||||
--new-instance. The GUI owns reporting after this CLI exits.
|
||||
-- Treat the following argument as a filename, even with a leading '-'.
|
||||
-h, --help Show this help without launching the player.
|
||||
--default-config Print the complete built-in TOML without launching.
|
||||
@@ -30,6 +33,23 @@ enum PlayerCommand {
|
||||
|
||||
static func parse(_ arguments: [String], directory: URL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)) throws -> PlayerCommand {
|
||||
var inputs = arguments
|
||||
var progressFile: URL?
|
||||
var index = 0
|
||||
while index < inputs.count && inputs[index] != "--" {
|
||||
if inputs[index] != "--progress-file" {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
guard progressFile == nil else { throw ArgumentError("--progress-file may only be supplied once.") }
|
||||
guard index + 1 < inputs.count, !inputs[index + 1].isEmpty,
|
||||
!inputs[index + 1].contains("\0"), !inputs[index + 1].hasPrefix("--") else {
|
||||
throw ArgumentError("--progress-file requires a filesystem path.")
|
||||
}
|
||||
let base = directory.hasDirectoryPath ? directory :
|
||||
URL(fileURLWithPath: directory.path, isDirectory: true)
|
||||
progressFile = URL(fileURLWithPath: inputs[index + 1], relativeTo: base).absoluteURL.standardizedFileURL
|
||||
inputs.removeSubrange(index...index + 1)
|
||||
}
|
||||
var newInstance = false
|
||||
var noConfig = false
|
||||
while let option = inputs.first, option == "--new-instance" || option == "--no-config" {
|
||||
@@ -42,12 +62,24 @@ enum PlayerCommand {
|
||||
}
|
||||
inputs.removeFirst()
|
||||
}
|
||||
if inputs.isEmpty { return .launch(nil, newInstance: newInstance, noConfig: noConfig) }
|
||||
if inputs == ["--help"] || inputs == ["-h"] { return .help }
|
||||
if inputs == ["--default-config"] { return .printDefaultConfig }
|
||||
func launch(_ url: URL?) throws -> PlayerCommand {
|
||||
guard progressFile == nil || newInstance else {
|
||||
throw ArgumentError("--progress-file requires --new-instance.")
|
||||
}
|
||||
return .launch(url, newInstance: newInstance, noConfig: noConfig, progressFile: progressFile)
|
||||
}
|
||||
if inputs.isEmpty { return try launch(nil) }
|
||||
if inputs == ["--help"] || inputs == ["-h"] {
|
||||
guard progressFile == nil else { throw ArgumentError("--progress-file cannot be combined with --help.") }
|
||||
return .help
|
||||
}
|
||||
if inputs == ["--default-config"] {
|
||||
guard progressFile == nil else { throw ArgumentError("--progress-file cannot be combined with --default-config.") }
|
||||
return .printDefaultConfig
|
||||
}
|
||||
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, noConfig: noConfig)
|
||||
return try launch(YouTubeResolver.validateURL(inputs[1]))
|
||||
}
|
||||
let filenameOnly = inputs.first == "--"
|
||||
if filenameOnly {
|
||||
@@ -67,7 +99,7 @@ enum PlayerCommand {
|
||||
}
|
||||
file = url
|
||||
} else {
|
||||
return .launch(try YouTubeResolver.validateURL(input), newInstance: newInstance, noConfig: noConfig)
|
||||
return try launch(YouTubeResolver.validateURL(input))
|
||||
}
|
||||
} else {
|
||||
let base = directory.hasDirectoryPath ? directory : URL(fileURLWithPath: directory.path, isDirectory: true)
|
||||
@@ -77,7 +109,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, noConfig: noConfig)
|
||||
return try launch(file.standardizedFileURL)
|
||||
}
|
||||
|
||||
private struct ArgumentError: LocalizedError {
|
||||
|
||||
@@ -17,7 +17,16 @@ enum TerebiCLI {
|
||||
print(PlayerCommand.usage)
|
||||
case .printDefaultConfig:
|
||||
print(PlayerConfiguration.defaultTOML, terminator: "")
|
||||
case .launch(let url, let newInstance, let noConfig):
|
||||
case .launch(let url, let newInstance, let noConfig, let progressFile):
|
||||
if let progressFile {
|
||||
var existing = stat()
|
||||
guard lstat(progressFile.path, &existing) != 0 else {
|
||||
fail("Progress file already exists: \(progressFile.path). Supply a nonexistent destination.", code: 73)
|
||||
}
|
||||
guard errno == ENOENT else {
|
||||
fail("Could not inspect progress file \(progressFile.path): \(String(cString: strerror(errno))).", code: 73)
|
||||
}
|
||||
}
|
||||
let configURL = PlayerConfiguration.fileURL(environment: ProcessInfo.processInfo.environment,
|
||||
homeDirectory: FileManager.default.homeDirectoryForCurrentUser)
|
||||
if !noConfig {
|
||||
@@ -39,6 +48,9 @@ enum TerebiCLI {
|
||||
configuration.createsNewApplicationInstance = newInstance
|
||||
// LaunchServices does not inherit the invoking shell's XDG environment.
|
||||
configuration.arguments = noConfig ? ["--no-config"] : ["--config-file", configURL.absoluteString]
|
||||
if let progressFile {
|
||||
configuration.arguments += ["--progress-file", progressFile.path]
|
||||
}
|
||||
if newInstance, let url {
|
||||
configuration.arguments += ["--open-media", url.absoluteString]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user