feat: configure explicit state-dependent player keybindings
This commit is contained in:
+58
-2
@@ -48,6 +48,17 @@ def check_cli_instances(root, app, fixtures):
|
||||
def settings(state):
|
||||
return (state["showBufferInfo"], state["automaticQuality"], state["snapToEdges"])
|
||||
|
||||
def input_request(pid, count, ready, message, events=None, media=None):
|
||||
request = {"id": uuid.uuid4().hex, "events": events or []}
|
||||
if media is not None:
|
||||
request["media"] = str(media)
|
||||
path = root / f"keys-{pid}.json"
|
||||
pending = path.with_suffix(".tmp")
|
||||
pending.write_text(json.dumps(request))
|
||||
pending.replace(path)
|
||||
return wait_states(count, lambda states: states[pid]["requestID"] == request["id"] and ready(states[pid]),
|
||||
message)
|
||||
|
||||
with StreamingServer(fixtures) as server:
|
||||
if extractor and runtime:
|
||||
metadata = root / "youtube.json"
|
||||
@@ -80,9 +91,25 @@ def check_cli_instances(root, app, fixtures):
|
||||
invoke([])
|
||||
wait_states(1, lambda states: states[first_pid]["media"] == movie.name,
|
||||
"warm default CLI reuses the existing process without replacing media")
|
||||
input_request(first_pid, 1, lambda state: state["status"] == 2, "default Space pauses once",
|
||||
events=[{"key": " ", "code": 49}])
|
||||
input_request(first_pid, 1, lambda state: state["status"] == 2, "held Space cannot oscillate",
|
||||
events=[{"key": " ", "code": 49, "repeat": True}])
|
||||
input_request(first_pid, 1, lambda state: state["status"] == 3, "same Space resumes",
|
||||
events=[{"key": " ", "code": 49}])
|
||||
input_request(first_pid, 1, lambda state: state["status"] == 3, "rapid pause/play preserves latest intent",
|
||||
events=[{"key": " ", "code": 49}, {"key": " ", "code": 49}])
|
||||
input_request(first_pid, 1, lambda state: state["compact"], "P enters the same compact window",
|
||||
events=[{"key": "p", "code": 35}])
|
||||
input_request(first_pid, 1, lambda state: state["compact"], "held P cannot exit compact mode",
|
||||
events=[{"key": "p", "code": 35, "repeat": True}])
|
||||
input_request(first_pid, 1, lambda state: not state["compact"], "same P restores normal mode",
|
||||
events=[{"key": "p", "code": 35}])
|
||||
|
||||
config.parent.mkdir(parents=True)
|
||||
config_text = '"show_buffer_info" = true # optional display\n automatic_quality = true\nsnap_to_edges = false\n'
|
||||
config_text = ('automatic_quality = true\n[ui]\n"buffer_info" = true # optional display\n'
|
||||
'compact_snap_to_edges = false\n[keys]\nplay = ["cmd+8", "cmd+x", "x"]\n'
|
||||
'pause = ["cmd+8", "cmd+x", "x"]\nshow_buffer_info = "b"\nhide_buffer_info = "b"\n')
|
||||
config.write_text(config_text)
|
||||
invoke(["--new-instance"])
|
||||
second = wait_states(2, lambda states: any(state["media"] == "" and state["status"] == 0
|
||||
@@ -92,6 +119,35 @@ def check_cli_instances(root, app, fixtures):
|
||||
empty_pid = next(pid for pid in second if pid != first_pid)
|
||||
if settings(second[first_pid]) != (False, False, True) or config.read_text() != config_text:
|
||||
raise RuntimeError(f"config loading changed an existing session or wrote the file: {second}")
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 3, "configured player loads real video",
|
||||
media=movie)
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2, "configured X pauses actual playback",
|
||||
events=[{"key": "x"}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2, "old K binding is removed",
|
||||
events=[{"key": "k", "code": 40}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2 and state["editorText"] == "x",
|
||||
"typing X reaches the editor without controlling playback",
|
||||
events=[{"key": "x", "editing": True}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2, "configured Cmd+8 leaves text editing alone",
|
||||
events=[{"key": "8", "code": 28, "flags": 1 << 20, "editing": True}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 3, "configured Cmd+X resumes actual playback",
|
||||
events=[{"key": "x", "flags": 1 << 20}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 3, "held modified playback chord does not toggle",
|
||||
events=[{"key": "x", "flags": 1 << 20, "repeat": True}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2, "same Cmd+X pauses actual playback",
|
||||
events=[{"key": "x", "flags": 1 << 20}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 3, "configured native menu Cmd+8 resumes",
|
||||
events=[{"key": "8", "code": 28, "flags": 1 << 20, "menu": True}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 2, "native Pause item applies current state",
|
||||
events=[{"select": "Pause"}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 3, "native Play item resumes the same player",
|
||||
events=[{"select": "Play"}])
|
||||
input_request(empty_pid, 2, lambda state: not state["showBufferInfo"], "paired B hides buffer info",
|
||||
events=[{"key": "b", "code": 11}])
|
||||
input_request(empty_pid, 2, lambda state: state["showBufferInfo"], "same B shows buffer info",
|
||||
events=[{"key": "b", "code": 11}])
|
||||
input_request(empty_pid, 2, lambda state: state["status"] == 0, "configured close-file menu restores empty player",
|
||||
events=[{"key": "w", "code": 13, "flags": (1 << 20) | (1 << 17), "menu": True}])
|
||||
invoke(["--new-instance", str(audio)])
|
||||
third = wait_states(3, lambda states: any(state["media"] == audio.name and state["status"] == 3
|
||||
and state["hasAudio"] and not state["hasVideo"]
|
||||
@@ -99,7 +155,7 @@ def check_cli_instances(root, app, fixtures):
|
||||
"independent local media reaches only the freshly launched process")
|
||||
if third[first_pid]["media"] != movie.name or third[first_pid]["status"] != 3 or third[empty_pid]["media"]:
|
||||
raise RuntimeError(f"independent launch disturbed an existing player: {third}")
|
||||
config.write_text("show_buffer_info = [\n")
|
||||
config.write_text("[ui]\nbuffer_info = [\n")
|
||||
failure = invoke(["--new-instance"], expected_code=78)
|
||||
if str(config) not in failure.stderr:
|
||||
raise RuntimeError(f"configuration failure omitted its path: {failure.stderr}")
|
||||
|
||||
Reference in New Issue
Block a user