Leçon 4 — Produire

Vidéo Veo et textes par canal

50 min

Sur LinkedIn, un post avec une vidéo courte retient l'œil plus longtemps qu'une image fixe ; c'est l'argument que l'équipe d'Ondée entend de toutes les agences. Mais une vidéo générée coûte de 4 à 9 fois une image (0,30 aˋ0,60à 0,60 contre 0,068 $), met d'une dizaine de secondes à plusieurs minutes à revenir, et ne se retouche pas comme une image. On en fait donc une, courte, à partir d'un plan déjà validé, et on connaît son prix avant de cliquer. Les textes, eux, sont bon marché mais piégés : longueurs par canal, mots interdits, mention IA.

Le mécanisme

flowchart LR
  subgraph Video["Vidéo"]
    A["Fond LinkedIn 16:9 validé"] --> B["premiere-image.jpg 1280x720"]
    B --> C["Veo 3.1 Lite, 6 s, 720p<br/>opération longue, sondée toutes les 10 s"]
    C --> D["ffmpeg : logo en calque, H.264 2,3 Mbit/s"]
  end
  subgraph Textes["Textes"]
    E["Schéma Textes"] --> F["gemini-3.5-flash-lite"]
    F --> G{"verifier() : longueurs,<br/>5 diapositives, hashtags,<br/>mots interdits"}
    G -- "écarts" --> H["1 tour de correction<br/>avec la liste exacte"]
    G -- "ok" --> I["Mention IA ajoutée par le code"]
    H --> I
  end

Tarifs relevés le 2026-10-15 sur la page officielle, par seconde de vidéo générée :

Modèle720p1080p (8 s uniquement)Clip de 6 s en 720p
veo-3.1-lite-generate-preview0,05 $0,08 $0,30 $
veo-3.1-fast-generate-preview0,10 $0,12 $0,60 $

Veo 3.1 Lite accepte 4, 6 ou 8 secondes, en 16:9 ou 9:16, à partir d'un texte ou d'une image de départ. Les vidéos restent 2 jours sur le serveur : le studio les télécharge immédiatement.

La mise en œuvre

L'appel et l'attente, dans models.py :

files/studio/models.py
op = self.client.models.generate_videos(
    model=model, prompt=prompt,
    image=types.Image(image_bytes=image, mime_type=_mime(image)),
    config=types.GenerateVideosConfig(number_of_videos=1, duration_seconds=secondes,
                                      aspect_ratio=aspect, resolution="720p"),
)
while not op.done:
    if time.time() - debut > 600:
        raise TimeoutError("Veo n'a pas répondu en 10 minutes")
    time.sleep(10)
    op = self.client.operations.get(op)
v = op.response.generated_videos[0].video
if not v.video_bytes:                 # Gemini API : fichier à télécharger ; Vertex : octets directs
    self.client.files.download(file=v)
self._noter("video", model, cout=PRIX[model]["s720"] * secondes)

La finalisation par ffmpeg (pipeline.py) pose le logo en calque, comme sur les images, et vise un débit qui garde 8 s sous 3 Mo :

files/studio/pipeline.py
"[0:v]scale=-2:720[v];[1:v]scale=84:84[l];[v][l]overlay=W-w-36:36",
"-c:v", "libx264", "-b:v", "2300k", "-maxrate", "2500k", "-bufsize", "5000k", ...

Côté textes, textes.py déclare les limites dans le schéma (le modèle les voit) et les vérifie dans le code (le modèle ne les respecte pas toujours) :

files/studio/textes.py
textes = models.json(prompt, Textes, nature="textes")
problemes = verifier(textes, kit)
if problemes:   # un seul tour : le modèle reçoit la liste exacte des écarts
    textes = models.json(prompt + "\n\nCorrige ces écarts :\n- " + "\n- ".join(problemes) + ..., Textes)
    problemes = verifier(textes, kit)

Les mots interdits sont cherchés par préfixe (\bécologique attrape « écologiques »), dans tous les champs à la fois. La mention « Visuels créés avec l'IA (Gemini, Veo), sélectionnés et validés par notre équipe » n'est pas demandée au modèle : post_linkedin() l'ajoute, toujours, à la fin.

