fix(legendas): impede legenda comum sob composição dinâmica e duplicação ao regerar
Marca cada título gerado (dynamic/plain) em metadata para que regenerar substitua a saída anterior em vez de empilhar, e usa os spans de ênfase revisados (não os segmentos brutos do Whisper) como janela da composição dinâmica, evitando que ela invada o trecho de legenda comum seguinte. suppress_plain_under_dynamic corta qualquer sobra visível como rede de segurança. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
32d78d0f8d
commit
0fdfe33613
@@ -0,0 +1,72 @@
|
||||
"""Exercise the real subtitle handler across emphasis gaps and regeneration."""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from fcpxml.models import DynamicSubtitleConfig
|
||||
from fcpxml.writer import FCPXMLModifier
|
||||
from server_tools import subtitles
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def caption_job(tmp_path, monkeypatch):
|
||||
modifier = FCPXMLModifier(Path(__file__).parents[1] / "examples/sample.fcpxml")
|
||||
parent = modifier._require_clip("Interview_A")
|
||||
parent.set("start", "0s")
|
||||
parent.set("duration", "10s")
|
||||
media = tmp_path / "speech.mov"
|
||||
media.touch()
|
||||
modifier.resources[parent.get("ref")]["src"] = media.as_uri()
|
||||
monkeypatch.setattr(modifier, "_iter_spine_clips", lambda: iter([(0, parent)]))
|
||||
monkeypatch.setattr(subtitles, "_setup_modifier", lambda *args: (
|
||||
"input.fcpxml", str(tmp_path / "output.fcpxml"), modifier,
|
||||
))
|
||||
monkeypatch.setattr(subtitles, "get_active_dynamic_subtitle_layouts", lambda: [
|
||||
{"role": "titles.dinamicas"},
|
||||
])
|
||||
monkeypatch.setattr(subtitles, "_build_dynamic_subtitle_config", lambda *args: DynamicSubtitleConfig())
|
||||
monkeypatch.setattr(subtitles, "load_plain_subtitle_config", lambda: {
|
||||
"role": "titles.convencionais", "font": "Helvetica Neue", "font_size": 48,
|
||||
"font_color": "1 1 1 1", "max_words": 1, "position_y": -100,
|
||||
"uppercase": False, "keep_punctuation": True,
|
||||
})
|
||||
monkeypatch.setattr(subtitles, "_load_plain_exclude_spans", lambda _: [])
|
||||
monkeypatch.setattr(subtitles, "_load_emphasis_spans", lambda _: [
|
||||
{"start": 0, "end": 1}, {"start": 4, "end": 5},
|
||||
])
|
||||
monkeypatch.setattr(subtitles, "_load_or_transcribe", lambda *args: ({
|
||||
"words": [
|
||||
{"word": "corpo", "start": 0, "end": 1},
|
||||
{"word": "muda", "start": 2, "end": 3},
|
||||
{"word": "também", "start": 4, "end": 5},
|
||||
],
|
||||
"segments": [{"start": 0, "end": 1}, {"start": 4, "end": 5}],
|
||||
}, None))
|
||||
return modifier, parent
|
||||
|
||||
|
||||
def test_dynamic_composition_clears_before_plain_words_in_emphasis_gap(caption_job):
|
||||
modifier, parent = caption_job
|
||||
asyncio.run(subtitles.handle_generate_subtitles_by_emphasis({}))
|
||||
dynamic = parent.findall("ref-clip")
|
||||
plain = parent.findall("title")
|
||||
assert dynamic and plain
|
||||
for composition in dynamic:
|
||||
start = modifier._parse_time(composition.get("offset"))
|
||||
end = start + modifier._parse_time(composition.get("duration"))
|
||||
for title in plain:
|
||||
plain_start = modifier._parse_time(title.get("offset"))
|
||||
plain_end = plain_start + modifier._parse_time(title.get("duration"))
|
||||
assert not (start < plain_end and plain_start < end), "dynamic and plain overlap"
|
||||
|
||||
|
||||
def test_regeneration_replaces_generated_subtitles_and_preserves_manual_titles(caption_job):
|
||||
modifier, parent = caption_job
|
||||
manual = modifier.add_text_title(parent, "Nome da médica", role="titles.manual")
|
||||
asyncio.run(subtitles.handle_generate_subtitles_by_emphasis({}))
|
||||
initial = len(parent.findall("ref-clip")) + len(parent.findall("title"))
|
||||
asyncio.run(subtitles.handle_generate_subtitles_by_emphasis({}))
|
||||
assert len(parent.findall("ref-clip")) + len(parent.findall("title")) == initial
|
||||
assert manual in list(parent)
|
||||
Reference in New Issue
Block a user