feat(legendas): liga compound_subphrases por padrão no pipeline
generate_dynamic_subtitles e a metade dinâmica de generate_subtitles_by_emphasis passam a empacotar cada sub-frase da legenda dinâmica num compound clip por padrão (compound_subphrases=True), completando o wrap_titles_in_compound e split_into_subphrases do commit anterior — que ainda não tinham chamador em produção. Também torna validate_subtitle_layout ciente de compound clips: media cada grupo (spine principal + cada <media> de compound) no seu próprio espaço de tempo, em vez de uma varredura .//title global — sem isso, âncoras de compounds diferentes liam offset "0s" e acusavam colisão espacial entre frases que nunca dividem a tela, só porque compartilham o mesmo zero de tempo local. Testado ponta a ponta na gravação real (Mastopexia): 12 compounds, 41 títulos todos empacotados, zero soltos, zero IDs duplicados, DTD válida. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
688bdeddb6
commit
c99274895c
@@ -1,6 +1,6 @@
|
||||
# 03 — Camada MCP (`server.py` + `server_tools/`) — 77 ferramentas
|
||||
# 03 — Camada MCP (`server.py` + `server_tools/`) — 78 ferramentas
|
||||
|
||||
> **Escopo:** As 77 ferramentas MCP: helpers, categorias e como criar uma nova.
|
||||
> **Escopo:** As 78 ferramentas MCP: helpers, categorias e como criar uma nova.
|
||||
> **Não cobre:** Lógica de edição, que mora no engine (→ 02) · comandos do app (→ 08)
|
||||
|
||||
`server.py` (592 linhas) é só o transporte: dispatch por dicionário
|
||||
@@ -82,8 +82,21 @@ continua funcionando. A coluna diz o módulo real, para quando você precisar
|
||||
|
||||
### Voz (análise → decisão → aplicação)
|
||||
`analyze_voice_features`, `build_voice_timeline`, `refine_voice_timeline`,
|
||||
`remove_speakers`, `apply_voice_actions`, `generate_voice_script`,
|
||||
`get_voice_analysis_config`, `save_voice_analysis_config`.
|
||||
`remove_speakers`, `remove_speech_gaps`, `apply_voice_actions`,
|
||||
`generate_voice_script`, `get_voice_analysis_config`,
|
||||
`save_voice_analysis_config`.
|
||||
|
||||
`remove_speech_gaps` corta pelo que a **transcrição** já sabe que não tem
|
||||
fala — lê `words[].start/end` do `_voice_timeline.json` (função
|
||||
`speech_gap_cut_actions`, em `fcpxml/voice_actions.py`) em vez de medir
|
||||
volume. É o complemento correto para o caso que `remove_media_silence`
|
||||
(silêncio por dB, ver seção "Silêncio e beats") não cobre: um trecho sem
|
||||
fala mas com som real acima do limiar (respiração, ruído de roupa, batida) —
|
||||
`remove_media_silence` nunca vai cortar isso porque tecnicamente não é
|
||||
silêncio. Não corta a lacuna antes da primeiríssima palavra (pode ser quase
|
||||
o arquivo inteiro, antes da tomada realmente começar) — isso continua
|
||||
decisão manual na Fase 6 do `apply_voice_actions`
|
||||
(`.claude/skills/editar-por-voz/criterios/06-texto-corte-marcador.md`).
|
||||
|
||||
O fluxo manual é: `build_voice_timeline` mede (caro, roda uma vez) →
|
||||
o modelo decide os cortes → **`refine_voice_timeline` renormaliza sobre o que
|
||||
@@ -143,6 +156,30 @@ e avisa no relatório ("Sem revisão de ênfase"). Não expõe overrides de esti
|
||||
por chamada — usa a config salva ("Legendas Dinâmicas"/plain); para estilo
|
||||
pontual, use `generate_dynamic_subtitles`/`generate_plain_subtitles` direto.
|
||||
|
||||
**Separação por role (didática na timeline):** `generate_dynamic_subtitles` e
|
||||
`generate_plain_subtitles` (e a metade dinâmica/comum do `by_emphasis`) aplicam
|
||||
`role="titles.dinamicas"` e `role="titles.convencionais"` em cada `<title>`
|
||||
criado — sub-roles de `titles`, **nunca** `subtitles.*` (que esconderia o título
|
||||
atrás de Code). O parâmetro `role` de cada ferramenta MCP sobrescreve o default
|
||||
(vindo de `load_dynamic_subtitle_config()["role"]` /
|
||||
`load_plain_subtitle_config()["role"]`). Ver `02_MODULES.md` (seção "Separação
|
||||
de role") e `05_EXPERIENCIAS.md` entrada 32 (DTD: `<title>` leva `role`, não
|
||||
`videoRole`).
|
||||
|
||||
**Compound clip por sub-frase (padrão em `generate_dynamic_subtitles` e na
|
||||
metade dinâmica do `by_emphasis`):** `compound_subphrases=True` divide cada
|
||||
frase em sub-frases pela vírgula (`transcribe.split_into_subphrases`) e
|
||||
empacota os `<title>` de cada uma num `<ref-clip>` — a dúzia de títulos
|
||||
empilhados por lane que uma frase gera vira uma barra só, arrastável/mutável
|
||||
como unidade. Exceção: um trecho curto depois da vírgula ("né?", "Então...",
|
||||
< 3 palavras) funde de volta na sub-frase anterior em vez de virar compound
|
||||
próprio — soa como parte da mesma respiração, não uma frase nova. A estrutura
|
||||
replica o que o próprio Final Cut gera em "New Compound Clip": o título mais
|
||||
cedo vira âncora do spine interno em offset 0, os demais penduram nele por
|
||||
lane. `validate_subtitle_layout` mede cada compound no seu próprio espaço de
|
||||
tempo — sem isso, âncoras de compounds diferentes leem "0s" e colidem no
|
||||
papel mesmo estando segundos distantes na timeline real.
|
||||
|
||||
**Antes de atribuir uma colisão ao gerador, confirme que é o gerador.**
|
||||
Um `<title>` de nome estranho (`ref` diferente, params tipo `Auto-Shrink`/
|
||||
`Left Margin` que `_make_text_title_clip` nunca escreve) é conteúdo humano
|
||||
|
||||
Reference in New Issue
Block a user