fix: the time before a user-written cut phrase is not part of a prompt's identity; the hint names the recognised cut phrases; tests for the share, per-cut rolls and user phrases
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
c0c298f231
commit
848cf2e041
@@ -43,7 +43,7 @@ SETTINGS = {
|
||||
"hint": "1 = always cut between those shortcuts, 0 = never. Ratings nudge the values in between.",
|
||||
"when": {"cut_same_shot": True}},
|
||||
"cut_difference_chance": {"type": "float", "default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05, "label": "Chance of an unusual transition",
|
||||
"hint": "0 = plain cuts only. In between, that share of cuts is a dissolve, fade or wipe. 1 = every cut is one, mixed. A cut phrase you wrote yourself stays as written. Needs “Shot cut times” or “Cut within the same shot”."},
|
||||
"hint": "0 = plain cuts only. In between, that share of cuts is a dissolve, fade or wipe. 1 = every cut is one, mixed. A cut phrase you wrote yourself, like “the shot cuts to…” or “the shot fades to…”, stays as written. Needs “Shot cut times” or “Cut within the same shot”."},
|
||||
"reframe_same_shot": {"type": "bool", "default": False, "label": "Reframe within the shot",
|
||||
"hint": "Where one shortcut ends and the next begins inside a shot that is not cut, the camera moves on to what the next action is about, without a cut: one continuous take, the actions follow on. The view may turn or stay. A shot that reframes gets no extra camera move."},
|
||||
"reframe_chance": {"type": "float", "default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05, "label": "Chance of a reframe",
|
||||
|
||||
@@ -993,6 +993,7 @@ def content_fingerprint(text):
|
||||
body = re.sub(r"\[\s*Shot\s+\d+\s*\]", " ", text or "", flags=re.I)
|
||||
body = re.sub(r"At \d\d:\d\d\.\d{3},\s*(?:" + "|".join(re.escape(o) for o in OUR_OPENERS) + r")\.",
|
||||
" ", body, flags=re.I)
|
||||
body = re.sub(r"At \d\d:\d\d\.\d{3},\s*(?=(?:the\s+)?(?:camera|shot)\s+" + _CUT_VERB + r"\s+to\b)", " ", body, flags=re.I) # the time before a cut phrase the user wrote
|
||||
body = _REFRAME.sub(" ", body)
|
||||
body = re.sub(r"\s+", " ", body)
|
||||
body = re.sub(r"(?:^|(?<=[.!?]\s))(?:" + "|".join(re.escape(v) for v in VIEWS) + r")\.", " ", body)
|
||||
|
||||
@@ -80,3 +80,38 @@ def test_a_move_is_never_aimed_at_a_transition_word(monkeypatch, tmp_path):
|
||||
for sentence in out.replace("\n", " ").split(". "):
|
||||
if "camera" in sentence and "At 00:" not in sentence:
|
||||
assert not any(w in sentence for w in ("black", "white", "angle", "moment", "follows")), sentence
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("phrase", ["The shot fades to black.", "The shot fades out to black.", "The shot dissolves to her hand.",
|
||||
"The camera wipes across to her hand.", "The shot cross-dissolves to her hand."])
|
||||
def test_the_cut_phrases_the_hint_names_are_kept_without_a_second_opener(monkeypatch, tmp_path, phrase):
|
||||
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
|
||||
out, _ = engine.add_shot_cuts(f"[Shot 1] {DANCE} [Shot 2] {phrase} {WAVE}", 10, seed=1, chance=0, pieces=[DANCE, WAVE], transition=1.0)
|
||||
assert sum(out.count(o) for o in engine.OUR_OPENERS) == 0 and phrase.lower()[:12] in out.lower(), out
|
||||
|
||||
|
||||
def test_the_time_before_a_user_written_cut_is_not_part_of_the_prompts_identity(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
|
||||
text = f"[Shot 1] {DANCE} [Shot 2] The shot cross-dissolves to her hands. {WAVE}"
|
||||
prints = {engine.content_fingerprint(engine.add_shot_cuts(text, secs, seed=1, chance=0, pieces=[DANCE, WAVE])[0]) for secs in (12, 20, 30)}
|
||||
assert prints == {engine.content_fingerprint(text)}, "one prompt however long the video"
|
||||
|
||||
|
||||
def test_between_zero_and_one_the_share_of_transitions_follows_the_slider():
|
||||
for difference in (0.1, 0.5, 0.9):
|
||||
hits = sum(engine._opener(random.Random(seed), difference, None)[1] is not None for seed in range(800))
|
||||
assert abs(hits / 800 - difference) < 0.08, difference
|
||||
|
||||
|
||||
def test_each_cut_rolls_for_itself_so_a_shot_can_mix_plain_and_transition_cuts(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("SHOT_CAMERA_MEMORY", str(tmp_path / "m.json"))
|
||||
text = f"[Shot 1] {DANCE} {WAVE} {DANCE} {WAVE}"
|
||||
mixes = 0
|
||||
for seed in range(40):
|
||||
out, _ = engine.add_shot_cuts(text, 16, seed=seed, chance=1, pieces=[DANCE, WAVE], transition=0.5)
|
||||
plain = sum(out.count(o) for o in engine.CUT_OPENERS)
|
||||
mixes += plain > 0 and sum(out.count(p) for _, p in engine.TRANSITIONS) > 0
|
||||
assert mixes > 5
|
||||
|
||||
Reference in New Issue
Block a user