README.md (14160B)
1 # Lazier 2 3 Sistema CLI e Web para **transcrição** e **sumarização** de áudios, vídeos, textos e PDFs usando os modelos mais recentes da OpenAI (família GPT-5 + `gpt-4o-mini-transcribe`/`whisper-1`). Além de transcrição/sumário tradicionais, gera **sumário inteligente** estruturado (TL;DR, pontos-chave, decisões, *action items*, tópicos e citações) e **capítulos com timestamps** para áudio/vídeo. Suporta upload de arquivos, URLs do YouTube e de centenas de outros sites (via yt-dlp), e páginas web. Exporta em DOCX, TXT, Markdown, JSON e PDF. 4 5 ## Requisitos 6 7 - **OpenAI API Key** (Whisper para transcrição, GPT para sumário) 8 - **Redis** (opcional, para cache de transcrições/sumários) 9 - **ffmpeg** e **yt-dlp** (instalados automaticamente no Docker; em instalação local, instale manualmente) 10 11 ## Configuração (.env) 12 13 Copie o exemplo e preencha: 14 15 ```bash 16 cp .env.example .env 17 ``` 18 19 | Variável | Obrigatório | Descrição | 20 |----------|-------------|-----------| 21 | `OPENAI_API_KEY` | Sim | Chave da API OpenAI ([obter aqui](https://platform.openai.com/api-keys)) | 22 | `SESSION_SECRET_KEY` | Sim (Web) | Chave para sessões. Gere com: `openssl rand -hex 32` | 23 | `ADMIN_USER` / `ADMIN_PASSWORD` | Não | Usuário admin da WebGUI (criado na primeira execução se definidos) | 24 25 ### Modelos e inteligência (opcionais) 26 27 Defaults seguros já vêm configurados; sobrescreva apenas se quiser mudar. 28 29 | Variável | Default | Descrição | 30 |----------|---------|-----------| 31 | `OPENAI_CHAT_MODEL` | `gpt-4o-mini` | Modelo de chat usado para sumário, conversão para PT-BR, detecção de tipo e capítulos. Alternativas: `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1-mini`. | 32 | `OPENAI_TRANSCRIBE_MODEL` | `gpt-4o-mini-transcribe` | Modelo padrão de transcrição (saída em texto). Alternativas: `gpt-4o-transcribe`, `whisper-1`. | 33 | `OPENAI_TRANSCRIBE_TIMESTAMPS_MODEL` | `whisper-1` | Modelo usado quando precisamos de `verbose_json` para gerar capítulos. Atualmente `whisper-1` é o mais confiável para retornar `start`/`end`. | 34 | `OPENAI_ENABLE_SMART_SUMMARY` | `true` | Liga/desliga o sumário estruturado (TL;DR, pontos-chave, decisões, ações, tópicos, citações, perguntas em aberto). Quando `false`, mantém o sumário textual legado. | 35 | `OPENAI_ENABLE_CHAPTERS` | `false` | Capítulos com timestamps (exige STT `verbose_json`, mais lento). | 36 | `OPENAI_REASONING_EFFORT` | `minimal` | Esforço de raciocínio para modelos da família `gpt-5`/`o-series`. Valores: `minimal`, `low`, `medium`, `high`. | 37 | `LAZIER_QUALITY_PRESET` | `economico` | Preset global `economico` / `equilibrado` / `maximo` → defaults de chat, transcrição e `reasoning`. | 38 | `LAZIER_SUMMARY_HIERARCHICAL` | `true` | Acima de `LAZIER_SUMMARY_DIRECT_MAX_CHARS`, o sumário inteligente usa map-reduce com chunks e overlap em vez de um único passe sobre o texto completo. | 39 | `LAZIER_SUMMARY_DIRECT_MAX_CHARS` | `48000` | Limiar (caracteres) para ativar sumarização hierárquica. | 40 | `LAZIER_SUMMARY_MAP_CHUNK_CHARS` | `16000` | Tamanho alvo de cada chunk no map. | 41 | `LAZIER_SUMMARY_CHUNK_OVERLAP_CHARS` | `800` | Sobreposição entre chunks consecutivos. | 42 | `LAZIER_ENABLE_PT_POLISH` | `false` | Revisão ortográfica extra após PT-BR (mais chamadas chat). | 43 | `LAZIER_DETECT_CONTENT_TYPE` | `false` | Classificador de tipo antes do sumário (1 chamada chat). | 44 | `LAZIER_STT_PARALLEL_WORKERS` | `3` | Transcrição paralela de chunks de áudio. | 45 | `LAZIER_SUMMARY_PARALLEL_WORKERS` | `3` | Sumário inteligente paralelo por chunk. | 46 | `LAZIER_ALT_STT_ENABLED` | `false` | Reserva para spike de segundo STT ou diarização. | 47 | `LAZIER_ALWAYS_SUMMARY` | `false` | Se `true`, força geração de sumário mesmo quando o modo pedido é só `transcribe` (override de instalação). | 48 | `LAZIER_DIARIZATION_PROVIDER` | `none` | Diarização opcional de falantes (`none`, ou provedores futuros). | 49 50 Opcional: `YOUTUBE_PO_TOKEN` para melhor suporte a alguns vídeos do YouTube ([guia](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide)). 51 52 **Segredos:** nunca commite ficheiros `.env` com valores reais, chaves API ou passwords. Mantenha apenas `.env.example` no Git; use variáveis de ambiente no host ou *secrets* do CI/CD (GitHub Actions, etc.). 53 54 ## Instalação Rápida 55 56 ### Docker (Recomendado) 57 58 ```bash 59 # 1. Clone o repositório 60 git clone <repo-url> 61 cd lazier 62 63 # 2. Configure o .env (OPENAI_API_KEY, SESSION_SECRET_KEY e opcionalmente ADMIN_USER/ADMIN_PASSWORD) 64 cp .env.example .env 65 # Edite .env com suas chaves 66 67 # 3. Inicie os serviços (a partir da pasta do projeto) 68 docker compose -f docker/docker-compose.yml up -d --build 69 ``` 70 71 Ou, a partir da pasta `docker/`: 72 73 ```bash 74 cd docker 75 docker compose up -d --build 76 ``` 77 78 Acesse: **http://localhost:19283** 79 80 Arquivos gerados são salvos em `outputs/` na raiz do projeto. 81 82 ### Instalação Local 83 84 ```bash 85 pip install -r requirements.txt 86 cp .env.example .env 87 # Preencha OPENAI_API_KEY e SESSION_SECRET_KEY no .env 88 89 # Redis (opcional, para cache) 90 docker run -d -p 52847:6379 redis:7-alpine 91 92 lazier web 93 ``` 94 95 Acesse **http://localhost:19283** (ou use `--port` para outra porta). 96 97 ## Uso 98 99 ### CLI 100 101 ```bash 102 # Só transcrição (texto completo em PT-BR — default, mais económico) 103 lazier transcribe audio.mp3 104 lazier transcribe video.mp4 105 lazier transcribe "https://www.youtube.com/watch?v=VIDEO_ID" 106 107 # Só sumário (STT/extração internos; exporta apenas o sumário) 108 lazier summarize document.pdf 109 lazier summarize "https://example.com/artigo" --format md 110 lazier summarize aula.mp3 --gpt-model gpt-5 --reasoning high 111 112 # Transcrição + sumário (dois artefactos; maior consumo de tokens) 113 lazier process aula.mp3 --format md 114 115 # Desligar features opcionais 116 lazier summarize aula.mp3 --no-smart --no-chapters 117 118 # Outras opções 119 lazier summarize video.mp4 --format json # docx, txt, md, json, pdf 120 lazier config # mostra a config corrente 121 lazier web # inicia servidor web 122 lazier cache clear # limpa cache 123 ``` 124 125 Flags principais: 126 127 - `--model` sobrescreve `OPENAI_TRANSCRIBE_MODEL` para esse comando. 128 - `--gpt-model` sobrescreve `OPENAI_CHAT_MODEL`. 129 - `--smart/--no-smart` força ligar/desligar o sumário estruturado. 130 - `--chapters/--no-chapters` força ligar/desligar capítulos com timestamps. 131 - `--reasoning {minimal,low,medium,high}` ajusta o esforço de raciocínio para modelos `gpt-5`/`o-series`. 132 133 O pacote custo/qualidade (`economico` / `equilibrado` / `maximo`) vem só de `LAZIER_QUALITY_PRESET` no `.env` (`lazier config` mostra o valor atual). 134 135 ### Economia de tokens 136 137 | Modo | O que gera | Notas | 138 |------|------------|-------| 139 | `transcribe` | Transcrição PT-BR | Não chama sumário inteligente nem detecção de tipo de conteúdo. | 140 | `summarize` | Sumário PT-BR | Áudio/vídeo ainda passam por STT; transcrição completa não é exportada. | 141 | `process` | Transcrição + sumário | Dois ficheiros na WebGUI; custo total de chat. | 142 143 Na WebGUI escolha **Transcrição**, **Sumário** ou **Ambos**. Na API, omitir `mode` equivale a `transcribe`. Use `"mode": "process"` ou `transcribe: true, summarize: true` para os dois. 144 145 ### Velocidade e custo (defaults) 146 147 O perfil **económico** shipado prioriza latência e preço: 148 149 - **`gpt-4o-mini`** para chat (conversão PT-BR e sumário). 150 - **Capítulos desligados** — evita STT com `whisper-1` + geração de capítulos. 151 - **Sem polish nem detecção de tipo** — poupa 1–3 chamadas chat por job. 152 - **Áudio normalizado** mono 16 kHz antes do STT (ficheiros menores). 153 - **Chunks STT/sumário em paralelo** (até 3 workers). 154 155 Para máxima qualidade: `LAZIER_QUALITY_PRESET=maximo`, `OPENAI_ENABLE_CHAPTERS=true`, `LAZIER_ENABLE_PT_POLISH=true`. 156 157 ### WebGUI 158 159 Acesse http://localhost:19283 após iniciar com `lazier web` ou Docker. Na primeira vez, faça login com o usuário e senha definidos em `ADMIN_USER` e `ADMIN_PASSWORD` no `.env` (se configurados). 160 161 ### API HTTP 162 163 `POST /api/process` e `POST /api/upload` aceitam, além dos campos clássicos, overrides por requisição: 164 165 ```json 166 { 167 "url": "https://example.com/aula.mp3", 168 "format": "md", 169 "mode": "summarize" 170 } 171 ``` 172 173 Modos: `transcribe` (default), `summarize`, `process` (ambos). Overrides opcionais: 174 175 ```json 176 { 177 "url": "https://example.com/aula.mp3", 178 "format": "md", 179 "mode": "process", 180 "chat_model": "gpt-5", 181 "transcribe_model": "gpt-4o-transcribe", 182 "smart": true, 183 "chapters": true 184 } 185 ``` 186 187 Os campos `chat_model`, `transcribe_model`, `smart` e `chapters` são opcionais e sobrescrevem os defaults definidos no `.env` apenas para esse job. `GET /api/jobs/{id}/details` passa a devolver `smart_summary`, `chapters` e `content_type` quando disponíveis. 188 189 ## Inteligência 190 191 ### Sumário estruturado 192 193 Quando `OPENAI_ENABLE_SMART_SUMMARY=true` (default), o Lazier gera um objeto `SmartSummary` validado por Pydantic com Structured Outputs. Cada saída inclui: 194 195 - `tldr`: resumo de 1-3 frases 196 - `key_points`: pontos principais em ordem lógica 197 - `decisions`: decisões explícitas tomadas no conteúdo 198 - `action_items`: lista de `{owner, task, due_hint}` 199 - `topics`: temas curtos 200 - `quotes`: trechos literais marcantes 201 - `open_questions`: perguntas em aberto 202 203 O caminho legado (`summarize_text` em texto livre) ainda é usado como fallback se o modelo não suportar Structured Outputs ou se `OPENAI_ENABLE_SMART_SUMMARY=false`. 204 205 ### Detecção de tipo de conteúdo 206 207 Antes de gerar o sumário, o sistema chama um classificador curto que escolhe entre `lecture`, `podcast`, `interview`, `news`, `tutorial`, `meeting`, `tech_doc` ou `other`. O tipo detectado é exibido na CLI/API e usado para ajustar o prompt do sumário (ex.: reuniões enfatizam `decisions`/`action_items`; palestras enfatizam conceitos e exemplos). 208 209 ### Capítulos com timestamps 210 211 Para áudio/vídeo, quando `OPENAI_ENABLE_CHAPTERS=true`, o Lazier: 212 213 1. Pede ao modelo de transcrição (`whisper-1` por padrão para timestamps) saída em `verbose_json` com `segments`. 214 2. Envia esses segmentos ao modelo de chat, que devolve uma lista de capítulos com título, resumo e índices inicial/final. 215 3. Materializa cada capítulo com `start`, `end` e `start_hms`/`end_hms`. 216 217 Os capítulos aparecem nos exports DOCX/Markdown/PDF/TXT e no JSON cru. 218 219 Os ficheiros exportados usam o **título do vídeo ou do conteúdo** (sanitizado) como nome e como cabeçalho do documento. Quando há transcrição e sumário em ficheiros separados, os sufixos `- transcricao` e `- sumario` distinguem cada artefato. 220 221 ## Sites suportados (vídeo/áudio) 222 223 Além do YouTube, você pode colar URLs de vídeo ou áudio de **centenas de sites**. O Lazier usa o [yt-dlp](https://github.com/yt-dlp/yt-dlp) para extrair o áudio; se a URL não for um vídeo, o sistema tenta extrair o texto da página e sumarizar. 224 225 **Exemplos de sites que você pode processar:** 226 227 | | | | 228 |---|---|---| 229 | YouTube | TED | Reddit | 230 | Vimeo | Twitter / X | TikTok | 231 | Instagram | Facebook | Twitch | 232 | Dailymotion | BBC, CNN, NBC | NPR, PBS | 233 | Arte, France TV, RTVE | Khan Academy | Coursera, Udemy | 234 | LinkedIn Learning | Loom | Streamable | 235 | BitChute, Odysee | Rumble | PeerTube | 236 | archive.org | Patreon | Substack | 237 | Wistia | Niconico | Bilibili | 238 | Kick, Floatplane | Nebula | CuriosityStream | 239 | C-SPAN | Al Jazeera | DW, Reuters | 240 | ESPN, Fox Sports | Formula 1 | Olympics | 241 | NYTimes | Washington Post | The Guardian | 242 243 E muitos outros. **Lista completa** mantida pelo yt-dlp: [Supported sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). 244 245 **Importante:** Conteúdo cujo **tema é música** (categoria Music no YouTube, tags de clipe oficial, domínios só de música) **não é processado** pelo Lazier. Vídeos com **música de fundo** (vlogs, palestras, tutoriais) continuam sendo processados normalmente. 246 247 ## Docker 248 249 ```bash 250 # A partir da raiz do projeto 251 docker compose -f docker/docker-compose.yml up -d --build # Iniciar (com rebuild) 252 docker compose -f docker/docker-compose.yml logs -f # Ver logs 253 docker compose -f docker/docker-compose.yml down # Parar 254 docker compose -f docker/docker-compose.yml down -v # Parar e limpar volumes 255 ``` 256 257 Ou entre na pasta `docker/` e use `docker compose up -d` (sem o `-f`). 258 259 ## Cache Redis 260 261 Cache automático de transcrições e sumários (TTL: 7 dias). 262 263 ```bash 264 lazier cache clear # Limpar cache 265 lazier cache stats # Estatísticas 266 ``` 267 268 269 **Nota:** ffmpeg e yt-dlp são instalados automaticamente no container Docker. 270 271 ## Troubleshooting 272 273 - **"OPENAI_API_KEY não encontrada"**: Crie/edite o `.env` com `OPENAI_API_KEY=sua_chave` 274 - **"SESSION_SECRET_KEY não configurada"**: Necessária para a WebGUI. Gere com `openssl rand -hex 32` e adicione ao `.env` 275 - **"Redis não disponível"**: O sistema funciona sem cache. Para usar cache: `docker run -d -p 52847:6379 redis:7-alpine` (no host use `REDIS_PORT=52847` no `.env` se conectar ao Redis local) 276 - **Arquivo muito grande**: A API Whisper tem limite de 25 MB. Divida arquivos grandes ou use a divisão automática do Lazier 277 - **Conteúdo detectado como música**: URLs de música (ex.: categoria Music no YouTube) não são processadas; use apenas vídeos/podcasts 278 279 ## Segurança e repositório público 280 281 Antes de tornar o repositório público no GitHub: 282 283 1. **Secret scanning** — Em *Settings* → *Code security and analysis*, ative **Secret scanning** (e, se disponível, *push protection*) para alertas sobre segredos no código. 284 2. **Rever o histórico** — Com o remoto atualizado (`git fetch --all`), confirme que nunca houve `.env` ou chaves no histórico, por exemplo: 285 - `git log --all --full-history -p -- .env` 286 - `git log --all -S "sk-" --oneline -- "*.py" "*.md"` 287 3. **Se algum segredo já foi commitado** — Revogue ou regenere imediatamente essa chave na respetiva plataforma (OpenAI, etc.). Limpar o ficheiro no último commit **não** remove o segredo do histórico; use ferramentas como [`git-filter-repo`](https://github.com/newren/git-filter-repo) ou BFG Repo-Cleaner e faça *force push* com consciência do impacto em colaboradores. 288 289 ## Contato 290 291 Pablo Murad - pablomurad@pm.me