{
  "name": "Capslane: YouTube to knowledge-base Markdown (HTTP)",
  "nodes": [
    {
      "id": "run-manually",
      "name": "Run manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "parameters": {},
      "position": [
        0,
        0
      ]
    },
    {
      "id": "video-url",
      "name": "Video URL",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "url",
              "name": "url",
              "value": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "position": [
        240,
        0
      ]
    },
    {
      "id": "validate-input",
      "name": "Validate input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const row = $input.item.json;\nconst value = String(row.url ?? '').trim();\nconst short = value.match(/^https:\\/\\/youtu\\.be\\/([A-Za-z0-9_-]{11})(?:[/?#].*)?$/);\nconst shorts = value.match(/^https:\\/\\/(?:www\\.|m\\.)?youtube\\.com\\/shorts\\/([A-Za-z0-9_-]{11})(?:[/?#].*)?$/);\nconst watch = value.match(/^https:\\/\\/(?:www\\.|m\\.)?youtube\\.com\\/watch\\?([^#]*)/);\nconst videoId = /^[A-Za-z0-9_-]{11}$/.test(value) ? value : short?.[1] ?? shorts?.[1] ?? watch?.[1].match(/(?:^|&)v=([A-Za-z0-9_-]{11})(?:&|$)/)?.[1];\nif (!videoId) throw new Error('Provide a public YouTube URL or an 11-character video ID in url.');\nreturn { json: { ...row, videoId, url: 'https://www.youtube.com/watch?v=' + videoId } };"
      },
      "position": [
        480,
        0
      ]
    },
    {
      "id": "get-native-transcript",
      "name": "Get native transcript",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "parameters": {
        "url": "https://capslane.com/v1/transcript",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $json.videoId }}"
            },
            {
              "name": "mode",
              "value": "native"
            },
            {
              "name": "text",
              "value": "false"
            },
            {
              "name": "chunkSize",
              "value": "1000"
            }
          ]
        },
        "options": {
          "timeout": 45000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "redirect": {
            "redirect": {
              "followRedirects": false
            }
          }
        }
      },
      "position": [
        720,
        0
      ]
    },
    {
      "id": "prepare-document",
      "name": "Prepare document",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const response = $input.item.json;\nconst input = $('Validate input').item.json;\nif (!Array.isArray(response.content) || !response.content.length) throw new Error('No complete timestamped transcript returned.');\nconst sections = response.content.map(segment => {\n  const seconds = Math.floor(segment.offset / 1000);\n  return '[' + seconds + 's](' + input.url + '&t=' + seconds + ')\\n\\n' + segment.text;\n});\nconst document = '# YouTube transcript: ' + input.videoId + '\\n\\nSource: ' + input.url + '\\nLanguage: ' + response.lang + '\\nTranscript source: ' + response.source + '\\n\\n' + sections.join('\\n\\n');\nreturn { json: { videoId: input.videoId, sourceUrl: input.url, language: response.lang, document } };"
      },
      "position": [
        960,
        0
      ]
    },
    {
      "id": "download-markdown",
      "name": "Download Markdown",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "parameters": {
        "operation": "toText",
        "sourceProperty": "document",
        "binaryPropertyName": "data",
        "options": {
          "fileName": "={{ $json.videoId + \".md\" }}",
          "encoding": "utf8"
        }
      },
      "position": [
        1200,
        0
      ]
    }
  ],
  "connections": {
    "Run manually": {
      "main": [
        [
          {
            "node": "Video URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Video URL": {
      "main": [
        [
          {
            "node": "Validate input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate input": {
      "main": [
        [
          {
            "node": "Get native transcript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get native transcript": {
      "main": [
        [
          {
            "node": "Prepare document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare document": {
      "main": [
        [
          {
            "node": "Download Markdown",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  }
}
