Files
olivv-cs-ComfyUI-FunPack/modules/conditioning/shot_camera/tests/test_reframe.py
T

70 lines
3.8 KiB
Python

"""Reframe within the shot: a kept shortcut boundary turns the camera in one continuous take, never a cut."""
from .. import engine, memory, nodes
DANCE = "She sways her hips from side to side, smiling at the room."
WAVE = "She raises her hand and waves at the camera with her fingers."
def test_reframe_turns_the_camera_between_shortcuts_without_a_cut(tmp_path, monkeypatch):
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
text = f"[Shot 1] {DANCE} {WAVE}"
out, said, chose = nodes.rewrite(text, {"reframe_same_shot": True, "reframe_chance": 1}, seconds=10, pieces=[DANCE, WAVE])
assert out.count("[Shot ") == 1 and "cuts to" not in out, "one take: no new shot, no cut words"
head, tail = out.split("Without a cut, the camera ")
assert DANCE in head and WAVE in tail, "the reframe sits between the two actions, in order"
assert "framing" in tail.split(".")[0], "aimed at what the next action is about"
assert "reframes: shot 1" in said
assert chose["arms"] == ["reframe:yes"] and chose["views"], "learned like the other camera choices"
def test_the_view_never_repeats_and_respects_what_the_part_shows():
text = f"[Shot 1] {DANCE} {DANCE} {DANCE} {DANCE}"
out, info = engine.add_reframes(text, seed="s", chance=1, pieces=[DANCE])
views = [a["view"] for a in info["added"]]
assert len(views) == 3 and all(a != b for a, b in zip(views, views[1:]))
assert all(v != "View from behind" for v in views), "a smiling face is not shown from behind"
def test_zero_chance_adds_nothing_and_no_boundary_says_so(tmp_path, monkeypatch):
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
text = f"[Shot 1] {DANCE} {WAVE}"
assert engine.add_reframes(text, seed=1, chance=0, pieces=[DANCE, WAVE])[0] == text
out, said, _ = nodes.rewrite("[Shot 1] A woman walks.", {"reframe_same_shot": True, "reframe_chance": 1}, seconds=5, pieces=[])
assert out == "[Shot 1] A woman walks." and "no shortcut boundary inside a shot" in said
def test_a_cut_boundary_is_not_also_reframed(tmp_path, monkeypatch):
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
text = f"[Shot 1] {DANCE} {WAVE}"
out, _, _ = nodes.rewrite(text, {"cut_same_shot": True, "shot_cuts_chance": 1, "reframe_same_shot": True, "reframe_chance": 1},
seconds=10, pieces=[DANCE, WAVE])
assert out.count("[Shot ") == 2 and "Without a cut" not in out
def test_the_reframe_is_not_content_and_does_not_block_the_opening_view():
text = f"[Shot 1] A woman walks in. [Shot 2] {DANCE} {WAVE}"
out, _ = engine.add_reframes(text, seed=2, chance=1, pieces=[DANCE, WAVE])
assert engine.content_fingerprint(out) == engine.content_fingerprint(text), "same prompt, same seed next run"
shot2 = engine.shot_texts(out)[2]
assert "Without a cut" not in shot2
_, added = engine.add_shot_views(out, seed=0, chance=1)
assert [a["shot"] for a in added] == [2], "a mid-shot reframe is not the view the shot opens on"
def test_ratings_tilt_the_reframe_chance(tmp_path, monkeypatch):
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
assert memory.reframe_chance(0.5) == 0.5
memory.record_run("r", {"views": [], "arms": ["reframe:yes"]})
memory.on_rating("r", "liked")
assert memory.reframe_chance(0.5) > 0.5
assert memory.reframe_chance(1.0) == 1.0, "always stays always"
def test_cut_within_the_shot_at_zero_leaves_every_boundary_to_reframe(tmp_path, monkeypatch):
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
text = f"[Shot 1] {DANCE} {WAVE} {DANCE}"
out, _, _ = nodes.rewrite(text, {"cut_same_shot": True, "shot_cuts_chance": 0, "reframe_same_shot": True, "reframe_chance": 1},
seconds=10, pieces=[DANCE, WAVE])
assert out.count("[Shot ") == 1 and out.count("Without a cut") == 2