Skip to content

Repository files navigation

🎙️ MeetingTranscriptor

Grave, transcreva e gere atas de reunião — tudo 100% local, em português brasileiro, com identificação de falantes (diarização).

Como o projeto nasceu (e o que ele é hoje)

O projeto começou como uma CLI (transcrever) para transcrever arquivos de áudio de reunião localmente, com WhisperX: faster-whisper (transcrição), wav2vec2 (alinhamento por palavra) e pyannote.audio (diarização). Depois evoluiu para uma extensão Chrome que grava a reunião ao vivo (Meet/Teams/Zoom), transcreve pelo backend local — que reusa o mesmo pipeline da CLI — e abre uma página de revisão com player, edição de falantes e ata gerada por IA.

Seu áudio nunca sai da máquina 🔒 — a única etapa que usa um serviço externo é a ata (opcional), gerada pelo claude CLI da sua conta.

Panorama para quem está chegando

Componente O que faz Código Instalação / docs
CLI transcrever Transcreve qualquer arquivo de áudio (m4a, mp3, wav, mp4…) com diarização transcrever/ Setup rápido abaixo
Backend FastAPI (porta 8765) Fila de transcrição + geração de ata; usado pela extensão server/ server/README.md
Extensão Chrome Grava a reunião (aba + mic), revisão completa, ata, biblioteca de reuniões extension/ extension/README.md
Contratos & prompt da ata Como as partes se integram; prompt padrão da ata docs/ CONTRACTS.md · PROMPT_ATA.md

Roteiro de embarque:

  1. Todo mundo começa igual: instale ffmpeg + rode ./scripts/install.sh
    • login no Hugging Face (Setup rápido) — isso habilita o pipeline de transcrição usado por CLI e backend.
  2. Só quer transcrever um arquivo? Use a CLI (Uso) e pare aqui.
  3. Quer gravar reuniões no Chrome? Suba o backend (./scripts/install_backend_service.sh) e carregue a extensão (como).
  4. Quer a ata automática? Configure o claude CLI (como).

Setup rápido

1. Pré-requisitos

brew install ffmpeg

2. Instalar o projeto

git clone git@github.com:felipemaion/MeetingTranscriptor.git
cd MeetingTranscriptor
./scripts/install.sh
source .venv/bin/activate

O install.sh cria o venv e instala tudo. WhisperX puxa PyTorch (~2 GB), então a primeira instalação demora alguns minutos.

3. Login Hugging Face (se ainda não fez)

huggingface-cli login
# cole seu token Read

Sem token, a diarização não funciona (a transcrição funciona normalmente).

4. Aceitar termos dos modelos pyannote (1 vez na vida, gratuito)

Logado na HF, abra estas duas páginas e clique em "Agree and access repository":

Pronto. Da próxima vez não precisa mais.

Uso

Transcrição com diarização (default)

transcrever caminho/para/reuniao.m4a

Gera em ./saidas/:

  • reuniao.txt — texto separado por falante e com timestamps
  • reuniao.srt — legendas com FALANTE 1: ...
  • reuniao.json — payload completo com segmentos, palavras e speakers

Exemplo do .txt:

[00:00:01] FALANTE 1: Olá pessoal, vamos começar a reunião.
[00:00:04] FALANTE 2: Beleza, eu trouxe os dados de ontem.
[00:00:09] FALANTE 1: Perfeito. Pode compartilhar a tela?

Renomear falantes

Você ouve o início da reunião e mapeia:

transcrever reuniao.m4a \
  --mapeamento "SPEAKER_00=Felipe,SPEAKER_01=Ana,SPEAKER_02=Bruno"

Saída:

[00:00:01] Felipe: Olá pessoal, vamos começar a reunião.
[00:00:04] Ana: Beleza, eu trouxe os dados de ontem.

Também aceita um arquivo JSON:

{ "SPEAKER_00": "Felipe", "SPEAKER_01": "Ana" }
transcrever reuniao.m4a --mapeamento ./mapa.json

