feat: enqueue streams in running player

This commit is contained in:
2026-10-22 16:03:56 +02:00
parent ac8e284d80
commit 6a4afc9f0e
17 changed files with 416 additions and 61 deletions
+15 -3
View File
@@ -6,6 +6,7 @@ enum PlayerCommand {
case launch(URL?, newInstance: Bool, noConfig: Bool, progressFile: URL?)
case launchSource(sourceFile: URL, newInstance: Bool, noConfig: Bool, progressFile: URL?)
case launchQueue(queueFile: URL, newInstance: Bool, noConfig: Bool)
case enqueue(queueFile: URL, noConfig: Bool)
static let usage = """
Usage: terebi [--new-instance] [--no-config] [FILE | STREAM_URL]
@@ -13,6 +14,7 @@ enum PlayerCommand {
terebi --new-instance --url STREAM_URL --progress-file PATH
terebi --new-instance --source-file PATH [--progress-file PATH]
terebi --new-instance [--no-config] --queue-file PATH
terebi --enqueue [--no-config] --queue-file PATH
terebi [--new-instance] [--no-config] -- FILE
terebi --help
terebi --default-config
@@ -26,6 +28,8 @@ enum PlayerCommand {
--url URL Open a YouTube video or Miruro episode URL.
--source-file PATH Open a validated resolved HTTP(S) source in a new player.
--queue-file PATH Open an ordered version-1 stream queue with per-item reports.
--enqueue Append a queue to the running player without interrupting playback;
launch and start its first item if no player is running.
--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 '-'.
@@ -58,16 +62,23 @@ enum PlayerCommand {
}
var newInstance = false
var noConfig = false
while let option = inputs.first, option == "--new-instance" || option == "--no-config" {
var enqueue = false
while let option = inputs.first, ["--new-instance", "--no-config", "--enqueue"].contains(option) {
if option == "--new-instance" {
guard !newInstance else { throw ArgumentError("--new-instance may only be supplied once.") }
newInstance = true
} else if option == "--enqueue" {
guard !enqueue else { throw ArgumentError("--enqueue may only be supplied once.") }
enqueue = true
} else {
guard !noConfig else { throw ArgumentError("--no-config may only be supplied once.") }
noConfig = true
}
inputs.removeFirst()
}
guard !enqueue || (!newInstance && progressFile == nil && inputs.first == "--queue-file") else {
throw ArgumentError("--enqueue requires --queue-file and cannot combine with --new-instance, --progress-file or other media.")
}
func launch(_ url: URL?) throws -> PlayerCommand {
guard progressFile == nil || newInstance else {
throw ArgumentError("--progress-file requires --new-instance.")
@@ -94,12 +105,13 @@ enum PlayerCommand {
return .printDefaultConfig
}
if inputs.first == "--queue-file" {
guard newInstance, progressFile == nil, inputs.count == 2,
guard newInstance || enqueue, progressFile == nil, inputs.count == 2,
inputs[1].hasPrefix("/"), !inputs[1].contains("\0") else {
throw ArgumentError("--queue-file requires --new-instance and one absolute manifest path; it cannot be combined with --progress-file.")
throw ArgumentError("--queue-file requires --new-instance or --enqueue and one absolute manifest path; it cannot be combined with --progress-file.")
}
let queueFile = URL(fileURLWithPath: inputs[1]).standardizedFileURL
_ = try StreamQueue.load(from: queueFile)
if enqueue { return .enqueue(queueFile: queueFile, noConfig: noConfig) }
return .launchQueue(queueFile: queueFile, newInstance: newInstance, noConfig: noConfig)
}
if inputs.first == "--source-file" {
+3
View File
@@ -24,6 +24,9 @@ enum TerebiCLI {
case .launchQueue(let queueFile, let newInstance, let noConfig):
await launch(url: nil, sourceFile: nil, newInstance: newInstance, noConfig: noConfig,
progressFile: nil, queueFile: queueFile)
case .enqueue(let queueFile, let noConfig):
await launch(url: StreamQueue.enqueueURL(for: queueFile), sourceFile: nil,
newInstance: false, noConfig: noConfig, progressFile: nil)
}
}