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
@@ -7,7 +7,7 @@
|
||||
> mudança de código. Se algo aqui divergir do código, **o código está certo e
|
||||
> este documento está velho** — corrija-o no mesmo commit.
|
||||
|
||||
Última varredura: 2026-08-19 · 74 ferramentas MCP · 1.454 testes · versão `0.6.35`
|
||||
Última varredura: 2026-08-19 · 77 ferramentas MCP · 1.498 testes · versão `0.6.35`
|
||||
|
||||
---
|
||||
|
||||
@@ -29,7 +29,7 @@ entrada diferentes** para o mesmo motor:
|
||||
▼ ▼
|
||||
┌──────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ admin/models_api.py │ │ server.py + server_tools/ │
|
||||
│ + admin/api/ │ │ 74 tools, dispatch, schemas │
|
||||
│ + admin/api/ │ │ 77 tools, dispatch, schemas │
|
||||
│ 37 comandos da ponte │ │ NÃO tem lógica de timeline │
|
||||
└───────────┬──────────────┘ └───────────────┬──────────────┘
|
||||
└───────────────┬────────────────────┘
|
||||
@@ -65,7 +65,7 @@ no mesmo engine, então uma correção ali vale para as duas.
|
||||
| **Deps opcionais** | `librosa`/`ffmpeg`/`huggingface_hub` importados **lazy**, degradam com `None`. |
|
||||
| **Idioma** | Comunicação com o usuário em português. Código e comentários em inglês. |
|
||||
| **Validação** | `./Engine/run_after_fix.sh` **sempre** após cada correção. |
|
||||
| **App** | Alterou `MacApp/`? Compile e rode: `./MacApp/build_app.sh --run`. |
|
||||
| **App** | Alterou `MacApp/`? Compile e rode: `admin/run_app.command` (padrão de revisão; equivale a `./MacApp/build_app.sh --run`). |
|
||||
|
||||
---
|
||||
|
||||
@@ -140,7 +140,7 @@ programático. Round-trips sempre voltam pelas ferramentas XML.
|
||||
| Controle Live do FCP | `fcpxml/live.py` |
|
||||
| Segurança XML | `fcpxml/safe_xml.py` |
|
||||
| Validação contra DTDs da Apple | `fcpxml/dtd.py` |
|
||||
| Transporte MCP (74 tools) | `server.py` + `server_tools/` |
|
||||
| Transporte MCP (77 tools) | `server.py` + `server_tools/` |
|
||||
| Ponte com o app (37 comandos) | `admin/models_api.py` + `admin/api/` |
|
||||
| Interface do usuário | `MacApp/Sources/` |
|
||||
|
||||
@@ -174,4 +174,4 @@ Se você precisar disso, a lógica está no lugar errado.
|
||||
| Uma **regra de edição** nova | `fcpxml/` sempre. Se você está escrevendo `if` sobre timeline fora de `fcpxml/`, pare. |
|
||||
|
||||
O trabalho principal é **sempre** no engine. As camadas de cima são finas de
|
||||
propósito: é o que permite testar 1.454 casos sem abrir o app nem subir o MCP.
|
||||
propósito: é o que permite testar 1.498 casos sem abrir o app nem subir o MCP.
|
||||
|
||||
@@ -26,13 +26,14 @@ Versão: `0.6.35` · Última varredura: 2026-08-19
|
||||
| `text_layout.py` | 901 | Diagramação das legendas dinâmicas |
|
||||
| `rough_cut.py` | 798 | Geração de timelines novas |
|
||||
| `model_manager.py` | 748 | Modelos Whisper: catálogo, download, config |
|
||||
| `voice_timeline.py` | 594 | O JSON de voz que a IA lê |
|
||||
| `voice_timeline.py` | 600 | O JSON de voz que a IA lê |
|
||||
| `phrase_review.py` | 547 | Revisão de frases (etapa 5 do assistente) |
|
||||
| `collision.py` | 472 | Colisão entre títulos na tela |
|
||||
| `font_metrics.py` | 445 | Largura real de glifos por fonte |
|
||||
| `templates.py` | 387 | Templates de timeline |
|
||||
| `parser.py` | 367 | FCPXML → objetos Python |
|
||||
| `transcribe.py` | 300 | Transcrição Whisper e corte por texto |
|
||||
| `transcribe.py` | 332 | Transcrição Whisper e corte por texto |
|
||||
| `forced_align.py` | 181 | Alinhamento forçado opcional (whisperx/wav2vec2) que corrige o viés de ~0,4s no início das palavras |
|
||||
| `live.py` | 273 | Modo Live (push_to_fcp) |
|
||||
| `diff.py` | 269 | Comparação de timelines |
|
||||
| `voice_actions.py` | 263 | Decisões de edição (cut/zoom/text/marker) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 03 — Camada MCP (`server.py` + `server_tools/`) — 74 ferramentas
|
||||
# 03 — Camada MCP (`server.py` + `server_tools/`) — 77 ferramentas
|
||||
|
||||
> **Escopo:** As 74 ferramentas MCP: helpers, categorias e como criar uma nova.
|
||||
> **Escopo:** As 77 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
|
||||
@@ -43,7 +43,7 @@ continua funcionando. A coluna diz o módulo real, para quando você precisar
|
||||
| `_cut_transcript_spans()` | `_shared/media.py` | Corte por trecho falado |
|
||||
| `_apply_placed_action()` | `_shared/media.py` | Aplica zoom/text/marker já posicionado |
|
||||
|
||||
## As 74 ferramentas por categoria
|
||||
## As 77 ferramentas por categoria
|
||||
|
||||
### Timeline & análise (Projeto)
|
||||
`list_projects`, `analyze_timeline`, `list_clips`, `list_markers`, `list_connected_clips`,
|
||||
@@ -82,18 +82,31 @@ 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`, `get_voice_analysis_config`,
|
||||
`save_voice_analysis_config`.
|
||||
`remove_speakers`, `apply_voice_actions`, `generate_voice_script`,
|
||||
`get_voice_analysis_config`, `save_voice_analysis_config`.
|
||||
|
||||
O fluxo é sempre o mesmo: `build_voice_timeline` mede (caro, roda uma vez) →
|
||||
O fluxo manual é: `build_voice_timeline` mede (caro, roda uma vez) →
|
||||
o modelo decide os cortes → **`refine_voice_timeline` renormaliza sobre o que
|
||||
sobrou** (barato, sem reabrir áudio) e propõe as janelas de zoom → o modelo
|
||||
corta a lista pelo ritmo → `apply_voice_actions` aplica. Pular a renormalização
|
||||
faz o ranking de ênfase apontar para as palavras erradas (ver
|
||||
`05_EXPERIENCIAS.md`).
|
||||
|
||||
`generate_voice_script` é o fluxo **automático e fechado** (sem wizard, sem
|
||||
copiar-e-colar): transcreve (cache) → `build_voice_timeline` → entrega a
|
||||
timeline a um **modelo local Ollama** que dirige a edição → devolve o roteiro
|
||||
legível (markdown) **e** o JSON de ações, e opcionalmente aplica num FCPXML.
|
||||
O cliente fica em `fcpxml/llm_local.py`; o modelo é tratado como entrada não
|
||||
confiável e cada ação é validada por `parse_actions`. Padrão:
|
||||
`qwen2.5:7b-instruct-q4_K_M` (troca de `gemma3:12b` — não cabia em máquina de
|
||||
8GB de RAM; Gemma 3 4B foi testado antes e falhou por apagar o roteiro
|
||||
principal em vez de só cortar bastidor). Passe `model=` para usar outro
|
||||
servido pelo Ollama.
|
||||
|
||||
### Legendas dinâmicas (geração → validação → aplicação)
|
||||
`generate_dynamic_subtitles`, `validate_subtitle_layout`, `transcript_markers`.
|
||||
`generate_dynamic_subtitles`, `generate_plain_subtitles`,
|
||||
`generate_subtitles_by_emphasis`, `validate_subtitle_layout`,
|
||||
`transcript_markers`.
|
||||
|
||||
**Sempre gere e depois valide — nunca dê a geração como pronta sem
|
||||
`validate_subtitle_layout`.** A composição garante "sem sobreposição" só
|
||||
@@ -111,6 +124,23 @@ severidade probable/severe → investigar CADA colisão pela fração exata do
|
||||
XML antes de mudar código (ver checklist abaixo)
|
||||
```
|
||||
|
||||
**`generate_subtitles_by_emphasis`** gera as duas legendas numa passada só —
|
||||
mas não divide as palavras entre elas. A comum é gerada **completa, do início
|
||||
ao fim do clipe**, sempre; a dinâmica é gerada só sobre as frases marcadas
|
||||
como ênfase na etapa 5 (zoom aplicado, nível ≥ 1); e onde a dinâmica cobre um
|
||||
trecho, os títulos comuns daquele trecho recebem `enabled="0"` — continuam no
|
||||
XML (editáveis/reativáveis no Final Cut), só não são desenhados. É a tradução
|
||||
literal de `10-revisao-humana.md` (skill `editar-por-voz`): "a frase de
|
||||
ênfase recebe zoom E legenda dinâmica; as demais recebem legenda comum" —
|
||||
sem nunca deixar um vão sem legenda nenhuma se a ênfase for desativada depois
|
||||
(a comum já estava lá, só desligada). A decisão vem de
|
||||
`<mídia>_phrase_actions.json["emphasis_spans"]`, escrito por
|
||||
`save_phrase_review` quando o editor termina a etapa 5 — sem esse arquivo (ou
|
||||
sem `zoom`/`text` marcados na revisão), a tool gera só a comum, tudo ligado,
|
||||
e avisa no relatório ("Sem revisão de ênfase"). Não expõe overrides de estilo
|
||||
por chamada — usa a config salva ("Legendas Dinâmicas"/plain); para estilo
|
||||
pontual, use `generate_dynamic_subtitles`/`generate_plain_subtitles` direto.
|
||||
|
||||
**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
|
||||
|
||||
@@ -31,7 +31,7 @@ que merece entrada.
|
||||
| 11 | 2026-08-18 | Preview das legendas dinâmicas desproporcional ao render do FCP (stagger/gap/canvas divergentes) e `inactive_color` exposto sem efeito | `resolvido` |
|
||||
| 12 | 2026-08-18 | Espaço de coordenadas do modelo "Text": `fontSize`, `kerning` e `Position` no espaço do quadro — converter só o tamanho descolou o espaçamento | `resolvido` |
|
||||
| 13 | 2026-08-19 | Reanálise de ênfase implementada no Engine mas sem ferramenta MCP — Fase 4 da skill era inexecutável | `resolvido` |
|
||||
| 14 | 2026-08-19 | Offset sistemático de ~0,4s no timing por palavra (faster-whisper sem alinhamento forçado) — corrigido manualmente no teste, WhisperX pendente | `parcialmente resolvido` |
|
||||
| 14 | 2026-08-19 | Offset sistemático de ~0,4s no timing por palavra (faster-whisper sem alinhamento forçado) — agora corrigido em pipeline por alinhamento forçado opcional | `resolvido` |
|
||||
| 15 | 2026-08-19 | `add_zoom` perdia o enquadramento real (voltava a 100%) quando dois zooms caiam no mesmo clipe pós-corte; agora empilha ou substitui conforme as janelas se sobrepõem | `resolvido` |
|
||||
| 16 | 2026-08-19 | `validate_subtitle_layout` acusava colisão severa em títulos que só se tocam na borda, por não-associatividade de float; 7 de 8 colisões reportadas no teste real eram falso positivo | `resolvido` |
|
||||
| 17 | 2026-08-19 | Linha de ênfase das legendas dinâmicas sem limite de largura — palavra longa/maiúscula estourava o frame inteiro; auto-fit encolhe até caber, nunca abaixo do corpo | `resolvido` |
|
||||
@@ -43,6 +43,7 @@ que merece entrada.
|
||||
| 23 | 2026-08-19 | Dividir `writer.py` em pacote quebrou `@patch('fcpxml.writer.subprocess')` — a suíte protege comportamento, não localização | `resolvido` |
|
||||
| 24 | 2026-08-19 | `admin/test_models_api.py` existia mas estava fora de `testpaths` — 13 testes que nunca rodaram | `resolvido` |
|
||||
| 25 | 2026-08-20 | `admin/api/shared.py` apontava para `admin/code` (inexistente) após a divisão — install editável mascarou o bug em toda validação anterior | `resolvido` |
|
||||
| 26 | 2026-08-21 | `generate_voice_script` (IA local/Ollama) caía com "Falha ao gerar roteiro por IA local" — prompt embutia a timeline inteira (47k tokens) e estourava `num_ctx`; e `response.json()` de conexão caída escapava como `JSONDecodeError` | `resolvido` |
|
||||
|
||||
> Mantenha o índice acima sempre sincronizado com as entradas mais recentes.
|
||||
|
||||
@@ -118,9 +119,9 @@ Use o bloco abaixo como modelo. Uma entrada = um problema resolvido/reconhecido.
|
||||
- **Por que isso importa mais do que parece:** o erro contamina toda decisão temporal a jusante — zoom disparava ~0,4s antes da palavra-alvo, `gap_before` subestimava pausas reais na mesma medida (o que afeta diretamente a régua de silêncio recém-adotada), e as folgas de corte saíam erradas nas emendas.
|
||||
- **Decisão tomada:** não rodei `remove_media_silence` bruto sobre o corte. A detecção (ffmpeg, limiar -30dB/0,5s) não distingue "batida entre frases dentro da régua de 1,5s" de "ar morto de emenda" — cortar ambos teria apertado frases fluidas. Corrigi os tempos manualmente medindo o ataque real nos pontos críticos (cabeça, 2 emendas, cauda, 3 zooms) e refiz o corte numa passada só.
|
||||
- **Solução adotada (paliativa, aplicada manualmente neste teste):** medir o RMS real com `ffmpeg -af astats=metadata=1:reset=1:length=0.05,ametadata=print` em janelas curtas ao redor de cada ponto crítico antes de fixar um corte ou zoom que dependa de precisão de frame. Não é o padrão do sistema — é o que cobre a lacuna até o alinhamento forçado existir.
|
||||
- **Solução estrutural ainda pendente:** ligar o WhisperX (ou alinhamento forçado equivalente) em `transcribe.py`, o que levaria o erro de ~400ms para ~30ms e corrigiria zoom, corte e `gap_before` de uma vez, sem paliativo por projeto. Não implementado ainda — é mudança de pipeline, exige regerar todos os `_transcript.json`/`_voice_timeline.json` existentes.
|
||||
- **Solução estrutural implementada:** `transcribe.py` agora roda alinhamento forçado fonético (wav2vec2 via whisperx) como passo opcional pós-transcrição, em `fcpxml/forced_align.py` (classe `ForcedAligner`). O erro cai de ~400ms para ~30ms e corrige zoom, corte e `gap_before` de uma vez. É **dependência opcional** (`[align]` extra / pacote `whisperx` do PyPI) — quando ausente ou em qualquer falha, degrada e devolve os tempos brutos sem quebrar a transcrição. O `transcript` traz `"alignment": true/false` e o `voice_timeline` expõe `layers.alignment`, para quem lê o JSON saber se o offset manual ainda é necessário. Não reaproveitamos código da pasta `WHISPERX/` local (problemas conhecidos) — só a ideia documentada aqui. Exige regerar os `_transcript.json`/`_voice_timeline.json` existentes para aplicar nos caches antigos.
|
||||
- **Aprendizado:** "não reestime tempos no olho" (critério 01) continua certo para decisão *editorial* — mas não cobre erro sistemático de *medição* na fonte dos tempos. Um offset constante e na mesma direção, em vários pontos do material, é sinal de bug no pipeline de transcrição, não de julgamento errado sobre o material. Vale conferir com uma amostra de áudio real antes de confiar cegamente em timestamp de word-level de qualquer fonte nova.
|
||||
- **Estado:** `parcialmente resolvido` — paliativo documentado e aplicado neste teste; correção estrutural (WhisperX) pendente de implementação.
|
||||
- **Estado:** `resolvido` — alinhamento forçado implementado em `transcribe.py`/`fcpxml/forced_align.py`; paliativo de medição manual mantido apenas para transcripts antigos sem `layers.alignment=true`.
|
||||
|
||||
---
|
||||
|
||||
@@ -1366,3 +1367,50 @@ o outro; percentil entrega um punhado útil nos dois casos.
|
||||
instaladas por fora do mecanismo sendo testado — ou o teste prova que o
|
||||
ambiente de teste está bem configurado, não que o código está certo.
|
||||
- **Estado:** `resolvido`
|
||||
|
||||
---
|
||||
|
||||
## Entrada #26 — Prompt da IA local estoura o contexto do Ollama (e erro de parse escapa)
|
||||
|
||||
- **Sintoma:** botão "Gerar roteiro por IA local" (etapa 4 do assistente)
|
||||
devolvia "Falha ao gerar roteiro por IA local". Rodando a ponte direto, o
|
||||
erro real aparecia como *"Server disconnected without sending a response"*
|
||||
ou *"Connection refused"* do Ollama, e 0 decisões ("Decisões do modelo: 0").
|
||||
- **Causa raiz (dupla):**
|
||||
1. `build_edit_messages` embutia o JSON da voice timeline **inteiro** no
|
||||
prompt. Uma gravação de 3min vira ~188KB / **~47k tokens** (cada palavra
|
||||
carrega energia, pitch, arousal, valence, `samples`…). Como `num_ctx`
|
||||
estava em 32768, o prompt estourava a janela e o Ollama **dropava a
|
||||
conexão** sem resposta.
|
||||
2. Quando a conexão cai sem resposta, `httpx` entrega um body vazio e
|
||||
`response.json()` lançava `JSONDecodeError` — que **não** é
|
||||
`httpx.HTTPError`, então escapava do `try/except` de `ollama_chat` e
|
||||
virava a exceção genérica que o `cmd_generate_voice_script` transforma
|
||||
em `ok:false` com a mensagem "Falha ao gerar roteiro por IA local: …".
|
||||
- **Correção (em `fcpxml/llm_local.py` + `server_tools/voice.py`):**
|
||||
- `build_edit_messages` agora projeta a timeline (**`_project_timeline`**):
|
||||
mantém só `text`/`start`/`end`/`speaker`/`emphasis`/`pause_before` das
|
||||
palavras e `id`/`name` dos locutores; descarta `layers`, `scales`,
|
||||
`samples` e os floats de áudio. Caiu de ~47k para **~17k tokens** (69KB).
|
||||
- Salvaguarda `_shrink_to_fit`: se ainda passar de `max_chars` (110k),
|
||||
remove os `words` dos segmentos de menor `peak_emphasis` até caber.
|
||||
- `ollama_chat` envolve `post`+`raise_for_status`+`json()` num único
|
||||
`except Exception` que relança como `RuntimeError` claro — fim do
|
||||
`JSONDecodeError` escapando.
|
||||
- `_extract_json` agora desembrulha a lista de 1 elemento `[{source,
|
||||
actions}]` que alguns modelos devolvem, senão o `parse_actions` tratava o
|
||||
objeto-wrapper como uma ação sem `kind` e rejeitava tudo (0 decisões).
|
||||
- `handle_generate_voice_script` levanta `RuntimeError` com a causa quando o
|
||||
modelo não devolve nenhuma decisão utilizável, então o app mostra a
|
||||
mensagem real ("O modelo local não devolveu decisões utilizáveis: …")
|
||||
em vez do genérico.
|
||||
- **Validação:** `tests/test_llm_local.py` ganhou `test_build_edit_messages_is_compact`
|
||||
(prompt < raw, sem `samples`/`energy_raw`/`pitch_hz`) e
|
||||
`test_ollama_chat_wraps_empty_response`. Ponte testada com Ollama mockado
|
||||
nos dois sentidos (sucesso aplica; falha → `ok:false` com msg clara).
|
||||
- **Estado:** `resolvido`
|
||||
|
||||
> **Aprendizado:** modelo local tem contexto finito — nunca embutir o objeto
|
||||
> de análise cru no prompt; projetar só o que a decisão usa. E qualquer parse
|
||||
> de resposta de servidor local deve tratar body vazio/quebrado como erro de
|
||||
> transporte, não como sucesso mudo.
|
||||
|
||||
@@ -19,7 +19,7 @@ o resultado.
|
||||
|
||||
```bash
|
||||
cd code && ./MacApp/build_app.sh # compila e monta o .app
|
||||
cd code && ./MacApp/build_app.sh --run # compila e abre
|
||||
admin/run_app.command # compila, fecha a instância antiga e abre (padrão de revisão)
|
||||
```
|
||||
|
||||
Consequências práticas, todas já sentidas:
|
||||
@@ -121,14 +121,15 @@ quando o botão "Continuar" libera.
|
||||
| 1 | Projeto | Escolhe a pasta de saída e o `.fcpxml` | `project_config` |
|
||||
| 2 | Transcrever | Transcreve toda a mídia do projeto | `transcribe` |
|
||||
| 3 | Analisar voz | Mede ênfase, locutores, emoção | `analyze_voice` |
|
||||
| 4 | Decisões da IA | Copia para o chat, cola o JSON de volta, aplica | `apply_voice_actions` |
|
||||
| 4 | Decisões da IA | Copia para o chat **ou** gera por IA local (Ollama/Gemma 3), aplica | `apply_voice_actions` / `generate_voice_script` |
|
||||
| 5 | **Revisar ênfases** | Lapida frase a frase — ver seção 5 | `build_phrase_review` / `save_phrase_review` |
|
||||
| 6 | Processar | Silêncios, preenchimento, legendas | vários, em cadeia |
|
||||
| 7 | Concluído | Abre no FCP ou mostra no Finder | — |
|
||||
|
||||
**A etapa 4 é a única manual do fluxo**, e de propósito: o julgamento de qual
|
||||
tomada usar e onde dar zoom é conversa com uma IA (skill `editar-por-voz`), não
|
||||
um botão. O app monta o pedido pronto no clipboard e recebe o JSON de volta.
|
||||
**A etapa 4 tem duas saídas:**
|
||||
|
||||
- **Manual (chat):** o app monta o pedido pronto no clipboard (skill `editar-por-voz`) e recebe o JSON de volta — o julgamento de qual tomada usar e onde dar zoom fica com a IA numa conversa.
|
||||
- **Automática (IA local):** botão "Gerar roteiro por IA local (Ollama/Gemma 3)". Ele manda a *voice timeline inteira* (o arquivo) junto com o brief para um modelo local (Ollama), que decide cortes/zooms/textos de uma vez, devolve o roteiro legível + o JSON de ações e já aplica no FCPXML (non-destructive). Não precisa sair do app nem colar nada. O modelo é escolhido num **picker que lista os modelos instalados no Ollama** (populado via `list_ollama_models` quando a etapa abre); se o Ollama estiver fora do ar, cai para um campo de texto livre. Troque para `llama3` etc. se tiver outro modelo. Requer o Ollama rodando em `localhost:11434`.
|
||||
|
||||
**Etapa 1 — armadilha registrada:** não escolha como "o projeto" um arquivo já
|
||||
gerado pelo fluxo (`_voice_edit`, `_silence_removed`, …). Os cortes de voz
|
||||
|
||||
@@ -7,7 +7,7 @@ Este é o documento de rota. Os outros descrevem o que **é**; este diz o que
|
||||
**fazer** e por onde começar quando chega uma implementação, uma melhoria ou
|
||||
uma correção.
|
||||
|
||||
Última varredura: 2026-08-19 · 1.454 testes · lint zerado
|
||||
Última varredura: 2026-08-19 · 1.466 testes · lint zerado
|
||||
|
||||
---
|
||||
|
||||
@@ -98,8 +98,8 @@ quanto arquivo gigante.
|
||||
## 4. Checklist antes de dar algo por pronto
|
||||
|
||||
```bash
|
||||
cd code && ./Engine/run_after_fix.sh # lint zerado + 1.454 testes
|
||||
cd code && ./MacApp/build_app.sh --run # se mexeu no app
|
||||
cd code && ./Engine/run_after_fix.sh # lint zerado + 1.466 testes
|
||||
admin/run_app.command # se mexeu no app (padrão de revisão)
|
||||
```
|
||||
|
||||
E, além do script:
|
||||
@@ -150,7 +150,7 @@ Estão em `01_ARCHITECTURE.md` §2 e valem repetir as três que mais custaram:
|
||||
|
||||
- [01 Arquitetura](01_ARCHITECTURE.md) — camadas e onde cada coisa mora
|
||||
- [02 Módulos](02_MODULES.md) — mapa do engine, módulo a módulo
|
||||
- [03 Server/Tools](03_SERVER_TOOLS.md) — as 74 ferramentas MCP
|
||||
- [03 Server/Tools](03_SERVER_TOOLS.md) — as 77 ferramentas MCP
|
||||
- [04 Testes & Workflow](04_TESTS_AND_WORKFLOW.md)
|
||||
- [05 Experiências](05_EXPERIENCIAS.md) — o que já quebrou e por quê
|
||||
- [06 Boas Práticas](06_BOAS_PRATICAS.md)
|
||||
|
||||
Reference in New Issue
Block a user