Um sistema que produz e publica Shorts no YouTube sozinho, todo dia, em três canais ao mesmo tempo. Não abro editor, não escolho o clipe, não aperto publicar.
Começou como um script pra cortar um vídeo em pedaços. Hoje são três canais em dois idiomas, mais de cem vídeos no ar e um custo de operação que cabe em alguns centavos por dia.
O código-fonte é fechado — explico o motivo no final. O que está aqui é a parte que eu acho mais interessante mesmo: as decisões de engenharia, os erros que custaram caro e o que os números me obrigaram a admitir.
Canal de cortes vive de volume. Três vídeos por dia, todo dia, ou o algoritmo esquece de você. Fazendo na mão isso é umas duas horas diárias de trabalho chato e repetitivo: achar o momento bom, cortar, enquadrar em vertical, legendar, escrever título, agendar, publicar. Repetir.
Nada nessa lista precisa de gente. Todo passo é uma regra que dá pra escrever. Então escrevi.
O objetivo nunca foi "automatizar por automatizar" — era descobrir se um canal de cortes inteiramente operado por software consegue competir com um operado por pessoa. A resposta curta é: em um nicho sim, em outro não, e a diferença entre os dois é a parte mais valiosa que aprendi.
flowchart LR
A[Fontes de vídeo] --> B[Descoberta]
B --> C[Filtros de qualidade]
C --> D[Deduplicação]
D --> E[Transcrição local]
E --> F[Curadoria por LLM]
F --> G[Renderização]
G --> H[Fila com horários]
H --> I[Publicação]
O sistema roda três vezes por dia em tarefas agendadas. Cada rodada varre as fontes, decide o que vale a pena, produz os vídeos e joga numa fila com data e hora. Um segundo processo, mais leve, acorda de meia em meia hora só pra ver se chegou a hora de publicar alguma coisa.
Essa separação entre produzir e publicar foi a primeira decisão que se mostrou certa. Renderizar vídeo é pesado e demorado, publicar é uma chamada de API. Amarrar os dois no mesmo processo significaria que qualquer travada na renderização faria perder o horário de publicação — e horário, num canal de Shorts, é metade do jogo.
O requisito que moldou a arquitetura inteira: adicionar um canal novo não pode exigir código novo. Tudo que muda de um canal pro outro — fonte, idioma, nicho, horários, estilo de legenda, enquadramento, critérios de seleção — mora em configuração. O motor é um só.
Isso pagou rápido. Os três canais de hoje têm fontes diferentes, idiomas diferentes, formatos de vídeo diferentes e regras de seleção diferentes. Compartilham o mesmo código, linha por linha.
Os primeiros dois segundos decidem se alguém fica. A ideia é abrir com a frase mais forte do vídeo, antes do contexto.
O caminho ingênuo é mover essa frase pro começo. Não dá: mover obriga a remontar o resto, e aí toda legenda depois dela sai de sincronia — foi exatamente o defeito que eu estava tentando evitar.
Então a frase é clonada. O corpo é renderizado inteiro e intacto, com a frase no lugar original, e o gancho entra concatenado na frente. Como nada dentro do corpo se desloca, dessincronizar virou impossível por construção — não por cuidado.
| Canais em operação | 3 |
| Idiomas | 2 |
| Vídeos publicados | 100+ |
| Intervenção humana por vídeo | nenhuma |
| Custo de IA | centavos por dia |
| Testes automatizados | 78 |
O custo baixo não é acidente. A parte cara de um pipeline desses seria mandar vídeo inteiro pra uma IA analisar. Eu não faço isso: a transcrição roda localmente e o modelo de linguagem recebe só texto, num pedido curto, pra fazer o que ele faz melhor — julgar o que é interessante e escrever título. Cada vídeo custa uma fração de centavo.
Essa é a parte que eu realmente queria contar. Todo bug aqui foi ao ar antes de eu perceber, e cada um me ensinou algo que eu não teria aprendido lendo documentação.
Dois canais, mesmo motor, mesmo formato, mesma duração de vídeo, mesmos horários de publicação, mesma quantidade de vídeos no ar. Um deles fez uma média de 840 visualizações por vídeo. O outro, 10.
Passei um tempo procurando o defeito no código. Não tinha defeito. A única variável diferente era o nicho: um dos canais cobre um assunto disputadíssimo, onde dezenas de canais publicam o mesmo recorte em poucas horas; o outro cobre um assunto que quase ninguém cobre.
Foi o resultado mais útil do projeto inteiro, e ele não veio de escrever código — veio de montar a comparação certa. Dois sistemas idênticos rodando lado a lado isolaram a variável que importava. Se eu tivesse só um canal, teria passado meses culpando o formato do vídeo.
Fui atrás do relatório de impressões e taxa de cliques pra entender por que um canal não crescia. Somei os cliques que vieram de impressão: 22. Somei as visualizações desses mesmos vídeos: 314.
Ou seja, 93% do tráfego chegava por um caminho que aquele relatório simplesmente não mede. Eu estava prestes a otimizar título e capa — as duas coisas que governam exatamente os 7% restantes.
A lição não é sobre YouTube. É sobre pegar uma métrica emprestada de um contexto e usar em outro sem checar se ela ainda significa a mesma coisa.
As legendas apareciam levemente fora de hora e eu não achava o motivo. Era uma conta de conversão de segundos pra centésimos que truncava em vez de arredondar. Um número como 1,4 segundo virava 1,39 — porque o resto da divisão dava 0,3999... e o corte simplesmente jogava fora o final.
Erro de um centésimo por legenda. Imperceptível numa, visível quando são quarenta seguidas, todas puxadas pro mesmo lado. Nenhuma delas estava "quebrada" o bastante pra eu conseguir apontar o dedo.
Hoje a conta é feita em centésimos inteiros desde o começo, e tem um teste que fixa justamente esse caso. Foi o bug que mais me ensinou sobre viés sistemático: erro aleatório se cancela, erro que sempre aponta pro mesmo lado se acumula.
Coloquei um efeito de transição nos vídeos e ele saía inaudível. O comando que eu usava pra ajustar volume atenua em 3 decibéis — não normaliza para 3 decibéis abaixo do pico, que era o que eu queria. Parecido o suficiente na escrita pra eu ler e achar certo, várias vezes.
O som saía com pico 21 decibéis abaixo do teto, debaixo da voz. Existia, ninguém ouvia. A correção foi medir primeiro e aplicar depois, em duas passadas.
Dois vídeos do mesmo canal saíram praticamente iguais, com horas de diferença. Eram recortes distintos — arquivos diferentes, títulos diferentes, identificadores diferentes — do mesmo instante da mesma transmissão. Quando acontece algo bom ao vivo, vários espectadores recortam aquele momento em poucos segundos, e cada recorte nasce com identidade própria.
Minha checagem de duplicata olhava só a identidade. Passou reto.
O conserto foi parar de perguntar "é o mesmo arquivo?" e passar a perguntar "é o mesmo momento?". Quando a fonte informa a posição exata na gravação, dá pra comparar diretamente. Quando não informa — e frequentemente não informa — dá pra usar o horário de criação, com uma tolerância que acompanha a duração do clipe em vez de ser um número chutado. Dois recortes criados mais perto um do outro do que o comprimento do maior deles quase certamente mostram a mesma coisa.
Foi quando entendi que identidade e conteúdo são perguntas diferentes, e eu vinha usando uma como se fosse a outra.
Eu deixava passar clipes de até 90 segundos. Fui olhar a porcentagem assistida por duração:
| Duração | Assistido |
|---|---|
| 14s | 48% |
| 22s | 30% |
| 61s | 27% |
Clipe longo não entrega mais minutos — entrega abandono mais cedo, em proporção. E abandono é justamente o sinal que faz a distribuição parar. Baixei o teto pra 35 segundos, o que descarta clipe bom de vez em quando e vale a pena assim mesmo.
Configurei um canal pra buscar só clipes das últimas 12 horas (pra chegar antes da concorrência) e só clipes com pelo menos 300 visualizações na origem (pra não publicar enchimento). Fazia sentido separadamente.
Juntas, produziram zero vídeos por dois dias seguidos. Clipe acumula visualização com o tempo: numa janela de 12 horas quase nenhum teve tempo de chegar a 300, por melhor que fosse. Eu tinha pedido "recente" e "já popular" ao mesmo tempo, que é uma contradição.
A correção certa não é mexer nos números — é medir velocidade em vez de total. Um clipe de duas horas com 100 visualizações é mais forte que um de quarenta horas com 300.
Um dia inteiro sem publicar. Fui atrás dos registros do sistema: a máquina suspendeu de madrugada e acordou quase meio-dia. A tarefa agendada do meio da manhã simplesmente não existiu.
Antes disso já tinha perdido outro dia por um motivo diferente: um agendamento configurado pra repetir a cada meia hora durante 24 horas parecia certo, mas expirava ao fim das 24 horas em vez de recomeçar no dia seguinte. O painel não reclama, o horário da próxima execução só fica em branco.
Os dois casos viraram comentário no script de instalação, com a data e o custo. Automação que roda em máquina de casa tem uma categoria inteira de falha que não aparece em nenhum teste: a de coisas que não aconteceram.
Bug silencioso é o caro. Quase tudo aqui — o arredondamento, o efeito sonoro, o lance duplicado, a tarefa que expirou — funcionava o bastante pra não gritar. Sistema que roda sozinho precisa ser barulhento quando pula uma etapa, senão você descobre semanas depois, olhando o resultado.
Comentário bom explica a armadilha, não o óbvio. Os comentários que valem no meu código não descrevem o que a linha faz — registram por que ela é daquele jeito, o que eu tentei antes e o que quebrou. Voltei neles várias vezes.
Teste que fixa um bug real vale por dez genéricos. Cada erro dessa lista virou um teste com os números verdadeiros do dia em que aconteceu. São os que eu confio.
O gargalo raramente é onde você procura. Passei muito mais tempo mexendo em renderização do que em escolha de conteúdo. Os dados mostraram que o que decide o resultado é a escolha, não o acabamento.
Python para a orquestração. ffmpeg para todo o processamento de vídeo e áudio. Reconhecimento de fala local para transcrição com marcação por palavra. Um modelo de linguagem para curadoria e redação, sempre com saída estruturada e validada. APIs oficiais das plataformas para descoberta e publicação. Agendador do sistema operacional para o ciclo diário. Suíte de testes automatizados cobrindo a lógica de decisão.
Sem serviço pago de terceiros, sem serviço de nuvem, sem editor de vídeo na jogada.
O sistema está em operação e gera receita. Publicar o código inteiro seria entregar de graça um pipeline pronto pra quem quisesse concorrer comigo nos mesmos nichos — e o valor dele não está em nenhuma linha específica, está no ajuste fino que só apareceu depois de meses de dados reais.
O que dá pra avaliar sem o código, eu coloquei aqui: como está arquitetado, que decisões foram tomadas e por quê, que erros eu cometi e como descobri cada um.
Se você quiser ver o código funcionando de perto, é só me chamar — mostro numa conversa com prazer.
Aberto a conversar sobre automação, pipelines de mídia e sistemas que rodam sozinhos.