{
  "id": "CXHBDt6tSiHrMyZj",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "\ud83c\udfac AI YouTube Video Generator - One-Click Automation",
  "tags": [
    {
      "id": "MRQ42hNfYxVuWDnW",
      "name": "Selling",
      "createdAt": "2025-07-02T18:32:26.295Z",
      "updatedAt": "2025-07-02T18:32:26.295Z"
    }
  ],
  "nodes": [
    {
      "id": "sticky-main-info",
      "name": "Main Info",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        -400
      ],
      "parameters": {
        "width": 389,
        "height": 464,
        "content": "## \ud83c\udfac AI YouTube Video Generator\n\n**One-Click Automation Workflow**\n\nThis workflow automatically creates complete YouTube videos from a simple text prompt using:\n\n\u2022 **OpenAI** - Content generation & image prompts\n\u2022 **ElevenLabs** - Text-to-speech conversion  \n\u2022 **Leonardo AI** - Image & video generation\n\u2022 **Creatomate** - Final video assembly\n\u2022 **Cloudinary** - Asset storage\n\n### \ud83d\ude80 Setup Required:\n1. Configure API credentials for all services\n2. Set Cloudinary environment variable\n3. Customize voice ID in ElevenLabs node\n4. Test with simple prompts first\n\n**Input:** Text query about any topic\n**Output:** Complete YouTube video with audio + visuals"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-content-gen",
      "name": "Content Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1680,
        -450
      ],
      "parameters": {
        "width": 289,
        "height": 242,
        "content": "## \ud83e\udde0 Content Generation\n\n**Step 1:** AI creates video concept\n\n\u2022 Generates structured script (Intro/Base/CTA)\n\u2022 Creates engaging title & description\n\u2022 Optimized for YouTube engagement\n\u2022 Uses latest OpenAI models\n\n**Input:** Your topic query\n**Output:** Complete video concept in JSON"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-audio",
      "name": "Audio Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1220,
        -450
      ],
      "parameters": {
        "width": 289,
        "height": 242,
        "content": "## \ud83c\udf99\ufe0f Audio Processing\n\n**Step 2:** Convert text to speech\n\n\u2022 ElevenLabs high-quality TTS\n\u2022 Returns audio + timing alignment\n\u2022 Segments into 4-second chunks\n\u2022 Optimized for video sync\n\n**Input:** Generated script\n**Output:** Audio file + timing data"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-visual",
      "name": "Visual Generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -100,
        -450
      ],
      "parameters": {
        "width": 349,
        "height": 242,
        "content": "## \ud83c\udfa8 Visual Generation\n\n**Step 3:** Create video segments\n\n\u2022 AI generates image prompts for each segment\n\u2022 Leonardo AI creates high-quality images\n\u2022 Converts images to animated videos\n\u2022 Each segment matches audio timing\n\n**Process:** Image \u2192 Motion \u2192 Video\n**Output:** Individual video segments"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-assembly",
      "name": "Final Assembly",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2800,
        -450
      ],
      "parameters": {
        "width": 309,
        "height": 242,
        "content": "## \ud83c\udfac Final Assembly\n\n**Step 4:** Combine everything\n\n\u2022 Merges all video segments\n\u2022 Adds background audio track\n\u2022 Creates smooth transitions\n\u2022 Renders final MP4 video\n\n**Output:** Complete YouTube-ready video\n**Format:** 1080x1920 (vertical) MP4"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-config",
      "name": "Configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        120
      ],
      "parameters": {
        "width": 309,
        "height": 183,
        "content": "## \u2699\ufe0f Configuration Notes\n\n**Required Environment Variables:**\n\u2022 `CLOUDINARY_CLOUD_NAME`\n\n**API Credentials Needed:**\n\u2022 OpenAI API key\n\u2022 ElevenLabs API key  \n\u2022 Leonardo AI API key\n\u2022 Creatomate API key\n\u2022 Cloudinary credentials"
      },
      "typeVersion": 1
    },
    {
      "id": "4212a15b-bfc4-4d0f-9b2c-40e7efab0240",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1640,
        -180
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4cc730d1-8da6-4c4a-b099-eed5fd54d41c",
      "name": "Ideator \ud83e\udde0",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -1420,
        -180
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "=o3-mini-2025-01-31"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "you create video concepts based on the user query.\n\nYou output the following in JSON:\n{\n  \"Script\": {\n    \"Intro\": \"40-70 characters\",\n    \"Base\": \"280-350 characters\",\n    \"CTA\": \"\u224850 characters\"\n  },\n  \"Title\": \"30-50 characters (short, engaging, curiosity-driven e.g. \u201cI tried THIS for 7 days\u2026 Crazy results!\u201d)\",\n  \"Description\": \"50-150 characters (punchy hook, add context, CTA, #hashtags)\"\n}\n\n}"
            },
            {
              "content": "<user-query>\nGive me top 5 interesting facts about plastic \n</user-query>"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "bb218c67-74bd-40db-9a44-26d37c506fc7",
      "name": "Script",
      "type": "n8n-nodes-base.set",
      "position": [
        -1200,
        -180
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d8012b83-168e-4de8-9d4a-dc91e24d9964",
              "name": "Script",
              "type": "string",
              "value": "=  {{ $json.message.content.Script.Intro }} {{ $json.message.content.Script.Base }} {{ $json.message.content.Script.CTA }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1c60fb80-27af-4ceb-81bf-7b66fd8bb420",
      "name": "Script Generator",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -980,
        -180
      ],
      "parameters": {
        "url": "https://api.elevenlabs.io/v1/text-to-speech/2qfp6zPuviqeCOZIE9RZ/with-timestamps?output_format=mp3_44100_128",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ $json.Script }}"
            },
            {
              "name": "output_format",
              "value": "mp3_44100_128"
            },
            {
              "name": "model_id",
              "value": "eleven_multilingual_v2"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "85005f99-2e9d-44c4-b52a-13848d2c4137",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -760,
        -180
      ],
      "parameters": {
        "url": "https://prod.0codekit.com/code/python",
        "method": "POST",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "code",
              "value": "=# -*- coding: utf-8 -*-\n\"\"\"\nn8n  \u25b8  \u201cExecute Python\u201d (or \u201cRun Python\u201d) node\nPurpose: slice an ElevenLabs alignment object into fixed-length\n         caption / transcript chunks and return them as JSON.\n\nInput  (item JSON) must contain:\n  \u251c\u2500 audio_base64            \u2013 MP3/WAV in base-64 (not used here but kept in case you want it)\n  \u251c\u2500 alignment               \u2013 full alignment object from ElevenLabs\n  \u2514\u2500 normalized_alignment    \u2013 (optional) same structure but already normalised\n\nOutput:\n  {\n    \"data\": [\n      { \"words\": \"Hello world\", \"id\": 1, \"duration\": 3.98 },\n      { \"words\": \"\u2026 next chunk \u2026\", \"id\": 2, \"duration\": 4.0 },\n      \u2026\n    ]\n  }\n\"\"\"\nimport json\nfrom typing import List, Dict, Union\n\n# Extract data from N8N context\naudio_base64 = \"{{ $json.audio_base64 }}\"\nalignment_str = r'''{{ JSON.stringify($json.alignment).replace(/\\u2014/g, '-') }}'''\nnormalized_alignment_str = r'''{{ JSON.stringify($json.normalized_alignment).replace(/\\u2014/g, '-') }}'''\n\ndef sanitize_unicode(text: str) -> str:\n    \"\"\"Remove problematic Unicode characters that could break JSON parsing.\"\"\"\n    return text.encode(\"ascii\", \"ignore\").decode(\"ascii\")\n\ndef parse_alignment_data(alignment_str: str, normalized_str: str) -> tuple:\n    \"\"\"Parse and sanitize alignment JSON strings.\"\"\"\n    clean_alignment = sanitize_unicode(alignment_str)\n    clean_normalized = sanitize_unicode(normalized_str)\n    \n    alignment = json.loads(clean_alignment or \"{}\")\n    normalized_alignment = json.loads(clean_normalized or \"{}\")\n    \n    return alignment, normalized_alignment\n\n# Parse alignment data\nalignment, normalized_alignment = parse_alignment_data(alignment_str, normalized_alignment_str)\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# 4.  Helper: split the aligned characters into 6-second (default) windows\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ndef create_audio_segments(alignment: Dict, segment_duration: float = 4.0) -> List[Dict[str, Union[str, int, float]]]:\n    \"\"\"\n    Split aligned audio data into fixed-duration segments optimized for video generation.\n    \n    Args:\n        alignment: Dictionary containing characters and timing data from ElevenLabs\n        segment_duration: Target duration for each segment in seconds (default: 4.0)\n        \n    Returns:\n        List of segment dictionaries with words, id, and precise duration\n    \"\"\"\n    chars = alignment.get(\"characters\", [])\n    start_times = [float(t) for t in alignment.get(\"character_start_times_seconds\", [])]\n    end_times = [float(t) for t in alignment.get(\"character_end_times_seconds\", [])]\n    \n    # Validate input data\n    if not chars or not start_times or not end_times:\n        return []\n    \n    total_duration = end_times[-1] if end_times else 0\n    segments = []\n    \n    # Handle short audio that fits in one segment\n    if 0 < total_duration <= segment_duration:\n        return [{\n            \"words\": \"\".join(chars).strip(),\n            \"id\": 1,\n            \"duration\": round(total_duration, 2)\n        }]\n    \n    # Create time-based segments with improved character distribution\n    current_time = 0.0\n    segment_id = 1\n    \n    while current_time < total_duration:\n        segment_end = min(current_time + segment_duration, total_duration)\n        \n        # Extract characters within this time window\n        segment_chars = [\n            char for char, start_time in zip(chars, start_times)\n            if current_time <= start_time < segment_end\n        ]\n        \n        # Only add non-empty segments with meaningful content\n        if segment_chars:\n            segment_text = \"\".join(segment_chars).strip()\n            if segment_text:  # Ensure we don't add empty segments\n                segments.append({\n                    \"words\": segment_text,\n                    \"id\": segment_id,\n                    \"duration\": round(segment_end - current_time, 2)\n                })\n                segment_id += 1\n        \n        current_time = segment_end\n    \n    return segments\n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# 5.  Run it & hand the result back to n8n\n# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ntry:\n    segments = create_audio_segments(alignment, segment_duration=4.0)\n    result = {\"data\": segments}\nexcept Exception as e:\n    # makes debugging in n8n easier\n    result = {\"data\": {\"error\": \"Processing failed\", \"details\": str(e)}}\n\n# n8n expects the Python node to expose a variable named \u201cresult\u201d\n# (one per incoming item) \u2013 that\u2019s exactly what we return above.\n"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e0092152-adf3-4feb-9c3a-4a1b9af9aeaf",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -540,
        -180
      ],
      "parameters": {
        "include": "allOtherFields",
        "options": {},
        "fieldToSplitOut": "result.data"
      },
      "typeVersion": 1
    },
    {
      "id": "f77297d3-c351-48ff-acf8-fbde536a4dd9",
      "name": "image-prompter",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -320,
        -180
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "o3-mini-2025-01-31"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are an image-prompt generator agent for video production.\nYour job is to turn each script segment into a concise, visually descriptive prompt that will serve as the opening frame of a longer video.\n\nReturn a JSON object for the current script segment only:\n\n{\n  \"Prompt\": \"enter prompt here\"\n}\nYour prompt must depict the first frame of the 4-second scene and set the overall visual tone for that shot.\n\nIMPORTANT INSTRUCTIONS\n\nNever include text in the image; on-screen words cannot be animated later.\nKeep the prompt under 240 characters.\n\nMake the image concept extremely simple. The video-generation model struggles with people, complex motion, and busy scenes, but it excels at landscapes, POV shots, and close-ups."
            },
            {
              "content": "=Here's the full script:\n{{ $('Script').item.json.Script }}\n\nHere's the current scene:\nscript portion: {{ $json['result.data'].words }}\nscript position: {{ $json['result.data'].id }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "f400da48-a2ec-45a9-beb0-61d1bdfe5dd2",
      "name": "request image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        40,
        -180
      ],
      "parameters": {
        "url": "https://cloud.leonardo.ai/api/rest/v1/generations",
        "method": "POST",
        "options": {},
        "jsonBody": "={ \"prompt\":\"{{ $json.message.content.Prompt }}\",\n  \"modelId\":\"6bef9f1b-29cb-40c7-b9df-32b51c1f67d3\",\n  \"width\":832, \"height\":480, \"num_images\":1 }\n",
        "sendBody": true,
        "contentType": "=json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b4901763-9973-4fc8-8f3a-f5530ceb23f0",
      "name": "Upload Cloudinary",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -780,
        220
      ],
      "parameters": {
        "url": "=https://api.cloudinary.com/v1_1/YOUR_CLOUD_NAME/raw/upload",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "value": "=data:audio/mp3;base64,{{ $json.audio_base64 }}"
            },
            {
              "name": "upload_preset",
              "value": "default"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "3c2434d7-b313-4c05-911e-87028dfcbd9a",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2060,
        -180
      ],
      "parameters": {
        "include": "specifiedFields",
        "options": {},
        "aggregate": "aggregateAllItemData",
        "fieldsToInclude": "=output",
        "destinationFieldName": "Videos"
      },
      "typeVersion": 1
    },
    {
      "id": "023a1abc-c85f-4e90-bef4-b790a7d29fa2",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        2300,
        0
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "9a660895-f394-4758-9213-44db831ee8ad",
      "name": "Create editor JSON",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2700,
        -100
      ],
      "parameters": {
        "url": "https://prod.0codekit.com/code/python",
        "method": "POST",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "code",
              "value": "=#!/usr/bin/env python3\n\"\"\"\nOptimized Video Render JSON Generator for N8N\nCreates Creatomate-compatible render configuration with enhanced video composition\n\nInput: Video segments and audio URL\nOutput: Creatomate render JSON with optimized transitions and timing\n\"\"\"\nimport json\nfrom typing import List, Dict, Union, Any\n\ndef create_video_element(video: Dict, start_time: float, crossfade_duration: float = 0.3) -> Dict[str, Any]:\n    \"\"\"\n    Create a single video element with optimized animations.\n    \n    Args:\n        video: Video segment data with 'output' URL and 'duration'\n        start_time: Start time for this video segment\n        crossfade_duration: Duration of crossfade transition\n        \n    Returns:\n        Formatted video element for Creatomate\n    \"\"\"\n    duration = video.get(\"duration\", 4.0)\n    \n    return {\n        \"type\": \"video\",\n        \"source\": video[\"output\"],\n        \"duration\": duration,\n        \"time\": start_time,\n        \"track\": 1,\n        \"animations\": [{\n            \"type\": \"fade\",\n            \"duration\": crossfade_duration,\n            \"direction\": \"both\",\n            \"transition\": True\n        }]\n    }\n\ndef build_optimized_render_json(videos: List[Dict], audio_url: str, crossfade_duration: float = 0.3) -> Dict[str, Any]:\n    \"\"\"\n    Build optimized render JSON for Creatomate with enhanced video composition.\n    \n    Args:\n        videos: List of video segments with URLs and durations\n        audio_url: Background audio URL\n        crossfade_duration: Duration of crossfade transitions between videos\n        \n    Returns:\n        Complete render configuration for Creatomate API\n    \"\"\"\n    elements = []\n    timeline_position = 0.0\n    \n    # Add background audio track (spans entire video duration)\n    elements.append({\n        \"type\": \"audio\",\n        \"source\": audio_url,\n        \"track\": 0  # Audio on separate track\n    })\n    \n    # Add video segments with optimized transitions\n    for i, video in enumerate(videos):\n        if not video or \"output\" not in video:\n            continue  # Skip invalid video segments\n            \n        video_element = create_video_element(video, timeline_position, crossfade_duration)\n        elements.append(video_element)\n        \n        # Advance timeline by full duration (overlaps handled by crossfade)\n        timeline_position += video.get(\"duration\", 4.0)\n    \n    # Return optimized render configuration\n    return {\n        \"source\": {\n            \"output_format\": \"mp4\",\n            \"width\": 1080,\n            \"height\": 1920,  # Vertical format for social media\n            \"frame_rate\": 30,  # Smooth playback\n            \"elements\": elements\n        }\n    }\n\ndef main() -> Dict[str, Any]:\n    \"\"\"Main function with enhanced error handling and validation.\"\"\"\n    try:\n        # Parse N8N input data\n        videos_json = '{{ JSON.stringify($json.Videos) }}'\n        audio_url = \"{{ $json.secure_url }}\"\n        \n        # Validate inputs\n        if not audio_url:\n            raise ValueError(\"Audio URL is required\")\n            \n        videos = json.loads(videos_json) if videos_json else []\n        \n        if not videos:\n            raise ValueError(\"At least one video segment is required\")\n        \n        # Generate optimized render configuration\n        render_config = build_optimized_render_json(videos, audio_url, crossfade_duration=0.3)\n        \n        return render_config\n        \n    except json.JSONDecodeError as e:\n        return {\"error\": \"Invalid JSON in video data\", \"details\": str(e)}\n    except ValueError as e:\n        return {\"error\": \"Validation failed\", \"details\": str(e)}\n    except Exception as e:\n        return {\"error\": \"Render configuration failed\", \"details\": str(e)}\n\n# Execute and return result for N8N\nresult = main()\n"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "667a6ffd-13ef-4673-8cbf-4374fa77dc93",
      "name": "SET JSON VARIABLE",
      "type": "n8n-nodes-base.set",
      "position": [
        2920,
        -100
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3028cff7-716e-4f7e-afed-fed7985751c6",
              "name": "Creatomate Request",
              "type": "object",
              "value": "={{ $json.result.source }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "99b1cbb9-98f9-4a32-83d0-c4d756f81cf3",
      "name": "Editor",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3140,
        -100
      ],
      "parameters": {
        "url": "https://api.creatomate.com/v1/renders",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "source",
              "value": "={{ $json['Creatomate Request'] }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e69860af-ca44-4341-a4a9-65f536b2ff20",
      "name": "Rendering wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        3360,
        -100
      ],
      "parameters": {
        "amount": 70
      },
      "typeVersion": 1.1
    },
    {
      "id": "8d15aea0-7ca7-41b0-bb44-8b41ecff8eed",
      "name": "Get final video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3560,
        -100
      ],
      "parameters": {
        "url": "=https://api.creatomate.com/v1/renders/{{ $('Editor').item.json.id }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "940f83fc-ddb0-4e95-a5f9-8cc7840aaf1b",
      "name": "request image1",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 5,
      "position": [
        480,
        -180
      ],
      "parameters": {
        "url": "={{ \n  'https://cloud.leonardo.ai/api/rest/v1/generations/' + \n  ($json.sdGenerationJob?.generationId || $json.generationId || $json.sdGenerationId)\n}}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "notesInFlow": false,
      "retryOnFail": false,
      "typeVersion": 4.2,
      "alwaysOutputData": true,
      "waitBetweenTries": 5000
    },
    {
      "id": "cafd1f35-2f0c-4c88-87fa-d6f38ada63d8",
      "name": "request image2",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        860,
        -180
      ],
      "parameters": {
        "url": "https://cloud.leonardo.ai/api/rest/v1/generations-motion-svd",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        },
        "sendBody": true,
        "contentType": "=json",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "=imageId",
              "value": "={{$json.body.generations_by_pk.generated_images[0].id}}"
            },
            {
              "name": "=motionStrength",
              "value": "={{ 2 }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "6ee4427e-b994-4062-9c65-3e6787dc9ec5",
      "name": "Request Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1320,
        -180
      ],
      "parameters": {
        "url": "=https://cloud.leonardo.ai/api/rest/v1/generations/{{ $json.body.motionSvdGenerationJob.generationId }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a041856d-e21b-4002-9881-05b085791d33",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        1780,
        -180
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "7858977a-d50a-4fad-b0cc-ef787a697f1f",
              "name": "output",
              "type": "string",
              "value": "={{ $json.body.generations_by_pk.generated_images[0].motionMP4URL }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "dc0d72a4-7ca0-4597-a912-daa03f683525",
      "name": "Wait3",
      "type": "n8n-nodes-base.wait",
      "position": [
        660,
        -180
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "84a66ca3-e7a6-49d2-adae-4c215e01cf08",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        1540,
        -180
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "a3c77e5a-40b4-460d-af50-37ced1bd78c1",
      "name": "Wait2",
      "type": "n8n-nodes-base.wait",
      "position": [
        260,
        -180
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "a3fd3ed3-31ce-4898-9a38-acf528727882",
      "name": "Wait4",
      "type": "n8n-nodes-base.wait",
      "position": [
        1080,
        -180
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 2
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "552e189c-3d6c-46fd-aa9c-96af0515dde8",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Create editor JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "request image1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait3": {
      "main": [
        [
          {
            "node": "request image2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait4": {
      "main": [
        [
          {
            "node": "Request Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Editor": {
      "main": [
        [
          {
            "node": "Rendering wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Script": {
      "main": [
        [
          {
            "node": "Script Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "image-prompter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ideator \ud83e\udde0": {
      "main": [
        [
          {
            "node": "Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request Video": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "request image": {
      "main": [
        [
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rendering wait": {
      "main": [
        [
          {
            "node": "Get final video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "image-prompter": {
      "main": [
        [
          {
            "node": "request image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "request image1": {
      "main": [
        [
          {
            "node": "Wait3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "request image2": {
      "main": [
        [
          {
            "node": "Wait4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Script Generator": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upload Cloudinary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SET JSON VARIABLE": {
      "main": [
        [
          {
            "node": "Editor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Cloudinary": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Create editor JSON": {
      "main": [
        [
          {
            "node": "SET JSON VARIABLE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Ideator \ud83e\udde0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}