{
  "name": "VietlinhAI | Dựng lại video viral bằng Veo 3 kèm lồng tiếng",
  "nodes": [
    {
      "parameters": {
        "formTitle": "Clone Viral Video",
        "formDescription": "Upload video viral cần clone (mp4, dọc 9:16)",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Video Viral",
              "fieldType": "file",
              "fieldName": "Video",
              "acceptFileTypes": ".mp4"
            },
            {
              "fieldLabel": "Ghi chú (sản phẩm/thay đổi mong muốn - có thể bỏ trống)"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.5,
      "position": [
        -256,
        624
      ],
      "id": "47eecce0-6bf4-4364-889f-ae9feec34844",
      "name": "On form submission",
      "webhookId": "65a50a75-d464-426c-b8df-0b8eb88d2716"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/upload/v1beta/files?uploadType=media",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi",
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "Video",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        64,
        624
      ],
      "id": "a4cadae8-7ea3-4fe9-bd29-902615924954",
      "name": "Upload Video to Gemini"
    },
    {
      "parameters": {
        "amount": 10
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        288,
        624
      ],
      "id": "d1f12675-2569-407c-8c94-10eecf1a81d2",
      "name": "Wait File Processing",
      "webhookId": "ac8e2f05-38e3-428c-8510-101aed057cb3"
    },
    {
      "parameters": {
        "url": "=https://generativelanguage.googleapis.com/v1beta/{{ $('Upload Video to Gemini').item.json.file.name }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        496,
        624
      ],
      "id": "e1414422-bf9c-461c-9abe-c80466602616",
      "name": "Get File Status"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "b2000001-0000-4000-8000-000000000001",
              "leftValue": "={{ $json.state }}",
              "rightValue": "ACTIVE",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        704,
        624
      ],
      "id": "02f49704-91ab-4118-bdb9-9494e7218549",
      "name": "File Active?"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  contents: [{\n    parts: [\n      { file_data: { file_uri: $json.uri, mime_type: 'video/mp4' } },\n      { text: `Bạn là chuyên gia phân tích video viral. Hãy phân tích video này và bóc tách phân cảnh chi tiết.\\n\\nTrả về DUY NHẤT một JSON hợp lệ theo cấu trúc:\\n{\\n  \\\"total_duration_seconds\\\": number,\\n  \\\"hook_analysis\\\": \\\"vì sao 3 giây đầu giữ chân người xem\\\",\\n  \\\"structure_notes\\\": \\\"cấu trúc tổng thể: hook - body - CTA, nhịp cắt cảnh\\\",\\n  \\\"scenes\\\": [\\n    {\\n      \\\"start\\\": \\\"0:00\\\",\\n      \\\"end\\\": \\\"0:07\\\",\\n      \\\"visual_description\\\": \\\"mô tả chi tiết hình ảnh: nhân vật, hành động, bối cảnh, ánh sáng\\\",\\n      \\\"camera\\\": \\\"góc máy, chuyển động camera\\\",\\n      \\\"on_screen_text\\\": \\\"text hiển thị trên màn hình nếu có\\\",\\n      \\\"dialogue_or_voiceover\\\": \\\"lời thoại/voiceover trong đoạn này\\\",\\n      \\\"role\\\": \\\"hook | body | cta\\\"\\n    }\\n  ]\\n}\\n\\nYêu cầu: mô tả visual đủ chi tiết để tái tạo lại cảnh bằng AI video. Không thêm bất kỳ text nào ngoài JSON.` }\n    ]\n  }],\n  generationConfig: { response_mime_type: 'application/json', temperature: 0.4 }\n}) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        64,
        1136
      ],
      "id": "877555f2-935c-4108-a2a8-3a90267ef108",
      "name": "Analyze Viral Video"
    },
    {
      "parameters": {
        "jsCode": "// Parse JSON phân tích phân cảnh từ Gemini\nconst raw = $input.first().json.candidates[0].content.parts[0].text;\nconst clean = raw.replace(/```json|```/g, '').trim();\nconst analysis = JSON.parse(clean);\n\nreturn {\n  json: {\n    analysis,\n    user_note: $('On form submission').first().json['Ghi chú (sản phẩm/thay đổi mong muốn - có thể bỏ trống)'] || ''\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        304,
        1136
      ],
      "id": "31ca639f-8433-447d-9ce9-3b8a56d1a7bb",
      "name": "Parse Scene JSON"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Dưới đây là kết quả phân tích phân cảnh của một video viral. Hãy tạo bộ prompt Veo 3 để clone lại video này và viết voiceover script tiếng Việt.\n\nGhi chú của người dùng (có thể trống - nếu có, thay sản phẩm/nội dung theo ghi chú):\n{{ $json.user_note }}\n\nPhân tích video gốc:\n{{ JSON.stringify($json.analysis, null, 2) }}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "=ROLE\nBạn là Viral Video Clone Agent. Nhiệm vụ: từ phân tích phân cảnh video viral gốc, tạo ra (1) chuỗi prompt Veo 3 để tái tạo video theo CÙNG CẤU TRÚC và (2) một voiceover script tiếng Việt hoàn chỉnh.\n\nQUY TẮC SỐ LƯỢNG CLIP (BẮT BUỘC - QUAN TRỌNG NHẤT)\n- Mỗi clip Veo 3 tối đa 8 giây.\n- Số clip phải PHỦ HẾT thời lượng video gốc. Tính số clip = LÀM TRÒN LÊN (total_duration_seconds / 8).\n  Ví dụ: video 16s → 2 clip; 24s → 3 clip; 30s → 4 clip; 40s → 5 clip.\n- Tối thiểu 3 clip. Tối đa 6 clip. Nếu video dài hơn 48s, gộp nội dung để vừa 6 clip.\n- TUYỆT ĐỐI KHÔNG chỉ tạo 1 clip cho video dài. Phải chia đủ số clip theo công thức trên.\n- Các clip nối tiếp mạch lạc, cùng nhân vật, kể trọn câu chuyện từ hook đến hết.\n\nQUY TẮC PROMPT VIDEO (BẮT BUỘC)\n- Prompt viết bằng tiếng Anh, mô tả chi tiết: nhân vật, hành động, bối cảnh, ánh sáng, góc máy, chuyển động camera.\n- Phong cách UGC chân thật như quay bằng điện thoại, dọc 9:16.\n- KHÔNG có lời thoại trong video. Thêm vào cuối mỗi prompt: \"No dialogue, no speech, natural ambient sound only.\"\n- Cùng nhân vật: mô tả nhân vật Y HỆT NHAU trong mọi prompt (ngoại hình, trang phục, kiểu tóc), cùng không gian, hành động nối tiếp như camera quay liên tục.\n- KHÔNG sao chép người thật/thương hiệu trong video gốc. Tạo nhân vật MỚI nhưng giữ nguyên cấu trúc, nhịp, vai trò từng cảnh.\n- Nếu ghi chú người dùng chỉ định sản phẩm/nội dung khác, thay cho khớp trong mọi cảnh.\n\nQUY TẮC NÉ CONTENT FILTER (Veo hay chặn \"chân dung người thật\")\n- TRÁNH: soi gương cận mặt, selfie chính diện, cận cảnh khuôn mặt tĩnh kiểu chân dung.\n- ƯU TIÊN: góc qua vai, tay thao tác, góc 3/4 nghiêng, nhân vật đang chuyển động/hành động tự nhiên.\n\nQUY TẮC VOICEOVER SCRIPT (BẮT BUỘC)\n- Tiếng Việt, giọng tự nhiên như review thật, không văn quảng cáo sáo rỗng.\n- Bám cấu trúc và thông điệp voiceover gốc nhưng viết lại hoàn toàn bằng lời của mình.\n- Độ dài khớp tổng thời lượng: khoảng 2.5 từ mỗi giây video (ví dụ video 24s ≈ 60 từ, 30s ≈ 75 từ).\n- Mở đầu là hook mạnh trong 3 giây đầu.\n- Chỉ trả script thuần, không ghi chú thời gian, không ký hiệu.\n\nOUTPUT: trả đúng JSON theo schema, không thêm gì khác."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        512,
        1136
      ],
      "id": "17e2bfeb-79b6-4ce0-b85a-d2ea79621938",
      "name": "Clone Prompt Builder"
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n  \"video_title\": \"string\",\n  \"video_description\": \"string\",\n  \"voiceover_script\": \"string\",\n  \"scenes\": [\n    {\n      \"scene_number\": 1,\n      \"video_prompt\": \"string\"\n    }\n  ]\n}",
        "autoFix": true
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        672,
        1408
      ],
      "id": "ea3011e6-fded-4b7f-b9fb-890e1709054e",
      "name": "Structured Output Parser"
    },
    {
      "parameters": {
        "fieldToSplitOut": "output.scenes",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1216,
        624
      ],
      "id": "4efd77ae-2daa-4e48-beda-8ab66b6c4d9c",
      "name": "Split Out Scenes"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1536,
        624
      ],
      "id": "4dd862cb-a78e-410c-9f51-93b4fa4fdcd5",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.shopaikey.com/v1/video/generations",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: \"veo3-fast\", prompt: $json.video_prompt, metadata: { aspect_ratio: \"9:16\", enhance_prompt: true, enable_upsample: true } }) }}",
        "options": {
          "redirect": {
            "redirect": {}
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1856,
        720
      ],
      "id": "c08b4564-4d14-4609-8bba-16c104dabd6d",
      "name": "Create Veo3 Video"
    },
    {
      "parameters": {
        "amount": 15
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        2080,
        720
      ],
      "id": "c94beca4-c84d-4bbc-ae77-0ec31674b162",
      "name": "Wait Veo",
      "webhookId": "b81b8741-4aca-4b8c-a52a-cd76bb8e1e43"
    },
    {
      "parameters": {
        "url": "=https://api.shopaikey.com/v1/video/generations/{{ $json.id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_API_KEY"
            }
          ]
        },
        "options": {
          "redirect": {
            "redirect": {}
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        2288,
        720
      ],
      "id": "5da3c2ba-f670-40ca-b14c-7d5eb119c5b0",
      "name": "Get Video"
    },
    {
      "parameters": {
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "data.result_url"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1840,
        496
      ],
      "id": "de77a540-c96c-419d-a388-0606b71ad629",
      "name": "Aggregate Videos"
    },
    {
      "parameters": {
        "jsCode": "// Build body cho NCA Toolkit concatenate\nconst item = $input.first();\n// Aggregate gom field 'data.result_url' -> n8n lưu key nguyên văn 'data.result_url'\nlet urls = item.json['data.result_url'];\nif (!urls) urls = item.json.result_url;\nif (!urls) urls = item.json.video_url;\nconst list = Array.isArray(urls) ? urls : [urls];\nconst formattedList = list.filter(Boolean).map(url => ({ video_url: url }));\nif (formattedList.length === 0) throw new Error('Khong co video URL de concat. Data: ' + JSON.stringify(item.json).slice(0,400));\nreturn { json: { output: JSON.stringify({ video_urls: formattedList, id: 'clone-concat' }) } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2064,
        496
      ],
      "id": "cb2220a7-4662-46c6-aa32-6110bfd23512",
      "name": "Build Concat Body"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://YOUR-NCA-TOOLKIT-URL/v1/video/concatenate",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "YOUR_X_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.output }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        2288,
        496
      ],
      "id": "a7858b6a-61fd-4161-b49e-a38972f8182d",
      "name": "Merge Videos (NCA)"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-tts:generateContent",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  contents: [{\n    parts: [{ text: 'Đọc đoạn sau bằng tiếng Việt, giọng nữ trẻ tự nhiên, năng lượng, như đang review sản phẩm trên TikTok, nhịp hơi nhanh:\\n\\n' + $json.output.voiceover_script }]\n  }],\n  generationConfig: {\n    responseModalities: ['AUDIO'],\n    speechConfig: {\n      voiceConfig: {\n        prebuiltVoiceConfig: { voiceName: 'Kore' }\n      }\n    }\n  }\n}) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1280,
        1248
      ],
      "id": "f493d96a-8758-41f5-a169-0dc6ea36c2f1",
      "name": "Gemini TTS Voiceover"
    },
    {
      "parameters": {
        "jsCode": "// Gemini TTS trả PCM s16le 24kHz mono (base64) -> bọc header WAV\nconst b64 = $input.first().json.candidates[0].content.parts[0].inlineData.data;\nconst pcm = Buffer.from(b64, 'base64');\n\nconst sampleRate = 24000;\nconst numChannels = 1;\nconst bitsPerSample = 16;\nconst byteRate = sampleRate * numChannels * bitsPerSample / 8;\nconst blockAlign = numChannels * bitsPerSample / 8;\n\nconst header = Buffer.alloc(44);\nheader.write('RIFF', 0);\nheader.writeUInt32LE(36 + pcm.length, 4);\nheader.write('WAVE', 8);\nheader.write('fmt ', 12);\nheader.writeUInt32LE(16, 16);\nheader.writeUInt16LE(1, 20);\nheader.writeUInt16LE(numChannels, 22);\nheader.writeUInt32LE(sampleRate, 24);\nheader.writeUInt32LE(byteRate, 28);\nheader.writeUInt16LE(blockAlign, 32);\nheader.writeUInt16LE(bitsPerSample, 34);\nheader.write('data', 36);\nheader.writeUInt32LE(pcm.length, 40);\n\nconst wav = Buffer.concat([header, pcm]);\nconst binaryData = await this.helpers.prepareBinaryData(wav, 'voiceover.wav', 'audio/wav');\n\nreturn [{ json: { fileName: 'voiceover.wav' }, binary: { data: binaryData } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1568,
        1248
      ],
      "id": "61c0264b-0b1f-48d9-b77a-6ce40bc6a1b2",
      "name": "PCM to WAV"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.cloudinary.com/v1_1/YOUR_CLOUD_NAME/video/upload",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "file",
              "inputDataFieldName": "data"
            },
            {
              "name": "upload_preset",
              "value": "n8n_unsigned"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1888,
        1248
      ],
      "id": "1dfce5cb-920f-4520-8faa-496b1ebaf496",
      "name": "Upload Audio Cloudinary"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        3008,
        592
      ],
      "id": "ad591012-c0eb-44aa-a70c-8633562d5f3f",
      "name": "Merge Video + Audio"
    },
    {
      "parameters": {
        "jsCode": "// Ghép video đã concat với voiceover bằng NCA /v1/ffmpeg/compose\nconst items = $input.all();\n\nlet videoUrl = null;\nlet audioUrl = null;\n\nfor (const item of items) {\n  const j = item.json;\n  // Audio từ Cloudinary\n  if (j.secure_url) audioUrl = j.secure_url;\n  // Video từ NCA concatenate: response là MẢNG object có file_url\n  if (Array.isArray(j.response) && j.response[0] && j.response[0].file_url) {\n    videoUrl = j.response[0].file_url;\n  }\n  // fallback nếu NCA trả kiểu khác\n  if (!videoUrl && typeof j.response === 'string' && j.response.includes('.mp4')) videoUrl = j.response;\n  if (!videoUrl && j.video_url && typeof j.video_url === 'string') videoUrl = j.video_url;\n}\n\nif (!videoUrl || !audioUrl) {\n  throw new Error('Thieu videoUrl hoac audioUrl. videoUrl=' + videoUrl + ' audioUrl=' + audioUrl);\n}\n\nconst payload = {\n  inputs: [\n    { file_url: videoUrl },\n    { file_url: audioUrl }\n  ],\n  filters: [],\n  outputs: [\n    {\n      options: [\n        { option: '-map', argument: '0:v:0' },\n        { option: '-map', argument: '1:a:0' },\n        { option: '-c:v', argument: 'copy' },\n        { option: '-c:a', argument: 'aac' },\n        { option: '-shortest' }\n      ]\n    }\n  ],\n  id: 'clone-final-mux'\n};\n\nreturn { json: { output: JSON.stringify(payload) } };"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3264,
        592
      ],
      "id": "da9d7945-3688-488e-b734-7211463d71c9",
      "name": "Build Mux Body"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://YOUR-NCA-TOOLKIT-URL/v1/ffmpeg/compose",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "YOUR_X_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.output }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        3520,
        592
      ],
      "id": "457a6fc2-92a2-43c1-9110-ea71c5de6cf9",
      "name": "Ghep Voiceover (NCA)"
    },
    {
      "parameters": {
        "content": "## CẦN CHUẨN BỊ\n- Gemini API key: tạo credential Google Gemini (PaLM) API, chọn ở 4 node gọi Gemini\n- Key ShopAIKey (hoặc dịch vụ Veo 3 khác): dán vào header Authorization của Create Veo3 Video và Get Video\n- NCA Toolkit tự dựng (No-Code Architects Toolkit): thay YOUR-NCA-TOOLKIT-URL và x-api-key ở 2 node NCA\n- Cloudinary: thay YOUR_CLOUD_NAME, tạo upload preset unsigned tên n8n_unsigned (hoặc sửa tên trong node)\n- 2 node Chat Model: kết nối key OpenAI hoặc dịch vụ cùng chuẩn, chọn model\n- Google Drive và Google Sheets: chọn tài khoản, chọn trang tính Post\n- Caption Writer: điền tên và mô tả thương hiệu của bạn",
        "height": 416,
        "width": 440,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -32,
        -16
      ],
      "id": "a2222c75-5bea-4e09-8e3b-ab634c6b6159",
      "name": "Sticky Setup"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn model của bạn --"
        },
        "responsesApiEnabled": false,
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        512,
        1408
      ],
      "id": "0378b63c-751b-409d-84e0-12ce3f18784e",
      "name": "Prompt Model"
    },
    {
      "parameters": {
        "content": "## 1. TẢI VIDEO \n",
        "height": 544,
        "width": 1024,
        "color": "#213B4F"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -32,
        432
      ],
      "id": "bf1830ad-c5e9-4746-93a4-1edc565ceeab",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## 2. PHÂN TÍCH VIDEO VÀ TẠO LẠI PROMPT\n",
        "height": 544,
        "width": 1024,
        "color": "#123B26"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -32,
        1040
      ],
      "id": "e36c2110-626d-4cb7-9c71-31ae1c7ce1da",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## 3. TẠO VIDEO VEO3\n",
        "height": 544,
        "width": 1664,
        "color": "#213B4F"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1056,
        432
      ],
      "id": "14713d7f-a5a3-4622-8323-f267a252dde8",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## 4. TẠO GIỌNG NÓI\n",
        "height": 544,
        "width": 1664,
        "color": "#213B4F"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1056,
        1040
      ],
      "id": "f254d396-2d6a-4176-94ff-14b45ec312f9",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## 5. EDIT VIDEO FINAL\n",
        "height": 544,
        "width": 960,
        "color": "#213B4F"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2816,
        432
      ],
      "id": "a1717e3a-3ba4-4e07-b144-58636ddaf401",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "url": "={{ $('Ghep Voiceover (NCA)').item.json.response[0].file_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        3040,
        1248
      ],
      "id": "c23940bc-7b13-47df-995e-5f0068a30f25",
      "name": "Download Final Video"
    },
    {
      "parameters": {
        "name": "={{ $('Clone Prompt Builder').item.json.output.video_title }}.mp4",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "root",
          "cachedResultName": "/ (Root folder)"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        3312,
        1248
      ],
      "id": "58d9db30-9b57-40fc-9821-c34ae422ce93",
      "name": "Upload file"
    },
    {
      "parameters": {
        "content": "## 6. TẢI VIDEO VỀ GOOGLE DRIVE\n",
        "height": 544,
        "width": 960,
        "color": "#213B4F"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2816,
        1040
      ],
      "id": "79b952d8-9b74-4321-9b6e-7eee62a60555",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Video vừa được tạo xong. Hãy viết caption + hashtag cho 3 nền tảng và tiêu đề YouTube.\n\nTiêu đề video: {{ $('Clone Prompt Builder').item.json.output.video_title }}\nMô tả video: {{ $('Clone Prompt Builder').item.json.output.video_description }}\nVoiceover script: {{ $('Clone Prompt Builder').item.json.output.voiceover_script }}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "=VAI TRÒ\nBạn là Content Copywriter cho [TÊN THƯƠNG HIỆU CỦA BẠN] - [một câu mô tả thương hiệu, lĩnh vực, khách hàng của bạn].\n\nNHIỆM VỤ\nDựa trên nội dung video, viết caption + hashtag riêng cho từng nền tảng và tiêu đề YouTube.\n\nQUY TẮC CHUNG\n- Giọng văn: tự nhiên, có chiều sâu, không sáo rỗng, không quảng cáo lộ liễu.\n- Mở đầu caption phải là một hook giữ chân trong 1-2 dòng đầu.\n- Kết bằng một câu hỏi hoặc lời kêu gọi tương tác nhẹ nhàng (comment, thả chữ...).\n- Emoji dùng tiết chế, chỉ 1-2 cái ở chỗ hợp lý.\n\nQUY TẮC TỪNG NỀN TẢNG\n- caption_tiktok: ngắn gọn, punchy, 3-5 dòng. Hashtag TikTok kiểu trend (#xuhuong #fyp cùng hashtag chủ đề). 4-6 hashtag.\n- caption_page (Facebook Page): dài hơn, có thể xuống dòng nhiều đoạn, kể chuyện/chiêm nghiệm, dẫn dắt cảm xúc. 4-6 hashtag không dấu ở cuối.\n- caption_youtube: mô tả đầy đủ cho YouTube, 2-3 đoạn, đoạn cuối có khối hashtag không dấu (5-8 hashtag) và các keyword tìm kiếm liên quan cách nhau bởi dấu phẩy.\n- youtube_title: tiêu đề YouTube hấp dẫn, tối đa 60 ký tự, có yếu tố tò mò.\n\nOUTPUT: trả đúng JSON theo schema, không thêm gì khác."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        4032,
        880
      ],
      "id": "63e5d640-272f-42ba-8056-4a4eff235187",
      "name": "Caption Writer"
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\n  \"caption_tiktok\": \"string\",\n  \"caption_page\": \"string\",\n  \"caption_youtube\": \"string\",\n  \"youtube_title\": \"string\"\n}",
        "autoFix": true
      },
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        4208,
        1088
      ],
      "id": "05b1400c-b322-4d72-acff-3025603b1b1f",
      "name": "Caption Parser"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn model của bạn --"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        4032,
        1088
      ],
      "id": "3099a5a5-e50d-4155-ab21-3a6ba393e599",
      "name": "Caption Model"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn file của bạn --",
          "cachedResultUrl": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "-- Chọn trang tính Post của bạn --",
          "cachedResultUrl": ""
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "ID": "={{ $now.toFormat('yyyyMMddHHmm') }}",
            "Caption + Hashtag TikTok": "={{ $('Caption Writer').item.json.output.caption_tiktok }}",
            "Caption + Hashtag Page": "={{ $('Caption Writer').item.json.output.caption_page }}",
            "Caption + Hashtag Youtube": "={{ $('Caption Writer').item.json.output.caption_youtube }}",
            "Youtube Title": "={{ $('Caption Writer').item.json.output.youtube_title }}",
            "TikTok Post Status": "Ready to Post",
            "Page Post Status": "Ready to Post",
            "Youtube Post Status": "Ready to Post",
            "Url": "={{ 'https://drive.google.com/file/d/' + $('Upload file').item.json.id + '/view' }}",
            "Status": "Ready to Post"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "ID",
              "displayName": "ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Caption + Hashtag TikTok",
              "displayName": "Caption + Hashtag TikTok",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Caption + Hashtag Page",
              "displayName": "Caption + Hashtag Page",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Caption + Hashtag Youtube",
              "displayName": "Caption + Hashtag Youtube",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Youtube Title",
              "displayName": "Youtube Title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Post Status",
              "displayName": "TikTok Post Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Page Post Status",
              "displayName": "Page Post Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Youtube Post Status",
              "displayName": "Youtube Post Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Url",
              "displayName": "Url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        4432,
        880
      ],
      "id": "4eb93093-e874-48be-887d-19ab0d37e38e",
      "name": "Save to Sheet"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.data.status }}",
                    "rightValue": "SUCCESS",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.data.status }}",
                    "rightValue": "FAILURE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        2496,
        720
      ],
      "id": "d53d0d24-5b8b-4ad0-a0e8-9167c28f8062",
      "name": "Check Video Status"
    },
    {
      "parameters": {
        "errorMessage": "=Video generation FAILED. Ly do: {{ $json.data.fail_reason || JSON.stringify($json.data) }}"
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        2720,
        960
      ],
      "id": "05d0096e-6362-4eee-8ad0-70ce32e6439e",
      "name": "Video Failed"
    },
    {
      "parameters": {
        "content": "## 7. VIẾT CAPTION VÀ LƯU GOOGLE SHEETS",
        "height": 1152,
        "width": 960,
        "color": "#0C1845"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        3840,
        432
      ],
      "id": "d7cfb0e1-1cf8-4327-ac4a-e9f70f02dbfc",
      "name": "Sticky Note6"
    }
  ],
  "pinData": {},
  "connections": {
    "On form submission": {
      "main": [
        [
          {
            "node": "Upload Video to Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Video to Gemini": {
      "main": [
        [
          {
            "node": "Wait File Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait File Processing": {
      "main": [
        [
          {
            "node": "Get File Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get File Status": {
      "main": [
        [
          {
            "node": "File Active?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "File Active?": {
      "main": [
        [
          {
            "node": "Analyze Viral Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait File Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Viral Video": {
      "main": [
        [
          {
            "node": "Parse Scene JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Scene JSON": {
      "main": [
        [
          {
            "node": "Clone Prompt Builder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Clone Prompt Builder",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Clone Prompt Builder": {
      "main": [
        [
          {
            "node": "Split Out Scenes",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gemini TTS Voiceover",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out Scenes": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Aggregate Videos",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Veo3 Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Veo3 Video": {
      "main": [
        [
          {
            "node": "Wait Veo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Veo": {
      "main": [
        [
          {
            "node": "Get Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Video": {
      "main": [
        [
          {
            "node": "Check Video Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Videos": {
      "main": [
        [
          {
            "node": "Build Concat Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Concat Body": {
      "main": [
        [
          {
            "node": "Merge Videos (NCA)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Videos (NCA)": {
      "main": [
        [
          {
            "node": "Merge Video + Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini TTS Voiceover": {
      "main": [
        [
          {
            "node": "PCM to WAV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PCM to WAV": {
      "main": [
        [
          {
            "node": "Upload Audio Cloudinary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Audio Cloudinary": {
      "main": [
        [
          {
            "node": "Merge Video + Audio",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Video + Audio": {
      "main": [
        [
          {
            "node": "Build Mux Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Mux Body": {
      "main": [
        [
          {
            "node": "Ghep Voiceover (NCA)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ghep Voiceover (NCA)": {
      "main": [
        [
          {
            "node": "Download Final Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Final Video": {
      "main": [
        [
          {
            "node": "Upload file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload file": {
      "main": [
        [
          {
            "node": "Caption Writer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Caption Model": {
      "ai_languageModel": [
        [
          {
            "node": "Caption Writer",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Caption Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Caption Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Caption Writer",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Caption Writer": {
      "main": [
        [
          {
            "node": "Save to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Video Status": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Video Failed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait Veo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prompt Model": {
      "ai_languageModel": [
        [
          {
            "node": "Clone Prompt Builder",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "nodeGroups": [],
  "tags": []
}