feat(voz): legenda por ênfase, forced align, IA local e correções de zoom/revisão
Trabalho da branch feat/revisao-enfases: pipeline de edição por voz ganha alinhamento forçado (whisperx), roteirização por LLM local (Ollama), e a etapa 5 (revisão de frases) passa a refletir de verdade o que é aplicado. - generate_subtitles_by_emphasis: legenda comum cobre o clipe inteiro, legenda dinâmica só nas frases de ênfase, e a comum é desativada (enabled="0") onde a dinâmica cobre, em vez de nunca ser gerada ali. - validate_subtitle_layout ignora títulos com enabled="0" — corrige falso positivo de colisão contra o que está desativado no lugar dele. - Corrige zoom/marcador sendo descartado quando a borda encosta exatamente no início de um corte. - Etapa 5 do Assistente: recarrega quando as decisões da IA mudam (com fresh=true, ignorando a revisão salva antiga) — resolve a dessincronia entre "ativa" na tela e o que já foi cortado no FCPXML. - Etapa "Processar" reaplica as decisões da revisão (_phrase_actions.json) antes da cadeia de remoção de silêncio/legendas — antes, desativar uma frase na etapa 5 não tinha efeito nenhum no vídeo final. - Etapa "Concluído" fundida em "Processar" — abrir no Final Cut/Finder aparece assim que termina, sem slide extra. - Palavra clicável na etapa 5 agora funciona como toggle (clique de novo desfaz) e mostra a própria ênfase (sublinhado colorido + peso da fonte). - fcpxml/forced_align.py, fcpxml/llm_local.py, ai_edit.py: alinhamento fonético via whisperx e roteirização local via Ollama/Gemma. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
711c397dfe
commit
7b5aed79ee
@@ -68,6 +68,27 @@ Commands:
|
||||
-> {"ok": true, "review_path", "actions_path", "emphasis_count",
|
||||
"removed_count"}
|
||||
|
||||
generate_voice_script {"media_path": "...", "voice_timeline": "...", "filepath": "...",
|
||||
"model": "gemma3:12b", "base_url": "http://localhost:11434",
|
||||
"model_size": "base", "language": "pt"|"auto"|null,
|
||||
"hf_token": "..."|null, "num_speakers": ""|null,
|
||||
"output_dir": "...", "apply_to_fcpxml": true}
|
||||
The WHOLE voice-edit pass run INSIDE the engine against a LOCAL model
|
||||
(Ollama running Gemma 3 / Llama) — no wizard, no copy-paste. If
|
||||
`voice_timeline` is given (the analysis from an earlier step), it is
|
||||
reused and the transcription/acoustics are skipped; otherwise
|
||||
`media_path` is transcribed and analyzed. Then the local model directs
|
||||
the edit -> returns the readable script (roteiro) + the action JSON,
|
||||
and optionally applies to `filepath` (FCPXML, non-destructive).
|
||||
-> {"ok": true, "message", "roteiro_path", "actions_path", "applied_path"}
|
||||
or {"ok": false, "error": "..."}
|
||||
|
||||
list_ollama_models {"base_url": "http://localhost:11434"}
|
||||
Lists the models Ollama currently serves, for the app's model picker
|
||||
in step 4 (generate script by local AI). Empty list if Ollama is
|
||||
unreachable, so the UI falls back to a free-text field.
|
||||
-> {"ok": true, "models": ["gemma3:12b", ...]}
|
||||
|
||||
dynamic_subtitle_config {}
|
||||
-> {"ok": true, "band_height", "block_center_y", "line_gap", "font",
|
||||
"font_size", "emphasis_font", "emphasis_face", "emphasis_size",
|
||||
@@ -220,6 +241,8 @@ def main() -> int:
|
||||
"plain_subtitle_config": subtitles.cmd_plain_subtitle_config,
|
||||
"set_plain_subtitle_config": subtitles.cmd_set_plain_subtitle_config,
|
||||
"apply_voice_actions": voice.cmd_apply_voice_actions,
|
||||
"generate_voice_script": voice.cmd_generate_voice_script,
|
||||
"list_ollama_models": voice.cmd_list_ollama_models,
|
||||
"build_phrase_review": review.cmd_build_phrase_review,
|
||||
"save_phrase_review": review.cmd_save_phrase_review,
|
||||
"project_config": project.cmd_project_config,
|
||||
|
||||
Reference in New Issue
Block a user