{
  "name": "UGC \u2013 Screenshot\u2192Gemini\u2192Sora2\u2192VBR (Vertical)",
  "nodes": [
    {
      "id": "webhook",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1520,
        40
      ],
      "parameters": {
        "path": "ugc-screenshot-video",
        "options": {
          "responseData": "allEntries"
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "manual-trigger",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1520,
        280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "sticky-overview",
      "name": "\ud83d\udccb Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1980,
        -320
      ],
      "parameters": {
        "width": 520,
        "height": 620,
        "content": "## \ud83c\udfac AI UGC Ad Generator\n\n**What it does:**\n- Analyzes app screen recording with Gemini AI\n- Generates UGC ad structure (hook, problem, solution, CTA)\n- Creates AI actor video with Sora 2\n- Removes background from actor\n- Composites actor over screen recording\n- Saves final UGC ad to Google Drive\n\n**Perfect for:**\n- App developers creating UGC-style ads\n- SaaS products with screen demos\n- Mobile app marketing teams\n- Digital product launches\n- Feature announcement videos\n\n**Processing time:** 5-8 minutes total\n- Gemini analysis: ~30s\n- Sora 2 generation: ~2-4 min\n- Background removal + composition: ~2-3 min\n\n**Setup:** ~10 minutes (3 API keys)\n\n[\ud83d\udcd6 Full Documentation](https://docs.videobgremover.com/)"
      },
      "typeVersion": 1,
      "notesTextSize": "large",
      "notesBackgroundColor": 4
    },
    {
      "id": "sticky-api-keys",
      "name": "\ud83d\udd11 API Keys",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1520,
        -320
      ],
      "parameters": {
        "width": 420,
        "height": 480,
        "content": "## \ud83d\udd11 API Keys Setup (Required)\n\n**1. Gemini API Key**\n- Visit: https://aistudio.google.com/apikey\n- Create new API key\n- Add to n8n: Settings \u2192 Variables\n  - Name: `GEMINI_KEY`\n  - Value: your Gemini API key\n\n**2. FAL AI Key (for Sora 2)**\n- Visit: https://fal.ai/dashboard/keys\n- Sign up and create API key\n- Add to n8n: Settings \u2192 Variables\n  - Name: `FAL_KEY`\n  - Value: your FAL key\n\n**3. VideoBGRemover API Key**\n- Visit: https://videobgremover.com/api-management\n- Sign up (free tier available)\n- Add to n8n: Settings \u2192 Variables\n  - Name: `VIDEOBGREMOVER_KEY`\n  - Value: your API key\n\n\u2705 All keys use `$vars.` (secure)"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-inputs",
      "name": "\ud83d\udce5 Inputs",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1060,
        -320
      ],
      "parameters": {
        "width": 420,
        "height": 400,
        "content": "## \ud83d\udce5 Input Required\n\n**screenshot_video_url** (required)\n- URL to your app screen recording\n- Should be vertical (9:16 aspect ratio)\n- Duration: 4-12 seconds recommended\n- Must be publicly accessible\n\n**meta** (optional)\n- `product_name`: Name of app/feature\n- `benefits`: Array of key benefits\n- `tone`: Desired ad tone (friendly, professional, etc.)\n\n**Example:**\n```json\n{\n  \"screenshot_video_url\": \"https://...\",\n  \"meta\": {\n    \"product_name\": \"MyApp Pro\",\n    \"benefits\": [\"Fast\", \"Easy\", \"Powerful\"],\n    \"tone\": \"friendly\"\n  },\n  \"store_to_drive\": true\n}\n```"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-workflow",
      "name": "\ud83d\udd04 Workflow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -600,
        -320
      ],
      "parameters": {
        "width": 420,
        "height": 520,
        "content": "## \ud83d\udd04 Workflow Steps\n\n**1. Gemini Analysis** (~30s)\n- Uploads screen recording to Gemini\n- AI analyzes app/feature shown\n- Generates UGC ad structure:\n  - `ad_topic`: What's being promoted\n  - `hook`: Attention grabber (5-8 words)\n  - `problem`: Pain point (12-15 words)\n  - `solution`: Fix presented (12-15 words)\n  - `cta`: Call-to-action (5-8 words)\n  - `visual_details`: Actor description\n  - `emotional_journey`: Emotion flow\n\n**2. Sora 2 Generation** (~2-4 min)\n- Creates AI actor video\n- Natural UGC-style delivery\n- Matches emotional journey\n- 4, 8, or 12 second duration\n\n**3. VBR Composition** (~2-3 min)\n- Removes actor background\n- Composites over screen recording\n- Bottom-right corner positioning\n- Audio mixing (30% bg + 100% fg)"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-sora-structure",
      "name": "\ud83d\udcdd Sora Prompt",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -100,
        -320
      ],
      "parameters": {
        "width": 460,
        "height": 340,
        "content": "## \ud83c\udfaf Sora Prompt Structure\n\n**Template:**\n[ICP/visual details] filming a UGC ad about [APP CONTEXT].\nThey start by saying: [hook],\nthen explain: [problem],\npresent the solution: [solution],\nand end with: [CTA].\n[Emotional journey]. [Camera style].\nNatural hand gestures and expressions.\n\n**Key Elements:**\n- `ad_topic`: What app/feature is being promoted\n- `visual_details`: Actor description + setting\n- `ad_structure`: Hook, problem, solution, CTA\n- `emotional_journey`: How emotions progress\n- Camera style extracted from visual_details\n\n\u2705 This structure makes Sora understand CONTEXT vs WHAT TO GENERATE"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-gdrive",
      "name": "\ud83d\udcbe Google Drive",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        -320
      ],
      "parameters": {
        "width": 380,
        "height": 340,
        "content": "## \ud83d\udcbe Google Drive Setup\n\n**Step 1:** Connect Google Drive\n- Click \"Upload to Google Drive\" node\n- Click \"Connect\"\n- Authorize n8n\n\n**Step 2:** Choose folder (optional)\n- Default: Root of \"My Drive\"\n- Or select specific folder\n\n**Output:**\n- Permanent shareable link\n- Direct download URL\n- File metadata\n- Drive file ID\n\n\u23f1\ufe0f Setup time: ~2 minutes\n\n**Note:** Files are named automatically:\n`ugc_final_[timestamp].mp4`"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-usage",
      "name": "\ud83d\ude80 Usage",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        860,
        -320
      ],
      "parameters": {
        "width": 420,
        "height": 480,
        "content": "## \ud83d\ude80 How to Use\n\n**Manual testing:**\n1. Update `screenshot_video_url` in \"Sample Input\"\n2. Optionally update `meta` object\n3. Click \"Execute Workflow\"\n4. Wait 5-8 minutes for processing\n5. Check Google Drive for final video\n\n**Webhook automation:**\n1. Activate workflow\n2. Copy webhook URL\n3. POST to webhook:\n```json\n{\n  \"screenshot_video_url\": \"https://...\",\n  \"meta\": {\n    \"product_name\": \"MyApp\",\n    \"benefits\": [\"Fast\", \"Easy\"],\n    \"tone\": \"friendly\"\n  },\n  \"store_to_drive\": true\n}\n```\n\n**Tips:**\n- Use 9:16 vertical videos\n- Keep recordings 4-12 seconds\n- Test with sample video first"
      },
      "typeVersion": 1
    },
    {
      "id": "extract-webhook",
      "name": "Extract Webhook Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1300,
        40
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "screenshot_video_url",
              "type": "string",
              "value": "={{ $json.body?.screenshot_video_url ?? $json.screenshot_video_url }}"
            },
            {
              "name": "meta",
              "type": "object",
              "value": "={{ $json.body?.meta ?? $json.meta ?? {} }}"
            },
            {
              "name": "store_to_drive",
              "type": "boolean",
              "value": "={{ $json.body?.store_to_drive ?? $json.store_to_drive ?? false }}"
            },
            {
              "name": "reference_image_url",
              "type": "string",
              "value": "={{ $json.body?.reference_image_url ?? $json.reference_image_url ?? '' }}"
            },
            {
              "name": "source",
              "type": "string",
              "value": "webhook"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "sample-input",
      "name": "Sample Input (Edit Here)",
      "type": "n8n-nodes-base.set",
      "position": [
        -1300,
        280
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "screenshot_video_url",
              "type": "string",
              "value": "https://videos.videobgremover.com/public-videos/assets/screenrecord_video_affina.mp4"
            },
            {
              "name": "meta",
              "type": "object",
              "value": "={{ {product_name: 'Sample App', benefits: ['Easy to use', 'Fast', 'Powerful'], tone: 'friendly'} }}"
            },
            {
              "name": "store_to_drive",
              "type": "boolean",
              "value": true
            },
            {
              "name": "reference_image_url",
              "type": "string",
              "value": ""
            },
            {
              "name": "source",
              "type": "string",
              "value": "manual"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "merge-triggers",
      "name": "Merge Triggers",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1080,
        160
      ],
      "parameters": {
        "mode": "append",
        "options": {}
      },
      "typeVersion": 2.1
    },
    {
      "id": "sticky-section-gemini",
      "name": "Section: Gemini",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -900,
        380
      ],
      "parameters": {
        "width": 1100,
        "height": 100,
        "content": "## \ud83e\udd16 GEMINI AI ANALYSIS SECTION\nAnalyzes screen recording \u2192 Generates ad structure (hook, problem, solution, CTA, actor details)"
      },
      "typeVersion": 1,
      "notesBackgroundColor": 5
    },
    {
      "id": "download-shot",
      "name": "Download Screenshot Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -860,
        460
      ],
      "parameters": {
        "url": "={{ $json.screenshot_video_url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "gemini-upload",
      "name": "Upload to Gemini",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -640,
        460
      ],
      "parameters": {
        "url": "=https://generativelanguage.googleapis.com/upload/v1beta/files?key={{ $vars.GEMINI_KEY }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Goog-Upload-Command",
              "value": "start, upload, finalize"
            },
            {
              "name": "X-Goog-Upload-Header-Content-Length",
              "value": "={{ $binary.data.fileSize }}"
            },
            {
              "name": "X-Goog-Upload-Header-Content-Type",
              "value": "=video/{{ $binary.data.fileExtension }}"
            },
            {
              "name": "Content-Type",
              "value": "video/mp4"
            }
          ]
        },
        "inputDataFieldName": "=data"
      },
      "typeVersion": 4.2
    },
    {
      "id": "wait-gemini",
      "name": "Wait for Processing",
      "type": "n8n-nodes-base.wait",
      "position": [
        -420,
        460
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "gemini-analyze",
      "name": "Gemini \u2013 Analyze & Plan",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -180,
        460
      ],
      "parameters": {
        "url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key={{ $vars.GEMINI_KEY }}",
        "method": "POST",
        "jsonBody": "={\n  \"contents\": [{\n    \"role\": \"user\",\n    \"parts\": [\n      {\n        \"fileData\": {\n          \"fileUri\": \"{{ $('Upload to Gemini').item.json.file.uri }}\",\n          \"mimeType\": \"{{ $('Upload to Gemini').item.json.file.mimeType }}\"\n        }\n      },\n      {\n        \"text\": \"You are a UGC ad planner. Analyze this vertical app-screen recording (<=12s) and create a facecam UGC ad structure.\\n\\nObserve the recording and infer: who uses this, what they want, where frustration occurs, why it matters.\\n\\nReturn strict JSON:\\n{\\n  ad_topic: \\\"Short description of what app/feature is being promoted (e.g. 'Hinge unlimited likes upgrade')\\\",\\n  icp_summary: \\\"One sentence describing the target user and their core frustration\\\",\\n  ad_structure: {\\n    hook_0_2s: \\\"Opening line that stops scroll (5-8 words max)\\\",\\n    problem_2_6s: \\\"One brief sentence about the problem (12-15 words max)\\\",\\n    solution_6_10s: \\\"Present the fix with product name and benefit (12-15 words max)\\\",\\n    cta_10_12s: \\\"Clear call-to-action (5-8 words max)\\\"\\n  },\\n  visual_details: \\\"Describe actor (age, gender, wardrobe), setting (location, lighting, background), and camera style (framing, movement)\\\",\\n  emotional_journey: \\\"Brief description of how emotions change: starting state (0-2s), middle escalation (2-6s), resolution (6-12s)\\\",\\n  duration: 4 or 8 or 12\\n}\\n\\nUse meta if provided: product_name, benefits[], tone.\\nDo NOT add extra fields. Keep all fields concise.\"\n      }\n    ]\n  }],\n  \"generationConfig\": {\n    \"temperature\": 0.7,\n    \"topK\": 40,\n    \"topP\": 0.95,\n    \"maxOutputTokens\": 8192\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "extract-plan",
      "name": "Extract & Parse",
      "type": "n8n-nodes-base.code",
      "position": [
        60,
        460
      ],
      "parameters": {
        "jsCode": "// Extract raw text from Gemini response\nconst rawText = $input.item.json.candidates[0].content.parts[0].text;\n\n// Remove markdown code blocks\nconst cleanJson = rawText.replace(/```json\\n?/g, '').replace(/\\n?```/g, '').trim();\n\n// Parse the JSON\nconst data = JSON.parse(cleanJson);\n\n// Return the structured data\nreturn {\n  json: {\n    ad_topic: data.ad_topic,\n    icp_summary: data.icp_summary,\n    ad_structure: data.ad_structure,\n    visual_details: data.visual_details,\n    emotional_journey: data.emotional_journey,\n    duration: data.duration || 8\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "sticky-section-sora",
      "name": "Section: Sora 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        280,
        680
      ],
      "parameters": {
        "width": 940,
        "height": 100,
        "content": "## \ud83c\udfa8 SORA 2 AI VIDEO GENERATION SECTION\nGenerates AI actor video based on Gemini's plan \u2192 UGC-style talking head video"
      },
      "typeVersion": 1,
      "notesBackgroundColor": 6
    },
    {
      "id": "build-sora-prompt",
      "name": "Build Sora Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        300,
        760
      ],
      "parameters": {
        "jsCode": "// Build Sora prompt following the structure:\n// [ICP/visual] filming a UGC ad about [APP CONTEXT]. They start by saying [hook], then explain [problem], present the solution: [solution], and end with [CTA]. [Emotional journey]. [Camera style]. Natural hand gestures and expressions.\n\nconst visual = $input.item.json.visual_details;\nconst emotions = $input.item.json.emotional_journey;\nconst ad = $input.item.json.ad_structure;\nconst topic = $input.item.json.ad_topic;\n\n// Extract camera style from visual_details (usually at the end)\nconst cameraStyle = visual.includes('camera') ? visual.split('.').find(s => s.toLowerCase().includes('camera'))?.trim() || 'Handheld selfie-style close-up' : 'Handheld selfie-style close-up';\n\n// Build the person description (without camera details)\nconst personDescription = visual.split('.').filter(s => !s.toLowerCase().includes('camera')).join('.').trim();\n\nconst soraPrompt = `${personDescription} filming a UGC ad about ${topic}. They start by saying: ${ad.hook_0_2s}. Then explain: ${ad.problem_2_6s}. Present the solution: ${ad.solution_6_10s}. And end with: ${ad.cta_10_12s}. ${emotions}. ${cameraStyle}. Natural hand gestures and authentic expressions throughout. Raw selfie video of ONLY the person talking - no text overlays, no app UI, no graphics, no split-screen. Just the speaker.`;\n\nreturn {\n  json: {\n    sora_prompt: soraPrompt,\n    duration: $input.item.json.duration\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "sora-submit",
      "name": "Sora 2 \u2013 Submit (fal.ai)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        520,
        760
      ],
      "parameters": {
        "url": "https://queue.fal.run/fal-ai/sora-2/text-to-video",
        "method": "POST",
        "jsonBody": "={\n  \"prompt\": \"{{ $json.sora_prompt }}\",\n  \"aspect_ratio\": \"9:16\",\n  \"resolution\": \"720p\",\n  \"duration\": {{ $json.duration }}\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Key {{ $vars.FAL_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "sora-status",
      "name": "Sora \u2013 Check Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        740,
        760
      ],
      "parameters": {
        "url": "=https://queue.fal.run/fal-ai/sora-2/requests/{{ $json.request_id }}/status",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Key {{ $vars.FAL_KEY }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "wait-20",
      "name": "Wait 20s",
      "type": "n8n-nodes-base.wait",
      "position": [
        960,
        860
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "if-sora-done",
      "name": "Sora Completed?",
      "type": "n8n-nodes-base.if",
      "position": [
        960,
        760
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ok",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "COMPLETED"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "sora-result",
      "name": "Sora \u2013 Get Result",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1180,
        760
      ],
      "parameters": {
        "url": "=https://queue.fal.run/fal-ai/sora-2/requests/{{ $('Sora 2 \u2013 Submit (fal.ai)').item.json.request_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Key {{ $vars.FAL_KEY }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "sticky-section-vbr",
      "name": "Section: VideoBGRemover",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1400,
        980
      ],
      "parameters": {
        "width": 1240,
        "height": 100,
        "content": "## \ud83c\udfac VIDEOBGREMOVER COMPOSITION SECTION\nRemoves AI actor background \u2192 Composites over screen recording \u2192 Mixes audio"
      },
      "typeVersion": 1,
      "notesBackgroundColor": 2
    },
    {
      "id": "vbr-create",
      "name": "VBR \u2013 Create Job (Foreground=Sora)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1400,
        1060
      ],
      "parameters": {
        "url": "https://api.videobgremover.com/api/v1/jobs",
        "method": "POST",
        "jsonBody": "={ \n  \"video_url\": \"{{ $json.video.url }}\" \n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "={{ $vars.VIDEOBGREMOVER_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "vbr-start",
      "name": "VBR \u2013 Start Composition",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1620,
        1060
      ],
      "parameters": {
        "url": "=https://api.videobgremover.com/api/v1/jobs/{{ $json.id }}/start",
        "method": "POST",
        "jsonBody": "={\n  \"background\": {\n    \"type\": \"composition\",\n    \"composition\": {\n      \"template\": \"ai_ugc_ad\",\n      \"background_type\": \"video\",\n      \"background_url\": \"{{ $('Merge Triggers').item.json.screenshot_video_url }}\",\n      \"background_audio_enabled\": true,\n      \"background_audio_volume\": 0.3,\n      \"export_format\": \"h264\",\n      \"export_preset\": \"medium\"\n    }\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "={{ $vars.VIDEOBGREMOVER_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "vbr-status",
      "name": "VBR \u2013 Check Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1840,
        1060
      ],
      "parameters": {
        "url": "=https://api.videobgremover.com/api/v1/jobs/{{ $('VBR \u2013 Create Job (Foreground=Sora)').item.json.id }}/status",
        "method": "GET",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "={{ $vars.VIDEOBGREMOVER_KEY }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "wait-20b",
      "name": "Wait 20s (VBR)",
      "type": "n8n-nodes-base.wait",
      "position": [
        2060,
        1160
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "if-vbr-done",
      "name": "VBR Completed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2060,
        1060
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "done",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "prepare-final",
      "name": "Prepare Final URL",
      "type": "n8n-nodes-base.set",
      "position": [
        2280,
        1060
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "final_url",
              "type": "string",
              "value": "={{ $json.processed_video_url }}"
            },
            {
              "name": "length_seconds",
              "type": "number",
              "value": "={{ $json.length_seconds ?? $json.meta?.duration }}"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "sticky-section-output",
      "name": "Section: Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2480,
        1280
      ],
      "parameters": {
        "width": 900,
        "height": 100,
        "content": "## \ud83d\udcbe OUTPUT SECTION\nDownload final video \u2192 Upload to Google Drive \u2192 Return response"
      },
      "typeVersion": 1,
      "notesBackgroundColor": 3
    },
    {
      "id": "download-final",
      "name": "Download Final Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2480,
        1360
      ],
      "parameters": {
        "url": "={{ $('Prepare Final URL').item.json.final_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "gdrive-upload",
      "name": "Upload to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2700,
        1360
      ],
      "parameters": {
        "name": "=ugc_final_{{ new Date().getTime() }}.mp4",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {
          "simplifyOutput": true
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "root"
        },
        "operation": "upload",
        "binaryPropertyName": "data"
      },
      "typeVersion": 3
    },
    {
      "id": "build-drive-resp",
      "name": "Build Drive Response",
      "type": "n8n-nodes-base.set",
      "position": [
        2920,
        1360
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "response",
              "type": "object",
              "value": "={{ { url: $json.webViewLink, drive_file_id: $json.id, source: 'google_drive' } }}"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "if-webhook",
      "name": "From Webhook?",
      "type": "n8n-nodes-base.if",
      "position": [
        3140,
        1460
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "is-webhook",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Merge Triggers').item.json.source }}",
              "rightValue": "webhook"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "respond",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        3360,
        1360
      ],
      "parameters": {
        "responseBody": "={{ $json.response ?? $json }}",
        "responseCode": 200
      },
      "typeVersion": 1.1
    },
    {
      "id": "manual-complete",
      "name": "Manual Test Complete",
      "type": "n8n-nodes-base.set",
      "position": [
        3360,
        1560
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "name": "final_result",
              "type": "object",
              "value": "={{ $json.response ?? $json }}"
            }
          ]
        }
      },
      "typeVersion": 3.3
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Wait 20s": {
      "main": [
        [
          {
            "node": "Sora \u2013 Check Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "From Webhook?": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Manual Test Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Sample Input (Edit Here)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Triggers": {
      "main": [
        [
          {
            "node": "Download Screenshot Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VBR Completed?": {
      "main": [
        [
          {
            "node": "Prepare Final URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 20s (VBR)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 20s (VBR)": {
      "main": [
        [
          {
            "node": "VBR \u2013 Check Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract & Parse": {
      "main": [
        [
          {
            "node": "Build Sora Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sora Completed?": {
      "main": [
        [
          {
            "node": "Sora \u2013 Get Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 20s",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Extract Webhook Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Gemini": {
      "main": [
        [
          {
            "node": "Wait for Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Sora Prompt": {
      "main": [
        [
          {
            "node": "Sora 2 \u2013 Submit (fal.ai)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Final URL": {
      "main": [
        [
          {
            "node": "Download Final Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sora \u2013 Get Result": {
      "main": [
        [
          {
            "node": "VBR \u2013 Create Job (Foreground=Sora)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Processing": {
      "main": [
        [
          {
            "node": "Gemini \u2013 Analyze & Plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Drive Response": {
      "main": [
        [
          {
            "node": "From Webhook?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Final Video": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Webhook Data": {
      "main": [
        [
          {
            "node": "Merge Triggers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VBR \u2013 Check Status": {
      "main": [
        [
          {
            "node": "VBR Completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sora \u2013 Check Status": {
      "main": [
        [
          {
            "node": "Sora Completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Google Drive": {
      "main": [
        [
          {
            "node": "Build Drive Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample Input (Edit Here)": {
      "main": [
        [
          {
            "node": "Merge Triggers",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Download Screenshot Video": {
      "main": [
        [
          {
            "node": "Upload to Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini \u2013 Analyze & Plan": {
      "main": [
        [
          {
            "node": "Extract & Parse",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VBR \u2013 Start Composition": {
      "main": [
        [
          {
            "node": "VBR \u2013 Check Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sora 2 \u2013 Submit (fal.ai)": {
      "main": [
        [
          {
            "node": "Sora \u2013 Check Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VBR \u2013 Create Job (Foreground=Sora)": {
      "main": [
        [
          {
            "node": "VBR \u2013 Start Composition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}