fix(voz): frases desativadas em sequência deixavam fatias sobrando no corte
phrase_review_to_actions() cortava cada frase desativada isoladamente (start..end da própria frase) — quando várias seguidas estavam desativadas, a pausa ENTRE elas não pertencia a nenhuma frase e sobrevivia como um clipe minúsculo (0,1-0,5s) na timeline final. Confirmado no projeto Mastopexia real: 29 cuts individuais geravam mais de uma dezena de fatias sub-segundo; agrupar frases desativadas consecutivas num único cut (do início da primeira ao fim da última) reduziu para 3 cuts e 4 fatias residuais (menores, provavelmente do padding do remove_media_silence — registrado como dívida separada em 09_MANUTENCAO.md §2.5). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
fd791e116a
commit
8257155fd3
@@ -260,6 +260,42 @@ class TestBackToActions:
|
||||
)
|
||||
assert result["actions"] == []
|
||||
|
||||
def test_consecutive_inactive_phrases_merge_into_one_cut(self):
|
||||
"""Two deactivated phrases in a row must not leave the pause between
|
||||
them (2.0-2.3 here) uncut — a phrase-by-phrase cut would strand it as
|
||||
a tiny surviving sliver clip in the final timeline."""
|
||||
review = build_phrase_review(
|
||||
_timeline([_segment(0, 2), _segment(2.3, 4), _segment(4.5, 6)])
|
||||
)
|
||||
review["phrases"][0]["active"] = False
|
||||
review["phrases"][1]["active"] = False
|
||||
result = phrase_review_to_actions(review)
|
||||
cuts = [(a["start"], a["end"]) for a in result["actions"] if a["kind"] == "cut"]
|
||||
assert cuts == [(0.0, 4.0)]
|
||||
|
||||
def test_inactive_run_at_the_end_still_flushes(self):
|
||||
"""A run of deactivated phrases with nothing active after it must
|
||||
still produce its cut — regression for merging logic that only
|
||||
flushed on hitting the next active phrase."""
|
||||
review = build_phrase_review(_timeline([_segment(0, 2), _segment(2.3, 4)]))
|
||||
review["phrases"][0]["active"] = False
|
||||
review["phrases"][1]["active"] = False
|
||||
result = phrase_review_to_actions(review)
|
||||
cuts = [(a["start"], a["end"]) for a in result["actions"] if a["kind"] == "cut"]
|
||||
assert cuts == [(0.0, 4.0)]
|
||||
|
||||
def test_isolated_inactive_phrases_stay_separate_cuts(self):
|
||||
"""An active phrase between two inactive ones must not be swallowed —
|
||||
only truly CONSECUTIVE inactive phrases merge."""
|
||||
review = build_phrase_review(
|
||||
_timeline([_segment(0, 2), _segment(2.3, 4), _segment(4.5, 6)])
|
||||
)
|
||||
review["phrases"][0]["active"] = False
|
||||
review["phrases"][2]["active"] = False
|
||||
result = phrase_review_to_actions(review)
|
||||
cuts = [(a["start"], a["end"]) for a in result["actions"] if a["kind"] == "cut"]
|
||||
assert cuts == [(0.0, 2.0), (4.5, 6.0)]
|
||||
|
||||
|
||||
class TestManualZooms:
|
||||
def test_manual_zoom_becomes_an_action_without_a_scale(self):
|
||||
|
||||
Reference in New Issue
Block a user