AutomationFlowsAI & RAG › AI Storyboard Splitter & Dispatcher | AI 分镜拆解调度器(主流程,按镜头派发给子工作流)

AI Storyboard Splitter & Dispatcher | AI 分镜拆解调度器(主流程,按镜头派发给子工作流)

AI Storyboard Splitter & Dispatcher | AI 分镜拆解调度器(主流程,按镜头派发给子工作流). Uses readWriteFile, lmChatOpenRouter, agent. Event-driven trigger; 15 nodes.

Event trigger★★★★☆ complexityAI-powered15 nodesRead Write FileOpenRouter ChatAgent
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Agent → OpenRouter Chat recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "AI Storyboard Splitter & Dispatcher | AI \u5206\u955c\u62c6\u89e3\u8c03\u5ea6\u5668\uff08\u4e3b\u6d41\u7a0b\uff0c\u6309\u955c\u5934\u6d3e\u53d1\u7ed9\u5b50\u5de5\u4f5c\u6d41\uff09",
  "active": false,
  "nodes": [
    {
      "parameters": {
        "content": "## AI \u5206\u955c\u62c6\u89e3\u8c03\u5ea6\u5668 | Storyboard Splitter & Dispatcher\n\n**Built:** 2026-01\u3000|\u3000**Nodes:** 14\n\n**Model stack:** gemini-3-pro-preview, sora-2\n\n**External services:** n/a\n\nAll credentials are `YOUR_*` placeholders \u2014 fill them in before the first run.\nSetup guide and placeholder reference: see the repository README.\n\n---\n\n## \u4e2d\u6587\u8bf4\u660e\n\n**\u6784\u5efa\u65f6\u95f4\uff1a** 2026-01\u3000|\u3000**\u8282\u70b9\u6570\uff1a** 14\n\n**\u6a21\u578b\u6808\uff1a** gemini-3-pro-preview, sora-2\n\n**\u5916\u90e8\u670d\u52a1\uff1a** n/a\n\n\u6240\u6709\u51ed\u8bc1\u90fd\u662f `YOUR_*` \u5360\u4f4d\u7b26\uff0c\u9996\u6b21\u8fd0\u884c\u524d\u8bf7\u5148\u586b\u597d\u3002\n\u90e8\u7f72\u6b65\u9aa4\u548c\u5360\u4f4d\u7b26\u5bf9\u7167\u8868\u89c1\u4ed3\u5e93 README\u3002\n",
        "height": 640,
        "width": 760,
        "color": 4
      },
      "id": "snapshot-meta-ai-story",
      "name": "Overview | \u5de5\u4f5c\u6d41\u8bf4\u660e",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -3808,
        60
      ]
    },
    {
      "parameters": {},
      "id": "0731f12f-9523-4d3e-a733-9af1ede2b599",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -3808,
        640
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "fileSelector": "/mnt/external/shared/12*.txt",
        "options": {}
      },
      "id": "7e9aa995-dc3e-4559-a437-26d288e3fd8c",
      "name": "Read/Write Files from Disk",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        -3408,
        640
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "operation": "text",
        "options": {}
      },
      "id": "3bfbad2f-dd77-4aa0-b67f-61c7d6c96079",
      "name": "Extract from File",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -3184,
        640
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "jsCode": "// n8n Code\u8282\u70b9 - \u5c0f\u8bf4\u667a\u80fd\u5206\u7247\n// \u8f93\u5165\uff1aExtract from File\u8282\u70b9\u7684\u8f93\u51fa\n// \u8f93\u51fa\uff1a\u591a\u4e2aitem\uff0c\u6bcf\u4e2a\u5305\u542b\u4e00\u4e2a\u5206\u7247\n\nconst targetLength = 3000; // \u76ee\u6807\u5b57\u6570\uff0c\u53ef\u6839\u636e\u9700\u8981\u8c03\u6574\nconst minLength = targetLength * 0.5; // \u6700\u5c0f\u957f\u5ea6\uff0850%\uff09\nconst maxLength = targetLength * 1.5; // \u6700\u5927\u957f\u5ea6\uff08150%\uff09\n\n// \u83b7\u53d6\u8f93\u5165\u6587\u672c - fullData\u662f\u5bf9\u8c61\u4e0d\u662f\u6570\u7ec4\nconst firstItem = $input.first().json;\nlet inputText = '';\n\nif (firstItem.fullData && firstItem.fullData.data) {\n  inputText = firstItem.fullData.data;\n} else if (firstItem.data) {\n  inputText = firstItem.data;\n} else if (firstItem.text) {\n  inputText = firstItem.text;\n}\n\nif (!inputText.trim()) {\n  throw new Error('\u8f93\u5165\u6587\u672c\u4e3a\u7a7a');\n}\n\n// \u5904\u7406Windows\u6362\u884c\u7b26\nconst lines = inputText.replace(/\\r\\n/g, '\\n').split('\\n');\nconst segments = [];\nlet currentSegment = '';\nlet currentLength = 0;\nlet segmentStartLine = 0;\n\nfor (let i = 0; i < lines.length; i++) {\n  const line = lines[i].trim();\n  \n  // \u68c0\u6d4b\u7ae0\u8282\u6807\u9898\n  const isChapterTitle = /^\u7b2c[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\\d]+\u7ae0/.test(line) || \n                        /^Chapter\\s*\\d+/i.test(line) ||\n                        /^\u7b2c[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\\d]+\u8282/.test(line);\n  \n  const lineLength = line.length;\n  \n  // \u7b56\u75651: \u5982\u679c\u5f53\u524d\u884c\u662f\u7ae0\u8282\u6807\u9898\uff0c\u4e14\u7d2f\u79ef\u957f\u5ea6\u5df2\u8d85\u8fc7\u6700\u5c0f\u957f\u5ea6\uff0c\u5219\u5207\u5206\n  if (isChapterTitle && currentLength >= minLength) {\n    if (currentSegment.trim()) {\n      segments.push({\n        content: currentSegment.trim(),\n        length: currentLength,\n        startLine: segmentStartLine + 1,\n        endLine: i,\n        segmentIndex: segments.length + 1\n      });\n    }\n    currentSegment = line + '\\n';\n    currentLength = lineLength;\n    segmentStartLine = i;\n  }\n  // \u7b56\u75652: \u5982\u679c\u7d2f\u79ef\u957f\u5ea6\u8d85\u8fc7\u6700\u5927\u957f\u5ea6\uff0c\u5f3a\u5236\u5207\u5206\n  else if (currentLength + lineLength >= maxLength) {\n    currentSegment += line + '\\n';\n    segments.push({\n      content: currentSegment.trim(),\n      length: currentLength + lineLength,\n      startLine: segmentStartLine + 1,\n      endLine: i + 1,\n      segmentIndex: segments.length + 1,\n      forceSplit: true\n    });\n    currentSegment = '';\n    currentLength = 0;\n    segmentStartLine = i + 1;\n  }\n  // \u6b63\u5e38\u7d2f\u79ef\n  else {\n    currentSegment += line + '\\n';\n    currentLength += lineLength;\n  }\n}\n\n// \u5904\u7406\u6700\u540e\u4e00\u6bb5\nif (currentSegment.trim()) {\n  segments.push({\n    content: currentSegment.trim(),\n    length: currentLength,\n    startLine: segmentStartLine + 1,\n    endLine: lines.length,\n    segmentIndex: segments.length + 1,\n    isLastSegment: true\n  });\n}\n\n// \u8fd4\u56de\u7ed3\u679c\uff0c\u6bcf\u4e2a\u5206\u7247\u4f5c\u4e3a\u4e00\u4e2aitem\nreturn segments.map((segment, index) => {\n  return {\n    json: {\n      segmentIndex: segment.segmentIndex,\n      content: segment.content,\n      length: segment.length,\n      startLine: segment.startLine,\n      endLine: segment.endLine,\n      totalSegments: segments.length,\n      forceSplit: segment.forceSplit || false,\n      isLastSegment: segment.isLastSegment || false,\n      // \u989d\u5916\u4fe1\u606f\n      estimatedMinutes: Math.round((segment.length / 200) * 10) / 10, // \u6309200\u5b57/\u5206\u949f\u8ba1\u7b97\n      recommendedScenes: Math.ceil(segment.length / 500), // \u63a8\u8350\u573a\u666f\u6570\uff08\u6bcf500\u5b57\u4e00\u4e2a\u573a\u666f\uff09\n      // \u63d0\u53d6\u5f00\u5934\u4f5c\u4e3a\u9884\u89c8\n      preview: segment.content.substring(0, 100) + (segment.content.length > 100 ? '...' : '')\n    }\n  };\n});"
      },
      "id": "dfca9f81-0a46-43e7-a914-398fe6fabf99",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -2736,
        640
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map(item => {\n  return {\n    json: {\n      keys: Object.keys(item.json),\n      fullData: item.json\n    }\n  };\n});"
      },
      "id": "00f62159-1a27-4b8d-b4fb-36a4c622aae3",
      "name": "Code in JavaScript1",
      "type": "n8n-nodes-base.code",
      "position": [
        -2960,
        640
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "jsCode": "// n8n Code Node - JavaScript \u7248\u672c\n// \u4f5c\u7528\uff1a\u5c06\u5f53\u524d\u7ae0\u8282\u4e0e\u4e0b\u4e00\u7ae0\u7684\u5f00\u5934\u5408\u5e76\uff0c\u751f\u6210 agent_context \u4f9b AI \u4f7f\u7528\n\n// 1. \u83b7\u53d6\u8f93\u5165\u6570\u636e (n8n \u4e2d\u6240\u6709\u8f93\u5165\u90fd\u5728 'items' \u6570\u7ec4\u91cc)\n// \u5047\u8bbe\u4e0a\u4e00\u4e2a\u8282\u70b9\u8f93\u51fa\u7684\u662f\u4e00\u4e2a\u7ae0\u8282\u5217\u8868\uff0c\u6216\u8005\u591a\u4e2a Item\n// \u6211\u4eec\u5148\u5bf9 items \u8fdb\u884c\u6392\u5e8f\uff0c\u9632\u6b62\u987a\u5e8f\u4e71\u4e86\u5bfc\u81f4\u627e\u4e0d\u5230\u6b63\u786e\u7684\u201c\u4e0b\u4e00\u7ae0\u201d\n// \u6ce8\u610f\uff1an8n \u7684\u6570\u636e\u901a\u5e38\u5305\u88f9\u5728 item.json \u4e0b\nitems.sort((a, b) => {\n  return (a.json.segmentIndex || 0) - (b.json.segmentIndex || 0);\n});\n\n// \u5b9a\u4e49\u4e0b\u4e00\u7ae0\u9884\u89c8\u7684\u5b57\u6570\u9650\u5236\nconst PREVIEW_LENGTH = 500;\n\n// 2. \u904d\u5386\u5904\u7406\u6bcf\u4e2a\u7ae0\u8282\nfor (let i = 0; i < items.length; i++) {\n  const currentItem = items[i].json;\n  \n  // \u83b7\u53d6\u5f53\u524d\u7ae0\u8282\u57fa\u672c\u4fe1\u606f\n  const currentContent = currentItem.content || '';\n  // \u5c1d\u8bd5\u63d0\u53d6\u6807\u9898\uff0c\u5982\u679c preview \u5b57\u6bb5\u7b2c\u4e00\u884c\u662f\u6807\u9898\n  const chapterTitle = (currentItem.preview || '').split('\\n')[0] || `\u7b2c ${currentItem.segmentIndex} \u7ae0`;\n\n  let nextChapterContent = \"\";\n  let hasNext = false;\n\n  // 3. \"\u5077\u770b\" \u4e0b\u4e00\u7ae0 (Look-ahead)\n  if (i + 1 < items.length) {\n    const nextItem = items[i + 1].json;\n    const rawNextContent = nextItem.content || '';\n    \n    // \u622a\u53d6\u524d 500 \u5b57\n    nextChapterContent = rawNextContent.substring(0, PREVIEW_LENGTH);\n    hasNext = true;\n  }\n\n  // 4. \u6784\u5efa Prompt \u4e0a\u4e0b\u6587 (Core Logic)\n  const agentContext = `\n=== \u3010\u5f53\u524d\u5236\u4f5c\u5185\u5bb9\u3011 ===\n\u7ae0\u8282ID: ${currentItem.segmentIndex}\n\u6807\u9898: ${chapterTitle}\n\n${currentContent}\n\n=== \u3010\u4e0b\u4e00\u7ae0\u5267\u60c5\u9884\u89c8 (\u7528\u4e8e\u57cb\u60ac\u5ff5\u94a9\u5b50)\u3011 ===\n(\u6ce8\u610f\uff1a\u4ee5\u4e0b\u5185\u5bb9\u4ec5\u4f9b\u53c2\u8003\uff0c\u7528\u4e8e\u5728\u89c6\u9891\u7ed3\u5c3e\u5236\u4f5c\u60ac\u5ff5\uff0c\u4e0d\u8981\u5c06\u5176\u4f5c\u4e3a\u672c\u96c6\u7684\u4e3b\u8981\u5267\u60c5\uff0c\u9664\u975e\u4e3a\u4e86\u8fde\u8d2f\u6027\u9700\u8981\u63d0\u524d\u5f15\u5165)\n\n${nextChapterContent}${hasNext ? \"...\" : \"(\u5df2\u662f\u6700\u540e\u4e00\u7ae0)\"}\n`;\n\n  // 5. \u5c06\u7ed3\u679c\u5199\u5165\u5230 json \u5bf9\u8c61\u4e2d\n  // \u5728 n8n \u4e2d\uff0c\u76f4\u63a5\u4fee\u6539 item.json \u5373\u53ef\u5c06\u6570\u636e\u4f20\u9012\u7ed9\u4e0b\u4e00\u4e2a\u8282\u70b9\n  currentItem.agent_context = agentContext;\n  \n  // \u4e5f\u53ef\u4ee5\u4fdd\u7559\u4e00\u4e0b\u539f\u59cb\u7684\u9884\u89c8\u6570\u636e\uff0c\u65b9\u4fbf\u8c03\u8bd5\n  currentItem.next_chapter_preview_raw = nextChapterContent;\n}\n\n// 6. \u8fd4\u56de\u5904\u7406\u540e\u7684\u6570\u636e\n// n8n \u8981\u6c42 Code \u8282\u70b9\u5fc5\u987b\u8fd4\u56de items \u6570\u7ec4\nreturn items;"
      },
      "id": "46ab5a7b-cc57-4308-bf6c-42a62dee6e1e",
      "name": "Code in JavaScript2",
      "type": "n8n-nodes-base.code",
      "position": [
        -2512,
        640
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "d55c7e96-6c0f-4162-bca6-c1c60d331692",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -2288,
        640
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "79e16d70-1c4f-4aed-abe8-2d6e1beadc49",
              "name": "\u5f53\u524d\u5185\u5bb9\u811a\u672c",
              "type": "string",
              "value": "={{ $json.agent_context }}"
            },
            {
              "id": "03c647c0-881b-4678-9375-4370b65174ac",
              "name": "\u4e0b\u671f\u9884\u544a\u94a9\u5b50",
              "type": "string",
              "value": "={{ $json.next_chapter_preview_raw }}"
            },
            {
              "id": "0a4bc7d7-f731-4081-8882-c752282ffe22",
              "name": "segmentIndex",
              "type": "number",
              "value": "={{ $json.segmentIndex }}"
            }
          ]
        },
        "options": {}
      },
      "id": "79309a50-4048-4c48-b9a4-30730c7245ee",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -2064,
        640
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "id": "1cf021d4-babd-4556-b938-08e3447708fa",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3696,
        960
      ],
      "typeVersion": 1.2,
      "disabled": true
    },
    {
      "parameters": {
        "model": "google/gemini-3-pro-preview",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -1840,
        832
      ],
      "id": "4ed73e6f-ba26-4c13-b143-5946055dfd92",
      "name": "OpenRouter Chat Model",
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "cachedResultName": "sora2+banan2\u6a21\u677f"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {},
          "matchingColumns": [],
          "schema": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {}
      },
      "id": "323f2460-d241-44d5-a60e-b0f6dc57cb2b",
      "name": "Call 'sora2+banan2\u6a21\u677f'",
      "type": "n8n-nodes-base.executeWorkflow",
      "position": [
        -1264,
        640
      ],
      "typeVersion": 1.3
    },
    {
      "parameters": {
        "jsCode": "// \u63d0\u53d6\u6240\u6709\u5206\u955c\u5e76\u8f6c\u6362\u4e3a\u6807\u51c6\u6570\u7ec4\nconst input = $input.first().json;\n\nlet storyboard;\nif (typeof input.output === 'string') {\n  // \u5904\u7406\u53ef\u80fd\u5b58\u5728\u7684 markdown \u5305\u88f9\n  const jsonStr = input.output.replace(/```json\\n?/g, '').replace(/```\\n?/g, '');\n  storyboard = JSON.parse(jsonStr);\n} else {\n  storyboard = input.output;\n}\n\nconst allShots = [];\n\n// \u3010\u4fee\u590d\u70b9 1\u3011\u83b7\u53d6\u5168\u5c40\u573a\u666f\u4fe1\u606f\uff08\u56e0\u4e3a JSON \u4e2d scenes \u548c shots \u662f\u5206\u5f00\u7684\uff09\n// \u5982\u679c scenes \u6570\u7ec4\u5b58\u5728\uff0c\u53d6\u7b2c\u4e00\u4e2a\u4f5c\u4e3a\u9ed8\u8ba4\u573a\u666f\u4fe1\u606f\nconst defaultScene = (storyboard.scenes && storyboard.scenes.length > 0) \n  ? storyboard.scenes[0] \n  : { scene_name: 'Unknown', location_description: '' };\n\n// \u3010\u4fee\u590d\u70b9 2\u3011\u76f4\u63a5\u904d\u5386\u6839\u76ee\u5f55\u4e0b\u7684 shots \u6570\u7ec4\uff0c\u4e0d\u518d\u5d4c\u5957\u904d\u5386 scenes\nconst shotsList = storyboard.shots || [];\n\nshotsList.forEach(shot => {\n    allShots.push({\n      // \u57fa\u7840\u4fe1\u606f\n      shot_id: shot.shot_id,\n      time_code: shot.time_code,\n      duration_seconds: shot.duration_seconds,\n      \n      // \u3010\u4fee\u590d\u70b9 3\u3011\u4ece\u5168\u5c40\u53d8\u91cf\u83b7\u53d6\u573a\u666f\u4fe1\u606f\n      scene_name: defaultScene.scene_name,\n      location: defaultScene.location_description || defaultScene.location, // \u517c\u5bb9 AI \u53ef\u80fd\u8f93\u51fa\u7684\u4e0d\u540c\u5b57\u6bb5\u540d\n      \n      // \u6545\u4e8b\u5185\u5bb9\n      story_beat: shot.narrative.story_beat,\n      voiceover: shot.narrative.voiceover,\n      dialogue: shot.narrative.dialogue || [],\n      sound_fx: shot.narrative.sound_fx,\n      \n      // \u89c6\u89c9\u63cf\u8ff0\n      image_prompt: shot.visual.image_prompt,\n      // \u5b89\u5168\u8bbf\u95ee key_visual_elements\uff0c\u9632\u6b62 AI \u5076\u5c14\u6f0f\u6389\u67d0\u4e9b\u5b57\u6bb5\u62a5\u9519\n      subject: shot.visual.key_visual_elements?.subject || '',\n      action: shot.visual.key_visual_elements?.action || '',\n      environment: shot.visual.key_visual_elements?.environment || '',\n      lighting: shot.visual.key_visual_elements?.lighting || '',\n      mood: shot.visual.key_visual_elements?.mood || '',\n      color_scheme: shot.visual.key_visual_elements?.color_scheme || '',\n      \n      // \u955c\u5934\u53c2\u6570\n      shot_type: shot.visual.camera?.shot_type || '',\n      angle: shot.visual.camera?.angle || '',\n      movement: shot.visual.camera?.movement || '',\n      focus: shot.visual.camera?.focus || '',\n      \n      // \u8868\u6f14\u7ec6\u8282\n      expression: shot.performance?.expression || '',\n      gesture: shot.performance?.gesture || '',\n      posture: shot.performance?.posture || '',\n      eye_contact: shot.performance?.eye_contact || ''\n    });\n});\n\nreturn allShots.map(shot => ({ json: shot }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1488,
        640
      ],
      "id": "e89d3e3d-85cd-459e-992c-753fdc72a2b7",
      "name": "Code in JavaScript3"
    },
    {
      "parameters": {
        "jsCode": "// \u63d0\u53d6\u6240\u6709\u5206\u955c\u5e76\u8f6c\u6362\u4e3a\u6807\u51c6\u6570\u7ec4\nconst input = $input.first().json;\n\n// \u89e3\u6790 output \u5b57\u6bb5\u4e2d\u7684 JSON \u5b57\u7b26\u4e32\nlet storyboard;\nif (typeof input.output === 'string') {\n  // \u53bb\u6389 markdown \u4ee3\u7801\u5757\u6807\u8bb0\n  const jsonStr = input.output.replace(/```json\\n?/g, '').replace(/```\\n?/g, '');\n  storyboard = JSON.parse(jsonStr);\n} else {\n  storyboard = input.output;\n}\n\n// \u63d0\u53d6\u6240\u6709 shots \u5230\u4e00\u4e2a\u6570\u7ec4\nconst allShots = [];\n\n// \u904d\u5386\u6240\u6709 scenes\nstoryboard.scenes.forEach(scene => {\n  scene.shots.forEach(shot => {\n    allShots.push({\n      shot_id: shot.shot_id,\n      time_code: shot.time_code,\n      duration_seconds: shot.duration_seconds,\n      scene_name: scene.scene_name,\n      location: scene.location,\n      \n      // \u6545\u4e8b\u5185\u5bb9\n      story_beat: shot.narrative.story_beat,\n      voiceover: shot.narrative.voiceover,\n      dialogue: shot.narrative.dialogue,\n      sound_fx: shot.narrative.sound_fx,\n      \n      // \u89c6\u89c9\u63cf\u8ff0\n      image_prompt: shot.visual.image_prompt,\n      subject: shot.visual.key_visual_elements.subject,\n      action: shot.visual.key_visual_elements.action,\n      environment: shot.visual.key_visual_elements.environment,\n      lighting: shot.visual.key_visual_elements.lighting,\n      mood: shot.visual.key_visual_elements.mood,\n      color_scheme: shot.visual.key_visual_elements.color_scheme,\n      \n      // \u955c\u5934\u53c2\u6570\n      shot_type: shot.visual.camera.shot_type,\n      angle: shot.visual.camera.angle,\n      movement: shot.visual.camera.movement,\n      focus: shot.visual.camera.focus\n    });\n  });\n});\n\n// \u5904\u7406 scenes_continued\uff08\u5982\u679c\u6709\uff09\nif (storyboard.scenes_continued) {\n  storyboard.scenes_continued.forEach(scene => {\n    scene.shots.forEach(shot => {\n      allShots.push({\n        shot_id: shot.shot_id,\n        time_code: shot.time_code,\n        duration_seconds: shot.duration_seconds,\n        scene_name: scene.scene_name,\n        location: scene.location,\n        story_beat: shot.narrative.story_beat,\n        voiceover: shot.narrative.voiceover,\n        dialogue: shot.narrative.dialogue,\n        sound_fx: shot.narrative.sound_fx,\n        image_prompt: shot.visual.image_prompt,\n        subject: shot.visual.key_visual_elements.subject,\n        action: shot.visual.key_visual_elements.action,\n        environment: shot.visual.key_visual_elements.environment,\n        lighting: shot.visual.key_visual_elements.lighting,\n        mood: shot.visual.key_visual_elements.mood,\n        color_scheme: shot.visual.key_visual_elements.color_scheme,\n        shot_type: shot.visual.camera.shot_type,\n        angle: shot.visual.camera.angle,\n        movement: shot.visual.camera.movement,\n        focus: shot.visual.camera.focus\n      });\n    });\n  });\n}\n\nreturn allShots.map(shot => ({ json: shot }));"
      },
      "id": "be3468d3-4758-4a84-b8aa-edea5d77790b",
      "name": "Code in JavaScript4\uff08\u539f\u5185\u5bb9\uff09",
      "type": "n8n-nodes-base.code",
      "position": [
        -1504,
        480
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=={{ $json['\u5f53\u524d\u5185\u5bb9\u811a\u672c'] }}\n\n\u4f60\u5c06\u628a\u4ee5\u4e0a\u8d22\u7ecf/\u5e73\u53f0\u89c4\u5219\u5185\u5bb9\uff0c\u6539\u5199\u6210\u865a\u62df\u535a\u4e3b @your_persona \u7684\u7b2c {{ $json.segmentIndex }} \u671f\u53e3\u64ad\u89c6\u9891\u5206\u955cJSON\u3002\n\n\u786c\u6027\u8981\u6c42\uff1a\n1) \u4e25\u683c\u9075\u5b88 System Prompt \u7684\u8f93\u51faJSON\u7ed3\u6784\u4e0e\u5b57\u6bb5\u8def\u5f84\uff08\u540e\u7eed\u8282\u70b9\u4f9d\u8d56\uff09\u3002\n2) \u5355\u573a\u666f\uff1a\u5c45\u5bb6\u5f55\u64ad\u5de5\u4f5c\u5ba4\uff1b\u4e3b\u89d2\u56fa\u5b9a\uff1a@your_persona\uff1b\u4e0d\u8981\u5916\u8c8c\u63cf\u8ff0\u3002\n3) \u6bcf\u4e2ashot\u56fa\u5b9a10\u79d2\uff1btime_code\u630910\u79d2\u9012\u589e\u8fde\u7eed\uff1b\u603b\u65f6\u957f=total_shots*10\u3002\n4) \u8fde\u8d2f\u6027\u534f\u8bae\u5fc5\u987b\u6267\u884c\uff1a\u6bcf\u4e2ashot\u5fc5\u987b\u5305\u542b0.0-0.5s\u4e0e9.5-10.0s\u951a\u5b9a\u6bb5\uff1b\u53f0\u8bcd\u5fc5\u987b\u57289.2s\u524d\u8bf4\u5b8c\u3002\n5) \u7981\u7528\u5957\u8bdd\uff1a\u53ea\u6709S01\u5141\u8bb8\u5f00\u573a\u62db\u547c\uff1b\u53ea\u6709\u6700\u540e\u4e00\u955c\u5141\u8bb8\u5173\u6ce8/\u70b9\u8d5e/\u8bc4\u8bba/\u4e0b\u671f\u89c1/\u62dc\u62dc\u7b49\uff1b\u4e2d\u95f4\u955c\u5934\u4e25\u7981\u51fa\u73b0\u8fd9\u4e9b\u8bcd\u3002\n6) visual.image_prompt\u8981\u8f93\u51fa\u4f46\u8981\u77ed\uff1b\u63a7\u5236\u91cd\u70b9\u653e\u5728 key_visual_elements/camera/performance/continuity/timing_plan\u3002\n7) \u5141\u8bb8\u5c11\u91cf\u5f3a\u76f8\u5173 overlays\uff08\u753b\u4e2d\u753b/\u5b57\u5e55\u5361/\u7b80\u56fe\uff09\u6765\u7f13\u89e3A-roll\u5355\u8c03\uff0c\u4f46\u5fc5\u987b\u4e0e\u53f0\u8bcd\u5f3a\u76f8\u5173\uff0c\u4e14\u4e0d\u7834\u574f\u951a\u5b9a\u6bb5\u3002\n\n\u53ef\u9009\u201c\u4e0b\u671f\u9884\u544a\u94a9\u5b50\u201d\uff08\u53ea\u7528\u4e8e\u6700\u540e\u4e00\u955c\u505a\u60ac\u5ff5\uff0c\u4e0d\u8981\u62a2\u5360\u672c\u671f\u4e3b\u4f53\u5185\u5bb9\uff09\uff1a\n{{ $json['\u4e0b\u671f\u9884\u544a\u94a9\u5b50'] }}\n\n\u53ea\u8f93\u51faJSON\uff0c\u4e0d\u8981\u4efb\u4f55\u89e3\u91ca\u6216markdown\u5305\u88f9\u3002",
        "options": {
          "systemMessage": "=\u4f60\u7684\u8eab\u4efd\uff1a\n\u4f60\u662f\u201c\u53e3\u64ad\u77ed\u89c6\u9891\u5206\u955c\u5bfc\u6f14 + \u63d0\u793a\u8bcd\u5de5\u7a0b\u5e08\u201d\uff0c\u4e3a\u865a\u62df\u535a\u4e3b @your_persona \u751f\u6210\u53ef\u7528\u4e8e\u540e\u7eed Sora-2 \u6587\u751f\u89c6\u9891\u7684\u5206\u955cJSON\u3002\n\u4f60\u64c5\u957f\u628a\u201c\u5e73\u53f0\u89c4\u5219/\u8d22\u7ecf\u4e8b\u4ef6/\u91d1\u878d\u79d1\u666e\u201d\u53d8\u6210\u5f3a\u8282\u594f\u3001\u5f3a\u53ef\u63a7\u7684\u53e3\u64ad\u955c\u5934\u811a\u672c\u3002\n\n\u6700\u9ad8\u4f18\u5148\u7ea7\u786c\u89c4\u5219\uff08\u5fc5\u987b\u5168\u90e8\u9075\u5b88\uff09\uff1a\n1) \u53ea\u8f93\u51fa \u7eafJSON\uff08\u4e00\u4e2aJSON\u5bf9\u8c61\uff09\uff0c\u4e0d\u8981\u4efb\u4f55\u89e3\u91ca\u3001\u4e0d\u8981markdown\u3001\u4e0d\u8981\u4ee3\u7801\u5757\u56f4\u680f\uff0c\u4e0d\u8981\u591a\u4f59\u6587\u5b57\u3002\n2) \u5355\u573a\u666f\u56fa\u5b9a\uff1a\u5c45\u5bb6\u5f55\u64ad\u5de5\u4f5c\u5ba4\u3002\u4e3b\u89d2\u56fa\u5b9a\uff1a@your_persona\u3002\u63cf\u8ff0\u65f6\u53ea\u5199\u201c@your_persona\u201d\u6216\u201c\u535a\u4e3b\u201d\uff0c\u4e0d\u8981\u5199\u5916\u8c8c\u7ec6\u8282\u3002\n3) Sora\u9650\u5236\uff1a\u6bcf\u6761\u89c6\u9891\u7247\u6bb5\u53ea\u80fd 10\u79d2\u3002\u4f60\u7684\u5206\u955c\u5fc5\u987b\u505a\u5230\uff1a\u6bcf\u4e2ashot\u56fa\u5b9a10\u79d2\uff0ctime_code\u4e25\u683c\u630910\u79d2\u9012\u589e\u8fde\u7eed\u65e0\u91cd\u53e0\u3002\n4) \u8fde\u8d2f\u6027\u534f\u8bae\uff08Segment Continuity Contract\uff09\uff1a\n   - \u6bcf\u4e2a10\u79d2shot\u5fc5\u987b\u5305\u542b\u201c\u5f00\u5934\u951a\u5b9a0.0-0.5s\u201d\u548c\u201c\u7ed3\u5c3e\u951a\u5b9a9.5-10.0s\u201d\n   - \u951a\u5b9a\u6bb5\u8981\u6c42\uff1a\u5634\u5df4\u95ed\u5408\u3001\u624b\u56de\u5230\u4f11\u606f\u4f4d\u3001\u8eab\u4f53\u7a33\u5b9a\u3001\u76ee\u5149\u56de\u5230\u955c\u5934\uff08\u4e3a\u62fc\u63a5\u7559\u5e72\u51c0\u5207\u70b9\uff09\n   - \u53f0\u8bcd\u5fc5\u987b\u57289.2s\u4e4b\u524d\u81ea\u7136\u8bf4\u5b8c\uff0c9.2-10.0s\u7559\u7ed9\u6536\u52bf\u548c\u547c\u5438\uff08\u907f\u514d\u62fc\u63a5\u5361\u987f/\u53e3\u578b\u65ad\u88c2\uff09\n   - \u76f8\u90bbshot\u7684 continuity_out \u5e94\u5c3d\u91cf\u4e0e\u4e0b\u4e00shot\u7684 continuity_in \u5339\u914d\uff08\u59ff\u6001/\u624b\u4f4d/\u8868\u60c5\u5ef6\u7eed\uff09\n5) \u4e2d\u6bb5\u7981\u7528\u5957\u8bdd\uff1a\n   - \u53ea\u6709 S01 \u5141\u8bb8\u201c\u5f00\u573a\u62db\u547c/\u6293\u6ce8\u610f\u529b\u201d\u7c7b\u53e5\u5f0f\n   - \u53ea\u6709\u6700\u540e\u4e00\u955c\u5141\u8bb8\u201c\u5173\u6ce8/\u70b9\u8d5e/\u8bc4\u8bba/\u4e0b\u671f\u89c1/\u62dc\u62dc\u201d\u7b49\u884c\u52a8\u53f7\u53ec\n   - \u4efb\u4f55\u4e2d\u95f4\u955c\u5934\uff08S02\u5230\u5012\u6570\u7b2c\u4e8c\u955c\uff09\u4e25\u7981\u51fa\u73b0\uff1ahello/\u55e8/\u6211\u662fxxx/\u5173\u6ce8/\u70b9\u8d5e/\u8bc4\u8bba/\u62dc\u62dc/\u4e0b\u671f\u89c1/\u8bb0\u5f97\u5173\u6ce8 \u7b49\u7b49\n6) \u5185\u5bb9\u51c6\u786e\u6027\uff1a\n   - \u5bf9\u5e73\u53f0\u89c4\u5219/\u6570\u503c/\u9608\u503c/\u7981\u6b62\u9879\uff1a\u5c3d\u91cf\u5fe0\u5b9e\uff0c\u4e0d\u8981\u778e\u7f16\u3002\u5982\u679c\u8f93\u5165\u6ca1\u6709\u7ed9\u51fa\u5177\u4f53\u6570\u503c\uff0c\u4e0d\u8981\u786c\u9020\u6570\u5b57\uff0c\u53ef\u7528\u201c\u901a\u5e38/\u4e00\u822c/\u4ee5\u5e73\u53f0\u6700\u65b0\u89c4\u5219\u4e3a\u51c6\u201d\u66ff\u4ee3\u3002\n7) \u53f0\u8bcd\u65f6\u957f\u63a7\u5236\uff1a\n   - \u6bcf\u955c\u593410\u79d2\uff0c\u5efa\u8bae\u53e3\u64ad\u603b\u5b57\u6570 32-38\u5b57\uff08\u4e0d\u542b\u3010\u535a\u4e3b\u3011\u6807\u7b7e\uff0c\u4e0d\u542b\u6807\u70b9\uff09\u3002\n   - \u5fc5\u987b\u586b\u5199 voiceover_length\uff08\u4f30\u7b97\u5373\u53ef\uff0c\u4f46\u8981\u843d\u5728\u5408\u7406\u533a\u95f4\uff09\u3002\n8) \u89c6\u89c9\u8981\u6c42\uff08\u7528\u4e8e\u540e\u7eed\u89c6\u9891\u63d0\u793a\u8bcd\uff0c\u4e0d\u7528\u4e8e\u751f\u6210\u56fe\u7247\uff09\uff1a\n   - visual.image_prompt \u4ecd\u7136\u8981\u8f93\u51fa\uff0c\u4f46\u8981\u6c42\u201c\u77ed\u800c\u7cbe\u201d\uff0c\u4e3b\u8981\u63cf\u8ff0\uff1a\u8868\u60c5+\u624b\u52bf+\u59ff\u6001+\u56fa\u5b9a\u573a\u666f\u5143\u7d20+\u5149\u7ebf\n   - \u5173\u952e\u753b\u9762\u4fe1\u606f\u5fc5\u987b\u7ed3\u6784\u5316\u5199\u5728 key_visual_elements + camera + performance \u5b57\u6bb5\u91cc\uff08\u8fd9\u662f\u540e\u7eed\u63a7\u5236\u7684\u4e3b\u5165\u53e3\uff09\n\n\u955c\u5934\u8282\u594f\u5efa\u8bae\uff08\u9075\u5b88\u4f46\u4e0d\u6b7b\u677f\uff09\uff1a\n- S01\uff1a\u9ad8\u80fd\u94a9\u5b50\uff08\u53cd\u76f4\u89c9/\u75db\u70b9/\u8b66\u793a\uff09\uff0c\u80fd\u91cf\u9ad8\n- S02\uff1a\u5b89\u629a+\u5efa\u7acb\u4fe1\u4efb\uff0c\u80fd\u91cf\u4e2d\n- S03-S(\u5012\u65702)\uff1a\u5e72\u8d27\u62c6\u89e3\uff08\u5bf9\u6bd4/\u5217\u4e3e/\u5f3a\u8c03\uff09\uff0c\u80fd\u91cf\u4e2d\u9ad8\u6ce2\u52a8\n- \u6700\u540e\u4e00\u955c\uff1a\u603b\u7ed3\u91d1\u53e5 + \u884c\u52a8\u53f7\u53ec + \u53ef\u9009\u201c\u4e0b\u671f\u9884\u544a\u94a9\u5b50\u201d\n\nB-roll / \u753b\u4e2d\u753b\uff08\u53ef\u9009\uff0c\u4f46\u8981\u5f3a\u76f8\u5173\uff09\uff1a\n- \u9ed8\u8ba4\u662fA-roll\u53e3\u64ad\uff08\u535a\u4e3b\u59cb\u7ec8\u5728\u753b\u9762\u4e2d\uff09\n- \u9700\u8981\u589e\u5f3a\u4fe1\u606f\u5bc6\u5ea6\u65f6\uff0c\u5141\u8bb8 overlays\uff08\u753b\u4e2d\u753b/\u5b57\u5e55\u5361/\u7b80\u56fe\uff09\u53e0\u52a0 1-3\u79d2\uff0c\u5fc5\u987b\u4e0e\u5f53\u53e5\u53f0\u8bcd\u5f3a\u76f8\u5173\n- \u5c3d\u91cf\u907f\u514d\u6574\u6bb5\u5207\u8d70\u7eafB-roll\uff1b\u5982\u5fc5\u987b\u5168\u5c4f\u4fe1\u606f\u5361\uff0c\u8981\u6c420.0-0.5s\u4e0e9.5-10.0s\u4ecd\u56de\u5230A-roll\u951a\u5b9a\n\n\u8f93\u51faJSON\u7ed3\u6784\u8981\u6c42\uff08\u5fc5\u987b\u5305\u542b\u8fd9\u4e9b\u5b57\u6bb5\uff0c\u5141\u8bb8\u65b0\u589e\u4f46\u4e0d\u8981\u5220\u6539\u5173\u952e\u8def\u5f84\uff09\uff1a\n\u9876\u5c42\uff1a\n- meta: chapter_id, title, duration_seconds, total_shots, theme, emotional_arc, target_audience, forbidden_policy\n- scenes: \u4ec51\u4e2ascene\uff0cscene_name/location\u56fa\u5b9a\u4e3a\u201c\u5c45\u5bb6\u5f55\u64ad\u5de5\u4f5c\u5ba4\u201d\n- final_hook\n- quality_check\n\n\u6bcf\u4e2a shot \u5fc5\u987b\u5305\u542b\uff1a\n- shot_id (S01..), time_code (mm:ss-mm:ss), duration_seconds=10\n- narrative: story_beat, voiceover, voiceover_length, dialogue[], sound_fx, is_opening, is_ending\n- visual:\n  - image_prompt (\u77ed\u800c\u7cbe)\n  - key_visual_elements: subject/action/environment/lighting/mood/color_scheme\n  - camera: shot_type/angle/movement/focus\n  - overlays: (\u53ef\u9009\u6570\u7ec4) \u6bcf\u4e2aoverlay\u542b type, t_range, content, placement, style\n- emotion: primary_emotion, intensity(1-5), transition\n- performance: expression/gesture/posture/eye_contact\n- continuity:\n  - continuity_in: (0.0-0.5s\u951a\u5b9a\u72b6\u6001\u63cf\u8ff0)\n  - continuity_out: (9.5-10.0s\u951a\u5b9a\u72b6\u6001\u63cf\u8ff0)\n- timing_plan: (\u6570\u7ec4\uff0c\u63d0\u4f9b\u5173\u952e\u65f6\u95f4\u6bb5\u52a8\u4f5c\u4e0e\u53f0\u8bcd\u5206\u914d\uff0c\u5fc5\u987b\u542b0.0-0.5\u4e0e9.5-10.0\u4e24\u6bb5\uff1b\u5176\u4f59\u6bb5\u5efa\u8bae1-3\u6bb5\u5373\u53ef\uff0c\u907f\u514d\u8fc7\u957f)\n  - \u6bcf\u6761\u542b t_range, action, expression, gesture, voiceover_chunk(\u53ef\u4e3a\u7a7a)\n\nquality_check \u5fc5\u987b\u5305\u542b\uff08\u7528\u4e8e\u81ea\u52a8\u68c0\u6d4b\uff09\uff1a\n- timecode_continuous: true/false\n- all_shots_10s: true/false\n- forbidden_phrases_passed: true/false\n- voiceover_fit_10s: true/false\n- continuity_anchor_present: true/false\n- notes: \u7b80\u77ed\u5b57\u7b26\u4e32\u6570\u7ec4\uff08\u6700\u591a5\u6761\uff09\n\n\u6267\u884c\u65b9\u5f0f\uff1a\n\u6839\u636e\u7528\u6237\u63d0\u4f9b\u7684\u201c\u5f53\u524d\u5185\u5bb9\u811a\u672c\u201d\uff08\u5e73\u53f0\u89c4\u5219/\u8d22\u7ecf\u4e8b\u4ef6\u6587\u672c\uff09\u4e0e\u53ef\u9009\u201c\u4e0b\u671f\u9884\u544a\u94a9\u5b50\u201d\uff0c\u751f\u6210\u4e00\u5957\u5b8c\u6574\u5206\u955cJSON\u3002\u53ea\u8f93\u51faJSON\u3002\n"
        }
      },
      "id": "44b97f54-c0fe-4923-81be-13f6d0f52ebb",
      "name": "AI Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1840,
        640
      ],
      "typeVersion": 3
    }
  ],
  "connections": {
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Read/Write Files from Disk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read/Write Files from Disk": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Code in JavaScript2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript2": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Read/Write Files from Disk",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript3": {
      "main": [
        [
          {
            "node": "Call 'sora2+banan2\u6a21\u677f'",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent1": {
      "main": [
        [
          {
            "node": "Code in JavaScript3",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code in JavaScript4\uff08\u539f\u5185\u5bb9\uff09",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "createdAt": "2026-01-07 04:35:18.456",
  "updatedAt": "2026-02-25 15:55:38.121"
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AI Storyboard Splitter & Dispatcher | AI 分镜拆解调度器(主流程,按镜头派发给子工作流). Uses readWriteFile, lmChatOpenRouter, agent. Event-driven trigger; 15 nodes.

Source: https://github.com/gaoorange/n8n-ai-content-factory/blob/main/workflows/talking-head-video-pipeline/ai-storyboard-splitter-dispatcher.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

RAG CHATBOT Main. Uses telegram, telegramTrigger, lmChatOpenAi, n8n-nodes-mcp. Event-driven trigger; 87 nodes.

Telegram, Telegram Trigger, OpenAI Chat +8
AI & RAG

Sora 2 Pro Talking-Head Video Generator | AI 数字人口播视频生成(Sora 2 Pro + MiniMax TTS + FFmpeg 自动合成). Uses httpRequest, agent, lmChatOpenRouter, dataTable. Event-driven trigger; 74 nodes.

HTTP Request, Agent, OpenRouter Chat +5
AI & RAG

This workflow helps to automatically discover undocumented API endpoints by analysing JavaScript files from the website's HTML code.

Output Parser Autofixing, HTTP Request, Read Write File +5
AI & RAG

Turbo Videogen. Uses lmChatOpenRouter, agent, httpRequest, formTrigger. Event-driven trigger; 41 nodes.

OpenRouter Chat, Agent, HTTP Request +7
AI & RAG

pass@k. Uses agent, executeCommand, readWriteFile, n8n-nodes-mcp. Event-driven trigger; 27 nodes.

Agent, Execute Command, Read Write File +3