Communitygithub.com

Perso dub lip-sync — swap de voz multi-idioma

Dublagem lip-sync multi-idioma Perso AI para Claude/Cursor/Codex: upload→split→traduzir→merge→MP4. README com GIFs demo — boca alinhada à faixa dublada.

O que é Perso dub lip-sync — swap de voz multi-idioma?

Video Localization passo 2 (dub polish): perso-dubbing-plugin-dubbing. SRT/vídeo traduzido → MP4 dub lip-sync multi-idioma antes do hardburn bilíngue.

Funciona com✓Claude Code✓Codex CLI✓Cursor✓Antigravity✓Gemini CLI
npx skills add https://github.com/perso-ai/perso-dubbing-plugin/tree/main/skills/dubbing

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

/dubbing

Uma skill que faz dublagem automática de vídeos via API Perso AI Dubbing.

Regras principais (obrigatório seguir)

  • Apenas o worker vê a chave bruta. Nunca abra a chave com Read, a exiba ou a passe como argumento de linha de comando. Os workers a enviam apenas no cabeçalho XP-API-KEY.
  • Um comando = o trabalho inteiro. scripts/dubbing.mjs lida com upload · split · tradução · merge · salvamento por conta própria. Nunca execute prepare_input.mjs ou probe_split.mjs antes dele — são ferramentas de depuração, e probe_split.mjs faz um upload real (o vídeo seria enviado duas vezes).
  • Sempre execute dubbing.mjs em segundo plano. Os trabalhos levam de minutos a horas; um timeout do shell em primeiro plano mata a execução no meio.
  • Idiomas não especificados → source auto, target en.
  • Vários idiomas vão em um único comando (--target en,zh,ja) — upload/split acontece uma vez e é reutilizado por idioma. Nunca execute uma vez por idioma (re-envia a origem cada vez).
  • Transmita o progresso fielmente. Exiba as linhas [progress] do stdout do worker para o chat, textualmente ou resumidas — e não anuncie etapas que não apareceram (splitting/merging existem apenas para vídeos acima do limite). Os logs detalhados de stderr (com indentação) não precisam ser repassados.
  • Mantenha o usuário informado enquanto executa. Dublagem, lip-sync e separação de áudio imprimem uma pulsação [status] (estágio + contagens de itens, ex.: ja 4/6 · en 1/6 ou separating — file 2/5, com uma dica next check ~Nm), e cada item finalizado é transmitido como uma linha [progress]. Verifique a saída em segundo plano cerca de 3 minutos após o lançamento, depois no intervalo indicado (10 min para mídia ≤10 min, 30 min para mais longa): transmita qualquer nova linha [progress] imediatamente, caso contrário, publique a linha [status] mais recente como uma atualização de uma linha. Nunca invente porcentagens, tempo decorrido ou ETAs — transmita apenas o que as linhas dizem. Paradas que precisam do usuário ([space-select], [split-confirm], [free-limit], [resume-check], [credit-check], crédito insuficiente, conclusão) encerram o processo, então aparecem sem necessidade de polling.
  • Nomeie seu runtime para telemetria (silenciosamente). Adicione --host <claude-code|codex|cursor|antigravity|gemini> ao comando do worker, nomeando o agente que você está executando, se puder determinar. Ele apenas marca a telemetria de uso — nunca o exiba para o usuário e nunca pergunte sobre ele. Omita-o se não tiver certeza.
  • Formatos não suportados são ignorados automaticamente e o restante continua processando — transmita o aviso de ignorado.
  • Uma pausa não é uma falha. Uma linha [space-select], [split-confirm], [free-limit], [credit-check] ou [resume-check] significa que a execução parou para perguntar algo — ela sai com código 0 (então nunca parece um erro), mas o trabalho não está completo. Sempre que uma dessas linhas estiver presente, nunca relate a execução como concluída: transmita-a e aja sobre ela. As linhas são o sinal, não o código de saída.

