{
  "name": "ChannelScrape - Webhook",
  "nodes": [
    {
      "id": "2b9be77b-b766-47fe-8581-a9bea85634e4",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "channelscrape",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "*"
        }
      }
    },
    {
      "id": "154b6542-a98f-47f2-9582-940e767c11ce",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        200,
        0
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "559fbe3a-f159-4529-b5d7-069dae4dc018",
              "name": "apiKey",
              "value": "YOUR_YOUTUBE_API_KEY_HERE",
              "type": "string"
            },
            {
              "id": "7ee5178c-904e-43e5-b985-4607e9c89127",
              "name": "minSeconds",
              "value": 120,
              "type": "number"
            }
          ]
        },
        "includeOtherFields": false,
        "options": {}
      }
    },
    {
      "id": "8f2d9640-d290-400b-9f7c-eb8bb97b399d",
      "name": "Parse URL",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Defensive body extraction — handles n8n v1/v2 webhook body formats\nvar inp = $input.first().json;\nvar bodyRaw = inp.body;\nvar body;\nif (!bodyRaw) {\n  body = inp;\n} else if (typeof bodyRaw === 'string') {\n  try { body = JSON.parse(bodyRaw); } catch(e) { body = inp; }\n} else {\n  body = bodyRaw;\n}\n\nvar url = String(body.channel || body.url || inp.channel || inp.url || '').trim();\n\n// Normalize — add protocol if stripped by browser/proxy\nif (url.startsWith('//')) url = 'https:' + url;\nelse if (url.length > 0 && !url.startsWith('http')) url = 'https://' + url;\n\nvar handleMatch    = url.match(/youtube\\.com\\/@([\\w.-]+)/);\nvar channelIdMatch = url.match(/youtube\\.com\\/channel\\/(UC[\\w-]{22})/);\nvar customMatch    = url.match(/youtube\\.com\\/c\\/([\\w.-]+)/);\nvar userMatch      = url.match(/youtube\\.com\\/user\\/([\\w.-]+)/);\n\nvar queryParam = '', queryValue = '';\nif (handleMatch)         { queryParam = 'forHandle';   queryValue = handleMatch[1]; }\nelse if (channelIdMatch) { queryParam = 'id';           queryValue = channelIdMatch[1]; }\nelse if (customMatch)    { queryParam = 'forUsername';  queryValue = customMatch[1]; }\nelse if (userMatch)      { queryParam = 'forUsername';  queryValue = userMatch[1]; }\nelse { throw new Error('Cannot parse URL: \"' + url.slice(0,80) + '\". Use https://www.youtube.com/@channelname'); }\n\nreturn [{ json: { queryParam: queryParam, queryValue: queryValue } }];"
      }
    },
    {
      "id": "1ac15010-741a-4c93-823f-56b6f63ebdbf",
      "name": "Build Channel URL",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        600,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const apiKey = $('Config').first().json.apiKey;\nconst { queryParam, queryValue } = $input.first().json;\nconst channelUrl = `https://www.googleapis.com/youtube/v3/channels?part=snippet,statistics,contentDetails&${queryParam}=${encodeURIComponent(queryValue)}&key=${apiKey}`;\nreturn [{ json: { channelUrl } }];"
      }
    },
    {
      "id": "30e5caed-034a-4d1f-8319-9900cfbc0101",
      "name": "Get Channel Info",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        800,
        0
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $json.channelUrl }}",
        "authentication": "none",
        "options": {}
      }
    },
    {
      "id": "e72c4789-9ce4-47fc-add9-8057c623787d",
      "name": "Extract Channel Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1000,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const data = $input.first().json;\nif (!data.items || data.items.length === 0) {\n  throw new Error('Channel not found. Check URL and make sure the channel is public.');\n}\nconst ch = data.items[0];\nconst apiKey = $('Config').first().json.apiKey;\nconst uploadPlaylistId = ch.contentDetails.relatedPlaylists.uploads;\nconst playlistUrl = `https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails,snippet&playlistId=${uploadPlaylistId}&maxResults=50&key=${apiKey}`;\nreturn [{ json: { channelName: ch.snippet.title, subscribers: ch.statistics.subscriberCount || '0', totalVideos: ch.statistics.videoCount || '0', playlistUrl } }];"
      }
    },
    {
      "id": "8eba59e0-0112-43e6-b034-ca7f02e46c51",
      "name": "Get Playlist Items",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1200,
        0
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $json.playlistUrl }}",
        "authentication": "none",
        "options": {}
      }
    },
    {
      "id": "d36b6e53-0599-4f1d-90a1-42352463bd73",
      "name": "Build Video IDs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1400,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all();\nconst videoIds = items.map(i => i.json.contentDetails && i.json.contentDetails.videoId).filter(Boolean);\nconst apiKey = $('Config').first().json.apiKey;\nconst videosUrl = `https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=${videoIds.join(',')}&key=${apiKey}`;\nreturn [{ json: { videosUrl } }];"
      }
    },
    {
      "id": "6e2582fa-9401-4e38-b746-f905b4bf400d",
      "name": "Get Video Details",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1600,
        0
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $json.videosUrl }}",
        "authentication": "none",
        "options": {}
      }
    },
    {
      "id": "ace5270e-36b2-4d28-a337-2822a7d52693",
      "name": "Filter and Format",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1800,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "function iso8601ToSeconds(d) {\n  const m = d.match(/^PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?$/);\n  if (!m) return 0;\n  return parseInt(m[1]||0)*3600 + parseInt(m[2]||0)*60 + parseFloat(m[3]||0);\n}\nfunction fmt(sec) {\n  const h = Math.floor(sec/3600), m = Math.floor((sec%3600)/60), s = Math.floor(sec%60);\n  return h > 0 ? `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` : `${m}:${String(s).padStart(2,'0')}`;\n}\nconst minSec = $('Config').first().json.minSeconds;\nconst ch = $('Extract Channel Data').first().json;\nconst allVideos = ($input.first().json.items || []);\nconst filtered = allVideos\n  .map(v => ({ title: v.snippet.title, url: `https://www.youtube.com/watch?v=${v.id}`, duration: fmt(iso8601ToSeconds(v.contentDetails.duration)), durationSec: iso8601ToSeconds(v.contentDetails.duration) }))\n  .filter(v => v.durationSec >= minSec);\nreturn [{ json: { channelName: ch.channelName, subscribers: parseInt(ch.subscribers || 0).toLocaleString(), videoCount: ch.totalVideos, filteredCount: filtered.length, minDuration: Math.floor(minSec/60) + 'm+', recentVideos: filtered.slice(0, 10) } }];"
      }
    },
    {
      "id": "b0d54025-bdd3-4a9c-ab1c-2192ba2afcc8",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2000,
        0
      ],
      "parameters": {
        "respondWith": "json",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      }
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Parse URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse URL": {
      "main": [
        [
          {
            "node": "Build Channel URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Channel URL": {
      "main": [
        [
          {
            "node": "Get Channel Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Channel Info": {
      "main": [
        [
          {
            "node": "Extract Channel Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Channel Data": {
      "main": [
        [
          {
            "node": "Get Playlist Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Playlist Items": {
      "main": [
        [
          {
            "node": "Build Video IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Video IDs": {
      "main": [
        [
          {
            "node": "Get Video Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Video Details": {
      "main": [
        [
          {
            "node": "Filter and Format",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter and Format": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "id": "fae9fcff-4918-4440-b90a-2d5378dc5733",
  "tags": []
}