Restore generic 'scene N' split — but literal-only (Movie Editor needs it)
Dropping the generic scene-N pattern broke the Movie Editor: build_combined_prompt (for_generation) INJECTS 'scene N' markers into the generation prompt to force a split for any unit whose text lacks a leading transition trigger. Without that recognition the second unit never split -> 'scene 2 exists only on the timeline, not generated'. split_scenes now matches the generic scene-N label again, but ONLY in its literal-text scanner (never over shortcut expansions). So an editor-injected / user-typed 'scene 2' cuts, while a 'scene 2' buried inside a shortcut expansion does not — fixing both the user's phantom split AND the broken montage. For the reported prompt the piece-walker now returns 2 scenes (was 4), matching the old generation splitter's 2 -> the guard should pass and keys attribute while the chain/guides keep working. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
38509ea0d3
commit
2fae920839
+14
-11
@@ -886,17 +886,20 @@ def split_scenes(prompt, placement="start"):
|
||||
_open(effect)
|
||||
segs[-1]["raw"] += raw_t; segs[-1]["exp"] += exp_t
|
||||
|
||||
# Literal-text trigger matcher (custom triggers only; never the generic scene-N pattern).
|
||||
triggers = [t for t in custom_map.keys() if t]
|
||||
lit_pat = None
|
||||
if triggers:
|
||||
def _tp(t):
|
||||
end = r"\b" if re.search(r"\w$", t) else r"(?=\s|$)"
|
||||
return r"\b" + re.escape(t) + end
|
||||
try:
|
||||
lit_pat = re.compile("|".join(_tp(t) for t in sorted(triggers, key=len, reverse=True)), re.IGNORECASE)
|
||||
except re.error:
|
||||
lit_pat = None
|
||||
# Literal-text trigger matcher: the user's custom triggers PLUS the generic `scene <N>` label.
|
||||
# Scoped to LITERAL text only (this matcher never runs over shortcut expansions), so a "scene 2"
|
||||
# the user types — or, crucially, the one the Movie Editor INJECTS into the generation prompt to
|
||||
# force a split for a scene with no leading trigger (see build_combined_prompt for_generation) —
|
||||
# still cuts, while a "scene 2" buried inside a shortcut's expansion does NOT.
|
||||
def _tp(t):
|
||||
end = r"\b" if re.search(r"\w$", t) else r"(?=\s|$)"
|
||||
return r"\b" + re.escape(t) + end
|
||||
_pats = [_tp(t) for t in sorted((t for t in custom_map.keys() if t), key=len, reverse=True)]
|
||||
_pats.append(_GENERIC_SCENE_LABEL_PATTERN)
|
||||
try:
|
||||
lit_pat = re.compile("|".join(_pats), re.IGNORECASE)
|
||||
except re.error:
|
||||
lit_pat = None
|
||||
|
||||
for pc in pieces:
|
||||
raw = text[pc.get("orig_start", 0):pc.get("orig_end", 0)]
|
||||
|
||||
@@ -245,6 +245,25 @@ def test_split_scenes_ignores_triggers_inside_expansions(monkeypatch):
|
||||
assert "Scene cut." not in out["scenes"][1]["expanded"] # silent trigger dropped
|
||||
|
||||
|
||||
def test_split_scenes_literal_scene_label_still_splits(monkeypatch):
|
||||
"""The generic 'scene N' label MUST still split when it's LITERAL text — the Movie Editor
|
||||
injects it into the generation prompt to force a split for a unit with no leading trigger
|
||||
(build_combined_prompt for_generation). But the SAME label inside a shortcut expansion must NOT
|
||||
split (that was the user's phantom 'scene 2'). One rule: literal cuts, expansion-content doesn't."""
|
||||
db = _db({"qcut": (["qcut"], ["rapid cut"], ""),
|
||||
"tth": (["tth"], ["a backflip"], ""),
|
||||
"cof": (["cof"], ["sitting, scene 9 of many"], "cs")}) # 'scene 9' INSIDE expansion
|
||||
monkeypatch.setattr(templates, "load_shortcut_db", lambda: db)
|
||||
monkeypatch.setattr(templates, "load_custom_transition_triggers",
|
||||
lambda: {"qcut": {"placement": "start", "visual_effect": "none"}})
|
||||
# explicit qcut leads scene 0; the editor-injected literal 'scene 2' leads scene 1
|
||||
out = conditioning.split_scenes("CHAR a woman man qcut tth scene 2 cof")
|
||||
assert len(out["scenes"]) == 2
|
||||
assert out["scenes"][0]["keys"] == set() # tth has no key
|
||||
assert out["scenes"][1]["keys"] == {"cs"} # cof, despite 'scene 9' in its expansion
|
||||
assert out["anchor"] == "CHAR a woman man"
|
||||
|
||||
|
||||
def test_single_scene_with_custom_key_is_detected(monkeypatch):
|
||||
"""Regression: a custom key fired in a single-logical-scene prompt MUST be detected (was
|
||||
silently dropped to empty when the old code compared scene counts and fell back)."""
|
||||
|
||||
Reference in New Issue
Block a user