Configuração (preguiçosa — sem etapa prévia)

  1. Chave da API — não é uma etapa para executar primeiro. O worker se autoprotege no momento em que um comando que precisa da API Perso é executado: na primeira execução, ele abre uma página do navegador (faça login, um clique — a chave é armazenada criptografada) ou imprime instruções baseadas em arquivo se nenhum navegador puder abrir — transmita-as ao usuário. Em uma máquina headless/SSH, o worker pula o fluxo do navegador completamente e começa com registro de arquivo/--import (a página do navegador só pode entregar uma chave para a máquina em que é executada) — transmita essas instruções da mesma forma. Nunca acione o registro de chave você mesmo: o worker inicia connect.mjs/resolve_key.mjs --watch quando necessário, então não os execute, e não execute resolve_key.mjs --check como uma etapa prévia (só se o usuário perguntar explicitamente se uma chave está registrada — uma chave ausente não é um erro). Nunca cole a chave no chat. Obtenha uma chave: https://developers.perso.ai/api-keys
  2. ffmpeg/ffprobe — instalado automaticamente apenas quando um vídeo excede o limite do plano e precisa ser dividido (aprove se a permissão for solicitada). Verificação manual: node scripts/check_deps.mjs.

Execução

Colete a entrada (caminho local ou URL — pergunte novamente se estiver faltando) e execute em segundo plano — o worker faz a proteção da chave sozinho na primeira necessidade; apenas transmita suas instruções impressas se o registro iniciar:

  • Único: node scripts/dubbing.mjs "<arquivo|URL>" [--source auto] [--target en] [--space "nome do espaço"] [--out resultado.mp4] [--lipsync] [--no-save]
  • Vários idiomas / várias entradas (uma ou mais entradas; URLs e arquivos podem ser misturados): node scripts/dubbing.mjs "<URL>" "<arquivo>" --target en,ja — uma saída por entrada × idioma, salva ao lado de cada origem (--out <pasta> as coleta).
  • Pasta (lote): node scripts/dubbing.mjs "<pasta>" [--target en,zh] [--recursive] [--out pasta-de-saida]

Códigos de idioma — --target aceita um código de idioma (en, ja) ou uma tag regional. Variantes regionais existem apenas para Inglês (en = US padrão, en-GB = UK), Português (pt = Brasil padrão, pt-PT = Portugal) e Espanhol (es = México padrão, es-ES = Espanha); en-US, pt-BR e es-MX são grafias aceitas desses padrões. Cada outro idioma tem uma única variante, e --source só precisa do idioma (uma tag regional nele é tratada como o idioma simples).

  • Região nomeada na solicitação → use essa tag exata. Uma região sem variante (ex.: inglês australiano) → informe ao usuário que apenas as opções listadas existem e deixe-o escolher antes de executar.
  • Tokens de duas letras são códigos de idioma, não países: uk significa Ucraniano — inglês britânico é en-GB. Mesma armadilha para ca (Catalão), be (Bielorrusso), eu (Basco). Se a redação do usuário puder significar um ou outro, pergunte qual ele quer antes de executar.
  • Idioma nomeado sem região → a variante padrão. Para esses três idiomas, nomeie o padrão aplicado na nota de entrega (ex.: "Espanhol (México) — use es-ES para Espanha").

Local de salvamento — --out aceita uma pasta (um diretório existente ou um caminho terminando em /) que coleta os resultados dentro dela, ou um caminho de arquivo para uma única entrada (a extensão real é anexada se o caminho não tiver nenhuma). O padrão é ao lado de cada arquivo de origem; não pergunte. No primeiro trabalho da sessão, indique-o na linha de início (ex.: "Os resultados serão salvos ao lado dos originais — diga-me agora se quiser uma pasta diferente") e comece imediatamente, sem esperar por uma resposta. Se o usuário nomear uma pasta — com esta solicitação ou a qualquer momento depois nesta sessão — execute com --out "<pasta>" e continue usando essa pasta para cada trabalho posterior até que ele a altere. Se ele redirecionar após uma execução já iniciada, mova os arquivos concluídos para lá quando a execução terminar. Exceção: quando cada entrada for uma URL (sem pasta de origem local), pergunte onde salvar antes de começar.

Seleção de espaço — com vários workspaces, o worker para antes do upload, imprime linhas [space-select] (nome | (plano) | créditos restantes) e para. Mostre ao usuário APENAS essas opções (sem números internos), pergunte qual delas e execute novamente com --space "<nome do espaço>". Um espaço → nenhuma pergunta; PERSO_SPACE_SEQ o fixa.

