fix: transition phrases are the module's own words (no camera moves aimed at them), generic cut wording, no distance-only cuts, the slider is always shown

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-10-13 04:47:47 +03:00
co-authored by Claude Sonnet 5.5
parent 450d053a13
commit 8ebfe4d85d
3 changed files with 25 additions and 15 deletions
+1 -2
View File
@@ -43,8 +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 = every cut is a plain cut. In between, that share of cuts is a dissolve, fade or wipe. 1 = every cut is one of those, mixed.",
"when": {"cut_same_shot": True}},
"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."},
"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",
+14 -13
View File
@@ -605,19 +605,20 @@ STAMP = re.compile(r"\b\d\d:\d\d\.\d{3}\b")
# Dissolve, fade and wipe are for when the user asks for them, never default.
# Each cut says what is new: a new angle, place, subject, viewpoint, state or moment. Only these four verbs.
# The guide allows dissolve, fade and wipe only when the user asks for one: the transition slider is that request.
# Generic on purpose: a phrase that claims a new place or subject would be false for a cut inside one scene.
TRANSITIONS = (("dissolve", "the shot cross-dissolves to a new angle"),
("dissolve", "the shot cross-dissolves to a new location"),
("fade", "the shot fades out to black and fades in on a new angle"),
("fade", "the shot fades to white and back to the next moment"),
("dissolve", "the shot cross-dissolves to the next moment"),
("fade", "the shot fades out to black and fades in on the next moment"),
("fade", "the shot fades to white and back to what follows"),
("wipe", "the shot wipes across to a new angle"),
("wipe", "the shot wipes across to a new subject"))
CUT_OPENERS = ("the camera cuts to a new angle", "the camera cuts to a closer view",
"the shot cuts to a new angle", "the shot cuts to a new location", "the shot cuts to a later moment",
"the shot transitions to the next moment", "the shot transitions to a new location",
"the shot transitions to a new point of view",
"the shot changes to a new view", "the shot changes to a different subject",
"the shot changes to a new state of the scene",
"the shot switches to what follows", "the shot switches to a wider view")
("wipe", "the shot wipes across to what follows"))
# The approved cut verbs only (cuts, transitions, changes, switches); never a distance-only change, which the guide leaves to camera motion.
CUT_OPENERS = ("the camera cuts to a new angle", "the camera cuts to the next moment",
"the shot cuts to a new angle", "the shot cuts to what follows",
"the shot transitions to the next moment", "the shot transitions to a new view",
"the shot changes to a new view", "the shot changes to what comes next",
"the shot switches to what follows", "the shot switches to the next moment")
OUR_OPENERS = CUT_OPENERS + tuple(phrase for _, phrase in TRANSITIONS) # everything this module writes after a cut time
LEAD_CUT = re.compile(r"^\s*(?:the\s+)?(?:camera|shot)\s+(?:cuts|transitions|changes|switches)\s+to\b",
re.I)
MIN_SHOT_SECONDS = 2
@@ -979,7 +980,7 @@ def own_words_removed(picture):
return _REFRAME.sub("", _VIEW_SENTENCE.sub("", picture, count=1))
_OUR_OPENER = re.compile(r"^\s*At \d\d:\d\d\.\d{3},\s*(?:" + "|".join(re.escape(o) for o in CUT_OPENERS)
_OUR_OPENER = re.compile(r"^\s*At \d\d:\d\d\.\d{3},\s*(?:" + "|".join(re.escape(o) for o in OUR_OPENERS)
+ r")\.\s*", re.I)
_CUT_WORDS = re.compile(r"^\s*(?:At \d\d:\d\d\.\d{3},\s*)?(?:the\s+)?(?:camera|shot)\s+"
r"(?:cuts|transitions|changes|switches)\s+to\b\s*", re.I)
@@ -990,7 +991,7 @@ def content_fingerprint(text):
labels this module added on that run, so regenerating one prompt is one prompt."""
import hashlib
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 CUT_OPENERS) + r")\.",
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 = _REFRAME.sub(" ", body)
body = re.sub(r"\s+", " ", body)
@@ -26,3 +26,13 @@ def test_the_transition_slider_at_zero_is_always_a_plain_cut_and_at_one_always_a
kinds.append(last)
assert len(set(kinds)) == 3, "a mixture of dissolve, fade and wipe"
assert all(a != b for a, b in zip(kinds, kinds[1:])), "never the same kind twice running"
def test_every_phrase_the_module_writes_after_a_cut_time_is_its_own_words_never_content():
for opener in engine.OUR_OPENERS:
assert not engine.CAMERA.search(opener), opener
picture = f" At 00:05.000, {opener}. She waves at the camera."
assert engine.own_words_removed(picture).strip() == "She waves at the camera.", opener
prints = {engine.content_fingerprint(f"[Shot 1] A woman walks in. [Shot 2] At 00:05.000, {o}. She waves at the camera.")
for o in engine.OUR_OPENERS}
assert len(prints) == 1, "a re-roll of the cut wording is the same prompt"