/dubbing
Compétence qui double automatiquement des vidéos via l'API Perso AI Dubbing.
Règles fondamentales (à respecter impérativement)
- Seul le worker voit la clé brute. Ne jamais ouvrir la clé avec
Read, ne pas l'afficher, ni la passer comme argument en ligne de commande. Les workers l'envoient uniquement dans l'en-têteXP-API-KEY. - Une commande = tout le travail.
scripts/dubbing.mjsgère lui-même le téléversement, la segmentation, la traduction, la fusion et la sauvegarde. N'exécutez jamaisprepare_input.mjsouprobe_split.mjsavant — ce sont des outils de débogage, etprobe_split.mjseffectue un véritable téléversement (la vidéo serait téléversée deux fois). - Exécutez toujours
dubbing.mjsen arrière-plan. Les tâches prennent de quelques minutes à plusieurs heures ; un délai d'expiration du shell au premier plan interrompt l'exécution à mi-parcours. - Langues non spécifiées → source
auto, cibleen. - Plusieurs langues dans une seule commande (
--target en,zh,ja) — le téléversement/la segmentation s'effectue une fois et est réutilisé pour chaque langue. Ne lancez jamais une commande par langue (cela retéléverserait la source à chaque fois). - Relayez fidèlement la progression. Transmettez les lignes
[progress]de stdout du worker dans le chat, textuellement ou résumées — et n'annoncez pas d'étapes qui n'apparaissent pas (la segmentation/la fusion n'existent que pour les vidéos dépassant la limite). Les détails dans stderr (en retrait) n'ont pas besoin d'être relayés. - Tenez 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/6ouseparating — file 2/5, avec un indicenext check ~Nm), et chaque élément terminé est diffusé sous forme de ligne[progress]. Vérifiez 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) : relayez immédiatement toute nouvelle ligne[progress], sinon affichez la dernière ligne[status]comme mise à jour d'une seule ligne. N'inventez jamais de pourcentages, de temps écoulé ou d'ETA — relayez uniquement ce que disent les lignes. Les arrêts qui nécessitent l'utilisateur ([space-select],[split-confirm],[free-limit],[resume-check],[credit-check], crédit épuisé, achèvement) terminent le processus et apparaissent donc sans interrogation. - Nommez votre runtime pour la télémétrie (en silence). Ajoutez
--host <claude-code|codex|cursor|antigravity|gemini>à la commande du worker, en nommant l'agent que vous exécutez, si vous pouvez le déterminer. Cela sert uniquement à étiqueter la télémétrie d'utilisation — ne le communiquez jamais à l'utilisateur et ne lui demandez jamais son avis. Omettez-le en cas de doute. - Les formats non pris en charge sont automatiquement ignorés et le reste continue le traitement — relayez l'avis de saut.
- 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 (elle ne ressemble donc jamais à une erreur), mais la tâche n'est pas terminée. Chaque fois que l'une de ces lignes est présente, ne signalez jamais la tâche comme terminée : relayez-la et agissez en conséquence. Les lignes sont le signal, pas le code de sortie.
Configuration (paresseuse — aucune étape préalable)
- Clé API — pas une étape à exécuter en premier. Le worker se verrouille lui-même dès qu'une commande nécessitant l'API Perso est exécutée : 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 — relayez-les à l'utilisateur. Sur une machine sans tête/SSH, le worker ignore complètement le flux du navigateur et commence par l'enregistrement via un 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 déclenchez jamais vous-même l'enregistrement de la clé : le worker lanceconnect.mjs/resolve_key.mjs --watchsi nécessaire, donc n'exécutez pas ceux-ci, et n'exécutez pasresolve_key.mjs --checkcomme étape préalable (uniquement si l'utilisateur demande explicitement si une clé est enregistrée — une clé manquante n'est pas une erreur). Ne collez jamais la clé dans le chat. Obtenez une clé : https://developers.perso.ai/api-keys - ffmpeg/ffprobe — installé automatiquement uniquement lorsqu'une vidéo dépasse la limite du plan et doit être segmentée (approuvez si une autorisation est demandée). Vérification manuelle :
node scripts/check_deps.mjs.
Exécution
Recueillez l'entrée (chemin local ou URL — redemandez si manquant) et exécutez en arrière-plan — le worker effectue lui-même la vérification de la clé lors de la première nécessité ; relayez 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] - Multi-langue / multi-entrée (une ou plusieurs entrées ; les URL et les fichiers peuvent être mélangés) :
node scripts/dubbing.mjs "<URL>" "<fichier>" --target en,ja— un résultat par entrée × langue, sauvegardé à côté de chaque source (--out <dossier>les rassemble). - Dossier (traitement par lots) :
node scripts/dubbing.mjs "<dossier>" [--target en,zh] [--recursive] [--out dossier-de-sortie]
Codes de langue — --target accepte un code de langue (en, ja) ou une balise régionale. Des 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. Toutes les autres langues ont une seule variante, 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 → utilisez cette balise exacte. Une région sans variante (par ex. anglais australien) → dites à l'utilisateur que seules les options répertoriées existent et laissez-le choisir avant d'exécuter.
- Les jetons de deux lettres sont des codes de langue, pas des pays :
ukest ukrainien — l'anglais britannique esten-GB. Même piège pourca(catalan),be(biélorusse),eu(basque). Si le libellé de l'utilisateur pourrait signifier l'un ou l'autre, demandez-lui ce qu'il veut dire avant d'exécuter. - Langue nommée sans région → la variante par défaut. Pour ces trois langues, nommez la valeur par défaut appliquée dans la note de livraison (par ex. "Espagnol (Mexique) — utilisez
es-ESpour l'Espagne").
Emplacement de sauvegarde — --out accepte soit un dossier (un répertoire existant, ou un chemin se terminant par /) qui collecte 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). Par défaut, il est placé à côté de chaque fichier source ; ne demandez pas. Lors de la première tâche de la session, indiquez-le dans la ligne de lancement (par exemple "Les résultats seront sauvegardés à côté des originaux — dites-moi maintenant si vous préférez un dossier différent") et démarrez immédiatement, sans attendre de réponse. Si l'utilisateur nomme un dossier — avec cette demande ou à tout moment ultérieur dans cette session — exécutez avec --out "<dossier>" et continuez à utiliser ce dossier pour chaque tâche ultérieure jusqu'à ce qu'il le modifie. S'il redirige après le démarrage d'une exécution, déplacez les fichiers terminés vers cet emplacement une fois l'exécution terminée. Exception : lorsque chaque entrée est une URL (aucun dossier source local), demandez où sauvegarder avant de commencer.
Sélection de l'espace de travail — avec plusieurs espaces de travail, le worker s'arrête avant le téléversement, imprime les lignes [space-select] (nom | (plan) | crédits restants) et s'arrête. Montrez à l'utilisateur UNIQUEMENT ces options (pas de nombres internes), demandez laquelle, et réexécutez avec --space "<nom de l'espace>". Un espace → aucune question ; PERSO_SPACE_SEQ l'épingle.
Confirmation de la segmentation — si l'entrée dépasse la limite de durée ou de taille et doit être automatiquement segmentée et fusionnée (doublage, doublage + lip-sync, ou séparation audio), le worker s'arrête et imprime les lignes [split-confirm]. Relayez-les et demandez à l'utilisateur : cela dépasse la limite de durée/taille, donc une segmentation automatique → traitement → fusion est nécessaire, ce qui peut donner un résultat moins soigné que s'il le découpait lui-même — procéder automatiquement ? Sur un oui, réexécutez 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 segmentation dans l'exécution.
Ne pas sauvegarder (uniquement côté serveur) — lorsque l'utilisateur souhaite que la vidéo soit doublée mais pas sauvegardée en tant que fichier local, ajoutez --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, de sorte que le doublage peut être lip-synchronisé ou récupéré ultérieurement). Vidéos simples/non segmentées uniquement — le fichier fusionné d'une vidéo segmentée nécessite un téléchargement local, il est donc sauvegardé normalement et le worker le dit. Ne peut pas être combiné avec --lipsync (la vidéo lip-synchronisée doit être téléchargée).
Forfait gratuit (les résultats restent sur Perso) — un espace de travail Gratuit peut doubler, lip-synchroniser et séparer normalement, mais le serveur refuse chaque téléchargement, donc le worker n'y sauvegarde jamais de fichier. Il imprime Forfait gratuit — <nom>: … prêt sur Perso → <url> (les téléchargements nécessitent un forfait payant) — relayez 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 souhaite des fichiers réels, cela nécessite un forfait payant — proposez le chemin de mise à niveau (voir Mise à niveau du forfait et crédits), jamais une réexécution.
Pauses [free-limit] du forfait 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 de plus de 30 s s'arrête avec les lignes
[free-limit]: sur Gratuit, seules les 30 premières secondes seraient doublées, le reste étant ignoré. Relayer la question, et sur un oui, réexécutez 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é — la réponse est alors un fichier local, un clip plus court ou une mise à niveau. - Dépassement de la limite du forfait. Les médias dépassant la limite de durée/taille du forfait ne reçoivent aucune offre de segmentation sur Gratuit (
--allow-splitne s'applique pas) : les lignes[free-limit]indiquent qu'un fichier plus court ou une mise à niveau est la seule solution. Relayer-les et arrêtez.
Pendant l'exécution (pour expliquer l'attente) :
- Segmentation : le fichier entier est d'abord téléversé ; seul un rejet pour dépassement de la limite du forfait installe ffmpeg et segmente sans perte. Les URL 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 complète 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 un blocage infini.
- Sauvegarde : les parties sont fusionnées en un seul fichier par (entrée × langue). Une sortie non segmentée conserve le nom de fichier Perso ; une sortie fusionnée est
<nom-original>.dubbed.<langue>.<extension>; 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 une tâche longue : exécutez-la en arrière-plan et dites à l'utilisateur à l'avance que cela prend considérablement plus de temps que le doublage. Crédits (la facturation du serveur fait autorité) : doublage ≈ secondes ×1 · lip-sync ≈ ×2 · les deux ≈ ×3 — doublage maintenant + lip-sync plus tard coûte le même prix que les deux à la fois. Sources 4K+ : chaque tarif ×3 sur les forfaits pro/entreprise/affaires (par ex. un doublage+lip-sync 4K de 1 min ≈ 60×3×3 = 540) — mentionnez-le lorsque la vidéo est en 4K.
Choisissez le flux en fonction de ce qui existe :
- Nouvelle vidéo + lip-sync — une seule commande exécute toute la chaîne (doublage → lip-sync → sauvegarde) ; prévenez que les deux étapes sont facturées :
node scripts/dubbing.mjs "<fichier|URL>" --target en --lipsyncSi les lignes[credit-check]s'affichent et qu'il s'arrête, l'estimation dépasse les crédits restants : affichez ces lignes (URL de recharge incluse), et réexécutez avec--forceuniquement après que l'utilisateur a rechargé ou approuvé la poursuite quand même. - Doublé plus tôt dans cette session — chaque exécution terminée imprime une ligne
[project-ref] {...}. Conservez-la ; ne la montrez jamais à l'utilisateur. Lip-sync sans redoublage (×2, pas de frais de doublage) :node scripts/dubbing.mjs --lipsync-only '<ce JSON [project-ref]>' - Aucun [project-ref] dans cette session — demandez si l'utilisateur connaît le numéro du projet depuis le portail Perso (
--lipsync-only <nombre>, ×2). Sinon, la vidéo doit être doublée à nouveau (--lipsync, ×3) — confirmez avant de redoubler.
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-synchronisé, pointez vers le fichier existant et ne réexécutez que 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 — relayez-le 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 seule la fin du lip-sync se poursuit — relayez l'URL de recharge, et continuez via le chemin
[resume-state]une fois payé (voir Interruption et reprise).
Intervenant (projets de doublage uniquement)
Pour attribuer un nouvel intervenant à une ou plusieurs phrases d'un projet déjà doublé, exécutez :
node scripts/speaker.mjs "<URL du projet|projectSeq>" --at "5s,22s,98s" (codes temporels/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 barres verticales, 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 cible appropriée)
Projets de doublage uniquement — le worker rejette les projets de sous-titrage (STT), de séparation audio et de lip-sync, ainsi que les projets non terminés/ayant échoué. Il détermine l'espace de travail tout seul à 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 phrase (rien n'a été donné, rien n'a correspondu, ou plusieurs phrases ont 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 telle quelle à l'utilisateur. Juste après cette liste, une ligne [sentence-ref] imprime une carte JSON des mêmes nombres vers les numéros de phrase internes du worker — cette ligne est uniquement pour votre propre consultation : ne la montrez jamais et ne la lisez pas à voix haute. Une fois que l'utilisateur a choisi un numéro, recherchez-le dans cette carte et réexécutez 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/les nouvelle(s) choisie(s), 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
…
[la suite a été tronquée pour la traduction]