Confirmação de split — se a entrada exceder o limite de comprimento ou tamanho e precisar ser automaticamente dividida e mesclada (dublagem, dub+lip-sync, ou separação de áudio), o worker para e imprime linhas [split-confirm]. Transmita-as e pergunte ao usuário: excede o limite de comprimento/tamanho, então precisa de split automático → processamento → merge, que pode resultar menos polido do que dividir por conta própria — prosseguir automaticamente? Em caso afirmativo, execute novamente o mesmo comando com --allow-split (nada é cobrado até este ponto, então reexecutar é gratuito). Execuções em lote: --allow-split autoriza cada split na execução.

Não salvar (apenas servidor) — quando o usuário quiser o vídeo dublado, mas não salvo como arquivo local, adicione --no-save: o worker deixa o resultado no workspace Perso do usuário e pula o download, imprimindo Mantido no servidor, não salvo: … → projeto <seq> (a linha [project-ref] ainda é emitida, então a dublagem pode ser lip-syncada ou recuperada depois). Apenas vídeos únicos/não divididos — o arquivo mesclado de um vídeo dividido precisa de um download local, então é salvo normalmente e o worker informa isso. Não pode ser combinado com --lipsync (o vídeo com lip-sync deve ser baixado).

Plano gratuito (resultados permanecem no Perso) — um workspace Gratuito pode dublar, fazer lip-sync e separar normalmente, mas o servidor recusa todo download, então o worker nunca salva um arquivo lá. Ele imprime Plano gratuito — <nome>: … pronto no Perso → <url> (downloads precisam de um plano pago) — transmita esse link como o resultado entregue: a execução está completa, nada falhou e não há nada para retomar. Se o usuário quiser arquivos reais, isso requer um plano pago — ofereça o caminho de upgrade (veja Upgrade de plano e créditos), nunca uma reexecução.

Pausas [free-limit] do plano gratuito — duas delas, ambas antes de qualquer cobrança:

  • Pré-visualização de 30 segundos. Uma entrada de dublagem/lip-sync com mais de 30 s para com linhas [free-limit]: no Gratuito, apenas os primeiros 30 segundos seriam dublados, o resto omitido. Transmita a pergunta e, em caso afirmativo, execute novamente o mesmo comando com --allow-preview (o worker corta os primeiros 30 s localmente e envia apenas isso). Um link de plataforma importado pelo Perso não pode ser cortado — aí a resposta é um arquivo local, um clipe mais curto ou um upgrade.
  • Acima do limite do plano. Mídia acima do limite de comprimento/tamanho do plano não recebe oferta de split no Gratuito (--allow-split não se aplica): as linhas [free-limit] dizem que um arquivo mais curto ou um upgrade é a única saída. Transmita-as e pare.

Enquanto executa (para explicar a espera):

  • Split: o arquivo inteiro é enviado primeiro; apenas uma rejeição de limite do plano instala o ffmpeg e divide sem perdas. URLs externas (YouTube·TikTok·Drive·Vimeo) são tratadas no lado do servidor.
  • Fila: todas as entradas × partes × idiomas compartilham um pool; uma fila cheia é reverificada a cada 5 minutos. Um erro de engine em uma parte cancela os outros idiomas dessa parte; uma parte silenciosa passa o original diretamente; um guarda de inatividade impede que fique pendurado para sempre.
  • Salvamento: as partes são mescladas de volta em um arquivo por (entrada × idioma). Uma saída não dividida mantém o nome do arquivo Perso; uma mesclada fica <nome-original>.dublado.<lang>.<ext>; colisões recebem _2,_3….

Lip-sync

Lip-sync (boca sincronizada com o áudio dublado) é executado após a dublagem, no projeto de dublagem finalizado. Apenas vídeo — entradas de áudio são rejeitadas. É um trabalho longo: execute em segundo plano e avise o usuário de antemão que leva consideravelmente mais tempo que a dublagem. Créditos (a cobrança do servidor é autoritativa): dublagem ≈ segundos ×1 · lip-sync ≈ ×2 · ambos ≈ ×3 — dublagem agora + lip-sync depois custa o mesmo que ambos de uma vez. Fontes 4K+: cada taxa ×3 nos planos pro/business/enterprise (ex.: um dub+lip-sync de 1 min em 4K ≈ 60×3×3 = 540) — mencione isso quando o vídeo for 4K.

