creative-video-analyzer avatar

creative-video-analyzer

Pricing

Pay per usage

Go to Apify Store
creative-video-analyzer

creative-video-analyzer

AI-powered video ad analyzer using Google Gemini Flash. Extracts hooks, narrative structure, marketing angles, claims, transcript, scene-by-scene breakdown, and strategic notes from Meta ad videos. Supports batch analysis, 3 model fallbacks, and retry logic. Input: video URL + Gemini API key.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Jérôme Spiell

Jérôme Spiell

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

9 days ago

Last modified

Share

Creative Video Analyzer

Analyse une publicité vidéo via Gemini et renvoie sa structure narrative — hook verbatim, découpage scène par scène, angle, transcript, notes stratégiques — prête à transposer sur une autre marque.

C'est le pendant de creative-static-analyzer pour la vidéo. Même principe : Apify télécharge le média, Gemini l'analyse, Claude reçoit du texte.


Pourquoi cet actor existe

Claude ne peut pas lire une vidéo hébergée sur un domaine externe. Les URLs TrendTrack (medias.trendtrack.io) et les CDN Meta sont inaccessibles depuis son conteneur comme depuis son outil web.

Les serveurs Apify, eux, n'ont aucune de ces restrictions. Ils téléchargent le .mp4, l'envoient à Gemini, et renvoient une analyse textuelle exploitable.


Paramètres

ParamètreObligatoireDéfautÀ quoi ça sert
geminiApiKey✅—Clé Google AI Studio. Coffre-fort Notion, entrée « Google AI Studio (Gemini) »
videoUrl——URL directe de la vidéo à analyser
videoUrls——Plusieurs vidéos, pour une analyse en batch
brandContext——Contexte marque, pour orienter l'analyse : "[marque] — [produit] — [positionnement]. Que peut-on transposer ?"
analysisDepth—standarddeep ajoute les claims, les mécaniques de persuasion et le copywriting. Plus lent, plus cher

⚠️ Il n'y a pas de paramètre model

Le modèle Gemini est écrit dans le code (src/main.js), pas dans l'input. Conséquence directe : aucune skill, aucun prompt, aucun appel ne peut le changer. Le passer dans l'input ne fait rien — et croire qu'on a choisi le modèle est pire que savoir qu'on ne peut pas.

C'est la différence avec creative-static-analyzer, qui expose bien un model. Les skills du Creative OS connaissent cette asymétrie : elles passent model au static, jamais à la vidéo, et si la vidéo échoue sur un modèle arrêté, elles le disent au lieu de faire semblant de réparer.

Le modèle ne se change donc qu'en modifiant la source et en rebuildant. Voir « Améliorations possibles » plus bas pour l'exposer.


Les modèles Gemini

Vérifié le 12/10/2026 sur la page de dépréciation Google.

Toute la famille gemini-2.0-* est arrêtée depuis le 1er juin 2026. Un appel dessus renvoie une 404 définitive.

RôleModèleFallbacks, dans cet ordre
Analyse vidéogemini-3.6-flash (actuel, fonctionnel)gemini-3.8-flash est le modèle recommandé par Google pour les nouveaux usages

Noms morts, à ne jamais appeler : gemini-2.0-flash, gemini-2.0-flash-001, gemini-2.0-flash-lite, gemini-1.5-flash, gemini-1.5-pro.

gemini-flash-latest fonctionne comme dernier filet, mais ne pas en faire le défaut : Google déplace cet alias à chaque release, avec deux semaines de préavis. La qualité de l'analyse peut bouger sans qu'on l'ait demandé.

Reconnaître une erreur de modèle

Le message contient is not found, was not found, not supported for generateContent, ou renvoie un 404 depuis generativelanguage.googleapis.com.

→ Ne pas relancer à l'identique. Le retry ne sert qu'aux échecs transitoires — cold start, mid-build, timeout réseau. Un modèle supprimé, lui, le reste : relancer ne fait que brûler du temps.


État vérifié — build du 12/10/2026

L'actor embarque une cascade de trois modèles de secours, et sa première tentative est gemini-3.6-flash, un modèle vivant (aucun arrêt annoncé). Run de contrôle du 12/10/2026 : analyse réussie en 5 secondes, dès la première tentative.

Autrement dit : l'actor fonctionne. L'erreur gemini-2.0-flash remontée par un utilisateur vient d'un build antérieur à celui-ci, pas du code actuel. Avant d'aller plus loin, faire re-tester la personne concernée : si ça passe, le dossier est clos.


Améliorations possibles (pas des correctifs)

Rien d'urgent ci-dessous — l'actor tourne. Ce sont deux points qui le rendent plus durable.

1. Exposer le modèle dans l'input. Aujourd'hui il ne se choisit pas depuis l'appel : les skills ne peuvent ni le forcer, ni le corriger quand un nom meurt. Dans .actor/input_schema.json, après analysisDepth :

"model": {
"title": "Modele Gemini",
"description": "Flash suffit pour une video d'ad. Laisser le defaut sauf besoin precis.",
"type": "string",
"editor": "select",
"enum": ["gemini-3.8-flash", "gemini-3.6-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview", "gemini-2.5-flash"],
"default": "gemini-3.8-flash"
}

2. Donner un filet à la cascade. Les trois niveaux actuels sont codés en dur : le jour où la génération entière est retirée, ils tombent ensemble. Ce bloc interroge l'API pour trouver un modèle valide quand tous les noms connus sont refusés — à brancher sur la fonction d'appel existante :

