{
  "name": "Video Story Generator",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "gen-video",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        200,
        460
      ],
      "id": "webhook-node",
      "name": "Webhook"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{ $vars.GROQ_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "{\n  \"model\": \"llama-3.3-70b-versatile\",\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a creative storyteller. Always respond with valid JSON only. No markdown, no explanation.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Create a story based on the theme: '{{ $json.body.theme }}'. Divide the story into exactly 12 scenes, each suitable for a 5-second video clip. Each scene should have a vivid, visual description that can be used as a text-to-video prompt. Return a JSON object with a 'scenes' array containing exactly 12 strings, where each string is a detailed visual description of that scene (50-80 words, cinematic style).\"\n    }\n  ]\n}"
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        320,
        460
      ],
      "id": "groq-node",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "jsCode": "const content = $input.first().json.choices[0].message.content;\nconst cleanContent = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\nconst parsed = JSON.parse(cleanContent);\nconst scenes = parsed.scenes || parsed;\nreturn scenes.map((scene, i) => ({ json: { scene: typeof scene === 'string' ? scene : scene.description || scene.prompt || JSON.stringify(scene), sceneIndex: i, index: i } }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        460
      ],
      "id": "parse-scenes-node",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        600,
        460
      ],
      "id": "loop-node",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://queue.fal.run/fal-ai/wan/v2.1/t2v",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Key {{ $vars.FAL_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "{\n  \"prompt\": \"{{ $json.scene }}\",\n  \"duration\": \"5\",\n  \"aspect_ratio\": \"16:9\",\n  \"enable_safety_checker\": false\n}"
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        740,
        420
      ],
      "id": "fal-submit-node",
      "name": "HTTP Request1"
    },
    {
      "parameters": {
        "amount": 15,
        "unit": "seconds"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        880,
        420
      ],
      "id": "wait-node",
      "name": "Wait"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://queue.fal.run/fal-ai/wan/v2.1/t2v/requests/{{ $('HTTP Request1').item.json.request_id }}/status",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Key {{ $vars.FAL_API_KEY }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1020,
        420
      ],
      "id": "fal-status-node",
      "name": "HTTP Request2"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "status-check",
              "leftValue": "={{ $json.status }}",
              "rightValue": "COMPLETED",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1160,
        420
      ],
      "id": "if-node",
      "name": "If"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.response_url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Key {{ $vars.FAL_API_KEY }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1300,
        380
      ],
      "id": "fal-result-node",
      "name": "HTTP Request3"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nreturn items.map(item => ({\n  json: {\n    videoUrl: item.json.video?.url || item.json.output?.video?.url || item.json.output?.url || '',\n    sceneIndex: item.json.sceneIndex ?? item.json.index ?? 0,\n    scene: item.json.scene || ''\n  }\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1440,
        380
      ],
      "id": "extract-url-node",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "videoUrls",
        "include": "allFields",
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        740,
        530
      ],
      "id": "aggregate-node",
      "name": "Aggregate"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, videoUrls: $json.videoUrls.map(v => v.videoUrl).filter(u => u) }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        530
      ],
      "id": "respond-node",
      "name": "Respond to Webhook"
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "HTTP Request1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request1": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "HTTP Request2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request2": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "HTTP Request3",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request3": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond to Webhook": {
      "main": [
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": true
  }
}