Communitygithub.com

Perso dub lip-sync — swap voix multi-langues

Dubbing lip-sync multi-langues Perso AI pour Claude/Cursor/Codex : upload→split→traduire→merge→MP4. README avec GIFs démo — bouche calée sur la piste doublée.

Qu'est-ce que Perso dub lip-sync — swap voix multi-langues ?

Video Localization étape 2 (dub polish) : perso-dubbing-plugin-dubbing. SRT/vidéo traduits → MP4 dub lip-sync multi-langues avant hardburn bilingue.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

/dubbing

Une compétence qui double automatiquement des vidéos via l'API Perso AI Dubbing.

Règles principales (à suivre impérativement)

  • Seul le worker voit la clé brute. Ne jamais ouvrir la clé avec Read, l'afficher ou la passer en argument de ligne de commande. Les workers l'envoient uniquement dans l'en-tête XP-API-KEY.
  • Une commande = tout le travail. scripts/dubbing.mjs gère lui-même le téléchargement, le découpage, la traduction, la fusion et la sauvegarde. Ne jamais exécuter prepare_input.mjs ou probe_split.mjs avant — ce sont des outils de débogage, et probe_split.mjs effectue un vrai téléchargement (la vidéo serait téléchargée deux fois).
  • Toujours exécuter dubbing.mjs en arrière-plan. Les travaux prennent de quelques minutes à plusieurs heures ; un délai d'attente du shell au premier plan tuerait l'exécution en cours de route.
  • Langues non spécifiées → source auto, cible en.
  • Plusieurs langues en une seule commande (--target en,zh,ja) — le téléchargement/découpage a lieu une fois et est réutilisé par langue. Ne jamais exécuter une fois par langue (cela retéléchargerait la source à chaque fois).
  • Relayer fidèlement la progression. Afficher les lignes [progress] stdout du worker dans le chat, textuellement ou résumées — et ne pas annoncer des étapes qui n'apparaissent pas (le découpage/fusion n'existent que pour les vidéos dépassant la limite). Les logs détaillés de stderr en retrait n'ont pas besoin d'être relayés.
  • Tenir l'utilisateur informé pendant l'exécution. Le doublage, le lip-sync et la séparation audio affichent tous un battement de cœur [status] (étape + nombre d'éléments, par ex. ja 4/6 · en 1/6 ou separating — file 2/5, avec un indice next check ~Nm), et chaque élément terminé est diffusé sous forme de ligne [progress]. Vérifier la sortie en arrière-plan environ 3 minutes après le lancement, puis à l'intervalle indiqué (10 min pour les médias ≤10 min, 30 min pour les plus longs) : relayer immédiatement toute nouvelle ligne [progress], sinon publier la dernière ligne [status] comme mise à jour d'une seule ligne. Ne jamais inventer de pourcentages, de temps écoulé ou d'ETAs — ne relayer que ce que disent les lignes. Les arrêts nécessitant l'utilisateur ([space-select], [split-confirm], [free-limit], [resume-check], [credit-check], crédit épuisé, achèvement) terminent le processus, donc ils apparaissent sans sondage.
  • Nommer votre runtime pour la télémétrie (silencieuse). Ajouter --host <claude-code|codex|cursor|antigravity|gemini> à la commande du worker, en nommant l'agent que vous exécutez, si vous pouvez le dire. Cela ne fait que taguer la télémétrie d'utilisation — ne jamais le montrer à l'utilisateur et ne jamais lui demander. Omettre si vous n'êtes pas sûr.
  • Les formats non pris en charge sont ignorés automatiquement et le reste continue le traitement — relayer l'avis d'ignorance.
  • Une pause n'est pas un échec. Une ligne [space-select], [split-confirm], [free-limit], [credit-check] ou [resume-check] signifie que l'exécution s'est arrêtée pour vous demander quelque chose — elle se termine avec le code 0 (donc elle ne ressemble jamais à une erreur), mais le travail n'est pas terminé. Chaque fois que l'une de ces lignes est présente, ne jamais signaler l'exécution comme terminée : la relayer et agir en conséquence. Les lignes sont le signal, pas le code de sortie.

Configuration (paresseuse — aucune étape préalable)

  1. Clé API — pas une étape à exécuter en premier. Le worker se bloque lui-même au moment où une commande nécessitant l'API Perso s'exécute : lors de cette première exécution, il ouvre une page de navigateur (connexion, un clic — la clé est stockée chiffrée), ou imprime des instructions basées sur un fichier si aucun navigateur ne peut s'ouvrir — les relayer à l'utilisateur. Sur une machine headless/SSH, le worker ignore complètement le flux du navigateur et commence par l'enregistrement par fichier/--import (la page du navigateur ne peut délivrer une clé qu'à la machine sur laquelle elle s'exécute) — relayez ces instructions de la même manière. Ne jamais déclencher vous-même l'enregistrement de la clé : le worker lance connect.mjs/resolve_key.mjs --watch quand nécessaire, donc ne les exécutez pas, et n'exécutez pas resolve_key.mjs --check comme étape préalable (uniquement si l'utilisateur demande explicitement si une clé est enregistrée — une clé manquante n'est pas une erreur). Ne jamais coller la clé dans le chat. Obtenir une clé : https://developers.perso.ai/api-keys
  2. ffmpeg/ffprobe — installé automatiquement uniquement lorsqu'une vidéo dépasse la limite du plan et doit être découpée (approuver si une autorisation est demandée). Vérification manuelle : node scripts/check_deps.mjs.

Exécution

Collecter l'entrée (chemin local ou URL — redemander si manquant) et exécuter en arrière-plan — le worker effectue lui-même le blocage de la clé lors du premier besoin ; relayer simplement ses instructions imprimées si l'enregistrement démarre :

  • Simple : node scripts/dubbing.mjs "<fichier|URL>" [--source auto] [--target en] [--space "nom de l'espace"] [--out resultat.mp4] [--lipsync] [--no-save]
  • Multilingue / multi-entrée (une ou plusieurs entrées ; URLs et fichiers peuvent être mélangés) : node scripts/dubbing.mjs "<URL>" "<fichier>" --target en,ja — une sortie par entrée × langue, sauvegardée à côté de chaque source (--out <dossier> les rassemble).
  • Dossier (par lots) : node scripts/dubbing.mjs "<dossier>" [--target en,zh] [--recursive] [--out dossier-sortie]

Codes de langue — --target prend un code de langue (en, ja) ou une balise régionale. Les variantes régionales existent uniquement pour l'anglais (en = US par défaut, en-GB = Royaume-Uni), le portugais (pt = Brésil par défaut, pt-PT = Portugal) et l'espagnol (es = Mexique par défaut, es-ES = Espagne) ; en-US, pt-BR et es-MX sont des orthographes acceptées de ces valeurs par défaut. Chaque autre langue a une variante unique, et --source n'a besoin que de la langue (une balise régionale y est traitée comme la langue simple).

  • Région nommée dans la requête → utiliser cette balise exacte. Une région sans variante (par exemple l'anglais australien) → dire à l'utilisateur que seules les options listées existent et le laisser choisir avant d'exécuter.
  • Les jetons de deux lettres sont des codes de langue, pas des pays : uk est ukrainien — l'anglais britannique est en-GB. Même piège pour ca (catalan), be (biélorusse), eu (basque). Si le libellé de l'utilisateur peut signifier l'un ou l'autre, demander lequel il veut dire avant d'exécuter.
  • Langue nommée sans région → la variante par défaut. Pour ces trois langues, nommer la valeur par défaut appliquée dans la note de livraison (par exemple "Espagnol (Mexique) — utilisez es-ES pour l'Espagne").

Emplacement de sauvegarde — --out prend soit un dossier (un répertoire existant, ou un chemin se terminant par /) qui rassemble les résultats à l'intérieur, soit un chemin de fichier pour une seule entrée (l'extension réelle est ajoutée si le chemin n'en a pas). La valeur par défaut est à côté de chaque fichier source ; ne pas demander. Pour le premier travail de la session, l'indiquer dans la ligne de lancement (par exemple "Les résultats seront sauvegardés à côté des originaux — dites-moi maintenant si vous souhaitez un dossier différent") et commencer immédiatement, sans attendre de réponse. Si l'utilisateur nomme un dossier — avec cette demande ou à tout moment ultérieur de cette session — exécuter avec --out "<dossier>" et continuer à utiliser ce dossier pour chaque travail ultérieur jusqu'à ce qu'il le change. S'il redirige après qu'une exécution a déjà commencé, déplacer les fichiers terminés là-bas une fois l'exécution terminée. Exception : lorsque chaque entrée est une URL (pas de dossier source local), demander où sauvegarder avant de commencer.

Sélection de l'espace — avec plusieurs espaces de travail, le worker s'arrête avant le téléchargement, imprime les lignes [space-select] (nom | (plan) | crédits restants) et s'arrête. Montrer à l'utilisateur UNIQUEMENT ces options (pas de chiffres internes), demander laquelle, et réexécuter avec --space "<nom de l'espace>". Un espace → pas de question ; PERSO_SPACE_SEQ le fixe.

Confirmation du découpage — si l'entrée dépasse la limite de durée ou de taille et doit être automatiquement découpée et fusionnée (doublage, dub+lip-sync, ou séparation audio), le worker s'arrête et imprime les lignes [split-confirm]. Les relayer et demander à l'utilisateur : cela dépasse la limite de durée/taille, donc cela nécessite un découpage automatique → traitement → fusion, qui peut donner un résultat moins soigné que s'ils le découpaient eux-mêmes — procéder automatiquement ? Sur un oui, réexécuter la même commande avec --allow-split (rien n'est facturé jusqu'à ce point, donc la réexécution est gratuite). Exécutions par lots : --allow-split autorise chaque découpage de l'exécution.

Ne pas sauvegarder (serveur uniquement) — lorsque l'utilisateur veut que la vidéo soit doublée mais pas sauvegardée en fichier local, ajouter --no-save : le worker laisse le résultat dans l'espace de travail Perso de l'utilisateur et ignore le téléchargement, en imprimant Conservé sur le serveur, non sauvegardé : … → projet <seq> (la ligne [project-ref] est toujours émise, donc le doublage peut être lip-syncé ou récupéré plus tard). Vidéos simples/non découpées uniquement — le fichier fusionné d'une vidéo découpée nécessite un téléchargement local, donc il est sauvegardé normalement et le worker le dit. Ne peut pas être combiné avec --lipsync (la vidéo lip-syncée doit être téléchargée).

Plan gratuit (les résultats restent sur Perso) — un espace de travail Gratuit peut doubler, lip-syncer et séparer normalement, mais le serveur refuse tous les téléchargements, donc le worker n'y sauvegarde jamais de fichier. Il imprime Plan gratuit — <nom>: … prêt sur Perso → <url> (les téléchargements nécessitent un plan payant) — relayer ce lien comme le résultat livré : l'exécution est terminée, rien n'a échoué et il n'y a rien à reprendre. Si l'utilisateur veut des fichiers réels, cela nécessite un plan payant — proposer le chemin de mise à niveau (voir Mise à niveau du plan et crédits), jamais une réexécution.

Pauses [free-limit] du plan gratuit — deux d'entre elles, toutes deux avant que quoi que ce soit ne soit facturé :

  • Aperçu de 30 secondes. Une entrée de doublage/lip-sync plus longue que 30 s s'arrête avec des lignes [free-limit] : sur le plan Gratuit, seules les 30 premières secondes seraient doublées, le reste laissé de côté. Relayer la question, et sur un oui, réexécuter la même commande avec --allow-preview (le worker découpe les 30 premières secondes localement et ne soumet que cela). Un lien de plateforme importé par Perso ne peut pas être découpé — là, la réponse est un fichier local, un clip plus court ou une mise à niveau.
  • Dépassement de la limite du plan. Les médias dépassant la limite de durée/taille du plan ne reçoivent aucune offre de découpage sur le plan Gratuit (--allow-split ne s'applique pas) : les lignes [free-limit] disent qu'un fichier plus court ou une mise à niveau est la seule option. Les relayer et s'arrêter.

Pendant son exécution (pour expliquer l'attente) :

  • Découpage : le fichier entier est téléchargé en premier ; seul un rejet de limite de plan installe ffmpeg et découpe sans perte. Les URLs externes (YouTube·TikTok·Drive·Vimeo) sont traitées côté serveur.
  • File d'attente : toutes les entrées × parties × langues partagent un même pool ; une file d'attente pleine est revérifiée toutes les 5 minutes. Une erreur de moteur sur une partie annule les autres langues de cette partie ; une partie silencieuse transmet l'original ; une garde d'inactivité empêche les blocages infinis.
  • Sauvegarde : les parties sont fusionnées en un seul fichier par (entrée × langue). Une sortie non découpée conserve le nom de fichier Perso ; une sortie fusionnée est <nom-original>.doublée.<langue>.<ext> ; les collisions obtiennent _2,_3….

Lip-sync

Le lip-sync (bouche synchronisée avec l'audio doublé) s'exécute après le doublage, sur le projet de doublage terminé. Vidéo uniquement — les entrées audio sont rejetées. C'est un travail long : exécuter en arrière-plan et dire à l'utilisateur d'avance que cela prend considérablement plus de temps que le doublage. Crédits (la facturation du serveur est faisant autorité) : doublage ≈ secondes ×1 · lip-sync ≈ ×2 · les deux ≈ ×3 — doublage maintenant + lip-sync plus tard coûte le même montant que les deux à la fois. Sources 4K+ : chaque tarif ×3 sur les plans pro/business/entreprise (par exemple, une vidéo 4K de 1 min dub+lip-sync ≈ 60×3×3 = 540) — le mentionner lorsque la vidéo est en 4K.

Choisir le flux en fonction de ce qui existe :

  1. Nouvelle vidéo + lip-sync — une commande exécute toute la chaîne (dub → lip-sync → sauvegarde) ; avertir que les deux étapes sont facturées : node scripts/dubbing.mjs "<fichier|URL>" --target en --lipsync Si les lignes [credit-check] s'impriment et qu'il s'arrête, l'estimation dépasse les crédits restants : afficher ces lignes (URL de recharge incluse), et réexécuter avec --force uniquement après que l'utilisateur a rechargé ou approuvé la poursuite quand même.
  2. Doublé plus tôt dans cette session — chaque exécution terminée imprime une ligne [project-ref] {...}. La conserver ; ne jamais la montrer à l'utilisateur. Lip-sync sans re-doublage (×2, pas de frais de doublage) : node scripts/dubbing.mjs --lipsync-only '<ce [project-ref] JSON>'
  3. Pas de [project-ref] dans cette session — demander si l'utilisateur connaît le numéro de projet depuis le portail Perso (--lipsync-only <numéro>, ×2). Sinon, la vidéo doit être doublée à nouveau (--lipsync, ×3) — confirmer avant de re-doubler.

Règles :

  • Répéter le lip-sync sur le même projet facture à nouveau (pas de déduplication côté serveur). Si cette session l'a déjà lip-syncé, pointer vers le fichier existant et réexécuter uniquement sur confirmation explicite.
  • Si le lip-sync échoue, le worker sauvegarde la vidéo doublée à la place et le dit dans le rapport final — le relayer clairement ; les crédits de doublage ne sont pas gaspillés.
  • Crédits épuisés entre le doublage et le lip-sync : les vidéos doublées sont sauvegardées et la suite termine uniquement le lip-sync — relayer l'URL de recharge, et continuer via le chemin [resume-state] une fois payé (voir Interruption et reprise).

Locuteur (projets de doublage uniquement)

Pour attribuer un nouveau locuteur à une ou plusieurs phrases d'un projet déjà doublé, exécuter :

node scripts/speaker.mjs "<URL du projet|projectSeq>" --at "5s,22s,98s" (timecodes/plages séparés par des virgules) node scripts/speaker.mjs "<URL du projet|projectSeq>" --text "première ligne|deuxième ligne" (séparés par des pipes, texte entre guillemets) node scripts/speaker.mjs "<URL du projet|projectSeq>" --sentence 1234,1288 (réexécution après une question [sentence-select]) node scripts/speaker.mjs "<URL du projet|projectSeq>" --list (parcourir les phrases du projet pour trouver la bonne cible)

Projets de doublage uniquement — le worker rejette les projets de sous-titres (STT), de séparation audio et de lip-sync, ainsi que les projets inachevés/échoués. Il détermine lui-même l'espace de travail à partir du lien du projet ; vous n'avez pas besoin de demander quel espace.

Une pause n'est pas un échec. Une ligne [sentence-select] signifie que le worker n'a pas pu cibler une seule cible (rien n'a été donné, rien n'a correspondu, ou plus d'une phrase a correspondu) — il se termine proprement pour vous demander quelque chose, pas parce qu'il a échoué. La liste numérotée en dessous (index, étiquette temporelle, texte) peut être relayée à l'utilisateur telle quelle. Juste après cette liste, une ligne [sentence-ref] imprime une carte JSON des mêmes numéros vers les numéros de phrases internes du worker — cette ligne est uniquement pour votre propre consultation : ne jamais la montrer ou la lire à haute voix. Une fois que l'utilisateur a choisi un numéro, le chercher dans cette carte et réexécuter la même commande avec --sentence défini sur cette valeur. Un bloc [speaker-resolve] à côté signifie que certaines cibles ont déjà été résolues mais rien n'a encore été écrit — il a sa propre liste numérotée et sa propre ligne [sentence-ref] ; la liste --sentence de la réexécution doit inclure à la fois ces valeurs déjà résolues et la ou les nouvelles valeurs choisies, sinon les valeurs résolues seraient abandonnées. Lorsqu'une exécution imprime plus d'un groupe [sentence-select] (plusieurs cibles ambiguës à la fois), chaque groupe

…

[section tronquée pour la traduction]

Individual skills in this repo

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

Skills associés