{
  "nodes": [
    {
      "id": "412eca65-b548-4e92-a4d3-19e78c3aafa3",
      "name": "PUBLISHER NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        800
      ],
      "parameters": {
        "width": 468,
        "height": 180,
        "content": "## Flow 2: Approval & auto-publish\n\nEvery 15 minutes the workflow reads VideosTable, filters Status=approved, downloads the file and schedules it to the enabled platforms via Upload-Post. After a successful schedule, it updates the row to Status=scheduled."
      },
      "typeVersion": 1
    },
    {
      "id": "4b1448f5-3640-42d3-8453-f15be7f7d8e5",
      "name": "SAVE NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2672,
        144
      ],
      "parameters": {
        "width": 420,
        "height": 580,
        "content": "## Flow 1: Save drafts\n\nFormats the AI output plus schedule info and appends a new row in VideosTable with Status=draft. Change Status to approved when you want Flow 2 to publish it."
      },
      "typeVersion": 1
    },
    {
      "id": "7649ab2f-6975-466a-87d3-b8ba17bcd84b",
      "name": "AI NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        144
      ],
      "parameters": {
        "width": 712,
        "height": 584,
        "content": "## Flow 1: AI analysis & copy\n\nEach video is downloaded, analyzed with Gemini Flash, then Gemini Pro generates unique TikTok/Instagram/YouTube titles, descriptions and hashtags. The parser enforces valid JSON before saving."
      },
      "typeVersion": 1
    },
    {
      "id": "11ab6690-4752-41a5-aef8-f86944f18538",
      "name": "FETCH NOTE",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        208
      ],
      "parameters": {
        "width": 460,
        "height": 516,
        "content": "## Flow 1: Campaign intake & Drive fetch\n\nThe form collects campaign inputs. Google Drive lists files in the folder, then the filter keeps only video files before building the posting calendar."
      },
      "typeVersion": 1
    },
    {
      "id": "51f64105-513c-4ec7-a018-a8861f9b5f74",
      "name": "WORKFLOW INFO",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        -160
      ],
      "parameters": {
        "color": 4,
        "width": 560,
        "height": 456,
        "content": "## How it works\nThis template has two flows. Flow 1 starts with a Form, reads all videos from a Google Drive folder, filters to keep video files, and builds a posting calendar using the start date, cadence and publish hour. Each file is downloaded, analyzed with Gemini, and used to generate platform-specific titles, descriptions and hashtags for TikTok, Instagram Reels and YouTube Shorts. The workflow then formats the results and appends them to a Google Sheet as drafts.\n\n## Setup steps\n1) Prepare a Google Drive folder with your videos.\n2) Copy this template sheet in your drive: https://docs.google.com/spreadsheets/d/1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw/edit?usp=sharing\n3) Connect Google Drive, Google Sheets and Google Gemini credentials.\n4) Run the Form and provide the Drive Folder ID, Upload-Post profile username, platforms, timezone, start date, cadence, publish hour, and Sheet ID.\n5) Activate the workflow. Set any draft row Status to approved; Flow 2 checks every 15 minutes, schedules it via Upload-Post, and updates the row to scheduled."
      },
      "typeVersion": 1
    },
    {
      "id": "4bbab1e5-df87-450f-bc5d-daa110838cf4",
      "name": "Start Campaign",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        368,
        384
      ],
      "parameters": {
        "options": {},
        "formTitle": "Social Video Autopilot",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Google Drive Folder ID",
              "placeholder": "1jojIQ8TUjC_O-HfuhcjZfJSGSOl-h0Om",
              "requiredField": true
            },
            {
              "fieldLabel": "Profile Username (Upload-Post)",
              "placeholder": "mi_perfil_whitelabel",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Platforms",
              "fieldOptions": {
                "values": [
                  {
                    "option": "TikTok, Instagram, YouTube"
                  },
                  {
                    "option": "TikTok, Instagram"
                  },
                  {
                    "option": "TikTok, YouTube"
                  },
                  {
                    "option": "Instagram, YouTube"
                  },
                  {
                    "option": "TikTok"
                  },
                  {
                    "option": "Instagram"
                  },
                  {
                    "option": "YouTube"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Start Date (YYYY-MM-DD)",
              "placeholder": "2025-12-09",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Cadence",
              "fieldOptions": {
                "values": [
                  {
                    "option": "1_daily"
                  },
                  {
                    "option": "5_per_week"
                  },
                  {
                    "option": "3_per_week"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Publish Hour (HH:MM)",
              "placeholder": "09:00",
              "requiredField": true
            },
            {
              "fieldLabel": "Google Sheet ID (for tracking)",
              "placeholder": "1lg_YOUR_AWS_SECRET_KEY_HERE",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Batch process and schedule your videos across TikTok, Instagram & YouTube with AI-powered optimization"
      },
      "typeVersion": 2.2
    },
    {
      "id": "4b849329-0771-4a37-9491-0cd2315fb10f",
      "name": "Campaign Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        528,
        384
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "drive_folder_id",
              "name": "drive_folder_id",
              "type": "string",
              "value": "={{ $json['Google Drive Folder ID'] }}"
            },
            {
              "id": "profile_username",
              "name": "profile_username",
              "type": "string",
              "value": "={{ $json['Profile Username (Upload-Post)'] }}"
            },
            {
              "id": "platforms_raw",
              "name": "platforms_raw",
              "type": "string",
              "value": "={{ $json['Platforms'] }}"
            },
            {
              "id": "timezone",
              "name": "timezone",
              "type": "string",
              "value": "={{ $json['Timezone'] }}"
            },
            {
              "id": "start_date",
              "name": "start_date",
              "type": "string",
              "value": "={{ $json['Start Date (YYYY-MM-DD)'] }}"
            },
            {
              "id": "cadence",
              "name": "cadence",
              "type": "string",
              "value": "={{ $json['Cadence'] }}"
            },
            {
              "id": "publish_hour",
              "name": "publish_hour",
              "type": "string",
              "value": "={{ $json['Publish Hour (HH:MM)'] }}"
            },
            {
              "id": "sheet_id",
              "name": "sheet_id",
              "type": "string",
              "value": "={{ $json['Google Sheet ID (for tracking)'] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8d8a2adc-b83f-4e09-9fd4-520ad2cf6c00",
      "name": "Fetch Videos from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        800,
        384
      ],
      "parameters": {
        "filter": {},
        "options": {},
        "resource": "fileFolder",
        "returnAll": true,
        "queryString": "='{{ $json.drive_folder_id }}' in parents and trashed = false",
        "searchMethod": "query"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "bcc2ac88-8e63-4328-a060-575818f0f487",
      "name": "Video Files Only",
      "type": "n8n-nodes-base.filter",
      "position": [
        1040,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "video-mp4",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.name }}",
              "rightValue": ".mp4"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "dfc0a1f7-5c8c-4b2a-964a-c546d7f008af",
      "name": "Build Schedule Calendar",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        384
      ],
      "parameters": {
        "jsCode": "// Get configuration from Campaign Settings node\nconst config = $('Campaign Settings').first().json;\n\nconst items = $input.all();\nconst enrichedItems = [];\n\nconst startDate = new Date(config.start_date);\nlet cadenceDays = [];\n\nswitch(config.cadence) {\n  case '1_daily':\n    for(let i = 0; i < items.length; i++) cadenceDays.push(i);\n    break;\n  case '5_per_week':\n    let dayCounter5 = 0;\n    let weekDay5 = startDate.getDay();\n    for(let i = 0; i < items.length; i++) {\n      while(weekDay5 === 0 || weekDay5 === 6) {\n        dayCounter5++;\n        weekDay5 = (startDate.getDay() + dayCounter5) % 7;\n      }\n      cadenceDays.push(dayCounter5);\n      dayCounter5++;\n      weekDay5 = (startDate.getDay() + dayCounter5) % 7;\n    }\n    break;\n  case '3_per_week':\n    let dayCounter3 = 0;\n    for(let i = 0; i < items.length; i++) {\n      while(![1, 3, 5].includes((startDate.getDay() + dayCounter3) % 7)) dayCounter3++;\n      cadenceDays.push(dayCounter3);\n      dayCounter3++;\n    }\n    break;\n}\n\nfor (let i = 0; i < items.length; i++) {\n  const item = items[i];\n  const scheduleDaysOffset = cadenceDays[i] ?? i;\n\n  const scheduleDate = new Date(startDate);\n  scheduleDate.setDate(scheduleDate.getDate() + scheduleDaysOffset);\n  const scheduleDateStr = scheduleDate.toISOString().split('T')[0];\n  const scheduleDateTime = `${scheduleDateStr}T${config.publish_hour}:00`;\n\n  enrichedItems.push({\n    json: {\n      ...item.json,\n      video_index: i + 1,\n      total_videos: items.length,\n      config: {\n        drive_folder_id: config.drive_folder_id,\n        profile_username: config.profile_username,\n        platforms_raw: config.platforms_raw,\n        timezone: config.timezone,\n        start_date: config.start_date,\n        cadence: config.cadence,\n        publish_hour: config.publish_hour,\n        sheet_id: config.sheet_id\n      },\n      schedule: {\n        date: scheduleDateStr,\n        datetime: scheduleDateTime\n      }\n    }\n  });\n}\n\nreturn enrichedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "7f2ae67b-4dfc-4df8-a678-489a60fd260b",
      "name": "Process Each Video",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1520,
        384
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "e5844599-a45e-41df-bc14-c9ee5314fd48",
      "name": "Download from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1760,
        368
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "f69c1659-1e4e-4986-b870-5fda82c87f22",
      "name": "AI Video Analysis",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        2000,
        368
      ],
      "parameters": {
        "text": "=Analyze the attached video and provide:\n\n1) DETAILED DESCRIPTION: A thorough, faithful narrative of what is seen and heard.\n2) FULL TRANSCRIPTION of the audio.\n3) KEYWORDS: 10-15 SEO-relevant keywords.\n4) KEY MOMENTS with approximate timestamps.\n5) CONTENT TYPE (tutorial, review, vlog, entertainment, educational, etc.).\n6) TONE (serious, funny, professional, casual, etc.).\n7) DETECTED PRIMARY LANGUAGE of the spoken content.\n\nImportant output rule:\nStart your answer with a single line exactly like:\nDetected language: <English|Spanish|Other>\n\nThen write the rest of the analysis in English.\nThis analysis will be used to generate social titles and descriptions in the same detected language.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "options": {},
        "resource": "video",
        "inputType": "binary",
        "operation": "analyze"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ecb3ab65-a67e-47cc-8707-8d45af3608f4",
      "name": "Generate Social Copy",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2192,
        368
      ],
      "parameters": {
        "text": "=[VIDEO ANALYSIS]\n{{ $json.content.parts[0].text }}\n\n[FILE INFO]\nName: {{ $('Download from Drive').item.json.name }}\n\n[CAMPAIGN SETTINGS]\nPlatforms: {{ $json.config.platforms_raw }}\n\n[GOAL]\nGenerate platform-specific titles, descriptions and hashtags for the selected platforms.\n\nLanguage rule:\n- Use the SAME language as the video.\n- The analysis starts with \"Detected language:\". Follow it strictly.\n- If it says English, write everything in English.\n- If it says Spanish, write everything in Spanish (Spain).\n\nOutput rules:\n- Each platform must be UNIQUE (do not copy/paste across platforms).\n- Return ONLY valid JSON matching this exact structure:\n{\n  \"tiktok\": {\n    \"title\": \"Optimized TikTok title (max 150 chars, strong hook)\",\n    \"description\": \"Short, engaging TikTok description\",\n    \"hashtags\": \"#hashtag1 #hashtag2 ...\"\n  },\n  \"instagram\": {\n    \"title\": \"Optimized Instagram Reels caption\",\n    \"description\": \"Value-driven description with CTA\",\n    \"hashtags\": \"#hashtag1 #hashtag2 ...\"\n  },\n  \"youtube\": {\n    \"title\": \"SEO-first YouTube Shorts title\",\n    \"description\": \"YouTube description with natural keywords and CTA\",\n    \"tags\": \"tag1, tag2, tag3\"\n  },\n  \"summary\": \"Very short internal summary\"\n}",
        "options": {
          "systemMessage": "You are a social media content marketing expert specialized in TikTok, Instagram Reels and YouTube Shorts.\n\nYour goals:\n- Maximize CTR\n- Improve retention\n- Increase engagement\n- Improve organic reach\n- Boost SEO on YouTube\n\nPlatform guidelines:\n\nTikTok:\n- Short, punchy titles with an emotional hook\n- Use emojis sparingly if the language/style fits\n- 10-15 hashtags mixing trending + niche\n- Casual, native tone\n\nInstagram Reels:\n- Strong first line hook\n- Slightly longer caption with light storytelling\n- Clear CTA (save, share, comment)\n- 20-30 hashtags mixing large/medium/niche\n\nYouTube Shorts:\n- SEO-first title, main keyword near the start\n- Ideal 50-60 characters\n- Description with natural keywords and CTA\n- 10-15 tags comma-separated\n\nCritical rules:\n- Each platform content must be DIFFERENT\n- Do not invent facts not present in the video\n- Output ONLY valid JSON, no markdown\n- Use the language detected in the analysis line: \"Detected language:\""
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "retryOnFail": true,
      "typeVersion": 3
    },
    {
      "id": "a4f34bd2-8f50-41e7-8db8-2681c1add61b",
      "name": "Gemini Pro",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        2192,
        592
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "47d7f3de-a93d-47cf-9dbe-1b9070ba8f71",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        2336,
        576
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"tiktok\": {\n    \"title\": \"Optimized TikTok title (max 150 chars, strong hook)\",\n    \"description\": \"Short, engaging TikTok description\",\n    \"hashtags\": \"#hashtag1 #hashtag2 #hashtag3\"\n  },\n  \"instagram\": {\n    \"title\": \"Optimized Instagram Reels caption\",\n    \"description\": \"Value-driven description with CTA\",\n    \"hashtags\": \"#hashtag1 #hashtag2\"\n  },\n  \"youtube\": {\n    \"title\": \"SEO-first YouTube Shorts title\",\n    \"description\": \"YouTube description with natural keywords and CTA\",\n    \"tags\": \"tag1, tag2, tag3\"\n  },\n  \"summary\": \"Very short internal summary\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "0def5138-4385-45b0-96e0-cb334d208c8a",
      "name": "Format Content Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2496,
        368
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const calendarInfo = $('Build Schedule Calendar').item?.json ?? {};\n  const downloadInfo = $('Download from Drive').item?.json ?? {};\n\n  const makeBaseRow = () => ({\n    \"Video ID\": calendarInfo?.id || downloadInfo?.id || 'unknown',\n    \"Video Name\": downloadInfo?.name || calendarInfo?.name || 'unknown',\n    \"Index\": String(calendarInfo?.video_index ?? ''),\n    \"Status\": 'draft',\n    \"Schedule Date\": calendarInfo?.schedule?.date || '',\n    \"Schedule DateTime\": calendarInfo?.schedule?.datetime || '',\n\n    \"TikTok Title\": '',\n    \"TikTok Description\": '',\n    \"TikTok Hashtags\": '',\n\n    \"Instagram Title\": '',\n    \"Instagram Description\": '',\n    \"Instagram Hashtags\": '',\n\n    \"YouTube Title\": '',\n    \"YouTube Description\": '',\n    \"YouTube Tags\": '',\n\n    \"Summary\": '',\n    \"Profile\": calendarInfo?.config?.profile_username || '',\n    \"Platforms\": calendarInfo?.config?.platforms_raw || '',\n    \"Created At\": new Date().toISOString()\n  });\n\n  try {\n    const rawOutput = item.json.output ?? item.json.text ?? item.json;\n\n    let content;\n    if (rawOutput && typeof rawOutput === 'object' && !Array.isArray(rawOutput)) {\n      content = rawOutput;\n    } else {\n      let cleanedOutput = String(rawOutput)\n        .replace(/```json\\n?/g, '')\n        .replace(/```\\n?/g, '')\n        .trim();\n\n      const firstBrace = cleanedOutput.indexOf('{');\n      const lastBrace = cleanedOutput.lastIndexOf('}');\n      if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {\n        cleanedOutput = cleanedOutput.slice(firstBrace, lastBrace + 1);\n      }\n\n      content = JSON.parse(cleanedOutput);\n    }\n\n    const row = makeBaseRow();\n\n    row[\"TikTok Title\"] = content.tiktok?.title || '';\n    row[\"TikTok Description\"] = content.tiktok?.description || '';\n    row[\"TikTok Hashtags\"] = content.tiktok?.hashtags || '';\n\n    row[\"Instagram Title\"] = content.instagram?.title || '';\n    row[\"Instagram Description\"] = content.instagram?.description || '';\n    row[\"Instagram Hashtags\"] = content.instagram?.hashtags || '';\n\n    row[\"YouTube Title\"] = content.youtube?.title || '';\n    row[\"YouTube Description\"] = content.youtube?.description || '';\n    row[\"YouTube Tags\"] = content.youtube?.tags || '';\n\n    row[\"Summary\"] = content.summary || '';\n\n    results.push({ json: row });\n  } catch (error) {\n    const row = makeBaseRow();\n    row[\"Status\"] = 'error';\n    row[\"Summary\"] = `PARSE_ERROR: ${error.message}`;\n    results.push({ json: row });\n  }\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "f58e58d3-88a4-4f92-9d06-c9bc829ef206",
      "name": "Save Draft to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2736,
        368
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "Video ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Index",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Index",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Schedule Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule DateTime",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Schedule DateTime",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TikTok Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Hashtags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Instagram Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Tags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "YouTube Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Profile",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Platforms",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Platforms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "cellFormat": "USER_ENTERED"
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Campaign Settings').first().json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cb08748d-d99a-4743-a19a-9d94bd51c3ba",
      "name": "Publisher Config",
      "type": "n8n-nodes-base.set",
      "position": [
        560,
        1040
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sheet_id_2",
              "name": "sheet_id",
              "type": "string",
              "value": "1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw"
            },
            {
              "id": "drive_id_2",
              "name": "drive_folder_id",
              "type": "string",
              "value": "1LB5YDDVnt0vLeC9qN1jc0UgLjMsnCRYn"
            },
            {
              "id": "profile_2",
              "name": "profile_username",
              "type": "string",
              "value": "automated-tests"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8271992f-6325-401d-b3ba-cfcb5ee43121",
      "name": "Load Content Queue",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        800,
        1040
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2d6f6002-bb19-47c1-9bd3-4b023a5f42fa",
      "name": "Only Approved",
      "type": "n8n-nodes-base.filter",
      "position": [
        1040,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "status-approved",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Status }}",
              "rightValue": "approved"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "537be859-9d07-4492-835a-0958cc1702f8",
      "name": "Process Queue",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1280,
        1040
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "fff9142c-bca7-4bb2-b44d-64924ac9b695",
      "name": "Get Video File",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1520,
        1040
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json['Video ID'] }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "8fcd5a01-cce9-4657-93d6-65d8b54efb6b",
      "name": "Build Upload Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        1760,
        1040
      ],
      "parameters": {
        "jsCode": "const item = $input.first();\nconst sheetData = item.json;\nconst config = $('Publisher Config').first().json;\n\nconst platforms = (sheetData.Platforms || 'tiktok,instagram,youtube').toLowerCase();\n\nreturn {\n  json: {\n    video_id: sheetData['Video ID'],\n    video_name: sheetData['Video Name'],\n    row_index: sheetData.row_number,\n\n    has_tiktok: platforms.includes('tiktok'),\n    has_instagram: platforms.includes('instagram'),\n    has_youtube: platforms.includes('youtube'),\n\n    tiktok_content: [sheetData['TikTok Title'], sheetData['TikTok Description'], sheetData['TikTok Hashtags']].filter(Boolean).join('\\n\\n'),\n    instagram_content: [sheetData['Instagram Title'], sheetData['Instagram Description'], sheetData['Instagram Hashtags']].filter(Boolean).join('\\n\\n'),\n\n    youtube_title: sheetData['YouTube Title'] || '',\n    youtube_description: sheetData['YouTube Description'] || '',\n\n    schedule_datetime: sheetData['Schedule DateTime'] || '',\n\n    profile_username: sheetData.Profile || config.profile_username,\n    sheet_id: config.sheet_id\n  },\n  binary: item.binary\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "32780527-174c-4926-b760-911556755750",
      "name": "TikTok Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "tiktok-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_tiktok }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "05d73820-46e5-4dff-b6e4-1fe20482639e",
      "name": "Instagram Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ig-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_instagram }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8ba87910-da6f-4c0c-8d92-f0c4c30dee24",
      "name": "YouTube Enabled?",
      "type": "n8n-nodes-base.if",
      "position": [
        2000,
        1200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "yt-check",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.has_youtube }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "81515599-ec20-4e67-8039-e57bbd1d0470",
      "name": "Schedule TikTok",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        864
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.tiktok_content }}",
        "video": "data",
        "platform": [
          "tiktok"
        ],
        "operation": "uploadVideo",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "="
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "94ab80ea-33cf-4b4e-8b3f-30db536bd2e9",
      "name": "Schedule Instagram",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        1024
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.instagram_content }}",
        "video": "data",
        "platform": [
          "instagram"
        ],
        "operation": "uploadVideo",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "=",
        "instagramMediaType": "REELS"
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fe8db90a-f902-4c2e-b600-5b8583580b76",
      "name": "Schedule YouTube",
      "type": "n8n-nodes-upload-post.uploadPost",
      "position": [
        2240,
        1184
      ],
      "parameters": {
        "user": "automated-tests",
        "title": "={{ $json.youtube_title }}",
        "video": "data",
        "platform": [
          "youtube"
        ],
        "operation": "uploadVideo",
        "youtubeTitle": "={{ $json.youtube_title }}",
        "scheduledDate": "={{ $json.schedule_datetime }}",
        "waitForCompletion": "="
      },
      "credentials": {
        "uploadPostApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3cd7eafd-2a62-4fa4-9599-99012580bb04",
      "name": "Mark as Scheduled",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2576,
        1024
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "scheduled",
            "Video ID": "={{ $('Get Video File').item.json[\"Video ID\"] }}"
          },
          "schema": [
            {
              "id": "Video ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Video ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video Name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Video Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Index",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Index",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule Date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Schedule Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Schedule DateTime",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Schedule DateTime",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "TikTok Hashtags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "TikTok Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Instagram Hashtags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Instagram Hashtags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Title",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Description",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "YouTube Tags",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "YouTube Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Summary",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Profile",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Profile",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Platforms",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Platforms",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Video ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1275860196,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1cegJHxj7Kx4Tg8gMr3uixpzToNc62VEvuuz37iFvnRw/edit#gid=1275860196",
          "cachedResultName": "VideosTable"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Publisher Config').item.json.sheet_id }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cf513981-645e-4221-838f-68f75e9da08c",
      "name": "15 mins check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        304,
        1040
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "eb774e71-1f51-4da3-931a-0ceffd6d502d",
      "name": "FETCH NOTE1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        736
      ],
      "parameters": {
        "width": 460,
        "height": 628,
        "content": "## Upload to social networks\n\nIs configured Tiktok Instagram and Youtube, but you can add all : TikTok, Instagram, LinkedIn, YouTube, Facebook, X, Threads, Pinterest, Reddit, Bluesky"
      },
      "typeVersion": 1
    },
    {
      "id": "9182d207-8e6c-47e5-b092-c86a06b5b8e2",
      "name": "SAVE NOTE1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        736
      ],
      "parameters": {
        "width": 420,
        "height": 628,
        "content": "## Flow 1: Save Schedule\n\nFormats the AI output plus schedule info and appends a new row in VideosTable with Status=approved. Change Status to scheduled when you want Flow 2 to publish it."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Gemini Pro": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Social Copy",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "15 mins check": {
      "main": [
        [
          {
            "node": "Publisher Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only Approved": {
      "main": [
        [
          {
            "node": "Process Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Queue": {
      "main": [
        [],
        [
          {
            "node": "Get Video File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Video File": {
      "main": [
        [
          {
            "node": "Build Upload Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Campaign": {
      "main": [
        [
          {
            "node": "Campaign Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule TikTok": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TikTok Enabled?": {
      "main": [
        [
          {
            "node": "Schedule TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publisher Config": {
      "main": [
        [
          {
            "node": "Load Content Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule YouTube": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video Files Only": {
      "main": [
        [
          {
            "node": "Build Schedule Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YouTube Enabled?": {
      "main": [
        [
          {
            "node": "Schedule YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Video Analysis": {
      "main": [
        [
          {
            "node": "Generate Social Copy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Campaign Settings": {
      "main": [
        [
          {
            "node": "Fetch Videos from Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Scheduled": {
      "main": [
        [
          {
            "node": "Process Queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Instagram Enabled?": {
      "main": [
        [
          {
            "node": "Schedule Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Content Queue": {
      "main": [
        [
          {
            "node": "Only Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Each Video": {
      "main": [
        [],
        [
          {
            "node": "Download from Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Instagram": {
      "main": [
        [
          {
            "node": "Mark as Scheduled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download from Drive": {
      "main": [
        [
          {
            "node": "AI Video Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Content Data": {
      "main": [
        [
          {
            "node": "Save Draft to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Draft to Sheet": {
      "main": [
        [
          {
            "node": "Process Each Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Upload Payload": {
      "main": [
        [
          {
            "node": "TikTok Enabled?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Instagram Enabled?",
            "type": "main",
            "index": 0
          },
          {
            "node": "YouTube Enabled?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Social Copy": {
      "main": [
        [
          {
            "node": "Format Content Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Schedule Calendar": {
      "main": [
        [
          {
            "node": "Process Each Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Videos from Drive": {
      "main": [
        [
          {
            "node": "Video Files Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Generate Social Copy",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}