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
@@ -386,18 +386,40 @@ def phrase_review_to_actions(review: dict) -> dict:
|
||||
|
||||
actions: List[dict] = []
|
||||
emphasis_spans: List[dict] = []
|
||||
inactive_run: List[dict] = []
|
||||
|
||||
def flush_inactive_run() -> None:
|
||||
"""One cut per RUN of consecutive deactivated phrases, not one per
|
||||
phrase. A phrase-by-phrase cut leaves the pause BETWEEN two
|
||||
deactivated phrases uncut — that gap was never anyone's content, so
|
||||
nothing asked for it to survive, but it does anyway: a 0.1-0.5s
|
||||
sliver clip in the final timeline for every such gap. Spanning the
|
||||
whole run absorbs those gaps into the one cut."""
|
||||
if not inactive_run:
|
||||
return
|
||||
if len(inactive_run) == 1:
|
||||
reason = inactive_run[0]["reason"] or "desativada na revisão"
|
||||
else:
|
||||
reason = (
|
||||
f"desativadas na revisão ({len(inactive_run)} frases): "
|
||||
+ "; ".join(p["text"][:40] for p in inactive_run if p["text"])
|
||||
)
|
||||
actions.append(
|
||||
VoiceAction(
|
||||
kind="cut",
|
||||
start=inactive_run[0]["start"],
|
||||
end=inactive_run[-1]["end"],
|
||||
reason=reason,
|
||||
speaker=inactive_run[0]["speaker"],
|
||||
).as_dict()
|
||||
)
|
||||
inactive_run.clear()
|
||||
|
||||
for phrase in phrases:
|
||||
if not phrase["active"]:
|
||||
actions.append(
|
||||
VoiceAction(
|
||||
kind="cut",
|
||||
start=phrase["start"],
|
||||
end=phrase["end"],
|
||||
reason=phrase["reason"] or "desativada na revisão",
|
||||
speaker=phrase["speaker"],
|
||||
).as_dict()
|
||||
)
|
||||
inactive_run.append(phrase)
|
||||
continue
|
||||
flush_inactive_run()
|
||||
|
||||
# Head and tail the editor trimmed off — each becomes its own cut, so a
|
||||
# false start disappears without taking the line with it.
|
||||
@@ -436,6 +458,7 @@ def phrase_review_to_actions(review: dict) -> dict:
|
||||
"text": phrase["text"],
|
||||
}
|
||||
)
|
||||
flush_inactive_run()
|
||||
|
||||
# Hand-placed punch-ins carry no scale on purpose: an omitted scale lets the
|
||||
# applier use the shape configured in "Análise de Voz" (zoom_scale, ease in
|
||||
|
||||
Reference in New Issue
Block a user