{
  "name": "n8n AI video generator (Cygni Solutions)",
  "nodes": [
    {
      "parameters": {
        "content": "## 1. Write the scene\nA chat message is the idea. One model call returns an image prompt, a motion prompt and a narration line as JSON.",
        "width": 620,
        "height": 420,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -140
      ],
      "id": "3fcfa044-dccf-4bfc-980e-8f07c2fd3939",
      "name": "Note: 1. Write the scene"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        60
      ],
      "id": "7a965e91-d9ed-4b1f-acc8-d1c20c4609bc",
      "name": "When chat message received"
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4o-mini",
          "mode": "list",
          "cachedResultName": "GPT-4O-MINI"
        },
        "messages": {
          "values": [
            {
              "content": "=You plan one short vertical video clip from a single idea. Three other tools will use what you write: an image model draws the opening frame, a video model animates that frame for five seconds, and a voice model reads one line over it.\n\nThe idea: {{ $json.chatInput }}\n\nReturn a JSON object with exactly these three keys.\n\nimagePrompt\nOne paragraph, under 900 characters, describing the opening frame as a single photograph in a vertical 2:3 frame. Say who or what is in the frame and what they are doing at this exact moment, where it is, the time of day and where the light comes from, one texture you could touch, and where the camera sits. Leave room in the frame for the subject to move. No text, captions, logos, brand names or real people.\n\nmotionPrompt\nOne sentence, under 300 characters, saying what changes over the next five seconds: one movement by the subject and one camera move. Do not describe the scene again, because the video model can already see it.\n\nnarration\nOne or two sentences, 30 words at most, written to be spoken aloud over the clip. Plain language, present tense, no stage directions, no quotation marks, no hashtags. It should add something the picture does not show.\n\nAll three must describe the same moment. Keep everything safe for work."
            }
          ]
        },
        "jsonOutput": true,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        240,
        60
      ],
      "id": "e543e0ac-ba8a-49bd-97e9-771beb78df02",
      "name": "Write Scene"
    },
    {
      "parameters": {
        "content": "## 2. Draw the opening frame\nGPT Image, portrait, returned as a JPEG so it stays under Runway's 5 MB limit for inline images.",
        "width": 440,
        "height": 420,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        560,
        -140
      ],
      "id": "466a0472-4782-4ed7-a9a4-91b082b9a2a2",
      "name": "Note: 2. Draw the opening frame"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/images/generations",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openAiApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'gpt-image-1', prompt: $json.message.content.imagePrompt, size: '1024x1536', quality: 'medium', output_format: 'jpeg', output_compression: 90 }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        730,
        60
      ],
      "id": "7a52d95a-9620-465b-a00a-c9492032acf8",
      "name": "Generate Image"
    },
    {
      "parameters": {
        "content": "## 3. Animate it with Runway\nStart the job, then check every 15 seconds until Runway says SUCCEEDED, FAILED or CANCELED.",
        "width": 1340,
        "height": 620,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1020,
        -140
      ],
      "id": "982cd8ff-dc46-4ac2-94aa-61eb1d729665",
      "name": "Note: 3. Animate it with Runway"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.dev.runwayml.com/v1/image_to_video",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Runway-Version",
              "value": "2024-11-06"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'gen4_turbo', promptImage: 'data:image/jpeg;base64,' + $json.data[0].b64_json, promptText: $('Write Scene').first().json.message.content.motionPrompt, ratio: '720:1280', duration: 5 }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        60
      ],
      "id": "747a1bf5-3fe6-4c29-ba18-02a8734d8f2a",
      "name": "Start Video"
    },
    {
      "parameters": {
        "amount": 15,
        "unit": "seconds"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1320,
        60
      ],
      "id": "049b74cd-bdb2-47c5-85e6-623ece603d5e",
      "name": "Wait 15 Seconds"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.dev.runwayml.com/v1/tasks/{{ $('Start Video').first().json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Runway-Version",
              "value": "2024-11-06"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1540,
        60
      ],
      "id": "1b71d21b-9d54-4486-81fa-eba8f2a0a41b",
      "name": "Check Video"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "87f089b3-e4dd-431b-b482-5d21c017dc6c",
              "leftValue": "={{ $json.status }}",
              "rightValue": "SUCCEEDED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1760,
        60
      ],
      "id": "09b864e9-ac12-4a2e-a0c4-967f72a0d05a",
      "name": "Video Ready?"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "dc1d2871-96a0-4121-8ed1-e368ebe5dd2e",
              "leftValue": "={{ $json.status }}",
              "rightValue": "FAILED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "fd93743d-89fb-420c-afb2-51e4f3e7ded7",
              "leftValue": "={{ $json.status }}",
              "rightValue": "CANCELED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1980,
        300
      ],
      "id": "4072aea7-de0f-4c71-9340-66e15936e186",
      "name": "Video Failed?"
    },
    {
      "parameters": {
        "errorMessage": "={{ 'Runway task ' + $json.status + ': ' + ($json.failure || 'no reason given') + ($json.failureCode ? ' (' + $json.failureCode + ')' : '') }}"
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        2200,
        300
      ],
      "id": "671df113-7790-4f90-a2c0-6350ebe992df",
      "name": "Stop: Video Failed"
    },
    {
      "parameters": {
        "content": "## 4. Keep the video\nRunway's link expires within a day or two, so the file is copied to Google Drive.",
        "width": 520,
        "height": 420,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2380,
        -140
      ],
      "id": "56778ef3-3dc3-485c-b13a-28b4b5840e4d",
      "name": "Note: 4. Keep the video"
    },
    {
      "parameters": {
        "url": "={{ $json.output[0] }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2460,
        60
      ],
      "id": "2002d512-ee3b-41b1-89e9-f2c5d371bf27",
      "name": "Download Video"
    },
    {
      "parameters": {
        "name": "={{ 'clip-' + $('Start Video').first().json.id + '.mp4' }}",
        "driveId": {
          "__rl": true,
          "value": "My Drive",
          "mode": "list",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "https://drive.google.com/drive/folders/YOUR_FOLDER_ID",
          "mode": "url"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        2680,
        60
      ],
      "id": "647bdc81-5b1b-4a84-a711-3d42ad9a87af",
      "name": "Save Video"
    },
    {
      "parameters": {
        "content": "## 5. Record the narration\nElevenLabs reads the narration line. The MP3 goes to the same Drive folder.",
        "width": 520,
        "height": 420,
        "color": 2
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2920,
        -140
      ],
      "id": "239e9a8e-60e3-4c38-b0a7-c28604fd8579",
      "name": "Note: 5. Record the narration"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.elevenlabs.io/v1/text-to-speech/YOUR_VOICE_ID",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ text: $('Write Scene').first().json.message.content.narration, model_id: 'eleven_multilingual_v2' }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3000,
        60
      ],
      "id": "6c51bab1-8f28-4145-af36-e5d69dc347c6",
      "name": "Generate Narration"
    },
    {
      "parameters": {
        "name": "={{ 'narration-' + $('Start Video').first().json.id + '.mp3' }}",
        "driveId": {
          "__rl": true,
          "value": "My Drive",
          "mode": "list",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "https://drive.google.com/drive/folders/YOUR_FOLDER_ID",
          "mode": "url"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        3220,
        60
      ],
      "id": "50304045-6526-43b1-8241-868243d1f590",
      "name": "Save Narration"
    },
    {
      "parameters": {
        "content": "## 6. Log it\nOne row per clip, with links that do not expire, then a reply in the chat.",
        "width": 520,
        "height": 420,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        3460,
        -140
      ],
      "id": "2a956cd8-45d7-4c89-9d5d-24bbc2764fa3",
      "name": "Note: 6. Log it"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sheet1",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "created": "={{ $now.toISO() }}",
            "idea": "={{ $('When chat message received').first().json.chatInput }}",
            "narration": "={{ $('Write Scene').first().json.message.content.narration }}",
            "videoLink": "={{ $('Save Video').first().json.webViewLink }}",
            "narrationLink": "={{ $json.webViewLink }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "created",
              "displayName": "created",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "idea",
              "displayName": "idea",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "narration",
              "displayName": "narration",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "videoLink",
              "displayName": "videoLink",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "narrationLink",
              "displayName": "narrationLink",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        3540,
        60
      ],
      "id": "9ec95a2c-547d-4909-bd9f-34caa0617efe",
      "name": "Log to Sheet"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f8504e81-d762-4976-a038-13a2b926c915",
              "name": "output",
              "value": "={{ 'Done. Video: ' + $('Save Video').first().json.webViewLink + ' | Narration: ' + $('Save Narration').first().json.webViewLink }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3760,
        60
      ],
      "id": "1465504b-c8d1-4878-b314-cf638d93c0a3",
      "name": "Reply in Chat"
    }
  ],
  "pinData": {},
  "connections": {
    "When chat message received": {
      "main": [
        [
          {
            "node": "Write Scene",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Scene": {
      "main": [
        [
          {
            "node": "Generate Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Image": {
      "main": [
        [
          {
            "node": "Start Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Video": {
      "main": [
        [
          {
            "node": "Wait 15 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 15 Seconds": {
      "main": [
        [
          {
            "node": "Check Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Video": {
      "main": [
        [
          {
            "node": "Video Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Video": {
      "main": [
        [
          {
            "node": "Save Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Video": {
      "main": [
        [
          {
            "node": "Generate Narration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Narration": {
      "main": [
        [
          {
            "node": "Save Narration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Narration": {
      "main": [
        [
          {
            "node": "Log to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Sheet": {
      "main": [
        [
          {
            "node": "Reply in Chat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video Ready?": {
      "main": [
        [
          {
            "node": "Download Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Video Failed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video Failed?": {
      "main": [
        [
          {
            "node": "Stop: Video Failed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 15 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "executionTimeout": 1800
  },
  "tags": []
}