Ajudando a diarização (recomendado)

Se você sabe quantos falantes tem na reunião, diga ao modelo — a precisão melhora muito:

transcrever reuniao.m4a -n 3              # exatamente 3 falantes
transcrever reuniao.m4a --falantes-min 2 --falantes-max 5

Outras opções

# Sem diarização (mais rápido, áudio só com você)
transcrever reuniao.m4a --sem-diarizar

# Trocar modelo
transcrever reuniao.m4a -m medium                  # mais leve
transcrever reuniao.m4a -m distil-large-v3         # 6× mais rápido que large-v3

# Pasta de saída diferente
transcrever reuniao.m4a -o ~/Desktop/transcricoes

# Apenas TXT
transcrever reuniao.m4a -f txt

# Detectar idioma automaticamente
transcrever reuniao.m4a -l auto

# Ajudar com nomes próprios e jargões
transcrever reuniao.m4a --prompt "Reunião com Felipe Maion sobre o projeto Cation, GraphQL, FastAPI."

# Inspecionar config + checar dependências
transcrever --info

Configuração padrão (.env)

Tudo que está nas flags pode ir no .env para virar default:

WHISPER_MODEL=large-v3
WHISPER_LANGUAGE=pt
WHISPER_DEVICE=cpu
WHISPER_COMPUTE_TYPE=int8
WHISPER_BATCH_SIZE=8

DIARIZAR=1
FALANTES_MIN=2
FALANTES_MAX=6

Performance no Mac (Apple Silicon)

WhisperX no Mac roda em CPU (CTranslate2 ainda não tem backend Metal). Os tempos típicos para 60 minutos de áudio em M1/M2 com int8 são:

Modelo Transcrição Diarização Total
small ~3 min ~3 min ~6 min
medium ~7 min ~3 min ~10 min
distil-large-v3 ~6 min ~3 min ~9 min
large-v3 ~12 min ~3 min ~15 min

Para reuniões longas, distil-large-v3 costuma ser o melhor custo/benefício em pt-br: qualidade quase idêntica ao large-v3 e bem mais rápido.

Extensão Chrome (MeetingTranscriptor)

Extensão que grava reuniões (Meet/Teams/Zoom) e abre uma página de revisão completa. O que ela faz hoje:

Gravação e falantes

  • Grava aba + microfone em canais separados (estéreo): você é identificado deterministicamente pelo canal do mic.
  • Pedindo mais de 2 falantes, a diarização vira híbrida: o pyannote subdivide só o canal remoto (os participantes da chamada) — ver CONTRACTS §2.1.
  • Renomear falantes (persistido por reunião), reatribuir falante por linha, edição inline do texto, localizar e substituir.

Ata de reunião (via claude CLI — requer instalado e autenticado)

  • Prompt padrão profissional: resumo executivo, decisões e tabela de tarefas com responsável e prazo ("em aberto" quando não dito).
  • Cabeçalho automático com título, data, duração e participantes.
  • Prompt configurável na tela ⚙️: mostra o prompt em uso, salva prompts nomeados e alterna entre eles e o padrão.
  • Exporta copiar / .md / .html / PDF; histórico de atas por reunião.

Persistência e revisão

  • Biblioteca de reuniões (IndexedDB): transcrição, ata e histórico de versões reaparecem ao reabrir. Player sincronizado com a transcrição.
  • Configurações: URL do backend, modelo e prompt da ata, idioma, nº de falantes padrão, ganho do microfone, transcrições em paralelo (MAX_JOBS).

Configurar o "Gerar ata" (Claude CLI)

A ata é gerada pelo Claude Code CLI rodando na sua máquina, com a sua conta Claude (Pro/Max) — sem API key e sem custo por token além da assinatura. Configuração única:

# 1) instalar o CLI (uma das opções)
npm install -g @anthropic-ai/claude-code
# ou: curl -fsSL https://claude.ai/install.sh | bash

# 2) autenticar: rode `claude` e siga o login no navegador
claude

# 3) testar (deve responder no terminal)
claude -p "diga oi"
  • O backend chama o binário claude pelo PATH do processo. Se o serviço launchd não o encontrar (ex.: instalado via nvm), defina CLAUDE_BIN com o caminho completo (which claude) no ambiente do backend.
  • Sem o CLI configurado, só o botão "Gerar ata" falha — gravação e transcrição continuam funcionando (essas são 100% locais).
  • O modelo usado (Sonnet/Opus/Haiku) e o prompt da ata são ajustáveis na tela ⚙️ de configurações da extensão.

Documentação por componente:

# 1) backend — sempre disponível via launchd (sobe no login, reinicia sozinho)
./scripts/install_backend_service.sh
#    (ou, pontual: uvicorn server.app:app --host 127.0.0.1 --port 8765 --reload)
# 2) extensão
cd extension && npm install && npm run build   # carregue extension/dist/ no Chrome

Para remover o serviço do backend: ./scripts/uninstall_backend_service.sh.

Estrutura do projeto

MeetingTranscriptor/
├── transcrever/             # pipeline WhisperX + CLI
│   ├── cli.py               # CLI (Typer + Rich)
│   ├── transcriber.py       # pipeline (diarização por canal + híbrida + pyannote)
│   ├── diarizacao_canais.py # diarização por canal (aba=L/mic=R) + combinar_falantes
│   └── formatters.py        # exportadores TXT / SRT / VTT / JSON com falantes
├── server/                  # API FastAPI (porta 8765) — fila de jobs, ata via claude CLI
├── extension/               # extensão Chrome (MV3) — captura + revisão (React)
│   └── src/review/          # página de revisão: player, falantes, ata, histórico
├── docs/
│   ├── CONTRACTS.md         # contratos de integração entre os componentes
│   └── PROMPT_ATA.md        # prompt padrão da ata (sobrescritível na tela ⚙️)
├── tests/                   # pytest (pipeline + server); testes da extensão em extension/tests/
├── scripts/                 # install.sh, serviço launchd do backend
├── exemplos/                # áudios para testar
└── saidas/                  # arquivos gerados pela CLI (ignorado pelo git)

Tudo open-source / gratuito

Componente Licença Custo
Whisper (modelo) MIT (OpenAI) grátis
faster-whisper / CTranslate2 MIT grátis
WhisperX BSD-2 grátis
pyannote.audio MIT grátis
pyannote/speaker-diarization-3.1 MIT (gated) grátis
ffmpeg LGPL/GPL grátis

Troubleshooting

Could not download model from Hugging Face → Você ainda não aceitou os termos de pyannote/speaker-diarization-3.1 ou pyannote/segmentation-3.0. Volte para o passo 4 do setup.

401 Unauthorized ou Token is invalid → Token HF não está disponível. Rode huggingface-cli login ou exporte HF_TOKEN.

Diarização junta dois falantes em um só → Sotaques muito parecidos ou áudio de baixa qualidade. Use --falantes-min e --falantes-max para forçar a faixa correta. → Em gravações da extensão (estéreo aba/microfone), informe o Nº de falantes na tela de revisão: com >2, a diarização vira híbrida — o canal garante você (mic) × remotos (aba) e o pyannote subdivide só os remotos. Vários remotos chegam mixados na mesma aba do Meet, então separar um do outro é imperfeito; ajuste o que sobrar pelo seletor de falante por linha.

Diarização separa um único falante em vários → Idem: --falantes 1 força um único falante.

Travou em "Identificando falantes" → Diarização em CPU é mais lenta — espere alguns minutos. Se passar de 10 min para áudio de 30 min, tem algo errado: rode com --sem-diarizar para validar a transcrição primeiro.

Aviso torchcodec is not installed correctly → Inofensivo. O pipeline entrega o áudio pré-carregado em memória para o pyannote, então o torchcodec não chega a ser usado. O aviso já vem silenciado no código.

About

Transcrição local de reuniões com diarização (WhisperX) + plugin Chrome para captura de áudio

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages