965
Tradução de legendas .srt para português (ou qualquer idioma) usando IA
Envie uma legenda em outro idioma, escolha o provedor de IA e receba o .srt traduzido — com progresso em tempo real.
.srt com validação de extensão, MIME type e estrutura do arquivosk-ant••••qQAA)[entre colchetes] — opcional, tira sons/idioma/tom de voz ([música tensa], [em inglês]) da legenda antes de traduzir, preservando fala real e nomes de personagem.envHttpOnly, com tela Conta para trocar usuário e senha sem perder a sessãoia-translate/
├── src/ # Backend (Node.js + Express)
│ ├── app.js # Entry point
│ ├── config/
│ │ ├── providers.js # Catálogo de provedores e modelos suportados
│ │ ├── settings.store.js # Persistência de configurações (data/settings.json)
│ │ └── auth.store.js # Usuário/senha (hash+salt) em data/auth.json
│ ├── middleware/
│ │ └── auth.middleware.js # requireAuth — valida o cookie de sessão
│ ├── routes/
│ │ ├── subtitle.routes.js # Upload, progresso (SSE) e download
│ │ ├── settings.routes.js # GET/PUT de configurações
│ │ └── auth.routes.js # Login/logout, troca de usuário/senha
│ ├── services/
│ │ ├── translator.service.js # Orquestra tradução em lotes
│ │ ├── job-store.js # Jobs assíncronos em memória
│ │ ├── session.store.js # Sessões de login em memória (token -> usuário)
│ │ └── providers/ # Implementações por provedor (Anthropic/OpenAI/Google)
│ └── utils/srt-parser.js # Parse e serialização de .srt
│
└── frontend/ # Angular (standalone, sem Router)
└── src/app/
├── translate/ # Tela de tradução + barra de progresso + cancelamento
├── settings/ # Tela de configuração de provedor/modelo/chave/prompt
├── login/ # Tela de login
├── account/ # Tela de conta (trocar usuário/senha)
└── services/
├── settings.service.ts # Cliente HTTP das configurações
├── auth.service.ts # Cliente HTTP de login/conta
└── i18n.service.ts # Traduções da interface (pt/en) e idioma ativo
.srt via multipart/form-data para POST /api/subtitles/translate.jobId imediatamente.GET /api/subtitles/translate/:jobId/events) e recebe atualizações de progresso lote a lote.GET /api/subtitles/translate/:jobId/download.A tradução é feita em lotes de até 40 blocos por requisição ao modelo, preservando timestamps, formatação e ordem das falas.
O modelo deve responder em JSON, mas ocasionalmente devolve quebras de linha "cruas" ou aspas internas não escapadas dentro do texto (em vez de \n/\" escapado), ou trunca a resposta em lotes muito grandes — tudo isso é inválido em JSON puro. O backend (base.js) tenta o parse normal primeiro e, se falhar, tenta de novo escapando caracteres de controle e aspas ambíguas dentro das strings antes de desistir e reportar erro; o max_tokens do Anthropic também foi ampliado para reduzir truncamento em lotes grandes.
[entre colchetes]Legendas com faixa de acessibilidade (SDH) costumam incluir descrições de som, idioma e tom de voz entre colchetes — [música tensa], [em inglês], [com voz trêmula]. Na tela de Tradução, o checkbox "Remover anotações [entre colchetes]" (marcado por padrão) limpa isso antes de traduzir, economizando tokens e evitando que a IA tente "traduzir" uma anotação.
A regra, aplicada linha a linha dentro de cada legenda (em srt-parser.js):
[tiros], - [grunhe]) é descartada por completo.[em inglês] Abaixe-se!) tem só o [...] removido — a fala fica: Abaixe-se!.[Angie], [John Smith] continuam na legenda. A heurística considera nome quando o conteúdo do colchete tem no máximo 3 palavras, todas capitalizadas e sem dígito — [Policial 2] ou [tripulante 1], por exemplo, são tratados como rótulo genérico (removidos), não como nome.[empresário\nfala indistintamente], ou usando \N literal, comum em legendas exportadas de .ass/.ssa) também são reconhecidas e removidas por inteiro..srt final.Se preferir manter tudo como está no arquivo original, é só desmarcar o checkbox antes de enviar.
npm install
cp .env.example .env
Edite o .env e preencha a chave do provedor que for usar (ou configure depois direto pela interface, na tela Configurações).
npm run dev
O servidor sobe em http://localhost:3000.
cd frontend
npm install
npx ng serve
Acesse http://localhost:4200. As requisições para /api são automaticamente encaminhadas para o backend via proxy.conf.json.
docker compose up -d --build
A pasta /DATA/AppData/ai-neural-translation (no host) é montada como volume dentro do container em /config — seguindo a convenção de appdata usada por CasaOS, Unraid e NASs em geral. É lá que ficam settings.json (chaves de API e prompt) e auth.json (usuário/senha de login) — como é uma pasta do host montada por bind mount, ela nunca é apagada por um docker compose up --build/atualização de imagem. Configure as chaves de API pela tela Configurações em vez do .env: assim elas sobrevivem a qualquer rebuild.
Se o seu servidor não usa essa convenção, ajuste o caminho do host em
docker-compose.yml(ex:./data:/configpara um bind mount relativo ao projeto). O caminho dentro do container é controlado pela variávelDATA_DIR(padrão/configna imagem Docker).
⚠️ Atualizando de uma versão anterior à 3.0.3: até a 3.0.2 os dados ficavam em
/app/data(normalmente montado a partir de./data). A partir da 3.0.3 o caminho dentro do container mudou para/config, seguindo a convenção de appdata. Essa migração não é automática — antes de atualizar a imagem, você precisa mover manualmenteauth.jsonesettings.jsonpara o novo caminho do host, senão o app volta ao usuário/senha padrão (admin/admin) e perde as configurações salvas:
- Ache onde estão hoje seus
auth.json/settings.json(ex:find / -maxdepth 4 -iname auth.json 2>/dev/null, ou veja os mounts do container atual comdocker inspect <container_atual> --format '{{json .Mounts}}').- Crie o novo diretório de destino, ex:
mkdir -p /DATA/AppData/ai-neural-translation.- Copie os dois arquivos pra lá:
cp auth.json settings.json /DATA/AppData/ai-neural-translation/(ajuste os caminhos de origem/destino conforme seu ambiente — coloque os arquivos direto nessa pasta, sem subpasta extra).- Atualize/recrie o container com o
docker-compose.ymlnovo (volume apontando pra esse caminho em/config).
HTTPS atrás de proxy reverso: o cookie de sessão só recebe o atributo
Securequando a requisição chega como HTTPS (via TLS direto ou pelo headerX-Forwarded-Proto: https). Se você expuser o app atrás de um proxy reverso (Nginx, Traefik, Cloudflare Tunnel etc.) com TLS, garanta que ele encaminhe esse header — caso contrário, acessando via HTTP puro, o login funciona mas a sessão não é mantida entre requisições.
Sessões em memória: as sessões de login ficam em memória no processo Node — reiniciar o container (ex:
docker compose up -d --buildnuma atualização) invalida todas as sessões ativas, exigindo login novamente.
O app fica protegido por tela de login. Usuário e senha padrão na primeira execução:
usuário: admin
senha: admin
As credenciais ficam salvas (hash + salt, nunca em texto puro) em data/auth.json, na mesma pasta persistente usada pelas configurações — troque a senha assim que possível.
Pela tela Conta (aba ao lado de Configurações), com login já feito, dá para:
Nenhuma das duas operações derruba a sessão atual — não é necessário logar de novo depois de alterar usuário ou senha.
Pela tela Configurações você pode, a qualquer momento:
Se nenhuma chave for salva pela interface, o backend usa como fallback as variáveis de ambiente:
| Variável | Provedor |
|---|---|
ANTHROPIC_API_KEY | Anthropic |
OPENAI_API_KEY | OpenAI |
GOOGLE_API_KEY |
O catálogo de modelos de cada provedor (em providers.js) é curado manualmente e reflete os modelos disponíveis nas respectivas APIs — hoje 6 modelos Anthropic, 20 OpenAI (incluindo a série de raciocínio o1/o3/o4-mini) e 8 Google (Gemini). Como os provedores lançam e aposentam modelos com frequência, essa lista pode ficar desatualizada com o tempo — se notar um modelo faltando ou descontinuado, é só editar esse arquivo.
O texto enviado à IA a cada lote de legendas pode ser ajustado sem tocar em código:
TRANSLATION_PROMPT_TEMPLATE no .env (veja .env.example). Se não for definida, o backend usa um prompt embutido no código..env e fica salvo em data/settings.json..env.Dois placeholders são substituídos automaticamente antes de cada requisição ao modelo:
| Placeholder | Conteúdo |
|---|---|
{{targetLanguage}} | Idioma de destino escolhido no upload |
{{items}} | JSON com os blocos de legenda daquele lote |
O prompt precisa conter obrigatoriamente
{{items}}— sem ele a requisição não tem como enviar as legendas ao modelo, e o backend recusa salvar.
O que o prompt padrão já resolve:
Se sua tradução tiver esse tipo de erro (termo que deveria traduzir mas ficou no idioma original, ou gênero errado numa fala), primeiro confira se o prompt em uso é o padrão atual — clique em Restaurar padrão na tela de Configurações para garantir que está usando a versão mais recente.
O header tem duas bandeiras (🇧🇷 / 🇺🇸) para alternar o idioma dos textos da interface entre português e inglês. A escolha fica salva no navegador (localStorage) e não afeta o idioma de destino da tradução, que é selecionado separadamente na tela de Tradução.
/api/subtitles, /api/settings e /api/docs exigem login (cookie de sessão HttpOnly); só /api/auth/login fica aberta.scrypt + salt aleatório, nunca em texto puro.data/settings.json, data/auth.json e .env estão no .gitignore e não devem ser commitados..srt.Antes de publicar este repositório, confira se
.env,data/settings.jsonedata/auth.jsonnão estão sendo versionados (git statusnão deve listá-los) e se o.env.examplecontém apenas placeholders vazios.
.vtt, .ass)Feito com Node.js, Express e Angular.
Content type
Image
Digest
sha256:16005c629…
Size
64.1 MB
Last updated
about 1 month ago
docker pull starleydev/ai-neural-translation