Escolha o fluxo pelo que existe:

  1. Vídeo novo + lip-sync — um comando executa toda a cadeia (dub → lip-sync → save); avise que ambos os estágios cobram: node scripts/dubbing.mjs "<arquivo|URL>" --target en --lipsync Se linhas [credit-check] forem impressas e ele parar, a estimativa excede os créditos restantes: mostre essas linhas (URL de recarga incluída) e execute novamente com --force apenas após o usuário recarregar ou aprovar continuar mesmo assim.
  2. Dublado anteriormente nesta sessão — toda execução concluída imprime uma linha [project-ref] {...}. Guarde-a; nunca a mostre ao usuário. Lip-sync sem re-dublagem (×2, sem cobrança de dublagem): node scripts/dubbing.mjs --lipsync-only '<esse [project-ref] JSON>'
  3. Nenhum [project-ref] nesta sessão — pergunte se o usuário sabe o número do projeto no portal Perso (--lipsync-only <número>, ×2). Caso contrário, o vídeo deve ser dublado novamente (--lipsync, ×3) — confirme antes de re-dublar.

Regras:

  • Repetir lip-sync no mesmo projeto cobra novamente (sem dedup no servidor). Se esta sessão já fez lip-sync nele, aponte para o arquivo existente e execute novamente apenas com confirmação explícita.
  • Se o lip-sync falhar, o worker salva o vídeo dublado em vez disso e informa no relatório final — transmita isso claramente; os créditos de dublagem não são desperdiçados.
  • Créditos acabando entre dublagem e lip-sync: os vídeos dublados são salvos e continuar termina apenas o lip-sync — transmita a URL de recarga e continue pelo caminho [resume-state] assim que pago (veja Interrupção e retomada).

Falante (apenas projetos de dublagem)

Para atribuir um novo falante a uma ou mais frases de um projeto já dublado, execute:

node scripts/speaker.mjs "<URL do projeto|projectSeq>" --at "5s,22s,98s" (códigos de tempo/intervalos separados por vírgula) node scripts/speaker.mjs "<URL do projeto|projectSeq>" --text "primeira linha|segunda linha" (separado por pipe, texto entre aspas) node scripts/speaker.mjs "<URL do projeto|projectSeq>" --sentence 1234,1288 (reexecutar após uma pergunta [sentence-select]) node scripts/speaker.mjs "<URL do projeto|projectSeq>" --list (navegar pelas frases do projeto para encontrar o alvo certo)

Apenas projetos de dublagem — o worker rejeita projetos de legenda (STT), separação de áudio e lip-sync, e projetos inacabados/falhos. Ele descobre o workspace sozinho a partir do link do projeto; você não precisa perguntar qual espaço.

Uma pausa não é uma falha. Uma linha [sentence-select] significa que o worker não conseguiu fixar um único alvo (nada foi dado, nada correspondeu, ou mais de uma frase correspondeu) — ele sai limpo para perguntar algo, não porque falhou. A lista numerada abaixo dela (índice, rótulo de tempo, texto) é segura para transmitir ao usuário como está. Logo após essa lista, uma linha [sentence-ref] imprime um mapa JSON dos mesmos números para os números de frase internos do worker — essa linha é apenas para sua consulta: nunca a mostre ou leia em voz alta. Assim que o usuário escolher um número, procure-o nesse mapa e execute novamente o mesmo comando com --sentence definido para esse valor. Um bloco [speaker-resolve] junto a ele significa que alguns alvos já foram resolvidos, mas nada foi escrito ainda — ele tem sua própria lista numerada e sua própria linha [sentence-ref]; a lista --sentence da reexecução deve incluir tanto esses valores já resolvidos quanto o(s) novo(s) escolhido(s), ou os resolvidos seriam descartados. Quando uma execução imprime mais de um grupo [sentence-select] (vários alvos ambíguos de uma vez), cada grupo

…

[truncado para tradução]

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas