{
  "name": "TagDrop Content Automation",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tagdrop-start",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        200,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse input parameters\nconst input = $json.body || $json;\nconst videoFolder = input.videoFolder || '';\nconst startDate = input.startDate || '';\nconst platforms = input.platforms || [];\nconst driveUrl = input.driveUrl || '';\n\n// Validate inputs\nif (!startDate) {\n  throw new Error('Start date is required');\n}\nif (platforms.length === 0) {\n  throw new Error('At least one platform must be selected');\n}\nif (!videoFolder && !driveUrl) {\n  throw new Error('Either video folder or Google Drive URL is required');\n}\n\n// Mock video files for demo (replace with actual file reading)\nconst mockFiles = [\n  'dark-forest-figure-emerges-horror-dread.mp4',\n  'cafe-woman-'taylor-swift'-reveal-pop-whimsy.mp4',\n  'beach-dog-running-comedy-funny.mp4',\n  'space-station-floating-scifi-wonder.mp4'\n];\n\nconst files = mockFiles;\nconst results = [];\n\n// Parse each filename\nfor (const file of files) {\n  const parsed = parseFilename(file);\n  if (parsed) {\n    results.push({\n      filename: file,\n      ...parsed,\n      startDate,\n      platforms\n    });\n  }\n}\n\nfunction parseFilename(fname) {\n  const base = fname.replace(/\\.mp4$/i, '');\n  const parts = base.split('-');\n  \n  if (parts.length < 5) {\n    return null;\n  }\n  \n  return {\n    location: parts[0],\n    subject: parts[1], \n    action: parts[2],\n    genre: parts[3],\n    vibe: parts[4],\n    raw: base,\n    hasReveal: base.toLowerCase().includes('reveal'),\n    hasPopCulture: base.includes(\"'\"),\n    hasFunny: base.toLowerCase().includes('funny')\n  };\n}\n\nreturn results.map(item => ({ json: item }));"
      },
      "id": "parse-inputs",
      "name": "Parse Inputs & Files",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        300
      ]
    },
    {
      "parameters": {
        "model": "gpt-4",
        "messages": {
          "messageType": "multipleMessages",
          "messages": [
            {
              "role": "system",
              "message": "You are a social media copywriter with a quiet, strange, lightly funny or magical voice. Describe a fleeting cinematic moment without spoilers."
            },
            {
              "role": "user",
              "message": "=Metadata:\n- Location: {{ $json.location }}\n- Subject: {{ $json.subject }}\n- Action: {{ $json.action }}\n- Genre: {{ $json.genre }}\n- Vibe: {{ $json.vibe }}\n\n{{ $json.hasReveal ? \"Note: filename contains 'Reveal'. Tease without revealing.\\n\" : \"\" }}{{ $json.hasPopCulture ? \"Note: single quotes mark pop-culture references. Include subtly.\\n\" : \"\" }}{{ $json.hasFunny ? \"Note: filename contains 'funny'. Add a gentle pun.\\n\" : \"\" }}\nInstructions:\n1. TITLE (3\u20136 words, under 60 chars, no hashtags, no spoilers)\n2. CAPTION (1 sentence, rich in tone)\n3. DESCRIPTION (1\u20132 sentences, deepen intrigue)\n4. TAGS (5 lowercase, comma-separated keywords)\n\nStyle:\n- Avoid clickbait, CTAs, summaries\n- Match genre & vibe: dread\u2192horror, wonder\u2192surreal, whimsy\u2192cute\n- No repeated key phrases across posts\n\nFormat your response exactly as:\nTITLE: [your title]\nCAPTION: [your caption]\nDESCRIPTION: [your description]\nTAGS: [your tags]"
            }
          ]
        },
        "options": {
          "temperature": 0.8
        }
      },
      "id": "generate-content",
      "name": "Generate Content (GPT-4)",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1,
      "position": [
        600,
        300
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse GPT-4 response\nconst response = $json.message?.content || $json.response || '';\nconst lines = response.split('\\n');\n\nconst content = {\n  title: '',\n  caption: '', \n  description: '',\n  tags: ''\n};\n\nfor (const line of lines) {\n  const trimmed = line.trim();\n  if (trimmed.startsWith('TITLE:')) {\n    content.title = trimmed.replace('TITLE:', '').trim();\n  } else if (trimmed.startsWith('CAPTION:')) {\n    content.caption = trimmed.replace('CAPTION:', '').trim();\n  } else if (trimmed.startsWith('DESCRIPTION:')) {\n    content.description = trimmed.replace('DESCRIPTION:', '').trim();\n  } else if (trimmed.startsWith('TAGS:')) {\n    content.tags = trimmed.replace('TAGS:', '').trim();\n  }\n}\n\n// Calculate next weekday for scheduling\nconst startDate = new Date($('parse-inputs').item.json.startDate);\nconst currentIndex = $itemIndex;\n\n// Add days, skipping weekends\nlet scheduleDate = new Date(startDate);\nlet daysAdded = 0;\nwhile (daysAdded < currentIndex) {\n  scheduleDate.setDate(scheduleDate.getDate() + 1);\n  // Skip weekends (Saturday = 6, Sunday = 0)\n  if (scheduleDate.getDay() !== 0 && scheduleDate.getDay() !== 6) {\n    daysAdded++;\n  }\n}\n\nreturn {\n  json: {\n    filename: $json.filename,\n    location: $json.location,\n    subject: $json.subject,\n    action: $json.action,\n    genre: $json.genre,\n    vibe: $json.vibe,\n    platforms: $json.platforms,\n    scheduleDate: scheduleDate.toISOString().split('T')[0],\n    scheduleTime: '09:00',\n    ...content\n  }\n};"
      },
      "id": "parse-content",
      "name": "Parse Content & Schedule",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        800,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://app.metricool.com/api/v2/media/videos",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Mc-Auth",
              "value": "={{ $credentials.metricoolApi.token }}"
            }
          ]
        },
        "sendBody": true,
        "bodyContentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "file",
              "inputDataFieldName": "video"
            }
          ]
        },
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 5,
            "waitBetweenTries": 1000
          }
        }
      },
      "id": "upload-video",
      "name": "Upload Video to Metricool",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1000,
        300
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fieldsToSplitOut": "platforms",
        "options": {}
      },
      "id": "split-platforms",
      "name": "Split by Platform",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1200,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "instagram-condition",
              "leftValue": "={{ $json.platforms }}",
              "rightValue": "instagram",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true
      },
      "id": "instagram-check",
      "name": "Instagram Check",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1400,
        200
      ]
    },
    {
      "parameters": {
        "url": "https://app.metricool.com/api/v2/scheduler/posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Mc-Auth",
              "value": "={{ $credentials.metricoolApi.token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyContentType": "json",
        "jsonBody": "={\n  \"network\": \"instagram\",\n  \"title\": \"{{ $json.title }}\",\n  \"content\": \"{{ $json.caption }}\\n\\n{{ $json.description }}\", \n  \"media_url\": \"{{ $('upload-video').item.json.data.url }}\",\n  \"tags\": {{ $json.tags.split(',').map(tag => tag.trim()) }},\n  \"publish_at\": \"{{ $json.scheduleDate }}T{{ $json.scheduleTime }}:00Z\",\n  \"post_type\": \"reel\"\n}",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetweenTries": 2000
          }
        }
      },
      "id": "schedule-instagram",
      "name": "Schedule Instagram Post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1600,
        150
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "tiktok-condition",
              "leftValue": "={{ $json.platforms }}",
              "rightValue": "tiktok",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true
      },
      "id": "tiktok-check",
      "name": "TikTok Check",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1400,
        350
      ]
    },
    {
      "parameters": {
        "url": "https://app.metricool.com/api/v2/scheduler/posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Mc-Auth",
              "value": "={{ $credentials.metricoolApi.token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyContentType": "json",
        "jsonBody": "={\n  \"network\": \"tiktok\",\n  \"title\": \"{{ $json.title }}\",\n  \"content\": \"{{ $json.caption }} #{{ $json.tags.split(',').join(' #') }}\",\n  \"media_url\": \"{{ $('upload-video').item.json.data.url }}\",\n  \"publish_at\": \"{{ $json.scheduleDate }}T{{ $json.scheduleTime }}:00Z\",\n  \"privacy\": \"Public\",\n  \"allow_duet\": \"Yes\"\n}",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetweenTries": 2000
          }
        }
      },
      "id": "schedule-tiktok",
      "name": "Schedule TikTok Post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1600,
        300
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "youtube-condition",
              "leftValue": "={{ $json.platforms }}",
              "rightValue": "youtube",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true
      },
      "id": "youtube-check",
      "name": "YouTube Check",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1400,
        500
      ]
    },
    {
      "parameters": {
        "url": "https://app.metricool.com/api/v2/scheduler/posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Mc-Auth",
              "value": "={{ $credentials.metricoolApi.token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyContentType": "json",
        "jsonBody": "={\n  \"network\": \"youtube\",\n  \"title\": \"{{ $json.title }}\",\n  \"content\": \"{{ $json.description }}\",\n  \"media_url\": \"{{ $('upload-video').item.json.data.url }}\",\n  \"tags\": {{ $json.tags.split(',').map(tag => tag.trim()) }},\n  \"publish_at\": \"{{ $json.scheduleDate }}T{{ $json.scheduleTime }}:00Z\",\n  \"video_type\": \"short\",\n  \"audience\": \"No\",\n  \"privacy\": \"Public\",\n  \"category\": \"Film & Animation\"\n}",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetweenTries": 2000
          }
        }
      },
      "id": "schedule-youtube",
      "name": "Schedule YouTube Short",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1600,
        450
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Collect all results\nconst allResults = [];\n\n// Get results from all platform scheduling nodes\nconst instagramResults = $('schedule-instagram').all() || [];\nconst tiktokResults = $('schedule-tiktok').all() || [];\nconst youtubeResults = $('schedule-youtube').all() || [];\n\nconst allScheduleResults = [...instagramResults, ...tiktokResults, ...youtubeResults];\n\n// Process each result\nfor (const result of allScheduleResults) {\n  const success = result.json && !result.json.error;\n  allResults.push({\n    filename: $('parse-content').item.json.filename,\n    platform: result.json?.network || 'unknown',\n    scheduleDate: $('parse-content').item.json.scheduleDate,\n    status: success ? 'Success' : 'Failed',\n    error: result.json?.error || '',\n    postId: result.json?.id || ''\n  });\n}\n\n// Return summary\nreturn {\n  json: {\n    totalProcessed: allResults.length,\n    successful: allResults.filter(r => r.status === 'Success').length,\n    failed: allResults.filter(r => r.status === 'Failed').length,\n    results: allResults,\n    completedAt: new Date().toISOString()\n  }\n};"
      },
      "id": "collect-results",
      "name": "Collect Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1800,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"TagDrop workflow completed successfully\",\n  \"summary\": {\n    \"totalFiles\": {{ $json.totalProcessed }},\n    \"successful\": {{ $json.successful }},\n    \"failed\": {{ $json.failed }},\n    \"completedAt\": \"{{ $json.completedAt }}\"\n  },\n  \"results\": {{ $json.results }}\n}",
        "options": {}
      },
      "id": "webhook-response",
      "name": "Send Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        2000,
        300
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Parse Inputs & Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Inputs & Files": {
      "main": [
        [
          {
            "node": "Generate Content (GPT-4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Content (GPT-4)": {
      "main": [
        [
          {
            "node": "Parse Content & Schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Content & Schedule": {
      "main": [
        [
          {
            "node": "Upload Video to Metricool",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Video to Metricool": {
      "main": [
        [
          {
            "node": "Split by Platform",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split by Platform": {
      "main": [
        [
          {
            "node": "Instagram Check",
            "type": "main",
            "index": 0
          },
          {
            "node": "TikTok Check",
            "type": "main",
            "index": 0
          },
          {
            "node": "YouTube Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Instagram Check": {
      "main": [
        [
          {
            "node": "Schedule Instagram Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TikTok Check": {
      "main": [
        [
          {
            "node": "Schedule TikTok Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "YouTube Check": {
      "main": [
        [
          {
            "node": "Schedule YouTube Short",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Instagram Post": {
      "main": [
        [
          {
            "node": "Collect Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule TikTok Post": {
      "main": [
        [
          {
            "node": "Collect Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule YouTube Short": {
      "main": [
        [
          {
            "node": "Collect Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Results": {
      "main": [
        [
          {
            "node": "Send Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [],
  "triggerCount": 0,
  "updatedAt": "2025-01-01T00:00:00.000Z",
  "versionId": "1"
}