Export (Fichier final)
C'est le dernier maillon de la chaîne, et le seul endroit du produit où l'on dessine vraiment des pixels.
Avant lui, tout n'est qu'annotation : le journal enregistre « quels mots sont prononcés », les sous-titres « ce qui est écrit à l'écran », les calques visuels « ce qui est superposé », et l'aperçu les assemble en temps réel pour l'utilisateur, sans rien écrire sur le disque. L'export les grave dans un seul fichier.
Entrée edit-list.json (obligatoire), subtitles.json, visuals.json + modules/
Sortie Fichier final.mp4
Prérequis : Google Chrome (pour rendre les sous-titres et les animations sous forme d'images). FFmpeg / FFprobe installé par l'App de bureau se trouve déjà dans le répertoire géré du Produit ; une installation CLI autonome nécessite ffmpeg ≥ 6 sur le système. L'absence de Chrome provoquera une erreur explicite – ne tente pas de contourner : sans lui, pas de couche de sous-titres ni d'animations.
Lit et exécute d'abord la section « Niveaux de conclusion » du Contrat de phase du Skill métier. L'export n'entre pas dans la machine d'état de montage : il ne modifie aucun fichier projet, n'effectue pas d'écriture CAS, n'avance pas dans les étapes, et produit un nouveau fichier qui sera écrasé à chaque réexécution. Il n'a donc pas besoin de fiche de confirmation.
0. Prêt
Exécute d'abord la « Vérification de préparation » de Vérifier les mises à jour – les skills sont-ils à jour, le Runtime est-il compatible ; la racine du plugin y est également localisée (toutes les commandes <racine_plugin> ci-dessous utilisent ce chemin littéral). Continue seulement si « Prêt » ; si « Nouvelle session nécessaire » ou « Arrêt », applique ses instructions (y compris l'interdiction « Interdiction de créer une interface de remplacement maison »), le Skill métier n'intègre pas la logique d'environnement.
Si le résultat de la vérification est runtime.kind=desktop-managed, réutilise directement les outils CLI stables, les outils multimédia et le même service launchd/windows-task déjà installés par l'App de bureau ; ne résouds pas les chemins Electron, n'installe pas FFmpeg/Bun ou un second Runtime.
Commandes
node "<racine_plugin>/scripts/ensure-running.cjs" --json
node "<racine_plugin>/scripts/videocut-cli.cjs" export <projet> --dry-run --json # Plan d'abord, pas d'encodage
node "<racine_plugin>/scripts/videocut-cli.cjs" export <projet> --json # Fichier final (par défaut 2x, fréquence source)
node "<racine_plugin>/scripts/videocut-cli.cjs" export <projet> --out /chemin/fichier_final.mp4 --json
node "<racine_plugin>/scripts/videocut-cli.cjs" export <projet> --scale 1 --json # Taille source uniquement
node "<racine_plugin>/scripts/videocut-cli.cjs" export <projet> --keep-work --json # Conserve les fichiers intermédiaires (PNG par image) pour le débogage
Si ensure-running détecte une incompatibilité d'identité, un conflit de port ou un service défaillant, arrête immédiatement ; ne poursuis pas l'export en utilisant le mode foreground comme substitut temporaire.
Deux étapes, ne saute pas la seconde
① --dry-run rapporte d'abord le plan : durée, nombre d'images, nombre d'écrans de sous-titres, nombre de calques visuels, nombre de segments zoomés, taille de sortie
Lis-le à l'utilisateur. Si les chiffres ne correspondent pas, l'amont est erroné – dix minutes d'encodage ne les corrigeront pas
② Exécution réelle Assemble → Superposition → Composition → Vérification (quatre étapes de progression)
Les warnings dans --dry-run doivent être rapportés textuellement. Ils ne signalent qu'une seule chose : certains mots des sous-titres ou calques visuels ont déjà été coupés, donc ils n'apparaîtront pas dans le fichier final. C'est une décision à prendre en amont, pas quelque chose que l'export doit cacher.
Netteté : La source est le plafond – regarde d'abord la source avant d'agrandir
Avant l'export, vérifie la résolution source (le champ source de --dry-run donne cette info) :
Largeur source ≥ 2560 (enregistrement Retina natif) → --scale 1, la sortie est en pixels natifs, c'est le meilleur cas
Largeur source < 1920 (ex. 960×720) → Arrête-toi d'abord : demande à l'utilisateur s'il existe une exportation haute définition du même enregistrement.
Les outils de capture d'écran permettent souvent de réexporter la même session en 3x la résolution,
changer la source est plus efficace que n'importe quel post-traitement (voir section ci-dessous). S'il n'y en a vraiment pas → --scale 2
Pourquoi --scale 2 est utile sur une basse résolution : l'image de fond agrandie ne devient pas plus nette, mais les sous-titres et les animations sont redessinés à la taille de sortie – c'est ce que le spectateur lit réellement ; et la plateforme, en recomprimant, allouera un débit binaire plus élevé à une grande image.
Ne règle pas --fps pour « améliorer la netteté ». La fréquence d'images suit la source ; la modifier briserait la synchronisation entre l'échantillonnage de l'animation et l'enregistrement.
Changement de source : quand l'utilisateur fournit une version HD du même enregistrement
Ce n'est pas une partie du processus d'export – l'export lui-même est toujours en lecture seule. Le changement de source est une opération indépendante sur les médias, après accord explicite de l'utilisateur, puis retour à un export normal.
Détermine d'abord qu'il s'agit de la même prise (les deux premiers critères suffisent largement) :
ffprobe -v error -show_entries format=duration -of csv=p=0 <deux_fichiers> # Durée identique à la milliseconde près
ffmpeg -v error -i <fichier> -map 0:a:0 -c copy -f md5 - # Flux audio identique bit à bit
# Compare ensuite deux ou trois images de contenu avec tes yeux
Flux audio identique bit à bit = timeline identique : le script mot à mot, le journal, les sous-titres, les calques visuels sont tous liés à l'ID du mot – aucun besoin de les toucher. C'est exactement ce que signifie « lié au mot, pas à la seconde » en matière de changement de source.
Quatre étapes pour changer la source (réellement effectué le 2026-07-29 ; l'étape ③ avait été oubliée à l'époque, l'aperçu de montage a immédiatement signalé « Échec de la génération » – les empreintes ne sont pas stockées à un seul endroit, cherche les anciennes empreintes avec grep avant d'agir) :
① Remplacer le fichier input/source.mp4 et uploads/source.mp4 sont des hard links —
supprime les deux, copie le nouveau dans input/, puis recrée le hard link vers uploads/
(sous macOS, ln ; sous Windows, New-Item -ItemType HardLink)
② Mettre à jour l'empreinte Modifie le champ source.sha256 de project.json avec la nouvelle empreinte
③ Vérifie un autre emplacement workbench.json contient aussi sourceSha256 qui enregistre l'empreinte source,
et c'est précisément ce fichier que le pipeline de l'aperçu de montage valide —
si tu l'oublies, l'aperçu refusera de se générer
(c'est le comportement correct du produit : si l'empreinte ne correspond pas, échec plutôt que de montrer un ancien aperçu)
④ Réexport --scale 1 — tu changes la source pour les pixels natifs, ne réagrandis pas
En cas de doute : avec ton outil de recherche, cherche dans tous les fichiers *.json du <répertoire du projet> les 8 premiers caractères de l'ancienne empreinte, et traite chaque occurrence trouvée (les enregistrements de cache comme preview-edited/current.json se recalculent automatiquement, pas besoin de les modifier manuellement).
Si l'audio est différent (réenregistré, coupé, durée différente), ce n'est pas un changement de source – c'est un nouveau projet : le script mot à mot doit être retranscrit, toutes les annotations sont caduques. Ne force pas la situation.
Validation : trois choses, sans quoi l'export n'est pas terminé
① La commande retourne succès Le produit vérifie lui-même la taille, le nombre d'images et les pistes audio du fichier final par rapport au plan.
En cas de différence, c'est une erreur readback_mismatch ; le fichier reste sur le disque comme preuve,
l'export n'est pas considéré comme terminé
② Échantillonnage d'images Extrais des images du fichier final et regarde-les. Au moins : un segment zoomé,
un segment avec animation plein écran, un segment avec seulement des sous-titres,
et une frontière entre deux calques
③ Écoute humaine Personne ne l'a jamais réellement faite – toujours marqué human listening UNVERIFIED
② Ne peut pas être remplacée par des captures d'écran de l'aperçu. L'aperçu et le fichier final sont deux pipelines de rendu différents – la validation vérifie justement leur cohérence : utiliser des images de l'aperçu comme preuve pour le fichier final revient à préjuger de ce que l'on cherche à vérifier.
Pour juger un zoom, cherche des repères de coupure – l'impression globale trompe (vécu le 2026-07-29) : après un zoom 1.6x, la page à l'écran ressemble encore à « une page entière », en miniature elle est quasi identique à la vue panoramique – j'ai déjà mal interprété un zoom correct comme « zoom perdu », perdu 30 minutes et refait l'export deux fois. La bonne méthode : cherche des preuves que seul un recadrage peut créer : des éléments coupés en deux (bulle interrompue au milieu), des éléments de bordure disparus (barre latérale, barre de titre). Compare avec l'image de la source au même instant – c'est instantané.
Pour extraire une image avec ffmpeg :
ffmpeg -v error -ss 8.84 -i fichier_final.mp4 -frames:v 1 -y frame.png
Où chercher en cas de problème
--keep-work laisse trois types de fichiers dans .chengfeng-videocut/export/ du projet, qui séparent directement « quelle moitié a échoué » :
assembled.mkv Uniquement le montage, sans aucune superposition. S'il est erroné = journal ou découpage erroné
overlay/*.png Uniquement ce qui est superposé, fond transparent. S'ils sont erronés = style de sous-titres ou module erroné
spans/*.mp4 Segments après composition. S'ils sont erronés = zoom ou alignement erroné
Table de correspondance :
Fichier final sans sous-titres/animations Les PNG de superposition sont-ils entièrement transparents ? Le module n'a-t-il pas accepté le seek ?
Animation bloquée sur la première image Le module n'a pas implémenté seek, ou la timeline GSAP n'est pas en paused
Écran entièrement blanc Le module manque `:root { color-scheme: dark }`
Cadre de zoom déformé Le viewBox du module et le zoom du calque ne sont pas le même jeu de nombres
Fichier final plus court que prévu Un certain span a moins d'images que prévu – regarde l'erreur de la phase compose
Flash d'une image originale à la frontière Les captures overlay sont obsolètes (validation du marqueur d'image défaillante). Le produit se base sur la barre de marqueurs en haut de page pour prouver que chaque capture appartient à une image donnée ; si cela se reproduit, vérifie d'abord que les PNG overlay ont une barre de marqueurs en haut et que compose les recadre
Chrome introuvable Installe Google Chrome, ne change pas de pipeline de rendu
Ce qu'il ne faut pas faire
- Ne dis pas « export réussi » pour « validation passée » – le retour de commande confirme seulement la cohérence interne du produit, pas la qualité visuelle
- N'utilise pas les captures d'écran de l'aperçu, le DOM ou les logs pour remplacer l'extraction d'images du fichier final
- Ne marque pas human listening PASS si personne n'a écouté
- Ne modifie pas les fichiers projet (sous-titres, calques, journal) pour faire passer l'export. L'export est en lecture seule, n'écrit rien
- Ne fais pas le travail de l'amont dans l'export : s'il manque des sous-titres, écris-les ; s'il manque des calques visuels, crée-les – ne bricole pas une solution temporaire ici