const MODEL_FALLBACKS = [
'gemini-3.8-flash',
'gemini-3.6-flash',
'gemini-3.5-flash-lite',
'gemini-2.5-flash',
];
const isModelError = (msg = '') =>
/is not found|was not found|not supported for generateContent|NOT_FOUND/i.test(msg);
// Interroge l'API pour la liste reelle des modeles autorises par la cle.
async function discoverFlashModel(apiKey) {
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`);
if (!res.ok) return null;
const { models = [] } = await res.json();
const hit = models.find((m) => {
const id = (m.name || '').replace(/^models\//, '');
return id.includes('flash')
&& !/-lite|-tts|-image|-live|-embedding|-preview/.test(id)
&& (m.supportedGenerationMethods || []).includes('generateContent');
});
return hit ? hit.name.replace(/^models\//, '') : null;
}
// callGemini(model) doit faire UN appel generateContent et jeter en cas d'erreur.
async function generateWithFallback(callGemini, apiKey, preferred) {
const chain = [preferred, ...MODEL_FALLBACKS].filter((m, i, a) => m && a.indexOf(m) === i);
let last;
for (const model of chain) {
try {
const out = await callGemini(model);
console.log(`Modele utilise : ${model}`);
return { out, model };
} catch (err) {
last = err;
if (!isModelError(err.message)) throw err; // vraie panne : on ne masque pas
console.log(`Modele refuse (${model}), on passe au suivant`);
}
}
const found = await discoverFlashModel(apiKey);
if (found && !chain.includes(found)) {
console.log(`Modele resolu via l'API : ${found}`);
return { out: await callGemini(found), model: found };
}
throw new Error(
`Aucun modele Gemini valide. Essayes : ${chain.join(', ')}. `
+ `Derniere erreur : ${last && last.message}`
);
}

À l'appel :

const { model: requestedModel = 'gemini-3.8-flash' } = input;
const { out, model: usedModel } = await generateWithFallback(
(m) => callGemini(m, videoUrl, prompt), // la fonction d'appel existante
geminiApiKey,
requestedModel,
);

3. Écrire usedModel dans le dataset. C'est ce qui permettra de voir le jour où le défaut vieillit, au lieu de le découvrir par un bug chez un élève.

4. Build, puis Start sur un input de test.

Si ces points sont faits, supprimer le bloc « Il n'y a pas de paramètre model » plus haut et ajouter model au tableau des paramètres. Un README qui contredit le schéma, c'est le prochain bug.


Appel depuis Claude

call-actor("luteous_freezer/creative-video-analyzer", {
videoUrl: "<URL .mp4 de la créa>",
geminiApiKey: "<clé du Coffre-fort Notion>",
brandContext: "<marque> — <produit> — <positionnement>. Que peut-on transposer ?",
analysisDepth: "standard"
})

⚠️ Toujours enchaîner avec waitSecs: 5 puis get-dataset-items(run.defaultDatasetId). Une attente longue fait expirer le connecteur MCP et renvoie une erreur alors que le run, lui, aboutit — on croit l'actor cassé pour rien.


Ce que renvoie l'analyse

  • Hook — verbatim des 3 premières secondes, type de hook, score
  • Structure narrative — le découpage en segments, avec leur fonction
  • Scènes — le détail scène par scène
  • Angle marketing — l'angle principal
  • Éléments créatifs — décor, casting, produit, texte à l'écran, rythme de montage
  • Transcript — la voix off et les dialogues
  • Notes stratégiques — ce qui porte la performance, et ce qui est transposable

En analysisDepth: "deep", s'ajoutent les claims avancés, les mécaniques de persuasion et l'analyse du copywriting.


Coût

Une vidéo ≈ 0,01 $ en flash. Le compute Apify est négligeable à côté. Le mode deep coûte sensiblement plus — le réserver aux créas qu'on va réellement transposer.


Limites

  • L'URL doit être publiquement accessible et pointer vers le fichier vidéo, pas vers une page
  • Les URLs de CDN Meta sont signées et expirent : les récupérer juste avant l'appel, pas la veille
  • Une créa diffusée en ads n'est pas publiée sur le feed de la Page : son permalink_url existe mais le média n'est pas téléchargeable publiquement. Passer par TrendTrack (scan_ad → media URL) ou par un token de Page

Messages d'erreur

ErreurCauseQuoi faire
models/... is not found · not supported for generateContent · 404 GeminiModèle arrêté ou indisponible sur cette cléPas un retry. Vérifier d'abord qu'on est sur le dernier build. Si oui, le nom est à changer dans src/main.js — rien n'est réparable depuis l'appel tant que model n'est pas exposé
Cle API Gemini invalideMauvaise valeurVérifier le Coffre-fort Notion, entrée « Google AI Studio (Gemini) »
Quota Gemini depasseLimite de débit atteinteAttendre quelques minutes, ou vérifier le plan Google AI Studio
Telechargement impossible (403)URL signée expirée, ou média non publicRécupérer une URL fraîche
Échec au 1er essai, sans message de modèleCold start, mid-build, timeout réseauRelancer une fois à l'identique. Deux échecs d'affilée → mode dégradé (transcript TrendTrack + copy)
Le connecteur MCP renvoie une erreur mais le run aboutitAttente trop longue côté MCPAppeler avec waitSecs: 5, puis lire le dataset

Redéploiement

Copier les fichiers dans l'éditeur web de la console Apify (ou apify push avec la CLI), puis Build, puis Start sur un input de test.

.actor/actor.json
.actor/input_schema.json
.actor/dataset_schema.json
src/main.js
package.json
Dockerfile

⚠️ Vérifier le nom du modèle avant chaque build. C'est la seule ligne de cet actor qui peut casser sans que personne n'ait rien touché.