make test       # test_verifier_detecte_mot_interdit_et_longueurs, test_mention_ia_ajoutee_par_le_code,
                # test_finaliser_video_pose_le_logo_et_reste_sous_3_mo (vidéo de synthèse ffmpeg)

Résultat obtenu

Deux clips réels le 2026-10-15, 41 s de génération chacun, 0,30 $ chacun :

20:45:29 INFO studio.models video veo-3.1-lite-generate-preview coût=0.3000 $
20:45:29 INFO studio.models vidéo générée en 41 s
...
20:48:48 INFO studio.models video veo-3.1-lite-generate-preview coût=0.3000 $
20:48:48 INFO studio.models vidéo générée en 41 s
video.mp4 : 6,0 s, H.264 1280x720, AAC, 1,5 Mo

Et la boucle de correction des textes, au second test de bout en bout :

20:52:43 WARNING studio.textes textes à corriger : ['accroche linkedin : titre de 45 caractères > 42']
20:52:45 INFO studio.models textes gemini-3.5-flash-lite coût=0.0025 $
{"kit_corrections": [], "textes_problemes": []}

Ce qui casse en production

Les bandes noires. Le premier clip partait de l'image de référence, en portrait 4:5, pour une vidéo 16:9. Veo a conservé l'image entière : bandes noires à gauche et à droite sur la première seconde, puis un élargissement du cadre. 0,30 $ perdus.

Premier clip : la première image 4:5 laisse des bandes noires

Parade : la première image est toujours recadrée en 16:9 par le code avant l'appel (premiere-image.jpg, à partir du fond LinkedIn déjà validé). Second clip :

Second clip à partir d'une première image 16:9 : plein cadre dès la première image
Autre symptômeCauseParade
Veo n'a renvoyé aucune vidéofiltre de sécurité (personnes, marques réelles)message explicite, pas de nouvel essai automatique : chaque essai est facturé
Clip introuvable le surlendemainconservation serveur de 2 jourstéléchargement immédiat, fichier local dans la campagne
Régénération vidéo demandée depuis la galeriecoût et durée incompatibles avec un clicrefusée par l'API de la galerie ; make video en ligne de commande, en connaissance de cause
Post LinkedIn coupé à « …voir plus » au mauvais endroitpremière phrase trop longueconsigne « première ligne qui accroche » et limite de 1 300 caractères

La règle de décision

Vidéo : Lite en 720p pour un réseau social, 6 s, une seule par campagne, première image recadrée par le code au ratio de la vidéo. Passez à Fast seulement si un humain a constaté un défaut de mouvement que Lite reproduit. Textes : toute contrainte vérifiable est vérifiée par le code après la génération, et toute mention obligatoire est ajoutée par le code.

Pratique

La marque veut une story vidéo 9:16 de 8 s en 1080p. Combien coûte le clip en Veo 3.1 Lite et que changez-vous dans le code ?

Le modèle de texte écrit « nos flacons écologiques ». verifier() le signale, le tour de correction le remplace par « nos flacons éco-responsables ». Que faites-vous ?

Exercice. Ajoutez une limite : la légende Instagram doit contenir la date de lancement « 3 novembre ». Si elle manque, verifier() le signale.

Voir la solution
# textes.py, dans verifier()
if "3 novembre" not in t.legende_instagram:
    p.append("légende Instagram : la date de lancement « 3 novembre » est absente")

Mieux : la date vient du brief, pas du code. Ajoutez un champ date_lancement à Brief, passez le brief à verifier(), et testez avec une légende sans date dans tests/test_kit_et_textes.py.

  • Je sais lancer une génération Veo, attendre l'opération et récupérer le fichier
  • Je sais calculer le coût d'un clip avant de le lancer
  • Je sais pourquoi la première image doit avoir le ratio de la vidéo
  • Je sais faire vérifier par le code ce que le modèle a écrit
Suivant