From fa9024fef545c270491bfe7b6517f8c3fad6c6aa Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Jan 2026 10:26:59 +0000 Subject: [PATCH 1/4] Add n8n workflows for TikTok UGC ad generation - Full video generator workflow (script + images + voiceover + video assembly) - Simplified script generator workflow (script + optional images) - Comprehensive documentation with API endpoints, parameters, and examples - Multi-language support (FR, EN, ES, DE, IT) - Cost estimates and best practices included --- n8n-workflows/README.md | 389 ++++++++++++ n8n-workflows/tiktok-ugc-ads-generator.json | 596 ++++++++++++++++++ .../tiktok-ugc-script-generator.json | 407 ++++++++++++ 3 files changed, 1392 insertions(+) create mode 100644 n8n-workflows/README.md create mode 100644 n8n-workflows/tiktok-ugc-ads-generator.json create mode 100644 n8n-workflows/tiktok-ugc-script-generator.json diff --git a/n8n-workflows/README.md b/n8n-workflows/README.md new file mode 100644 index 0000000..5853e0e --- /dev/null +++ b/n8n-workflows/README.md @@ -0,0 +1,389 @@ +# n8n Workflows - InfiniteStories Marketing Automation + +This folder contains n8n workflows for automating marketing content creation for InfiniteStories. + +## Available Workflows + +| Workflow | File | Description | Cost/Run | +|----------|------|-------------|----------| +| Full Video Generator | `tiktok-ugc-ads-generator.json` | Complete video with voiceover | ~$2.85 | +| Script + Images | `tiktok-ugc-script-generator.json` | Script and images only | ~$2.10 | + +--- + +## 1. TikTok UGC Ads Generator (Full Video) + +**File:** `tiktok-ugc-ads-generator.json` + +Generates TikTok-ready UGC (User Generated Content) style video ads from a simple briefing. + +### Workflow Overview + +``` +Briefing Input -> AI Script Generation -> Scene Images -> Voiceover -> Video Assembly -> TikTok-Ready Video +``` + +### Features + +- AI-generated UGC-style scripts (GPT-4o) +- Multi-language support (FR, EN, ES, DE, IT) +- DALL-E 3 scene image generation (9:16 vertical format) +- ElevenLabs voiceover synthesis +- Automatic video assembly with Shotstack +- TikTok-optimized output (1080p, 9:16, 30fps) + +### Required Credentials + +Configure these credentials in n8n before importing: + +| Credential ID | Service | Required Scopes | +|--------------|---------|-----------------| +| `openai-credentials` | OpenAI API | GPT-4o, DALL-E 3 | +| `elevenlabs-api` | ElevenLabs | Text-to-Speech | +| `shotstack-api` | Shotstack | Video Rendering | +| `cloudinary-api` | Cloudinary | Asset Upload | + +### API Endpoint + +Once activated, the workflow exposes: + +``` +POST /webhook/generate-tiktok-ad +``` + +### Request Body + +```json +{ + "product_name": "InfiniteStories", + "target_audience": "Parents with children aged 2-10", + "key_benefits": [ + "Personalized AI-generated bedtime stories", + "Child becomes the hero of the story", + "Beautiful illustrations synced with audio", + "Multiple languages supported", + "Safe content for children" + ], + "tone": "warm, authentic, relatable", + "duration_seconds": 30, + "language": "fr", + "hook_type": "problem-solution", + "cta": "Telecharge InfiniteStories maintenant!", + "custom_instructions": "Focus on the magical bedtime routine" +} +``` + +### Request Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `product_name` | string | No | InfiniteStories | Product name | +| `target_audience` | string | No | Parents with children aged 2-10 | Target demographic | +| `key_benefits` | array | No | App default benefits | List of key selling points | +| `tone` | string | No | warm, authentic, relatable | Voice and style tone | +| `duration_seconds` | number | No | 30 | Target video duration | +| `language` | string | No | fr | Language code (fr/en/es/de/it) | +| `hook_type` | string | No | problem-solution | Hook style | +| `cta` | string | No | Telecharge InfiniteStories! | Call to action text | +| `custom_instructions` | string | No | "" | Additional creative direction | + +### Hook Types + +- `problem-solution` - Start with a relatable problem +- `question` - Open with an engaging question +- `story` - Begin with a mini-story +- `shocking-stat` - Lead with an attention-grabbing statistic +- `testimonial` - Start as a personal recommendation + +### Response + +```json +{ + "success": true, + "video_url": "https://cdn.shotstack.io/...", + "render_id": "abc123", + "script": { + "hook": "Vous cherchez comment endormir vos enfants facilement?", + "cta": "Telecharge InfiniteStories maintenant!", + "hashtags": ["parenting", "bedtimestories", "kidsapp"], + "music_suggestion": "Soft acoustic, feel-good vibe", + "scenes": [...] + }, + "generated_assets": { + "scene_images": [ + { + "scene_number": 1, + "image_url": "https://...", + "duration": 5 + } + ] + }, + "metadata": { + "product": "InfiniteStories", + "language": "fr", + "duration_seconds": 30, + "generated_at": "2026-01-10T12:00:00.000Z" + }, + "tiktok_ready": { + "caption": "Vous cherchez comment endormir vos enfants facilement?\n\n#parenting #bedtimestories #kidsapp", + "suggested_sounds": "Soft acoustic, feel-good vibe" + } +} +``` + +### Example cURL Request + +```bash +curl -X POST https://your-n8n-instance.com/webhook/generate-tiktok-ad \ + -H "Content-Type: application/json" \ + -d '{ + "product_name": "InfiniteStories", + "target_audience": "Parents fatigues qui veulent des moments magiques avec leurs enfants", + "key_benefits": [ + "Histoires personnalisees avec le prenom de votre enfant", + "Votre enfant devient le heros", + "Illustrations magnifiques synchronisees", + "5 langues disponibles", + "Contenu 100% adapte aux enfants" + ], + "tone": "authentique, chaleureux, comme une amie qui partage un bon plan", + "duration_seconds": 30, + "language": "fr", + "hook_type": "problem-solution", + "cta": "Telecharge InfiniteStories - Lien en bio!", + "custom_instructions": "Mettre en avant le moment du coucher et la fatigue des parents" + }' +``` + +### Installation + +1. Open your n8n instance +2. Go to **Workflows** > **Import** +3. Upload `tiktok-ugc-ads-generator.json` +4. Configure the required credentials +5. Activate the workflow + +### Estimated Costs per Video + +| Service | Estimated Cost | +|---------|---------------| +| OpenAI GPT-4o (script) | ~$0.05 | +| OpenAI DALL-E 3 (images) | ~$0.40/image x 5 = $2.00 | +| ElevenLabs (voiceover) | ~$0.30 | +| Shotstack (rendering) | ~$0.50 | +| **Total** | **~$2.85/video** | + +### Workflow Nodes Diagram + +``` +[Webhook] --> [Validate Input] --> [Generate Script (GPT-4o)] + | + +---------------------+---------------------+ + | | + v v + [Split Scenes] [Select Voice] + | | + v v + [Generate Image Prompts] [ElevenLabs TTS] + | | + v v + [Generate Images (DALL-E)] [Upload Audio] + | | + v | + [Collect Scene Data] | + | | + +-----------------> [Merge] <---------------+ + | + v + [Shotstack Video Render] + | + v + [Poll Render Status] + | + v + [Return Video URL] +``` + +### Customization + +#### Adding New Languages + +Edit the `Select Voice by Language` node to add new ElevenLabs voice IDs: + +```javascript +const voiceMap = { + 'fr': 'your-french-voice-id', + 'en': 'your-english-voice-id', + // Add more languages +}; +``` + +#### Changing Video Style + +Modify the `Shotstack - Create Video` node to adjust: +- Transitions (`fade`, `slideLeft`, `zoom`, etc.) +- Effects (`zoomIn`, `slideRight`, etc.) +- Text overlays and positioning +- Output resolution and format + +### Troubleshooting + +| Issue | Solution | +|-------|----------| +| Images not generating | Check OpenAI API quota and DALL-E 3 access | +| Voice sounds wrong | Verify ElevenLabs voice ID for the language | +| Video render fails | Check Shotstack API status and image URLs | +| Timeout errors | Increase n8n execution timeout settings | + +--- + +## 2. TikTok UGC Script Generator (Simplified) + +**File:** `tiktok-ugc-script-generator.json` + +A simpler workflow that generates UGC scripts and scene images without video assembly. Perfect for creators who want to film their own content using the AI-generated script and reference images. + +### Features + +- AI-generated UGC-style scripts (GPT-4o) +- Optional DALL-E 3 scene images for visual reference +- Multi-language support (FR, EN, ES, DE, IT) +- Filming tips and caption suggestions +- No video rendering service needed + +### Required Credentials + +| Credential ID | Service | Required For | +|--------------|---------|--------------| +| `openai-credentials` | OpenAI API | Script + Images | + +### API Endpoint + +``` +POST /webhook/generate-tiktok-script +``` + +### Request Body + +```json +{ + "product_name": "InfiniteStories", + "target_audience": "Parents fatigues", + "key_benefits": ["Histoires personnalisees", "Enfant devient le heros"], + "tone": "authentique, chaleureux", + "duration_seconds": 30, + "language": "fr", + "hook_type": "problem-solution", + "cta": "Lien en bio!", + "generate_images": true, + "custom_instructions": "Focus sur le moment du coucher" +} +``` + +### Additional Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `generate_images` | boolean | `true` | Generate DALL-E images for each scene | + +### Response + +```json +{ + "success": true, + "script": { + "hook": "Vous savez ce qui a change nos soirees?", + "scenes": [ + { + "id": 1, + "timestamp": "0:00-0:03", + "voiceover": "Texte a dire", + "visual": "Description visuelle", + "emotion": "curiosite", + "camera": "selfie" + } + ], + "cta": "Telecharge maintenant!", + "captions": ["Textes pour overlay"], + "hashtags": ["parenting", "bedtime"], + "music_mood": "Soft acoustic", + "filming_tips": ["Filmer en lumiere naturelle"] + }, + "generated_images": [ + { + "scene_id": 1, + "timestamp": "0:00-0:03", + "image_url": "https://...", + "voiceover": "Texte de la scene" + } + ], + "voiceover_text": "Script complet pour TTS externe", + "tiktok_post": { + "caption": "Hook + hashtags", + "captions_overlay": ["Textes"], + "music_mood": "Soft acoustic" + }, + "filming_guide": ["Tips de tournage"] +} +``` + +### Use Cases + +1. **DIY Filming**: Use the script and images as storyboard, film yourself +2. **Content Planning**: Generate multiple scripts to choose from +3. **External Video Tools**: Export script to CapCut, InShot, etc. +4. **Team Collaboration**: Share script with video production team + +### Estimated Costs + +| Service | Cost | +|---------|------| +| OpenAI GPT-4o (script) | ~$0.05 | +| OpenAI DALL-E 3 (images) | ~$0.40 x 5 scenes = $2.00 | +| **Total with images** | **~$2.05** | +| **Total script only** | **~$0.05** | + +--- + +## Quick Start + +### 1. Import Workflow + +```bash +# Using n8n CLI +n8n import:workflow --input=n8n-workflows/tiktok-ugc-script-generator.json +``` + +### 2. Configure OpenAI Credentials + +In n8n, go to **Settings > Credentials > Add Credential > OpenAI API** and enter your API key. + +### 3. Activate & Test + +```bash +curl -X POST http://localhost:5678/webhook/generate-tiktok-script \ + -H "Content-Type: application/json" \ + -d '{"language": "fr", "duration_seconds": 30}' +``` + +--- + +## Best Practices + +### Script Optimization + +- **Hook Types**: Test different hook types (problem-solution, question, story) to see what resonates +- **Duration**: 15-30s works best for TikTok ads +- **Language**: Match the language to your target market + +### Image Usage + +- Use generated images as **storyboard reference**, not final content +- Real UGC performs better than AI-generated visuals on TikTok +- Images help visualize the script for filming + +### Cost Control + +- Set `generate_images: false` for script-only generation ($0.05/request) +- Generate images only for approved scripts +- Batch multiple briefings to test different angles cheaply diff --git a/n8n-workflows/tiktok-ugc-ads-generator.json b/n8n-workflows/tiktok-ugc-ads-generator.json new file mode 100644 index 0000000..aba2d2a --- /dev/null +++ b/n8n-workflows/tiktok-ugc-ads-generator.json @@ -0,0 +1,596 @@ +{ + "name": "TikTok UGC Ads Generator - InfiniteStories", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "generate-tiktok-ad", + "responseMode": "responseNode", + "options": {} + }, + "id": "webhook-trigger", + "name": "Webhook - Receive Briefing", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [250, 300], + "webhookId": "tiktok-ad-generator" + }, + { + "parameters": { + "jsCode": "// Validate and structure the briefing input\nconst input = $input.first().json;\n\nconst briefing = {\n product_name: input.body?.product_name || 'InfiniteStories',\n target_audience: input.body?.target_audience || 'Parents with children aged 2-10',\n key_benefits: input.body?.key_benefits || [\n 'Personalized AI-generated bedtime stories',\n 'Child becomes the hero of the story',\n 'Beautiful illustrations synced with audio',\n 'Multiple languages supported',\n 'Safe content for children'\n ],\n tone: input.body?.tone || 'warm, authentic, relatable',\n duration_seconds: input.body?.duration_seconds || 30,\n language: input.body?.language || 'fr',\n hook_type: input.body?.hook_type || 'problem-solution',\n cta: input.body?.cta || 'Telecharge InfiniteStories maintenant!',\n custom_instructions: input.body?.custom_instructions || ''\n};\n\n// Validate required fields\nif (!briefing.product_name) {\n throw new Error('product_name is required');\n}\n\nreturn [{ json: briefing }];" + }, + "id": "validate-input", + "name": "Validate & Structure Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [470, 300] + }, + { + "parameters": { + "model": "gpt-4o", + "messages": { + "values": [ + { + "role": "system", + "content": "Tu es un expert en creation de scripts publicitaires UGC (User Generated Content) pour TikTok. Tu crees des scripts authentiques, engageants et qui convertissent.\n\nRegles importantes:\n- Style conversationnel et naturel (comme si quelqu'un parlait a un ami)\n- Commencer par un hook accrocheur dans les 3 premieres secondes\n- Utiliser des emotions et des histoires personnelles\n- Inclure des pauses naturelles pour le montage\n- Terminer par un CTA clair\n- Adapter la longueur au temps demande (environ 3 mots par seconde)\n\nFormat de sortie JSON:\n{\n \"hook\": \"Phrase d'accroche (3 sec max)\",\n \"script_scenes\": [\n {\n \"scene_number\": 1,\n \"duration_seconds\": 5,\n \"voiceover_text\": \"Texte a dire\",\n \"visual_description\": \"Description de ce qu'on voit\",\n \"emotion\": \"emotion du moment\"\n }\n ],\n \"cta\": \"Call to action final\",\n \"hashtags\": [\"hashtag1\", \"hashtag2\"],\n \"music_suggestion\": \"Type de musique recommandee\"\n}" + }, + { + "role": "user", + "content": "=Cree un script UGC TikTok pour:\n\nProduit: {{ $json.product_name }}\nAudience cible: {{ $json.target_audience }}\nBenefices cles: {{ $json.key_benefits.join(', ') }}\nTon: {{ $json.tone }}\nDuree: {{ $json.duration_seconds }} secondes\nLangue: {{ $json.language }}\nType de hook: {{ $json.hook_type }}\nCTA souhaite: {{ $json.cta }}\n\nInstructions supplementaires: {{ $json.custom_instructions }}\n\nGenere un script UGC authentique qui donne l'impression que c'est un vrai parent qui partage sa decouverte." + } + ] + }, + "options": { + "temperature": 0.8, + "maxTokens": 2000, + "responseFormat": "json_object" + } + }, + "id": "generate-script", + "name": "OpenAI - Generate UGC Script", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [690, 300], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "jsCode": "// Parse the script and prepare for video generation\nconst scriptResponse = $input.first().json;\nconst briefing = $('Validate & Structure Input').first().json;\n\nlet script;\ntry {\n script = typeof scriptResponse.message?.content === 'string' \n ? JSON.parse(scriptResponse.message.content) \n : scriptResponse.message?.content || scriptResponse;\n} catch (e) {\n throw new Error('Failed to parse script JSON: ' + e.message);\n}\n\n// Prepare scenes for parallel processing\nconst scenes = script.script_scenes.map((scene, index) => ({\n ...scene,\n index,\n language: briefing.language,\n product_name: briefing.product_name\n}));\n\nreturn [{\n json: {\n script,\n scenes,\n briefing,\n full_voiceover: script.hook + ' ' + script.script_scenes.map(s => s.voiceover_text).join(' ') + ' ' + script.cta\n }\n}];" + }, + "id": "parse-script", + "name": "Parse Script & Prepare Scenes", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [910, 300] + }, + { + "parameters": { + "batchSize": 1, + "options": {} + }, + "id": "split-scenes", + "name": "Split Into Scenes", + "type": "n8n-nodes-base.splitInBatches", + "typeVersion": 3, + "position": [1130, 200] + }, + { + "parameters": { + "model": "gpt-4o", + "messages": { + "values": [ + { + "role": "system", + "content": "Tu es un expert en direction artistique pour les publicites TikTok. Tu dois transformer une description de scene en un prompt DALL-E optimise pour generer une image de style UGC/lifestyle.\n\nRegles:\n- Style: photo realiste, eclairage naturel, ambiance chaleureuse\n- Cadrage: adapte au format vertical TikTok (9:16)\n- Eviter: texte, logos, contenu non-adapte aux enfants\n- Inclure: emotions authentiques, moments de vie, interactions parent-enfant\n\nReponds uniquement avec le prompt DALL-E en anglais, optimise pour la generation d'image." + }, + { + "role": "user", + "content": "=Scene {{ $json.scene_number }}: {{ $json.visual_description }}\nEmotion: {{ $json.emotion }}\nProduit: {{ $json.product_name }} (app de stories pour enfants)" + } + ] + }, + "options": { + "temperature": 0.7, + "maxTokens": 500 + } + }, + "id": "generate-image-prompt", + "name": "OpenAI - Generate Image Prompt", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [1350, 100], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "operation": "generate", + "prompt": "={{ $json.message.content }}", + "options": { + "model": "dall-e-3", + "size": "1024x1792", + "quality": "hd", + "style": "natural" + } + }, + "id": "generate-image", + "name": "OpenAI - Generate Scene Image", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [1570, 100], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "jsCode": "// Collect generated image and scene data\nconst scene = $('Split Into Scenes').first().json;\nconst imageResult = $input.first().json;\n\nreturn [{\n json: {\n scene_number: scene.scene_number,\n duration_seconds: scene.duration_seconds,\n voiceover_text: scene.voiceover_text,\n image_url: imageResult.data?.[0]?.url || imageResult.url,\n image_prompt: $('OpenAI - Generate Image Prompt').first().json.message?.content\n }\n}];" + }, + "id": "collect-scene-data", + "name": "Collect Scene Data", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [1790, 100] + }, + { + "parameters": { + "aggregate": "aggregateAllItemData", + "options": {} + }, + "id": "aggregate-scenes", + "name": "Aggregate All Scenes", + "type": "n8n-nodes-base.aggregate", + "typeVersion": 1, + "position": [1130, 400] + }, + { + "parameters": { + "url": "https://api.elevenlabs.io/v1/text-to-speech/{{ $json.voice_id }}", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"text\": \"{{ $('Parse Script & Prepare Scenes').first().json.full_voiceover }}\",\n \"model_id\": \"eleven_multilingual_v2\",\n \"voice_settings\": {\n \"stability\": 0.5,\n \"similarity_boost\": 0.8,\n \"style\": 0.4,\n \"use_speaker_boost\": true\n }\n}", + "options": { + "response": { + "response": { + "responseFormat": "file" + } + } + } + }, + "id": "generate-voiceover", + "name": "ElevenLabs - Generate Voiceover", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [1130, 550], + "credentials": { + "httpHeaderAuth": { + "id": "elevenlabs-api", + "name": "ElevenLabs API Key" + } + } + }, + { + "parameters": { + "jsCode": "// Select voice based on language\nconst briefing = $('Parse Script & Prepare Scenes').first().json.briefing;\n\n// ElevenLabs voice IDs by language (these are example IDs - replace with actual)\nconst voiceMap = {\n 'fr': 'pFZP5JQG7iQjIQuC4Bku', // French female voice\n 'en': 'EXAVITQu4vr4xnSDxMaL', // English female voice\n 'es': 'ErXwobaYiN019PkySvjV', // Spanish female voice\n 'de': 'yoZ06aMxZJJ28mfd3POQ', // German female voice\n 'it': 'jBpfuIE2acCO8z3wKNLl' // Italian female voice\n};\n\nconst voice_id = voiceMap[briefing.language] || voiceMap['fr'];\n\nreturn [{ json: { voice_id } }];" + }, + "id": "select-voice", + "name": "Select Voice by Language", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [910, 550] + }, + { + "parameters": { + "url": "https://api.shotstack.io/v1/render", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"timeline\": {\n \"soundtrack\": {\n \"src\": \"{{ $('Upload Voiceover').first().json.audio_url }}\",\n \"effect\": \"fadeOut\"\n },\n \"background\": \"#000000\",\n \"tracks\": [\n {\n \"clips\": {{ JSON.stringify($('Aggregate All Scenes').first().json.data.map((scene, i) => ({\n \"asset\": {\n \"type\": \"image\",\n \"src\": scene.image_url\n },\n \"start\": $('Aggregate All Scenes').first().json.data.slice(0, i).reduce((sum, s) => sum + s.duration_seconds, 0),\n \"length\": scene.duration_seconds,\n \"effect\": \"zoomIn\",\n \"transition\": {\n \"in\": \"fade\",\n \"out\": \"fade\"\n }\n }))) }}\n },\n {\n \"clips\": [\n {\n \"asset\": {\n \"type\": \"title\",\n \"text\": \"{{ $('Parse Script & Prepare Scenes').first().json.script.cta }}\",\n \"style\": \"chunk\",\n \"size\": \"medium\"\n },\n \"start\": {{ $('Aggregate All Scenes').first().json.data.reduce((sum, s) => sum + s.duration_seconds, 0) - 3 }},\n \"length\": 3,\n \"transition\": {\n \"in\": \"fade\"\n }\n }\n ]\n }\n ]\n },\n \"output\": {\n \"format\": \"mp4\",\n \"resolution\": \"1080\",\n \"aspectRatio\": \"9:16\",\n \"fps\": 30\n }\n}", + "options": {} + }, + "id": "create-video", + "name": "Shotstack - Create Video", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [1570, 400], + "credentials": { + "httpHeaderAuth": { + "id": "shotstack-api", + "name": "Shotstack API Key" + } + } + }, + { + "parameters": { + "url": "=https://api.shotstack.io/v1/render/{{ $json.response.id }}", + "options": {} + }, + "id": "poll-video-status", + "name": "Poll Video Render Status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [1790, 400], + "credentials": { + "httpHeaderAuth": { + "id": "shotstack-api", + "name": "Shotstack API Key" + } + } + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict" + }, + "conditions": [ + { + "id": "render-done", + "leftValue": "={{ $json.response.status }}", + "rightValue": "done", + "operator": { + "type": "string", + "operation": "equals" + } + } + ] + } + }, + "id": "check-render-status", + "name": "Check If Render Done", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [2010, 400] + }, + { + "parameters": { + "amount": 5, + "unit": "seconds" + }, + "id": "wait-for-render", + "name": "Wait 5s", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [2230, 500] + }, + { + "parameters": { + "jsCode": "// Compile final response\nconst script = $('Parse Script & Prepare Scenes').first().json.script;\nconst briefing = $('Parse Script & Prepare Scenes').first().json.briefing;\nconst renderResult = $input.first().json;\nconst scenes = $('Aggregate All Scenes').first().json.data;\n\nreturn [{\n json: {\n success: true,\n video_url: renderResult.response?.url,\n render_id: renderResult.response?.id,\n script: {\n hook: script.hook,\n cta: script.cta,\n hashtags: script.hashtags,\n music_suggestion: script.music_suggestion,\n scenes: script.script_scenes\n },\n generated_assets: {\n scene_images: scenes.map(s => ({\n scene_number: s.scene_number,\n image_url: s.image_url,\n duration: s.duration_seconds\n }))\n },\n metadata: {\n product: briefing.product_name,\n language: briefing.language,\n duration_seconds: briefing.duration_seconds,\n generated_at: new Date().toISOString()\n },\n tiktok_ready: {\n caption: script.hook + '\\n\\n' + script.hashtags.map(h => '#' + h).join(' '),\n suggested_sounds: script.music_suggestion\n }\n }\n}];" + }, + "id": "compile-response", + "name": "Compile Final Response", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [2230, 300] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": {} + }, + "id": "respond-success", + "name": "Respond With Video", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [2450, 300] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={\n \"success\": false,\n \"error\": \"{{ $json.error || 'An error occurred during video generation' }}\",\n \"details\": {{ JSON.stringify($json) }}\n}", + "options": { + "responseCode": 500 + } + }, + "id": "respond-error", + "name": "Respond With Error", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [2450, 550] + }, + { + "parameters": { + "url": "https://api.cloudinary.com/v1_1/{{ $credentials.cloudName }}/upload", + "sendBody": true, + "contentType": "multipart-form-data", + "bodyParameters": { + "parameters": [ + { + "name": "file", + "parameterType": "formBinaryData", + "inputDataFieldName": "data" + }, + { + "name": "upload_preset", + "value": "tiktok_ads" + }, + { + "name": "resource_type", + "value": "video" + } + ] + }, + "options": {} + }, + "id": "upload-voiceover", + "name": "Upload Voiceover", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [1350, 550], + "credentials": { + "httpBasicAuth": { + "id": "cloudinary-api", + "name": "Cloudinary API" + } + } + }, + { + "parameters": { + "errorMessage": "={{ $json.error || 'Video generation failed' }}" + }, + "id": "handle-error", + "name": "Handle Error", + "type": "n8n-nodes-base.stopAndError", + "typeVersion": 1, + "position": [2010, 550] + } + ], + "connections": { + "Webhook - Receive Briefing": { + "main": [ + [ + { + "node": "Validate & Structure Input", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validate & Structure Input": { + "main": [ + [ + { + "node": "OpenAI - Generate UGC Script", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI - Generate UGC Script": { + "main": [ + [ + { + "node": "Parse Script & Prepare Scenes", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse Script & Prepare Scenes": { + "main": [ + [ + { + "node": "Split Into Scenes", + "type": "main", + "index": 0 + }, + { + "node": "Select Voice by Language", + "type": "main", + "index": 0 + } + ] + ] + }, + "Split Into Scenes": { + "main": [ + [ + { + "node": "OpenAI - Generate Image Prompt", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Aggregate All Scenes", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI - Generate Image Prompt": { + "main": [ + [ + { + "node": "OpenAI - Generate Scene Image", + "type": "main", + "index": 0 + } + ] + ] + }, + "OpenAI - Generate Scene Image": { + "main": [ + [ + { + "node": "Collect Scene Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Collect Scene Data": { + "main": [ + [ + { + "node": "Split Into Scenes", + "type": "main", + "index": 0 + } + ] + ] + }, + "Select Voice by Language": { + "main": [ + [ + { + "node": "ElevenLabs - Generate Voiceover", + "type": "main", + "index": 0 + } + ] + ] + }, + "ElevenLabs - Generate Voiceover": { + "main": [ + [ + { + "node": "Upload Voiceover", + "type": "main", + "index": 0 + } + ] + ] + }, + "Upload Voiceover": { + "main": [ + [ + { + "node": "Shotstack - Create Video", + "type": "main", + "index": 0 + } + ] + ] + }, + "Aggregate All Scenes": { + "main": [ + [ + { + "node": "Shotstack - Create Video", + "type": "main", + "index": 0 + } + ] + ] + }, + "Shotstack - Create Video": { + "main": [ + [ + { + "node": "Poll Video Render Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Poll Video Render Status": { + "main": [ + [ + { + "node": "Check If Render Done", + "type": "main", + "index": 0 + } + ] + ] + }, + "Check If Render Done": { + "main": [ + [ + { + "node": "Compile Final Response", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Wait 5s", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait 5s": { + "main": [ + [ + { + "node": "Poll Video Render Status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Compile Final Response": { + "main": [ + [ + { + "node": "Respond With Video", + "type": "main", + "index": 0 + } + ] + ] + }, + "Handle Error": { + "main": [ + [ + { + "node": "Respond With Error", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "saveManualExecutions": true, + "callerPolicy": "workflowsFromSameOwner", + "errorWorkflow": "" + }, + "versionId": "1", + "meta": { + "instanceId": "infinite-stories-tiktok-ads" + }, + "tags": [ + { + "name": "marketing", + "id": "1" + }, + { + "name": "tiktok", + "id": "2" + }, + { + "name": "video-generation", + "id": "3" + } + ] +} diff --git a/n8n-workflows/tiktok-ugc-script-generator.json b/n8n-workflows/tiktok-ugc-script-generator.json new file mode 100644 index 0000000..4daf23b --- /dev/null +++ b/n8n-workflows/tiktok-ugc-script-generator.json @@ -0,0 +1,407 @@ +{ + "name": "TikTok UGC Script & Assets Generator (Simplified)", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "generate-tiktok-script", + "responseMode": "responseNode", + "options": {} + }, + "id": "webhook-trigger", + "name": "Webhook - Receive Briefing", + "type": "n8n-nodes-base.webhook", + "typeVersion": 2, + "position": [250, 300], + "webhookId": "tiktok-script-generator" + }, + { + "parameters": { + "jsCode": "// Validate and structure the briefing input\nconst input = $input.first().json;\n\nconst briefing = {\n product_name: input.body?.product_name || 'InfiniteStories',\n target_audience: input.body?.target_audience || 'Parents avec enfants de 2-10 ans',\n key_benefits: input.body?.key_benefits || [\n 'Histoires personnalisees generees par IA',\n 'Votre enfant devient le heros',\n 'Illustrations synchronisees avec audio',\n 'Multi-langues',\n 'Contenu securise pour enfants'\n ],\n tone: input.body?.tone || 'chaleureux, authentique, relatable',\n duration_seconds: input.body?.duration_seconds || 30,\n language: input.body?.language || 'fr',\n hook_type: input.body?.hook_type || 'problem-solution',\n cta: input.body?.cta || 'Telecharge InfiniteStories maintenant!',\n custom_instructions: input.body?.custom_instructions || '',\n generate_images: input.body?.generate_images !== false,\n generate_audio: input.body?.generate_audio === true\n};\n\nreturn [{ json: briefing }];" + }, + "id": "validate-input", + "name": "Validate & Structure Input", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [470, 300] + }, + { + "parameters": { + "model": "gpt-4o", + "messages": { + "values": [ + { + "role": "system", + "content": "Tu es un expert en creation de scripts publicitaires UGC (User Generated Content) pour TikTok specialise dans les apps pour parents.\n\nTon objectif: Creer des scripts qui semblent etre filmes par un vrai parent partageant sa decouverte avec ses amis.\n\nStructure obligatoire:\n1. HOOK (0-3s): Question ou affirmation qui arrete le scroll\n2. PROBLEME (3-8s): Identifier le pain point du parent\n3. DECOUVERTE (8-15s): Presenter l'app comme une solution trouvee\n4. DEMO/BENEFICES (15-25s): Montrer ce qui rend l'app geniale\n5. CTA (25-30s): Inciter au telechargement\n\nStyle UGC:\n- Parler comme a un ami, pas comme une pub\n- Utiliser \"je\", \"mon enfant\", \"vous savez quoi\"\n- Inclure des hesitations naturelles (\"euh\", \"genre\", \"vraiment\")\n- Emotions authentiques\n- Pas de jargon marketing\n\nFormat JSON requis:\n{\n \"hook\": \"Phrase d'accroche 3 sec max\",\n \"scenes\": [\n {\n \"id\": 1,\n \"timestamp\": \"0:00-0:03\",\n \"voiceover\": \"Texte exact a dire\",\n \"visual\": \"Description precise de ce qu'on filme\",\n \"emotion\": \"emotion/energie du moment\",\n \"camera\": \"type de plan (selfie, POV, produit)\"\n }\n ],\n \"cta\": \"Call to action\",\n \"captions\": [\"Textes a afficher sur la video\"],\n \"hashtags\": [\"sans le #\"],\n \"music_mood\": \"Description de l'ambiance musicale\",\n \"filming_tips\": [\"Conseils pour le tournage\"]\n}" + }, + { + "role": "user", + "content": "=Cree un script UGC TikTok pour:\n\n**Produit:** {{ $json.product_name }}\n**Audience:** {{ $json.target_audience }}\n**Benefices cles:** \n{{ $json.key_benefits.map((b, i) => `${i+1}. ${b}`).join('\\n') }}\n**Ton:** {{ $json.tone }}\n**Duree:** {{ $json.duration_seconds }} secondes\n**Langue:** {{ $json.language }}\n**Type de hook:** {{ $json.hook_type }}\n**CTA:** {{ $json.cta }}\n\n**Instructions speciales:** {{ $json.custom_instructions || 'Aucune' }}\n\n---\n\nGenere un script UGC complet et authentique. Le spectateur doit croire que c'est un vrai parent qui partage son experience." + } + ] + }, + "options": { + "temperature": 0.85, + "maxTokens": 2500, + "responseFormat": "json_object" + } + }, + "id": "generate-script", + "name": "GPT-4o - Generate UGC Script", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [690, 300], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "jsCode": "// Parse the script\nconst response = $input.first().json;\nconst briefing = $('Validate & Structure Input').first().json;\n\nlet script;\ntry {\n const content = response.message?.content || response.content || response;\n script = typeof content === 'string' ? JSON.parse(content) : content;\n} catch (e) {\n throw new Error('Failed to parse script: ' + e.message);\n}\n\n// Generate full voiceover text for TTS\nconst fullVoiceover = script.scenes\n .map(s => s.voiceover)\n .join(' ');\n\nreturn [{\n json: {\n script,\n briefing,\n fullVoiceover,\n scenesForImages: briefing.generate_images ? script.scenes : []\n }\n}];" + }, + "id": "parse-script", + "name": "Parse Script", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [910, 300] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict" + }, + "conditions": [ + { + "id": "should-generate-images", + "leftValue": "={{ $json.briefing.generate_images }}", + "rightValue": true, + "operator": { + "type": "boolean", + "operation": "equals" + } + } + ] + } + }, + "id": "check-generate-images", + "name": "Generate Images?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [1130, 300] + }, + { + "parameters": { + "fieldToSplitOut": "scenesForImages", + "options": {} + }, + "id": "split-scenes", + "name": "Split Scenes", + "type": "n8n-nodes-base.splitOut", + "typeVersion": 1, + "position": [1350, 200] + }, + { + "parameters": { + "model": "gpt-4o", + "messages": { + "values": [ + { + "role": "system", + "content": "Tu es un directeur artistique specialise en contenu UGC pour TikTok.\n\nTransforme la description de scene en un prompt DALL-E optimise pour generer une image style UGC/lifestyle.\n\nRegles:\n- Photo realiste, lumiere naturelle\n- Format vertical (pour 9:16)\n- Pas de texte ni logo\n- Ambiance chaleureuse, authentique\n- Adapte aux familles avec enfants\n\nReponds UNIQUEMENT avec le prompt en anglais, 1-2 phrases max." + }, + { + "role": "user", + "content": "=Scene: {{ $json.visual }}\nEmotion: {{ $json.emotion }}\nCamera: {{ $json.camera }}\nContexte: App d'histoires pour enfants \"InfiniteStories\"" + } + ] + }, + "options": { + "temperature": 0.7, + "maxTokens": 200 + } + }, + "id": "generate-image-prompt", + "name": "Generate Image Prompt", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [1570, 200], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "operation": "generate", + "prompt": "={{ $json.message.content }}", + "options": { + "model": "dall-e-3", + "size": "1024x1792", + "quality": "standard", + "style": "natural" + } + }, + "id": "generate-image", + "name": "DALL-E 3 - Generate Image", + "type": "@n8n/n8n-nodes-langchain.openAi", + "typeVersion": 1.8, + "position": [1790, 200], + "credentials": { + "openAiApi": { + "id": "openai-credentials", + "name": "OpenAI API" + } + } + }, + { + "parameters": { + "jsCode": "// Collect image result\nconst scene = $('Split Scenes').first().json;\nconst imageResult = $input.first().json;\nconst prompt = $('Generate Image Prompt').first().json.message?.content;\n\nreturn [{\n json: {\n scene_id: scene.id,\n timestamp: scene.timestamp,\n image_url: imageResult.data?.[0]?.url || imageResult.url,\n image_prompt: prompt,\n voiceover: scene.voiceover\n }\n}];" + }, + "id": "collect-image", + "name": "Collect Image Data", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [2010, 200] + }, + { + "parameters": { + "aggregate": "aggregateAllItemData", + "destinationFieldName": "images", + "options": {} + }, + "id": "aggregate-images", + "name": "Aggregate Images", + "type": "n8n-nodes-base.aggregate", + "typeVersion": 1, + "position": [2230, 200] + }, + { + "parameters": { + "jsCode": "// Compile response with images\nconst scriptData = $('Parse Script').first().json;\nconst images = $input.first().json.images || [];\n\nreturn [{\n json: {\n success: true,\n script: scriptData.script,\n generated_images: images,\n voiceover_text: scriptData.fullVoiceover,\n metadata: {\n product: scriptData.briefing.product_name,\n language: scriptData.briefing.language,\n duration_seconds: scriptData.briefing.duration_seconds,\n images_generated: images.length,\n generated_at: new Date().toISOString()\n },\n tiktok_post: {\n caption: scriptData.script.hook + '\\n\\n' + scriptData.script.hashtags.map(h => '#' + h).join(' '),\n captions_overlay: scriptData.script.captions,\n music_mood: scriptData.script.music_mood\n },\n filming_guide: scriptData.script.filming_tips\n }\n}];" + }, + "id": "compile-with-images", + "name": "Compile Response (With Images)", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [2450, 200] + }, + { + "parameters": { + "jsCode": "// Compile response without images\nconst scriptData = $('Parse Script').first().json;\n\nreturn [{\n json: {\n success: true,\n script: scriptData.script,\n generated_images: [],\n voiceover_text: scriptData.fullVoiceover,\n metadata: {\n product: scriptData.briefing.product_name,\n language: scriptData.briefing.language,\n duration_seconds: scriptData.briefing.duration_seconds,\n images_generated: 0,\n generated_at: new Date().toISOString()\n },\n tiktok_post: {\n caption: scriptData.script.hook + '\\n\\n' + scriptData.script.hashtags.map(h => '#' + h).join(' '),\n captions_overlay: scriptData.script.captions,\n music_mood: scriptData.script.music_mood\n },\n filming_guide: scriptData.script.filming_tips\n }\n}];" + }, + "id": "compile-without-images", + "name": "Compile Response (Script Only)", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [1350, 400] + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineAll", + "options": {} + }, + "id": "merge-results", + "name": "Merge Results", + "type": "n8n-nodes-base.merge", + "typeVersion": 3.1, + "position": [2670, 300] + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": {} + }, + "id": "respond", + "name": "Respond", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [2890, 300] + } + ], + "connections": { + "Webhook - Receive Briefing": { + "main": [ + [ + { + "node": "Validate & Structure Input", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validate & Structure Input": { + "main": [ + [ + { + "node": "GPT-4o - Generate UGC Script", + "type": "main", + "index": 0 + } + ] + ] + }, + "GPT-4o - Generate UGC Script": { + "main": [ + [ + { + "node": "Parse Script", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse Script": { + "main": [ + [ + { + "node": "Generate Images?", + "type": "main", + "index": 0 + } + ] + ] + }, + "Generate Images?": { + "main": [ + [ + { + "node": "Split Scenes", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Compile Response (Script Only)", + "type": "main", + "index": 0 + } + ] + ] + }, + "Split Scenes": { + "main": [ + [ + { + "node": "Generate Image Prompt", + "type": "main", + "index": 0 + } + ] + ] + }, + "Generate Image Prompt": { + "main": [ + [ + { + "node": "DALL-E 3 - Generate Image", + "type": "main", + "index": 0 + } + ] + ] + }, + "DALL-E 3 - Generate Image": { + "main": [ + [ + { + "node": "Collect Image Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Collect Image Data": { + "main": [ + [ + { + "node": "Aggregate Images", + "type": "main", + "index": 0 + } + ] + ] + }, + "Aggregate Images": { + "main": [ + [ + { + "node": "Compile Response (With Images)", + "type": "main", + "index": 0 + } + ] + ] + }, + "Compile Response (With Images)": { + "main": [ + [ + { + "node": "Merge Results", + "type": "main", + "index": 0 + } + ] + ] + }, + "Compile Response (Script Only)": { + "main": [ + [ + { + "node": "Merge Results", + "type": "main", + "index": 1 + } + ] + ] + }, + "Merge Results": { + "main": [ + [ + { + "node": "Respond", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "saveManualExecutions": true, + "callerPolicy": "workflowsFromSameOwner" + }, + "versionId": "1", + "meta": { + "instanceId": "infinite-stories-tiktok-script" + }, + "tags": [ + { + "name": "marketing", + "id": "1" + }, + { + "name": "tiktok", + "id": "2" + }, + { + "name": "content-generation", + "id": "3" + } + ] +} From e3b904ae73f489dbbc275ed14ec3efa31bda30c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Jan 2026 10:53:34 +0000 Subject: [PATCH 2/4] Update n8n TikTok ads workflow with Replicate + OpenAI integration - Replace ElevenLabs/Shotstack with Replicate for simpler billing - Add Flux 1.1 Pro for image generation - Add Minimax/Kling video generation support - Use OpenAI gpt-5-mini for script generation (same as backend) - Use OpenAI gpt-4o-mini-tts for voiceover - Add n8n Form trigger with comprehensive brief fields - Support 4 output modes: script_only, script_audio, script_images, full_video - Optimize prompts with XML structure for better results --- n8n-workflows/tiktok-ugc-ads-generator.json | 866 +++++++++----------- 1 file changed, 394 insertions(+), 472 deletions(-) diff --git a/n8n-workflows/tiktok-ugc-ads-generator.json b/n8n-workflows/tiktok-ugc-ads-generator.json index aba2d2a..0e1ecff 100644 --- a/n8n-workflows/tiktok-ugc-ads-generator.json +++ b/n8n-workflows/tiktok-ugc-ads-generator.json @@ -1,596 +1,518 @@ { - "name": "TikTok UGC Ads Generator - InfiniteStories", + "name": "TikTok UGC Ads Generator - InfiniteStories (Replicate + OpenAI)", "nodes": [ { "parameters": { - "httpMethod": "POST", - "path": "generate-tiktok-ad", + "formTitle": "TikTok Ad Brief - InfiniteStories", + "formDescription": "Génère un script UGC TikTok complet avec audio, images et vidéo.", + "formFields": { + "values": [ + { + "fieldLabel": "Nom du produit", + "fieldType": "text", + "requiredField": true, + "placeholder": "InfiniteStories" + }, + { + "fieldLabel": "Audience cible", + "fieldType": "textarea", + "requiredField": true, + "placeholder": "Parents avec enfants de 2-10 ans, fatigués le soir" + }, + { + "fieldLabel": "Problème principal", + "fieldType": "textarea", + "requiredField": true, + "placeholder": "Les parents sont épuisés et n'ont plus l'énergie de raconter des histoires" + }, + { + "fieldLabel": "Bénéfices clés (un par ligne)", + "fieldType": "textarea", + "requiredField": true, + "placeholder": "Histoires personnalisées\nL'enfant devient le héros\nIllustrations synchronisées" + }, + { + "fieldLabel": "Ton", + "fieldType": "dropdown", + "requiredField": true, + "fieldOptions": { + "values": [ + { "option": "Chaleureux et authentique" }, + { "option": "Enthousiaste et énergique" }, + { "option": "Calme et rassurant" }, + { "option": "Humoristique" } + ] + } + }, + { + "fieldLabel": "Type de hook", + "fieldType": "dropdown", + "requiredField": true, + "fieldOptions": { + "values": [ + { "option": "problem-solution" }, + { "option": "question" }, + { "option": "story" }, + { "option": "shocking" }, + { "option": "testimonial" } + ] + } + }, + { + "fieldLabel": "Durée (secondes)", + "fieldType": "number", + "requiredField": true, + "placeholder": "30" + }, + { + "fieldLabel": "Langue", + "fieldType": "dropdown", + "requiredField": true, + "fieldOptions": { + "values": [ + { "option": "fr" }, + { "option": "en" }, + { "option": "es" }, + { "option": "de" }, + { "option": "it" } + ] + } + }, + { + "fieldLabel": "Call to Action", + "fieldType": "text", + "requiredField": true, + "placeholder": "Télécharge InfiniteStories - Lien en bio!" + }, + { + "fieldLabel": "Output souhaité", + "fieldType": "dropdown", + "requiredField": true, + "fieldOptions": { + "values": [ + { "option": "script_only - Script uniquement" }, + { "option": "script_audio - Script + Audio" }, + { "option": "script_images - Script + Images" }, + { "option": "full_video - Vidéo complète" } + ] + } + }, + { + "fieldLabel": "Modèle vidéo", + "fieldType": "dropdown", + "requiredField": false, + "fieldOptions": { + "values": [ + { "option": "minimax - Minimax Video (rapide)" }, + { "option": "kling - Kling AI (qualité)" }, + { "option": "runway - Runway Gen-3" } + ] + } + } + ] + }, "responseMode": "responseNode", "options": {} }, - "id": "webhook-trigger", - "name": "Webhook - Receive Briefing", - "type": "n8n-nodes-base.webhook", - "typeVersion": 2, - "position": [250, 300], - "webhookId": "tiktok-ad-generator" + "id": "form-trigger", + "name": "Form - Brief", + "type": "n8n-nodes-base.formTrigger", + "typeVersion": 2.2, + "position": [200, 300], + "webhookId": "tiktok-ad-replicate" }, { "parameters": { - "jsCode": "// Validate and structure the briefing input\nconst input = $input.first().json;\n\nconst briefing = {\n product_name: input.body?.product_name || 'InfiniteStories',\n target_audience: input.body?.target_audience || 'Parents with children aged 2-10',\n key_benefits: input.body?.key_benefits || [\n 'Personalized AI-generated bedtime stories',\n 'Child becomes the hero of the story',\n 'Beautiful illustrations synced with audio',\n 'Multiple languages supported',\n 'Safe content for children'\n ],\n tone: input.body?.tone || 'warm, authentic, relatable',\n duration_seconds: input.body?.duration_seconds || 30,\n language: input.body?.language || 'fr',\n hook_type: input.body?.hook_type || 'problem-solution',\n cta: input.body?.cta || 'Telecharge InfiniteStories maintenant!',\n custom_instructions: input.body?.custom_instructions || ''\n};\n\n// Validate required fields\nif (!briefing.product_name) {\n throw new Error('product_name is required');\n}\n\nreturn [{ json: briefing }];" + "jsCode": "const form = $input.first().json;\n\nconst outputMode = (form['Output souhaité'] || 'script_only').split(' - ')[0];\nconst videoModel = (form['Modèle vidéo'] || 'minimax').split(' - ')[0];\n\nconst brief = {\n product_name: form['Nom du produit'] || 'InfiniteStories',\n target_audience: form['Audience cible'],\n main_problem: form['Problème principal'],\n key_benefits: (form['Bénéfices clés (un par ligne)'] || '').split('\\n').filter(b => b.trim()),\n tone: form['Ton'] || 'Chaleureux et authentique',\n hook_type: form['Type de hook'] || 'problem-solution',\n duration_seconds: parseInt(form['Durée (secondes)']) || 30,\n language: form['Langue'] || 'fr',\n cta: form['Call to Action'],\n output_mode: outputMode,\n video_model: videoModel,\n generate_audio: ['script_audio', 'full_video'].includes(outputMode),\n generate_images: ['script_images', 'full_video'].includes(outputMode),\n generate_video: outputMode === 'full_video'\n};\n\nreturn [{ json: brief }];" }, - "id": "validate-input", - "name": "Validate & Structure Input", + "id": "parse-form", + "name": "Parse Form", "type": "n8n-nodes-base.code", "typeVersion": 2, - "position": [470, 300] + "position": [420, 300] }, { "parameters": { - "model": "gpt-4o", - "messages": { - "values": [ - { - "role": "system", - "content": "Tu es un expert en creation de scripts publicitaires UGC (User Generated Content) pour TikTok. Tu crees des scripts authentiques, engageants et qui convertissent.\n\nRegles importantes:\n- Style conversationnel et naturel (comme si quelqu'un parlait a un ami)\n- Commencer par un hook accrocheur dans les 3 premieres secondes\n- Utiliser des emotions et des histoires personnelles\n- Inclure des pauses naturelles pour le montage\n- Terminer par un CTA clair\n- Adapter la longueur au temps demande (environ 3 mots par seconde)\n\nFormat de sortie JSON:\n{\n \"hook\": \"Phrase d'accroche (3 sec max)\",\n \"script_scenes\": [\n {\n \"scene_number\": 1,\n \"duration_seconds\": 5,\n \"voiceover_text\": \"Texte a dire\",\n \"visual_description\": \"Description de ce qu'on voit\",\n \"emotion\": \"emotion du moment\"\n }\n ],\n \"cta\": \"Call to action final\",\n \"hashtags\": [\"hashtag1\", \"hashtag2\"],\n \"music_suggestion\": \"Type de musique recommandee\"\n}" - }, - { - "role": "user", - "content": "=Cree un script UGC TikTok pour:\n\nProduit: {{ $json.product_name }}\nAudience cible: {{ $json.target_audience }}\nBenefices cles: {{ $json.key_benefits.join(', ') }}\nTon: {{ $json.tone }}\nDuree: {{ $json.duration_seconds }} secondes\nLangue: {{ $json.language }}\nType de hook: {{ $json.hook_type }}\nCTA souhaite: {{ $json.cta }}\n\nInstructions supplementaires: {{ $json.custom_instructions }}\n\nGenere un script UGC authentique qui donne l'impression que c'est un vrai parent qui partage sa decouverte." - } + "url": "https://api.openai.com/v1/responses", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { "name": "Authorization", "value": "=Bearer {{ $credentials.openAiApi.apiKey }}" }, + { "name": "Content-Type", "value": "application/json" } ] }, - "options": { - "temperature": 0.8, - "maxTokens": 2000, - "responseFormat": "json_object" - } + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"model\": \"gpt-5-mini\",\n \"input\": [\n {\n \"role\": \"system\",\n \"content\": \"Expert en scripts UGC TikTok pour apps parentales.\\n\\nCrée un script publicitaire UGC authentique de {{ $json.duration_seconds }}s.\\n\\n\\n1. HOOK (0-3s): Arrêter le scroll - type {{ $json.hook_type }}\\n2. PROBLEM (3-8s): Pain point parent\\n3. DISCOVERY (8-15s): Présenter la solution\\n4. BENEFITS (15-{{ $json.duration_seconds - 5 }}s): Démontrer les bénéfices\\n5. CTA ({{ $json.duration_seconds - 5 }}-{{ $json.duration_seconds }}s): Call to action\\n\\n\\n\\n\\n\\n{\\n \\\"hook\\\": \\\"string\\\",\\n \\\"scenes\\\": [{\\n \\\"id\\\": number,\\n \\\"phase\\\": \\\"HOOK|PROBLEM|DISCOVERY|BENEFITS|CTA\\\",\\n \\\"timestamp\\\": \\\"0:00-0:03\\\",\\n \\\"voiceover\\\": \\\"texte exact à dire\\\",\\n \\\"visual\\\": \\\"description visuelle détaillée\\\",\\n \\\"emotion\\\": \\\"string\\\",\\n \\\"camera\\\": \\\"selfie|POV|product|b-roll\\\",\\n \\\"video_prompt\\\": \\\"prompt pour génération vidéo AI\\\"\\n }],\\n \\\"cta\\\": \\\"string\\\",\\n \\\"hashtags\\\": [\\\"string\\\"],\\n \\\"music_mood\\\": \\\"string\\\",\\n \\\"filming_tips\\\": [\\\"string\\\"]\\n}\\n\"\n },\n {\n \"role\": \"user\",\n \"content\": \"\\nProduit: {{ $json.product_name }}\\nAudience: {{ $json.target_audience }}\\nProblème: {{ $json.main_problem }}\\nBénéfices: {{ $json.key_benefits.join(', ') }}\\nTon: {{ $json.tone }}\\nHook: {{ $json.hook_type }}\\nDurée: {{ $json.duration_seconds }}s\\nLangue: {{ $json.language }}\\nCTA: {{ $json.cta }}\\n\"\n }\n ],\n \"text\": { \"format\": { \"type\": \"json_object\" } }\n}", + "options": {} }, "id": "generate-script", - "name": "OpenAI - Generate UGC Script", - "type": "@n8n/n8n-nodes-langchain.openAi", - "typeVersion": 1.8, - "position": [690, 300], + "name": "OpenAI - Generate Script", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [640, 300], "credentials": { - "openAiApi": { - "id": "openai-credentials", - "name": "OpenAI API" - } + "httpHeaderAuth": { "id": "openai-api", "name": "OpenAI API" } } }, { "parameters": { - "jsCode": "// Parse the script and prepare for video generation\nconst scriptResponse = $input.first().json;\nconst briefing = $('Validate & Structure Input').first().json;\n\nlet script;\ntry {\n script = typeof scriptResponse.message?.content === 'string' \n ? JSON.parse(scriptResponse.message.content) \n : scriptResponse.message?.content || scriptResponse;\n} catch (e) {\n throw new Error('Failed to parse script JSON: ' + e.message);\n}\n\n// Prepare scenes for parallel processing\nconst scenes = script.script_scenes.map((scene, index) => ({\n ...scene,\n index,\n language: briefing.language,\n product_name: briefing.product_name\n}));\n\nreturn [{\n json: {\n script,\n scenes,\n briefing,\n full_voiceover: script.hook + ' ' + script.script_scenes.map(s => s.voiceover_text).join(' ') + ' ' + script.cta\n }\n}];" + "jsCode": "const response = $input.first().json;\nconst brief = $('Parse Form').first().json;\n\nlet script;\ntry {\n const output = response.output || response;\n let content;\n if (Array.isArray(output)) {\n const msg = output.find(o => o.type === 'message');\n content = msg?.content?.find(c => c.type === 'output_text')?.text;\n } else if (response.choices) {\n content = response.choices[0]?.message?.content;\n }\n script = typeof content === 'string' ? JSON.parse(content) : content;\n} catch (e) {\n throw new Error('Script parse error: ' + e.message);\n}\n\nconst fullVoiceover = script.scenes.map(s => s.voiceover).join(' ');\n\nreturn [{\n json: {\n script,\n brief,\n fullVoiceover,\n scenes: script.scenes,\n wordCount: fullVoiceover.split(/\\s+/).length\n }\n}];" }, "id": "parse-script", - "name": "Parse Script & Prepare Scenes", + "name": "Parse Script", "type": "n8n-nodes-base.code", "typeVersion": 2, - "position": [910, 300] + "position": [860, 300] }, { "parameters": { - "batchSize": 1, - "options": {} + "conditions": { + "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, + "conditions": [{ "id": "audio", "leftValue": "={{ $json.brief.generate_audio }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }] + } }, - "id": "split-scenes", - "name": "Split Into Scenes", - "type": "n8n-nodes-base.splitInBatches", - "typeVersion": 3, - "position": [1130, 200] + "id": "check-audio", + "name": "Audio?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [1080, 150] }, { "parameters": { - "model": "gpt-4o", - "messages": { - "values": [ - { - "role": "system", - "content": "Tu es un expert en direction artistique pour les publicites TikTok. Tu dois transformer une description de scene en un prompt DALL-E optimise pour generer une image de style UGC/lifestyle.\n\nRegles:\n- Style: photo realiste, eclairage naturel, ambiance chaleureuse\n- Cadrage: adapte au format vertical TikTok (9:16)\n- Eviter: texte, logos, contenu non-adapte aux enfants\n- Inclure: emotions authentiques, moments de vie, interactions parent-enfant\n\nReponds uniquement avec le prompt DALL-E en anglais, optimise pour la generation d'image." - }, - { - "role": "user", - "content": "=Scene {{ $json.scene_number }}: {{ $json.visual_description }}\nEmotion: {{ $json.emotion }}\nProduit: {{ $json.product_name }} (app de stories pour enfants)" - } + "url": "https://api.openai.com/v1/audio/speech", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { "name": "Authorization", "value": "=Bearer {{ $credentials.openAiApi.apiKey }}" }, + { "name": "Content-Type", "value": "application/json" } ] }, - "options": { - "temperature": 0.7, - "maxTokens": 500 - } - }, - "id": "generate-image-prompt", - "name": "OpenAI - Generate Image Prompt", - "type": "@n8n/n8n-nodes-langchain.openAi", - "typeVersion": 1.8, - "position": [1350, 100], - "credentials": { - "openAiApi": { - "id": "openai-credentials", - "name": "OpenAI API" - } - } - }, - { - "parameters": { - "operation": "generate", - "prompt": "={{ $json.message.content }}", - "options": { - "model": "dall-e-3", - "size": "1024x1792", - "quality": "hd", - "style": "natural" - } + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"model\": \"gpt-4o-mini-tts\",\n \"voice\": \"nova\",\n \"input\": \"{{ $('Parse Script').first().json.fullVoiceover.replace(/\\\"/g, '\\\\\"').replace(/\\n/g, ' ') }}\",\n \"response_format\": \"mp3\",\n \"speed\": 0.9,\n \"instructions\": \"Parle de manière naturelle et authentique, comme un parent partageant une découverte. Ton {{ $('Parse Form').first().json.tone }}. Pauses naturelles, enthousiasme sincère.\"\n}", + "options": { "response": { "response": { "responseFormat": "file" } } } }, - "id": "generate-image", - "name": "OpenAI - Generate Scene Image", - "type": "@n8n/n8n-nodes-langchain.openAi", - "typeVersion": 1.8, - "position": [1570, 100], - "credentials": { - "openAiApi": { - "id": "openai-credentials", - "name": "OpenAI API" - } - } + "id": "generate-audio", + "name": "OpenAI TTS", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [1300, 50], + "credentials": { "httpHeaderAuth": { "id": "openai-api", "name": "OpenAI API" } } }, { - "parameters": { - "jsCode": "// Collect generated image and scene data\nconst scene = $('Split Into Scenes').first().json;\nconst imageResult = $input.first().json;\n\nreturn [{\n json: {\n scene_number: scene.scene_number,\n duration_seconds: scene.duration_seconds,\n voiceover_text: scene.voiceover_text,\n image_url: imageResult.data?.[0]?.url || imageResult.url,\n image_prompt: $('OpenAI - Generate Image Prompt').first().json.message?.content\n }\n}];" - }, - "id": "collect-scene-data", - "name": "Collect Scene Data", + "parameters": { "jsCode": "return [{ json: { audio: { generated: true, format: 'mp3' } }, binary: $input.first().binary }];" }, + "id": "process-audio", + "name": "Process Audio", "type": "n8n-nodes-base.code", "typeVersion": 2, - "position": [1790, 100] + "position": [1520, 50] }, { - "parameters": { - "aggregate": "aggregateAllItemData", - "options": {} - }, - "id": "aggregate-scenes", - "name": "Aggregate All Scenes", - "type": "n8n-nodes-base.aggregate", - "typeVersion": 1, - "position": [1130, 400] + "parameters": { "jsCode": "return [{ json: { audio: { generated: false } } }];" }, + "id": "skip-audio", + "name": "Skip Audio", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [1300, 200] }, { "parameters": { - "url": "https://api.elevenlabs.io/v1/text-to-speech/{{ $json.voice_id }}", - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={\n \"text\": \"{{ $('Parse Script & Prepare Scenes').first().json.full_voiceover }}\",\n \"model_id\": \"eleven_multilingual_v2\",\n \"voice_settings\": {\n \"stability\": 0.5,\n \"similarity_boost\": 0.8,\n \"style\": 0.4,\n \"use_speaker_boost\": true\n }\n}", - "options": { - "response": { - "response": { - "responseFormat": "file" - } - } + "conditions": { + "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, + "conditions": [{ "id": "images", "leftValue": "={{ $('Parse Script').first().json.brief.generate_images }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }] } }, - "id": "generate-voiceover", - "name": "ElevenLabs - Generate Voiceover", - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.2, - "position": [1130, 550], - "credentials": { - "httpHeaderAuth": { - "id": "elevenlabs-api", - "name": "ElevenLabs API Key" - } - } + "id": "check-images", + "name": "Images?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [1080, 400] }, { - "parameters": { - "jsCode": "// Select voice based on language\nconst briefing = $('Parse Script & Prepare Scenes').first().json.briefing;\n\n// ElevenLabs voice IDs by language (these are example IDs - replace with actual)\nconst voiceMap = {\n 'fr': 'pFZP5JQG7iQjIQuC4Bku', // French female voice\n 'en': 'EXAVITQu4vr4xnSDxMaL', // English female voice\n 'es': 'ErXwobaYiN019PkySvjV', // Spanish female voice\n 'de': 'yoZ06aMxZJJ28mfd3POQ', // German female voice\n 'it': 'jBpfuIE2acCO8z3wKNLl' // Italian female voice\n};\n\nconst voice_id = voiceMap[briefing.language] || voiceMap['fr'];\n\nreturn [{ json: { voice_id } }];" - }, - "id": "select-voice", - "name": "Select Voice by Language", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [910, 550] + "parameters": { "fieldToSplitOut": "scenes", "options": {} }, + "id": "split-scenes", + "name": "Split Scenes", + "type": "n8n-nodes-base.splitOut", + "typeVersion": 1, + "position": [1300, 350] }, { "parameters": { - "url": "https://api.shotstack.io/v1/render", + "url": "https://api.replicate.com/v1/predictions", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { "name": "Authorization", "value": "=Bearer {{ $credentials.replicateApi.apiKey }}" }, + { "name": "Content-Type", "value": "application/json" } + ] + }, "sendBody": true, "specifyBody": "json", - "jsonBody": "={\n \"timeline\": {\n \"soundtrack\": {\n \"src\": \"{{ $('Upload Voiceover').first().json.audio_url }}\",\n \"effect\": \"fadeOut\"\n },\n \"background\": \"#000000\",\n \"tracks\": [\n {\n \"clips\": {{ JSON.stringify($('Aggregate All Scenes').first().json.data.map((scene, i) => ({\n \"asset\": {\n \"type\": \"image\",\n \"src\": scene.image_url\n },\n \"start\": $('Aggregate All Scenes').first().json.data.slice(0, i).reduce((sum, s) => sum + s.duration_seconds, 0),\n \"length\": scene.duration_seconds,\n \"effect\": \"zoomIn\",\n \"transition\": {\n \"in\": \"fade\",\n \"out\": \"fade\"\n }\n }))) }}\n },\n {\n \"clips\": [\n {\n \"asset\": {\n \"type\": \"title\",\n \"text\": \"{{ $('Parse Script & Prepare Scenes').first().json.script.cta }}\",\n \"style\": \"chunk\",\n \"size\": \"medium\"\n },\n \"start\": {{ $('Aggregate All Scenes').first().json.data.reduce((sum, s) => sum + s.duration_seconds, 0) - 3 }},\n \"length\": 3,\n \"transition\": {\n \"in\": \"fade\"\n }\n }\n ]\n }\n ]\n },\n \"output\": {\n \"format\": \"mp4\",\n \"resolution\": \"1080\",\n \"aspectRatio\": \"9:16\",\n \"fps\": 30\n }\n}", + "jsonBody": "={\n \"version\": \"black-forest-labs/flux-1.1-pro\",\n \"input\": {\n \"prompt\": \"UGC lifestyle photo for TikTok ad. {{ $json.visual }}. Emotion: {{ $json.emotion }}. Camera: {{ $json.camera }}. Style: authentic smartphone photo, natural lighting, warm tones, vertical 9:16, family-friendly. Promoting InfiniteStories bedtime stories app.\",\n \"aspect_ratio\": \"9:16\",\n \"output_format\": \"webp\",\n \"output_quality\": 90\n }\n}", "options": {} }, - "id": "create-video", - "name": "Shotstack - Create Video", + "id": "replicate-image", + "name": "Replicate - Flux Image", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, - "position": [1570, 400], - "credentials": { - "httpHeaderAuth": { - "id": "shotstack-api", - "name": "Shotstack API Key" - } - } + "position": [1520, 350], + "credentials": { "httpHeaderAuth": { "id": "replicate-api", "name": "Replicate API" } } }, { "parameters": { - "url": "=https://api.shotstack.io/v1/render/{{ $json.response.id }}", + "url": "={{ $json.urls.get }}", + "sendHeaders": true, + "headerParameters": { + "parameters": [{ "name": "Authorization", "value": "=Bearer {{ $credentials.replicateApi.apiKey }}" }] + }, "options": {} }, - "id": "poll-video-status", - "name": "Poll Video Render Status", + "id": "poll-image", + "name": "Poll Image Status", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, - "position": [1790, 400], - "credentials": { - "httpHeaderAuth": { - "id": "shotstack-api", - "name": "Shotstack API Key" - } - } + "position": [1740, 350], + "credentials": { "httpHeaderAuth": { "id": "replicate-api", "name": "Replicate API" } } }, { "parameters": { "conditions": { - "options": { - "caseSensitive": true, - "leftValue": "", - "typeValidation": "strict" - }, - "conditions": [ - { - "id": "render-done", - "leftValue": "={{ $json.response.status }}", - "rightValue": "done", - "operator": { - "type": "string", - "operation": "equals" - } - } - ] + "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, + "conditions": [{ "id": "done", "leftValue": "={{ $json.status }}", "rightValue": "succeeded", "operator": { "type": "string", "operation": "equals" } }] } }, - "id": "check-render-status", - "name": "Check If Render Done", + "id": "check-image-done", + "name": "Image Done?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, - "position": [2010, 400] + "position": [1960, 350] }, { - "parameters": { - "amount": 5, - "unit": "seconds" - }, - "id": "wait-for-render", - "name": "Wait 5s", + "parameters": { "amount": 2, "unit": "seconds" }, + "id": "wait-image", + "name": "Wait 2s", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, - "position": [2230, 500] + "position": [2180, 450] }, { "parameters": { - "jsCode": "// Compile final response\nconst script = $('Parse Script & Prepare Scenes').first().json.script;\nconst briefing = $('Parse Script & Prepare Scenes').first().json.briefing;\nconst renderResult = $input.first().json;\nconst scenes = $('Aggregate All Scenes').first().json.data;\n\nreturn [{\n json: {\n success: true,\n video_url: renderResult.response?.url,\n render_id: renderResult.response?.id,\n script: {\n hook: script.hook,\n cta: script.cta,\n hashtags: script.hashtags,\n music_suggestion: script.music_suggestion,\n scenes: script.script_scenes\n },\n generated_assets: {\n scene_images: scenes.map(s => ({\n scene_number: s.scene_number,\n image_url: s.image_url,\n duration: s.duration_seconds\n }))\n },\n metadata: {\n product: briefing.product_name,\n language: briefing.language,\n duration_seconds: briefing.duration_seconds,\n generated_at: new Date().toISOString()\n },\n tiktok_ready: {\n caption: script.hook + '\\n\\n' + script.hashtags.map(h => '#' + h).join(' '),\n suggested_sounds: script.music_suggestion\n }\n }\n}];" + "jsCode": "const scene = $('Split Scenes').first().json;\nconst result = $input.first().json;\nreturn [{\n json: {\n scene_id: scene.id,\n phase: scene.phase,\n timestamp: scene.timestamp,\n image_url: result.output,\n video_prompt: scene.video_prompt\n }\n}];" }, - "id": "compile-response", - "name": "Compile Final Response", + "id": "collect-image", + "name": "Collect Image", "type": "n8n-nodes-base.code", "typeVersion": 2, - "position": [2230, 300] + "position": [2180, 300] + }, + { + "parameters": { "aggregate": "aggregateAllItemData", "destinationFieldName": "images", "options": {} }, + "id": "aggregate-images", + "name": "Aggregate Images", + "type": "n8n-nodes-base.aggregate", + "typeVersion": 1, + "position": [2400, 350] + }, + { + "parameters": { "jsCode": "return [{ json: { images: [] } }];" }, + "id": "skip-images", + "name": "Skip Images", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [1300, 500] }, { "parameters": { - "respondWith": "json", - "responseBody": "={{ $json }}", - "options": {} + "conditions": { + "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, + "conditions": [{ "id": "video", "leftValue": "={{ $('Parse Script').first().json.brief.generate_video }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }] + } }, - "id": "respond-success", - "name": "Respond With Video", - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.1, - "position": [2450, 300] + "id": "check-video", + "name": "Video?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [2620, 350] }, { "parameters": { - "respondWith": "json", - "responseBody": "={\n \"success\": false,\n \"error\": \"{{ $json.error || 'An error occurred during video generation' }}\",\n \"details\": {{ JSON.stringify($json) }}\n}", - "options": { - "responseCode": 500 - } + "jsCode": "// Prepare video generation request based on model choice\nconst brief = $('Parse Script').first().json.brief;\nconst images = $input.first().json.images || [];\nconst script = $('Parse Script').first().json.script;\n\n// Build video prompts from scenes\nconst videoPrompts = script.scenes.map((scene, i) => ({\n scene_id: scene.id,\n prompt: scene.video_prompt || scene.visual,\n duration: 5, // seconds per scene\n image_url: images[i]?.image_url\n}));\n\nreturn [{\n json: {\n model: brief.video_model,\n prompts: videoPrompts,\n total_duration: brief.duration_seconds\n }\n}];" }, - "id": "respond-error", - "name": "Respond With Error", - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.1, - "position": [2450, 550] + "id": "prepare-video", + "name": "Prepare Video Request", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [2840, 300] }, { "parameters": { - "url": "https://api.cloudinary.com/v1_1/{{ $credentials.cloudName }}/upload", - "sendBody": true, - "contentType": "multipart-form-data", - "bodyParameters": { + "url": "https://api.replicate.com/v1/predictions", + "sendHeaders": true, + "headerParameters": { "parameters": [ - { - "name": "file", - "parameterType": "formBinaryData", - "inputDataFieldName": "data" - }, - { - "name": "upload_preset", - "value": "tiktok_ads" - }, - { - "name": "resource_type", - "value": "video" - } + { "name": "Authorization", "value": "=Bearer {{ $credentials.replicateApi.apiKey }}" }, + { "name": "Content-Type", "value": "application/json" } ] }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"version\": \"{{ $json.model === 'kling' ? 'kwaivgi/kling-v1.6-pro' : 'minimax/video-01' }}\",\n \"input\": {\n \"prompt\": \"{{ $json.prompts[0].prompt }}. UGC style TikTok ad, authentic feel, warm tones, family-friendly content about bedtime stories app.\",\n \"duration\": {{ Math.min($json.total_duration, 10) }},\n \"aspect_ratio\": \"9:16\"\n }\n}", "options": {} }, - "id": "upload-voiceover", - "name": "Upload Voiceover", + "id": "replicate-video", + "name": "Replicate - Video Gen", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, - "position": [1350, 550], - "credentials": { - "httpBasicAuth": { - "id": "cloudinary-api", - "name": "Cloudinary API" - } - } + "position": [3060, 300], + "credentials": { "httpHeaderAuth": { "id": "replicate-api", "name": "Replicate API" } } }, { "parameters": { - "errorMessage": "={{ $json.error || 'Video generation failed' }}" + "url": "={{ $json.urls.get }}", + "sendHeaders": true, + "headerParameters": { + "parameters": [{ "name": "Authorization", "value": "=Bearer {{ $credentials.replicateApi.apiKey }}" }] + }, + "options": {} }, - "id": "handle-error", - "name": "Handle Error", - "type": "n8n-nodes-base.stopAndError", - "typeVersion": 1, - "position": [2010, 550] - } - ], - "connections": { - "Webhook - Receive Briefing": { - "main": [ - [ - { - "node": "Validate & Structure Input", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validate & Structure Input": { - "main": [ - [ - { - "node": "OpenAI - Generate UGC Script", - "type": "main", - "index": 0 - } - ] - ] - }, - "OpenAI - Generate UGC Script": { - "main": [ - [ - { - "node": "Parse Script & Prepare Scenes", - "type": "main", - "index": 0 - } - ] - ] - }, - "Parse Script & Prepare Scenes": { - "main": [ - [ - { - "node": "Split Into Scenes", - "type": "main", - "index": 0 - }, - { - "node": "Select Voice by Language", - "type": "main", - "index": 0 - } - ] - ] - }, - "Split Into Scenes": { - "main": [ - [ - { - "node": "OpenAI - Generate Image Prompt", - "type": "main", - "index": 0 - } - ], - [ - { - "node": "Aggregate All Scenes", - "type": "main", - "index": 0 - } - ] - ] - }, - "OpenAI - Generate Image Prompt": { - "main": [ - [ - { - "node": "OpenAI - Generate Scene Image", - "type": "main", - "index": 0 - } - ] - ] - }, - "OpenAI - Generate Scene Image": { - "main": [ - [ - { - "node": "Collect Scene Data", - "type": "main", - "index": 0 - } - ] - ] - }, - "Collect Scene Data": { - "main": [ - [ - { - "node": "Split Into Scenes", - "type": "main", - "index": 0 - } - ] - ] - }, - "Select Voice by Language": { - "main": [ - [ - { - "node": "ElevenLabs - Generate Voiceover", - "type": "main", - "index": 0 - } - ] - ] - }, - "ElevenLabs - Generate Voiceover": { - "main": [ - [ - { - "node": "Upload Voiceover", - "type": "main", - "index": 0 - } - ] - ] + "id": "poll-video", + "name": "Poll Video Status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [3280, 300], + "credentials": { "httpHeaderAuth": { "id": "replicate-api", "name": "Replicate API" } } }, - "Upload Voiceover": { - "main": [ - [ - { - "node": "Shotstack - Create Video", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { + "conditions": { + "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, + "conditions": [{ "id": "done", "leftValue": "={{ $json.status }}", "rightValue": "succeeded", "operator": { "type": "string", "operation": "equals" } }] + } + }, + "id": "check-video-done", + "name": "Video Done?", + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [3500, 300] }, - "Aggregate All Scenes": { - "main": [ - [ - { - "node": "Shotstack - Create Video", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { "amount": 10, "unit": "seconds" }, + "id": "wait-video", + "name": "Wait 10s", + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [3720, 400] }, - "Shotstack - Create Video": { - "main": [ - [ - { - "node": "Poll Video Render Status", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { + "jsCode": "const result = $input.first().json;\nreturn [{\n json: {\n video: {\n generated: true,\n url: result.output,\n model: $('Prepare Video Request').first().json.model\n }\n }\n}];" + }, + "id": "process-video", + "name": "Process Video", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [3720, 250] }, - "Poll Video Render Status": { - "main": [ - [ - { - "node": "Check If Render Done", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { "jsCode": "return [{ json: { video: { generated: false } } }];" }, + "id": "skip-video", + "name": "Skip Video", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [2840, 450] }, - "Check If Render Done": { - "main": [ - [ - { - "node": "Compile Final Response", - "type": "main", - "index": 0 - } - ], - [ - { - "node": "Wait 5s", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { "mode": "combine", "combineBy": "combineAll", "options": {} }, + "id": "merge-audio", + "name": "Merge Audio", + "type": "n8n-nodes-base.merge", + "typeVersion": 3.1, + "position": [1740, 150] }, - "Wait 5s": { - "main": [ - [ - { - "node": "Poll Video Render Status", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { "mode": "combine", "combineBy": "combineAll", "options": {} }, + "id": "merge-images", + "name": "Merge Images", + "type": "n8n-nodes-base.merge", + "typeVersion": 3.1, + "position": [2620, 500] }, - "Compile Final Response": { - "main": [ - [ - { - "node": "Respond With Video", - "type": "main", - "index": 0 - } - ] - ] + { + "parameters": { "mode": "combine", "combineBy": "combineAll", "options": {} }, + "id": "merge-video", + "name": "Merge Video", + "type": "n8n-nodes-base.merge", + "typeVersion": 3.1, + "position": [3940, 350] }, - "Handle Error": { - "main": [ - [ - { - "node": "Respond With Error", - "type": "main", - "index": 0 - } - ] - ] - } - }, - "active": false, - "settings": { - "executionOrder": "v1", - "saveManualExecutions": true, - "callerPolicy": "workflowsFromSameOwner", - "errorWorkflow": "" - }, - "versionId": "1", - "meta": { - "instanceId": "infinite-stories-tiktok-ads" - }, - "tags": [ { - "name": "marketing", - "id": "1" + "parameters": { "mode": "combine", "combineBy": "combineAll", "options": {} }, + "id": "merge-all", + "name": "Merge All", + "type": "n8n-nodes-base.merge", + "typeVersion": 3.1, + "position": [4160, 300] }, { - "name": "tiktok", - "id": "2" + "parameters": { + "jsCode": "const items = $input.all();\nconst scriptData = $('Parse Script').first().json;\nconst brief = scriptData.brief;\n\nlet audio = items.find(i => i.json.audio)?.json.audio || { generated: false };\nlet images = items.find(i => i.json.images)?.json.images || [];\nlet video = items.find(i => i.json.video)?.json.video || { generated: false };\nlet audioBinary = items.find(i => i.binary?.data)?.binary;\n\nconst response = {\n success: true,\n \n script: {\n hook: scriptData.script.hook,\n cta: scriptData.script.cta,\n scenes: scriptData.script.scenes,\n hashtags: scriptData.script.hashtags,\n music_mood: scriptData.script.music_mood,\n filming_tips: scriptData.script.filming_tips\n },\n \n voiceover_text: scriptData.fullVoiceover,\n \n audio: audio,\n images: images,\n video: video,\n \n metadata: {\n product: brief.product_name,\n language: brief.language,\n duration: brief.duration_seconds,\n output_mode: brief.output_mode,\n models: {\n script: 'gpt-5-mini',\n audio: audio.generated ? 'gpt-4o-mini-tts' : null,\n images: images.length > 0 ? 'flux-1.1-pro' : null,\n video: video.generated ? brief.video_model : null\n },\n generated_at: new Date().toISOString()\n },\n \n tiktok_ready: {\n caption: scriptData.script.hook + '\\n\\n' + (scriptData.script.hashtags || []).map(h => '#' + h).join(' '),\n music: scriptData.script.music_mood\n }\n};\n\nif (audioBinary) {\n return [{ json: response, binary: audioBinary }];\n}\nreturn [{ json: response }];" + }, + "id": "compile-response", + "name": "Compile Response", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [4380, 300] }, { - "name": "video-generation", - "id": "3" + "parameters": { "respondWith": "json", "responseBody": "={{ $json }}", "options": {} }, + "id": "respond", + "name": "Respond", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.1, + "position": [4600, 300] } + ], + "connections": { + "Form - Brief": { "main": [[{ "node": "Parse Form", "type": "main", "index": 0 }]] }, + "Parse Form": { "main": [[{ "node": "OpenAI - Generate Script", "type": "main", "index": 0 }]] }, + "OpenAI - Generate Script": { "main": [[{ "node": "Parse Script", "type": "main", "index": 0 }]] }, + "Parse Script": { "main": [[{ "node": "Audio?", "type": "main", "index": 0 }, { "node": "Images?", "type": "main", "index": 0 }]] }, + "Audio?": { "main": [[{ "node": "OpenAI TTS", "type": "main", "index": 0 }], [{ "node": "Skip Audio", "type": "main", "index": 0 }]] }, + "OpenAI TTS": { "main": [[{ "node": "Process Audio", "type": "main", "index": 0 }]] }, + "Process Audio": { "main": [[{ "node": "Merge Audio", "type": "main", "index": 0 }]] }, + "Skip Audio": { "main": [[{ "node": "Merge Audio", "type": "main", "index": 1 }]] }, + "Images?": { "main": [[{ "node": "Split Scenes", "type": "main", "index": 0 }], [{ "node": "Skip Images", "type": "main", "index": 0 }]] }, + "Split Scenes": { "main": [[{ "node": "Replicate - Flux Image", "type": "main", "index": 0 }]] }, + "Replicate - Flux Image": { "main": [[{ "node": "Poll Image Status", "type": "main", "index": 0 }]] }, + "Poll Image Status": { "main": [[{ "node": "Image Done?", "type": "main", "index": 0 }]] }, + "Image Done?": { "main": [[{ "node": "Collect Image", "type": "main", "index": 0 }], [{ "node": "Wait 2s", "type": "main", "index": 0 }]] }, + "Wait 2s": { "main": [[{ "node": "Poll Image Status", "type": "main", "index": 0 }]] }, + "Collect Image": { "main": [[{ "node": "Aggregate Images", "type": "main", "index": 0 }]] }, + "Aggregate Images": { "main": [[{ "node": "Merge Images", "type": "main", "index": 0 }]] }, + "Skip Images": { "main": [[{ "node": "Merge Images", "type": "main", "index": 1 }]] }, + "Merge Audio": { "main": [[{ "node": "Merge All", "type": "main", "index": 0 }]] }, + "Merge Images": { "main": [[{ "node": "Video?", "type": "main", "index": 0 }]] }, + "Video?": { "main": [[{ "node": "Prepare Video Request", "type": "main", "index": 0 }], [{ "node": "Skip Video", "type": "main", "index": 0 }]] }, + "Prepare Video Request": { "main": [[{ "node": "Replicate - Video Gen", "type": "main", "index": 0 }]] }, + "Replicate - Video Gen": { "main": [[{ "node": "Poll Video Status", "type": "main", "index": 0 }]] }, + "Poll Video Status": { "main": [[{ "node": "Video Done?", "type": "main", "index": 0 }]] }, + "Video Done?": { "main": [[{ "node": "Process Video", "type": "main", "index": 0 }], [{ "node": "Wait 10s", "type": "main", "index": 0 }]] }, + "Wait 10s": { "main": [[{ "node": "Poll Video Status", "type": "main", "index": 0 }]] }, + "Process Video": { "main": [[{ "node": "Merge Video", "type": "main", "index": 0 }]] }, + "Skip Video": { "main": [[{ "node": "Merge Video", "type": "main", "index": 1 }]] }, + "Merge Video": { "main": [[{ "node": "Merge All", "type": "main", "index": 1 }]] }, + "Merge All": { "main": [[{ "node": "Compile Response", "type": "main", "index": 0 }]] }, + "Compile Response": { "main": [[{ "node": "Respond", "type": "main", "index": 0 }]] } + }, + "active": false, + "settings": { "executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner" }, + "versionId": "4", + "meta": { "instanceId": "infinite-stories-tiktok-replicate" }, + "tags": [ + { "name": "marketing", "id": "1" }, + { "name": "tiktok", "id": "2" }, + { "name": "replicate", "id": "3" }, + { "name": "video", "id": "4" } ] } From 2ccbd78d732ed71a4449239badbf38b091c387d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Jan 2026 11:22:19 +0000 Subject: [PATCH 3/4] Add comprehensive visual documentation for n8n workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ASCII diagrams for each workflow step - Document script structure (HOOK → PROBLEM → DISCOVERY → BENEFITS → CTA) - Add visual sections for audio, image, and video generation - Include cost breakdown per output mode - Add credentials configuration guide - Update to reflect Replicate + OpenAI architecture --- n8n-workflows/README.md | 775 +++++++++++++++++++++++----------------- 1 file changed, 457 insertions(+), 318 deletions(-) diff --git a/n8n-workflows/README.md b/n8n-workflows/README.md index 5853e0e..377e37b 100644 --- a/n8n-workflows/README.md +++ b/n8n-workflows/README.md @@ -1,389 +1,528 @@ # n8n Workflows - InfiniteStories Marketing Automation -This folder contains n8n workflows for automating marketing content creation for InfiniteStories. - -## Available Workflows - -| Workflow | File | Description | Cost/Run | -|----------|------|-------------|----------| -| Full Video Generator | `tiktok-ugc-ads-generator.json` | Complete video with voiceover | ~$2.85 | -| Script + Images | `tiktok-ugc-script-generator.json` | Script and images only | ~$2.10 | +Ce dossier contient les workflows n8n pour automatiser la création de contenu marketing TikTok pour InfiniteStories. --- -## 1. TikTok UGC Ads Generator (Full Video) - -**File:** `tiktok-ugc-ads-generator.json` - -Generates TikTok-ready UGC (User Generated Content) style video ads from a simple briefing. - -### Workflow Overview +## Vue d'ensemble ``` -Briefing Input -> AI Script Generation -> Scene Images -> Voiceover -> Video Assembly -> TikTok-Ready Video +┌─────────────────────────────────────────────────────────────────────────────┐ +│ TikTok UGC Ads Generator │ +│ │ +│ 📝 Brief → 🤖 Script AI → 🎨 Images → 🎙️ Audio → 🎬 Vidéo │ +│ │ +│ OpenAI gpt-5-mini Flux 1.1 gpt-4o-mini Minimax/ │ +│ Form Pro -tts Kling │ +└─────────────────────────────────────────────────────────────────────────────┘ ``` -### Features +## Workflows Disponibles -- AI-generated UGC-style scripts (GPT-4o) -- Multi-language support (FR, EN, ES, DE, IT) -- DALL-E 3 scene image generation (9:16 vertical format) -- ElevenLabs voiceover synthesis -- Automatic video assembly with Shotstack -- TikTok-optimized output (1080p, 9:16, 30fps) +| Workflow | Fichier | Description | Coût | +|----------|---------|-------------|------| +| **Full Video** | `tiktok-ugc-ads-generator.json` | Script + Audio + Images + Vidéo | ~$2-5 | +| **Script Only** | `tiktok-ugc-script-generator.json` | Script + Images (sans vidéo) | ~$0.05-2 | -### Required Credentials +--- -Configure these credentials in n8n before importing: +# 🎬 TikTok UGC Ads Generator -| Credential ID | Service | Required Scopes | -|--------------|---------|-----------------| -| `openai-credentials` | OpenAI API | GPT-4o, DALL-E 3 | -| `elevenlabs-api` | ElevenLabs | Text-to-Speech | -| `shotstack-api` | Shotstack | Video Rendering | -| `cloudinary-api` | Cloudinary | Asset Upload | +**Fichier:** `tiktok-ugc-ads-generator.json` -### API Endpoint +Génère des publicités TikTok style UGC (User Generated Content) complètes à partir d'un simple brief. -Once activated, the workflow exposes: +--- -``` -POST /webhook/generate-tiktok-ad -``` +## 📊 Architecture du Workflow -### Request Body - -```json -{ - "product_name": "InfiniteStories", - "target_audience": "Parents with children aged 2-10", - "key_benefits": [ - "Personalized AI-generated bedtime stories", - "Child becomes the hero of the story", - "Beautiful illustrations synced with audio", - "Multiple languages supported", - "Safe content for children" - ], - "tone": "warm, authentic, relatable", - "duration_seconds": 30, - "language": "fr", - "hook_type": "problem-solution", - "cta": "Telecharge InfiniteStories maintenant!", - "custom_instructions": "Focus on the magical bedtime routine" -} ``` - -### Request Parameters - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `product_name` | string | No | InfiniteStories | Product name | -| `target_audience` | string | No | Parents with children aged 2-10 | Target demographic | -| `key_benefits` | array | No | App default benefits | List of key selling points | -| `tone` | string | No | warm, authentic, relatable | Voice and style tone | -| `duration_seconds` | number | No | 30 | Target video duration | -| `language` | string | No | fr | Language code (fr/en/es/de/it) | -| `hook_type` | string | No | problem-solution | Hook style | -| `cta` | string | No | Telecharge InfiniteStories! | Call to action text | -| `custom_instructions` | string | No | "" | Additional creative direction | - -### Hook Types - -- `problem-solution` - Start with a relatable problem -- `question` - Open with an engaging question -- `story` - Begin with a mini-story -- `shocking-stat` - Lead with an attention-grabbing statistic -- `testimonial` - Start as a personal recommendation - -### Response - -```json -{ - "success": true, - "video_url": "https://cdn.shotstack.io/...", - "render_id": "abc123", - "script": { - "hook": "Vous cherchez comment endormir vos enfants facilement?", - "cta": "Telecharge InfiniteStories maintenant!", - "hashtags": ["parenting", "bedtimestories", "kidsapp"], - "music_suggestion": "Soft acoustic, feel-good vibe", - "scenes": [...] - }, - "generated_assets": { - "scene_images": [ - { - "scene_number": 1, - "image_url": "https://...", - "duration": 5 - } - ] - }, - "metadata": { - "product": "InfiniteStories", - "language": "fr", - "duration_seconds": 30, - "generated_at": "2026-01-10T12:00:00.000Z" - }, - "tiktok_ready": { - "caption": "Vous cherchez comment endormir vos enfants facilement?\n\n#parenting #bedtimestories #kidsapp", - "suggested_sounds": "Soft acoustic, feel-good vibe" - } -} +┌──────────────────────────────────────────────────────────────────────────────┐ +│ │ +│ ┌─────────────┐ │ +│ │ 📝 FORM │ Brief créatif avec tous les paramètres │ +│ │ TRIGGER │ (audience, problème, bénéfices, ton, durée...) │ +│ └──────┬──────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ ÉTAPE 1: GÉNÉRATION DU SCRIPT │ │ +│ │ ┌─────────────┐ │ │ +│ │ │ OpenAI │ Modèle: gpt-5-mini │ │ +│ │ │ gpt-5-mini │ Output: Script JSON structuré │ │ +│ │ └─────────────┘ (hook, scenes[], voiceover, visual, emotion) │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ├────────────────────┬────────────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ ÉTAPE 2A │ │ ÉTAPE 2B │ │ ÉTAPE 2C │ │ +│ │ 🎙️ AUDIO │ │ 🎨 IMAGES │ │ 🎬 VIDÉO │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ ``` -### Example cURL Request - -```bash -curl -X POST https://your-n8n-instance.com/webhook/generate-tiktok-ad \ - -H "Content-Type: application/json" \ - -d '{ - "product_name": "InfiniteStories", - "target_audience": "Parents fatigues qui veulent des moments magiques avec leurs enfants", - "key_benefits": [ - "Histoires personnalisees avec le prenom de votre enfant", - "Votre enfant devient le heros", - "Illustrations magnifiques synchronisees", - "5 langues disponibles", - "Contenu 100% adapte aux enfants" - ], - "tone": "authentique, chaleureux, comme une amie qui partage un bon plan", - "duration_seconds": 30, - "language": "fr", - "hook_type": "problem-solution", - "cta": "Telecharge InfiniteStories - Lien en bio!", - "custom_instructions": "Mettre en avant le moment du coucher et la fatigue des parents" - }' -``` - -### Installation - -1. Open your n8n instance -2. Go to **Workflows** > **Import** -3. Upload `tiktok-ugc-ads-generator.json` -4. Configure the required credentials -5. Activate the workflow - -### Estimated Costs per Video - -| Service | Estimated Cost | -|---------|---------------| -| OpenAI GPT-4o (script) | ~$0.05 | -| OpenAI DALL-E 3 (images) | ~$0.40/image x 5 = $2.00 | -| ElevenLabs (voiceover) | ~$0.30 | -| Shotstack (rendering) | ~$0.50 | -| **Total** | **~$2.85/video** | +--- -### Workflow Nodes Diagram +## 🔧 Étape 1: Génération du Script ``` -[Webhook] --> [Validate Input] --> [Generate Script (GPT-4o)] - | - +---------------------+---------------------+ - | | - v v - [Split Scenes] [Select Voice] - | | - v v - [Generate Image Prompts] [ElevenLabs TTS] - | | - v v - [Generate Images (DALL-E)] [Upload Audio] - | | - v | - [Collect Scene Data] | - | | - +-----------------> [Merge] <---------------+ - | - v - [Shotstack Video Render] - | - v - [Poll Render Status] - | - v - [Return Video URL] +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 📝 SCRIPT GENERATION │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Modèle: OpenAI gpt-5-mini (Responses API) │ +│ Prompt: Structure XML optimisée │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ INPUT (Brief) │ │ +│ │ ───────────── │ │ +│ │ • Nom du produit: InfiniteStories │ │ +│ │ • Audience: Parents avec enfants 2-10 ans │ │ +│ │ • Problème: Fatigue le soir, pas d'énergie pour histoires │ │ +│ │ • Bénéfices: Histoires personnalisées, enfant = héros │ │ +│ │ • Ton: Chaleureux et authentique │ │ +│ │ • Hook type: problem-solution │ │ +│ │ • Durée: 30 secondes │ │ +│ │ • Langue: Français │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ OUTPUT (Script JSON) │ │ +│ │ ──────────────────── │ │ +│ │ { │ │ +│ │ "hook": "Vous en avez marre de...", │ │ +│ │ "scenes": [ │ │ +│ │ { │ │ +│ │ "id": 1, │ │ +│ │ "phase": "HOOK", │ │ +│ │ "timestamp": "0:00-0:03", │ │ +│ │ "voiceover": "Texte exact à dire", │ │ +│ │ "visual": "Description visuelle", │ │ +│ │ "emotion": "curiosité", │ │ +│ │ "camera": "selfie", │ │ +│ │ "video_prompt": "Prompt pour génération vidéo" │ │ +│ │ } │ │ +│ │ ], │ │ +│ │ "cta": "Télécharge maintenant!", │ │ +│ │ "hashtags": ["parenting", "bedtime"], │ │ +│ │ "music_mood": "Soft acoustic", │ │ +│ │ "filming_tips": ["Conseil 1", "Conseil 2"] │ │ +│ │ } │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ ``` -### Customization - -#### Adding New Languages +### Structure du Script UGC -Edit the `Select Voice by Language` node to add new ElevenLabs voice IDs: - -```javascript -const voiceMap = { - 'fr': 'your-french-voice-id', - 'en': 'your-english-voice-id', - // Add more languages -}; +``` +┌─────────────────────────────────────────────────────────────────┐ +│ STRUCTURE 30 SECONDES │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────┐ 0-3s HOOK │ +│ │ 🎯 │ Arrêter le scroll immédiatement │ +│ └─────────┘ "Vous en avez marre de répéter les mêmes..." │ +│ │ │ +│ ▼ │ +│ ┌─────────┐ 3-8s PROBLEM │ +│ │ 😫 │ Identifier le pain point │ +│ └─────────┘ "Chaque soir c'est la même galère..." │ +│ │ │ +│ ▼ │ +│ ┌─────────┐ 8-15s DISCOVERY │ +│ │ 💡 │ Présenter la solution │ +│ └─────────┘ "J'ai découvert cette app qui..." │ +│ │ │ +│ ▼ │ +│ ┌─────────┐ 15-25s BENEFITS │ +│ │ ✨ │ Démontrer les bénéfices │ +│ └─────────┘ "Mon fils adore être le héros..." │ +│ │ │ +│ ▼ │ +│ ┌─────────┐ 25-30s CTA │ +│ │ 📲 │ Call to action │ +│ └─────────┘ "Télécharge InfiniteStories - Lien en bio!" │ +│ │ +└─────────────────────────────────────────────────────────────────┘ ``` -#### Changing Video Style - -Modify the `Shotstack - Create Video` node to adjust: -- Transitions (`fade`, `slideLeft`, `zoom`, etc.) -- Effects (`zoomIn`, `slideRight`, etc.) -- Text overlays and positioning -- Output resolution and format +--- -### Troubleshooting +## 🎙️ Étape 2A: Génération Audio -| Issue | Solution | -|-------|----------| -| Images not generating | Check OpenAI API quota and DALL-E 3 access | -| Voice sounds wrong | Verify ElevenLabs voice ID for the language | -| Video render fails | Check Shotstack API status and image URLs | -| Timeout errors | Increase n8n execution timeout settings | +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 🎙️ AUDIO GENERATION │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Modèle: OpenAI gpt-4o-mini-tts │ +│ Format: MP3 │ +│ Vitesse: 0.9x (plus lent pour authenticité) │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ VOIX DISPONIBLES │ │ +│ │ ───────────────── │ │ +│ │ │ │ +│ │ • nova → Chaleureuse, douce (défaut) │ │ +│ │ • shimmer → Énergique, brillante │ │ +│ │ • alloy → Neutre, polyvalente │ │ +│ │ • ash → Confiante, forte │ │ +│ │ • ballad → Expressive, artistique │ │ +│ │ • coral → Joyeuse, enjouée │ │ +│ │ • sage → Calme, réfléchie │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ INSTRUCTIONS VOCALES │ │ +│ │ ───────────────────── │ │ +│ │ │ │ +│ │ "Parle de manière naturelle et authentique, comme un parent │ │ +│ │ partageant une découverte avec un ami. Ton chaleureux. │ │ +│ │ Pauses naturelles, enthousiasme sincère." │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ OUTPUT: Fichier MP3 binaire (~30 secondes) │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` --- -## 2. TikTok UGC Script Generator (Simplified) +## 🎨 Étape 2B: Génération des Images -**File:** `tiktok-ugc-script-generator.json` - -A simpler workflow that generates UGC scripts and scene images without video assembly. Perfect for creators who want to film their own content using the AI-generated script and reference images. +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 🎨 IMAGE GENERATION │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Modèle: Replicate Flux 1.1 Pro │ +│ Format: 9:16 (vertical TikTok) │ +│ Qualité: WebP 90% │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ PROCESSUS PAR SCÈNE │ │ +│ │ ───────────────────── │ │ +│ │ │ │ +│ │ Pour chaque scène du script: │ │ +│ │ │ │ +│ │ Scene 1 ─────► Replicate API ─────► Poll Status ─────► Image URL │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ Scene 2 ─────► Replicate API ─────► Poll Status ─────► Image URL │ │ +│ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ Scene N ─────► Replicate API ─────► Poll Status ─────► Image URL │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ STYLE DU PROMPT │ │ +│ │ ─────────────── │ │ +│ │ │ │ +│ │ "UGC lifestyle photo for TikTok ad. │ │ +│ │ [Description visuelle de la scène]. │ │ +│ │ Emotion: [émotion]. Camera: [type de plan]. │ │ +│ │ Style: authentic smartphone photo, natural lighting, │ │ +│ │ warm tones, vertical 9:16, family-friendly. │ │ +│ │ Promoting InfiniteStories bedtime stories app." │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ OUTPUT: Array d'URLs d'images (1 par scène) │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` -### Features +--- -- AI-generated UGC-style scripts (GPT-4o) -- Optional DALL-E 3 scene images for visual reference -- Multi-language support (FR, EN, ES, DE, IT) -- Filming tips and caption suggestions -- No video rendering service needed +## 🎬 Étape 2C: Génération Vidéo -### Required Credentials +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 🎬 VIDEO GENERATION │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ MODÈLES DISPONIBLES (via Replicate) │ │ +│ │ ───────────────────────────────────── │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ +│ │ │ MINIMAX │ │ KLING │ │ RUNWAY │ │ │ +│ │ │ video-01 │ │ v1.6-pro │ │ Gen-3 │ │ │ +│ │ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ │ +│ │ │ ⚡ Rapide │ │ 🎨 Haute qualité│ │ 🌟 Premium │ │ │ +│ │ │ 💰 Économique │ │ 🎭 Réaliste │ │ 🎬 Cinématique │ │ │ +│ │ │ 📱 Bon pour UGC │ │ ⏱️ Plus lent │ │ 💎 Le meilleur │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────────┐ │ +│ │ PROCESSUS DE GÉNÉRATION │ │ +│ │ ───────────────────────── │ │ +│ │ │ │ +│ │ Prompt + Config │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────┐ │ │ +│ │ │ Replicate │ ──► Prediction créée │ │ +│ │ │ API │ │ │ +│ │ └─────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Poll │────►│ Wait 10s │──┐ │ │ +│ │ │ Status │ └─────────────┘ │ │ │ +│ │ └─────────────┘◄─────────────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ (status = "succeeded") │ │ +│ │ ┌─────────────┐ │ │ +│ │ │ Video URL │ ──► MP4 9:16 prêt pour TikTok │ │ +│ │ └─────────────┘ │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ OUTPUT: URL de la vidéo MP4 │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` -| Credential ID | Service | Required For | -|--------------|---------|--------------| -| `openai-credentials` | OpenAI API | Script + Images | +--- -### API Endpoint +## 📤 Output Final ``` -POST /webhook/generate-tiktok-script +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 📤 RESPONSE STRUCTURE │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ { │ +│ "success": true, │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ 📝 SCRIPT │ │ +│ │ ────────── │ │ +│ │ • hook: "Phrase d'accroche" │ │ +│ │ • scenes: [{ id, phase, timestamp, voiceover, visual, emotion }] │ │ +│ │ • cta: "Call to action" │ │ +│ │ • hashtags: ["tag1", "tag2"] │ │ +│ │ • music_mood: "Style musical suggéré" │ │ +│ │ • filming_tips: ["Conseil 1", "Conseil 2"] │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ 🎙️ AUDIO │ │ +│ │ ───────── │ │ +│ │ • generated: true/false │ │ +│ │ • format: "mp3" │ │ +│ │ • [binary data attaché à la réponse] │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ 🎨 IMAGES │ │ +│ │ ────────── │ │ +│ │ [ │ │ +│ │ { scene_id: 1, phase: "HOOK", image_url: "https://..." }, │ │ +│ │ { scene_id: 2, phase: "PROBLEM", image_url: "https://..." }, │ │ +│ │ ... │ │ +│ │ ] │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ 🎬 VIDEO │ │ +│ │ ───────── │ │ +│ │ • generated: true/false │ │ +│ │ • url: "https://replicate.delivery/..." │ │ +│ │ • model: "minimax" | "kling" | "runway" │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ 📱 TIKTOK READY │ │ +│ │ ────────────── │ │ +│ │ • caption: "Hook + hashtags formatés" │ │ +│ │ • music: "Suggestion musicale" │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ } │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ ``` -### Request Body - -```json -{ - "product_name": "InfiniteStories", - "target_audience": "Parents fatigues", - "key_benefits": ["Histoires personnalisees", "Enfant devient le heros"], - "tone": "authentique, chaleureux", - "duration_seconds": 30, - "language": "fr", - "hook_type": "problem-solution", - "cta": "Lien en bio!", - "generate_images": true, - "custom_instructions": "Focus sur le moment du coucher" -} -``` +--- -### Additional Parameters - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `generate_images` | boolean | `true` | Generate DALL-E images for each scene | - -### Response - -```json -{ - "success": true, - "script": { - "hook": "Vous savez ce qui a change nos soirees?", - "scenes": [ - { - "id": 1, - "timestamp": "0:00-0:03", - "voiceover": "Texte a dire", - "visual": "Description visuelle", - "emotion": "curiosite", - "camera": "selfie" - } - ], - "cta": "Telecharge maintenant!", - "captions": ["Textes pour overlay"], - "hashtags": ["parenting", "bedtime"], - "music_mood": "Soft acoustic", - "filming_tips": ["Filmer en lumiere naturelle"] - }, - "generated_images": [ - { - "scene_id": 1, - "timestamp": "0:00-0:03", - "image_url": "https://...", - "voiceover": "Texte de la scene" - } - ], - "voiceover_text": "Script complet pour TTS externe", - "tiktok_post": { - "caption": "Hook + hashtags", - "captions_overlay": ["Textes"], - "music_mood": "Soft acoustic" - }, - "filming_guide": ["Tips de tournage"] -} -``` +## 🔑 Credentials Requis -### Use Cases +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 🔑 CONFIGURATION DES CREDENTIALS │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ 1. OpenAI API ││ +│ │ ───────────── ││ +│ │ ││ +│ │ Credential ID: openai-api ││ +│ │ Type: Header Auth ││ +│ │ Header: Authorization ││ +│ │ Value: Bearer sk-xxxxx ││ +│ │ ││ +│ │ Utilisé pour: ││ +│ │ • gpt-5-mini (script generation) ││ +│ │ • gpt-4o-mini-tts (voiceover) ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ 2. Replicate API ││ +│ │ ─────────────── ││ +│ │ ││ +│ │ Credential ID: replicate-api ││ +│ │ Type: Header Auth ││ +│ │ Header: Authorization ││ +│ │ Value: Bearer r8_xxxxx ││ +│ │ ││ +│ │ Utilisé pour: ││ +│ │ • flux-1.1-pro (images) ││ +│ │ • minimax/video-01 (vidéo) ││ +│ │ • kling-v1.6-pro (vidéo alternative) ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` -1. **DIY Filming**: Use the script and images as storyboard, film yourself -2. **Content Planning**: Generate multiple scripts to choose from -3. **External Video Tools**: Export script to CapCut, InShot, etc. -4. **Team Collaboration**: Share script with video production team +--- -### Estimated Costs +## 💰 Estimation des Coûts -| Service | Cost | -|---------|------| -| OpenAI GPT-4o (script) | ~$0.05 | -| OpenAI DALL-E 3 (images) | ~$0.40 x 5 scenes = $2.00 | -| **Total with images** | **~$2.05** | -| **Total script only** | **~$0.05** | +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 💰 COÛTS PAR MODE DE SORTIE │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ MODE: script_only ││ +│ │ ───────────────── ││ +│ │ ││ +│ │ OpenAI gpt-5-mini ........................... ~$0.05 ││ +│ │ ──────────────────────────────────────────────────── ││ +│ │ TOTAL ........................................ $0.05 ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ MODE: script_audio ││ +│ │ ────────────────── ││ +│ │ ││ +│ │ OpenAI gpt-5-mini ........................... ~$0.05 ││ +│ │ OpenAI gpt-4o-mini-tts ...................... ~$0.30 ││ +│ │ ──────────────────────────────────────────────────── ││ +│ │ TOTAL ........................................ $0.35 ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ MODE: script_images ││ +│ │ ─────────────────── ││ +│ │ ││ +│ │ OpenAI gpt-5-mini ........................... ~$0.05 ││ +│ │ Replicate Flux 1.1 Pro (5 images) ........... ~$0.50 ││ +│ │ ──────────────────────────────────────────────────── ││ +│ │ TOTAL ........................................ $0.55 ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐│ +│ │ MODE: full_video ││ +│ │ ──────────────── ││ +│ │ ││ +│ │ OpenAI gpt-5-mini ........................... ~$0.05 ││ +│ │ OpenAI gpt-4o-mini-tts ...................... ~$0.30 ││ +│ │ Replicate Flux 1.1 Pro (5 images) ........... ~$0.50 ││ +│ │ Replicate Minimax/Kling (video) ............. ~$1-3 ││ +│ │ ──────────────────────────────────────────────────── ││ +│ │ TOTAL ........................................ $2-5 ││ +│ │ ││ +│ └─────────────────────────────────────────────────────────────────────────┘│ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` --- -## Quick Start +## 🚀 Quick Start -### 1. Import Workflow +### 1. Importer le Workflow ```bash -# Using n8n CLI -n8n import:workflow --input=n8n-workflows/tiktok-ugc-script-generator.json +n8n import:workflow --input=n8n-workflows/tiktok-ugc-ads-generator.json ``` -### 2. Configure OpenAI Credentials +### 2. Configurer les Credentials -In n8n, go to **Settings > Credentials > Add Credential > OpenAI API** and enter your API key. +Dans n8n: **Settings** → **Credentials** → **Add Credential** -### 3. Activate & Test +- OpenAI API (Header Auth) +- Replicate API (Header Auth) + +### 3. Activer et Tester ```bash -curl -X POST http://localhost:5678/webhook/generate-tiktok-script \ +curl -X POST http://localhost:5678/webhook/tiktok-ad-replicate \ -H "Content-Type: application/json" \ - -d '{"language": "fr", "duration_seconds": 30}' + -d '{ + "Nom du produit": "InfiniteStories", + "Audience cible": "Parents fatigués le soir", + "Problème principal": "Plus d énergie pour raconter des histoires", + "Bénéfices clés (un par ligne)": "Histoires personnalisées\nEnfant héros", + "Ton": "Chaleureux et authentique", + "Type de hook": "problem-solution", + "Durée (secondes)": 30, + "Langue": "fr", + "Call to Action": "Lien en bio!", + "Output souhaité": "script_audio", + "Modèle vidéo": "minimax" + }' ``` --- -## Best Practices +## 🎯 Types de Hook -### Script Optimization +| Type | Description | Exemple | +|------|-------------|---------| +| `problem-solution` | Commence par le problème | "Vous en avez marre de..." | +| `question` | Pose une question engageante | "Vous savez ce qui a changé nos soirées?" | +| `story` | Raconte une mini-histoire | "L'autre soir, mon fils m'a dit..." | +| `shocking` | Fait surprenant | "80% des parents sont épuisés le soir" | +| `testimonial` | Témoignage personnel | "Je dois vous parler de cette app..." | -- **Hook Types**: Test different hook types (problem-solution, question, story) to see what resonates -- **Duration**: 15-30s works best for TikTok ads -- **Language**: Match the language to your target market +--- + +## 🌍 Langues Supportées -### Image Usage +| Code | Langue | Voix par défaut | +|------|--------|-----------------| +| `fr` | Français | nova | +| `en` | English | nova | +| `es` | Español | nova | +| `de` | Deutsch | nova | +| `it` | Italiano | nova | -- Use generated images as **storyboard reference**, not final content -- Real UGC performs better than AI-generated visuals on TikTok -- Images help visualize the script for filming +--- -### Cost Control +## 🔧 Troubleshooting -- Set `generate_images: false` for script-only generation ($0.05/request) -- Generate images only for approved scripts -- Batch multiple briefings to test different angles cheaply +| Problème | Solution | +|----------|----------| +| Script non généré | Vérifier la clé OpenAI API | +| Images échouent | Vérifier quota Replicate | +| Vidéo timeout | Augmenter le temps de polling (10s → 30s) | +| Audio vide | Vérifier que le voiceover_text n'est pas trop long | From 4f9f0c1d473e82dc036a403850ec32a7668e8d99 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Jan 2026 13:10:26 +0000 Subject: [PATCH 4/4] Add OpenSpec proposal for n8n TikTok UGC Ads workflow Adds formal specification for the TikTok ad generation workflow including: - proposal.md: Why and what changes - tasks.md: Implementation checklist (mostly complete) - design.md: Technical decisions and architecture - specs/n8n-workflow/spec.md: Requirements with scenarios Covers: script generation, audio synthesis, image generation, video assembly, and conditional output modes. --- .../add-n8n-tiktok-ads-workflow/design.md | 125 ++++++++++++++++++ .../add-n8n-tiktok-ads-workflow/proposal.md | 23 ++++ .../specs/n8n-workflow/spec.md | 124 +++++++++++++++++ .../add-n8n-tiktok-ads-workflow/tasks.md | 27 ++++ 4 files changed, 299 insertions(+) create mode 100644 openspec/changes/add-n8n-tiktok-ads-workflow/design.md create mode 100644 openspec/changes/add-n8n-tiktok-ads-workflow/proposal.md create mode 100644 openspec/changes/add-n8n-tiktok-ads-workflow/specs/n8n-workflow/spec.md create mode 100644 openspec/changes/add-n8n-tiktok-ads-workflow/tasks.md diff --git a/openspec/changes/add-n8n-tiktok-ads-workflow/design.md b/openspec/changes/add-n8n-tiktok-ads-workflow/design.md new file mode 100644 index 0000000..3a10af6 --- /dev/null +++ b/openspec/changes/add-n8n-tiktok-ads-workflow/design.md @@ -0,0 +1,125 @@ +# Design: n8n TikTok UGC Ads Workflow + +## Context + +InfiniteStories is a children's bedtime story app that needs promotional content for TikTok. The team wants to leverage existing AI infrastructure (OpenAI models) while adding video generation capabilities. n8n was chosen as the workflow automation platform for its flexibility and self-hosting options. + +## Goals / Non-Goals + +**Goals:** +- Automate TikTok ad generation from a simple briefing input +- Maintain consistency with backend AI models (OpenAI) +- Support multiple output modes for flexibility +- Simplify billing through Replicate consolidation +- Produce TikTok-optimized 9:16 vertical videos + +**Non-Goals:** +- Direct TikTok posting (handled separately) +- A/B testing infrastructure +- Real-time video editing +- User-facing interface (n8n internal only) + +## Decisions + +### Decision 1: Use n8n for workflow orchestration +- **What**: n8n handles the entire pipeline from briefing to video output +- **Why**: Visual workflow editor, self-hostable, good API integration support +- **Alternatives**: Zapier (expensive), custom code (maintenance overhead), Make (less flexible) + +### Decision 2: OpenAI for script and audio generation +- **What**: Use `gpt-5-mini` for scripts, `gpt-4o-mini-tts` for voiceover +- **Why**: Consistency with backend models, proven quality, existing billing relationship +- **Alternatives**: Claude (no TTS), ElevenLabs (separate billing), Anthropic (no audio) + +### Decision 3: Replicate for image and video generation +- **What**: Use Replicate API with `flux-1.1-pro` for images, `minimax/video-01` for video +- **Why**: Single billing platform for compute-heavy media generation, model flexibility +- **Alternatives**: + - DALL-E 3 (expensive at scale) + - Runway (separate subscription) + - Pika (less API-friendly) + +### Decision 4: UGC-style script structure +- **What**: HOOK (0-3s) -> PROBLEM (3-8s) -> DISCOVERY (8-15s) -> BENEFITS (15-25s) -> CTA (25-30s) +- **Why**: Proven TikTok format for engagement, fits platform algorithm preferences +- **Alternatives**: Testimonial format, demo format (less engaging for app promotion) + +### Decision 5: Conditional output modes +- **What**: 4 output modes (script_only, script_audio, script_images, full_video) +- **Why**: Allow iterative review, cost control, faster turnaround for script-only needs +- **Alternatives**: Single full output (wastes resources if only script needed) + +## Technical Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ n8n WORKFLOW │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌──────────────┐ ┌────────────────────────┐ │ +│ │ Form │───▶│ OpenAI │───▶│ Conditional │ │ +│ │ Trigger │ │ gpt-5-mini │ │ Branching │ │ +│ └──────────┘ │ (Script) │ └──────────┬─────────────┘ │ +│ └──────────────┘ │ │ +│ ▼ │ +│ ┌──────────────────────────────┴─────────┐ │ +│ │ │ │ +│ ┌────────▼────────┐ ┌─────────▼─────────┐ │ │ +│ │ OpenAI TTS │ │ Replicate │ │ │ +│ │ gpt-4o-mini-tts │ │ flux-1.1-pro │ │ │ +│ │ (Audio) │ │ (Images x5) │ │ │ +│ └────────┬────────┘ └─────────┬─────────┘ │ │ +│ │ │ │ │ +│ └──────────┬──────────┘ │ │ +│ ▼ │ │ +│ ┌──────────────────────┐ │ │ +│ │ Replicate │ │ │ +│ │ minimax/video-01 │ │ │ +│ │ (Video Assembly) │ │ │ +│ └──────────┬───────────┘ │ │ +│ ▼ │ │ +│ ┌──────────────────────┐ │ │ +│ │ JSON Response │ │ │ +│ │ (All Assets) │ │ │ +│ └──────────────────────┘ │ │ +│ │ │ +└──────────────────────────────────────────────────────────┴─────┘ +``` + +## API Models Reference + +| Component | Provider | Model | Purpose | +|-----------|----------|-------|---------| +| Script | OpenAI | gpt-5-mini | UGC script generation | +| Audio | OpenAI | gpt-4o-mini-tts | Voiceover synthesis | +| Images | Replicate | flux-1.1-pro | Scene illustrations | +| Video | Replicate | minimax/video-01 | Video assembly | + +## Risks / Trade-offs + +- **Risk**: Replicate model availability/deprecation + - Mitigation: Abstract model selection, easy to swap alternatives + +- **Risk**: Cost overruns on full video generation + - Mitigation: Output modes allow script-only for review before committing to video + +- **Risk**: Video quality inconsistency + - Mitigation: Use proven models, image-to-video ensures visual consistency + +- **Trade-off**: Async polling vs webhooks for Replicate + - Chose polling for simplicity; webhooks for production at scale + +## Migration Plan + +1. Deploy n8n instance +2. Configure API credentials +3. Import workflow JSON +4. Test with sample briefings +5. Create production briefing templates +6. Train team on workflow usage + +## Open Questions + +- Q1: Should we add automatic TikTok posting integration? +- Q2: Do we need A/B testing for different script styles? +- Q3: Should we support other platforms (Instagram Reels, YouTube Shorts)? diff --git a/openspec/changes/add-n8n-tiktok-ads-workflow/proposal.md b/openspec/changes/add-n8n-tiktok-ads-workflow/proposal.md new file mode 100644 index 0000000..6a0e7ce --- /dev/null +++ b/openspec/changes/add-n8n-tiktok-ads-workflow/proposal.md @@ -0,0 +1,23 @@ +# Change: Add n8n TikTok UGC Ads Workflow + +## Why + +InfiniteStories needs a scalable, automated solution to generate TikTok UGC-style promotional videos. Manual ad creation is time-consuming and expensive. By leveraging the same AI models used in the app backend (OpenAI) combined with Replicate for video generation, we can produce high-quality ads quickly with simplified billing. + +## What Changes + +- Add new `n8n-workflows/` directory for automation workflows +- Create `tiktok-ugc-ads-generator.json` - Main n8n workflow for generating TikTok ads +- Create `README.md` - Visual documentation with architecture diagrams +- Integrate with: + - OpenAI `gpt-5-mini` for script generation (same as story generation) + - OpenAI `gpt-4o-mini-tts` for voiceover synthesis + - Replicate `flux-1.1-pro` for image generation + - Replicate `minimax/video-01` or `kling-v1.6-pro` for video generation + +## Impact + +- Affected specs: None (new capability) +- Affected code: New `n8n-workflows/` directory +- New external dependencies: Replicate API +- Infrastructure: Requires n8n instance (self-hosted or cloud) diff --git a/openspec/changes/add-n8n-tiktok-ads-workflow/specs/n8n-workflow/spec.md b/openspec/changes/add-n8n-tiktok-ads-workflow/specs/n8n-workflow/spec.md new file mode 100644 index 0000000..929a17b --- /dev/null +++ b/openspec/changes/add-n8n-tiktok-ads-workflow/specs/n8n-workflow/spec.md @@ -0,0 +1,124 @@ +# n8n Workflow Specification + +## ADDED Requirements + +### Requirement: TikTok UGC Script Generation +The workflow SHALL generate UGC-style scripts from a briefing input using OpenAI gpt-5-mini. + +The script MUST follow the structure: +- HOOK (0-3 seconds): Attention grabber +- PROBLEM (3-8 seconds): Pain point identification +- DISCOVERY (8-15 seconds): Solution introduction +- BENEFITS (15-25 seconds): Feature highlights +- CTA (25-30 seconds): Call to action + +#### Scenario: Script generation from briefing +- **GIVEN** a user provides a briefing with target audience, key message, and tone +- **WHEN** the workflow executes with output_mode containing "script" +- **THEN** the system SHALL generate a structured UGC script with all 5 sections +- **AND** the script SHALL be optimized for TikTok's 9:16 vertical format + +#### Scenario: Script includes visual directions +- **GIVEN** a script is generated +- **WHEN** the output is returned +- **THEN** each section SHALL include visual_direction for image/video generation +- **AND** each section SHALL include timing information + +--- + +### Requirement: Audio Voiceover Generation +The workflow SHALL generate audio voiceover from the script using OpenAI gpt-4o-mini-tts. + +#### Scenario: Audio generation from script +- **GIVEN** a generated script +- **WHEN** the workflow executes with output_mode "script_audio" or "full_video" +- **THEN** the system SHALL generate MP3 audio for the complete script +- **AND** the voice SHALL match the requested tone (energetic, warm, professional) + +#### Scenario: Audio timing matches script sections +- **GIVEN** audio is generated +- **WHEN** the output is returned +- **THEN** the audio duration SHALL approximately match the script timing (25-30 seconds) + +--- + +### Requirement: Scene Image Generation +The workflow SHALL generate scene images from visual directions using Replicate flux-1.1-pro. + +#### Scenario: Image generation for each section +- **GIVEN** a script with 5 sections and visual_directions +- **WHEN** the workflow executes with output_mode "script_images" or "full_video" +- **THEN** the system SHALL generate one image per section (5 images total) +- **AND** images SHALL be 1024x1792 (9:16 aspect ratio) + +#### Scenario: Image style consistency +- **GIVEN** images are generated for a script +- **WHEN** multiple images are created +- **THEN** all images SHALL maintain consistent style based on the briefing tone + +--- + +### Requirement: Video Assembly +The workflow SHALL assemble final video from audio and images using Replicate video models. + +#### Scenario: Full video generation +- **GIVEN** generated audio and images +- **WHEN** the workflow executes with output_mode "full_video" +- **THEN** the system SHALL generate a 9:16 vertical video +- **AND** video duration SHALL be 25-30 seconds +- **AND** images SHALL be animated with transitions + +#### Scenario: Video uses image-to-video model +- **GIVEN** the video generation step +- **WHEN** Replicate is called +- **THEN** the system SHALL use minimax/video-01 or kling-v1.6-pro model +- **AND** the system SHALL poll for completion with exponential backoff + +--- + +### Requirement: Conditional Output Modes +The workflow SHALL support multiple output modes to control generation scope. + +#### Scenario: Script only mode +- **GIVEN** output_mode is "script_only" +- **WHEN** the workflow completes +- **THEN** only the script JSON SHALL be returned +- **AND** no audio, images, or video SHALL be generated + +#### Scenario: Script with audio mode +- **GIVEN** output_mode is "script_audio" +- **WHEN** the workflow completes +- **THEN** script and audio_url SHALL be returned +- **AND** no images or video SHALL be generated + +#### Scenario: Script with images mode +- **GIVEN** output_mode is "script_images" +- **WHEN** the workflow completes +- **THEN** script and image_urls array SHALL be returned +- **AND** no video SHALL be generated + +#### Scenario: Full video mode +- **GIVEN** output_mode is "full_video" +- **WHEN** the workflow completes +- **THEN** script, audio_url, image_urls, and video_url SHALL be returned + +--- + +### Requirement: Form-Based Input Interface +The workflow SHALL provide a form-based interface for briefing input. + +#### Scenario: Form trigger with required fields +- **GIVEN** a user accesses the workflow +- **WHEN** the form is displayed +- **THEN** the form SHALL include: + - target_audience (required, text) + - key_message (required, text) + - tone (required, dropdown: energetic/warm/professional) + - output_mode (required, dropdown) + - additional_context (optional, text) + +#### Scenario: Form validation +- **GIVEN** a user submits the form +- **WHEN** required fields are missing +- **THEN** the form SHALL display validation errors +- **AND** the workflow SHALL NOT execute diff --git a/openspec/changes/add-n8n-tiktok-ads-workflow/tasks.md b/openspec/changes/add-n8n-tiktok-ads-workflow/tasks.md new file mode 100644 index 0000000..0e60213 --- /dev/null +++ b/openspec/changes/add-n8n-tiktok-ads-workflow/tasks.md @@ -0,0 +1,27 @@ +# Tasks: Add n8n TikTok UGC Ads Workflow + +## 1. Implementation + +- [x] 1.1 Create `n8n-workflows/` directory structure +- [x] 1.2 Design workflow architecture (Form Trigger -> AI Nodes -> Output) +- [x] 1.3 Implement script generation node with OpenAI `gpt-5-mini` +- [x] 1.4 Implement audio generation node with OpenAI `gpt-4o-mini-tts` +- [x] 1.5 Implement image generation node with Replicate `flux-1.1-pro` +- [x] 1.6 Implement video generation node with Replicate video models +- [x] 1.7 Add conditional output modes (script_only, script_audio, script_images, full_video) +- [x] 1.8 Create comprehensive README with visual documentation + +## 2. Documentation + +- [x] 2.1 Document workflow architecture with ASCII diagrams +- [x] 2.2 Document each workflow step (script, audio, images, video) +- [x] 2.3 Document credentials configuration +- [x] 2.4 Document cost breakdown per output mode + +## 3. Deployment (Pending) + +- [ ] 3.1 Deploy n8n instance (self-hosted or cloud) +- [ ] 3.2 Configure OpenAI API credentials +- [ ] 3.3 Configure Replicate API credentials +- [ ] 3.4 Import and test workflow +- [ ] 3.5 Create production briefing templates