{
  "id": "vpTLhn9nWcNBhYW0",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "YouTube Video to SRT Subtitles via ElevenLabs",
  "tags": [],
  "nodes": [
    {
      "id": "445d490d-0ab4-47d7-b34e-5f7c5c1c9946",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -800,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "12edc16a-bf05-41f2-b224-c9a835b07492",
      "name": "Transcribe audio or video",
      "type": "@elevenlabs/n8n-nodes-elevenlabs.elevenLabs",
      "position": [
        240,
        -16
      ],
      "parameters": {
        "resource": "speech",
        "operation": "speechToText",
        "requestOptions": {},
        "additionalOptions": {
          "diarize": false
        }
      },
      "credentials": {
        "elevenLabsApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "624d9f4c-608e-44f3-b9fb-f7bbbddfe325",
      "name": "From Elevenlabs to Srt",
      "type": "n8n-nodes-base.code",
      "position": [
        -416,
        304
      ],
      "parameters": {
        "jsCode": "function secondsToSrtTime(seconds) {\n  const hrs = Math.floor(seconds / 3600);\n  const mins = Math.floor((seconds % 3600) / 60);\n  const secs = Math.floor(seconds % 60);\n  const ms = Math.round((seconds - Math.floor(seconds)) * 1000);\n\n  return (\n    String(hrs).padStart(2, '0') + ':' +\n    String(mins).padStart(2, '0') + ':' +\n    String(secs).padStart(2, '0') + ',' +\n    String(ms).padStart(3, '0')\n  );\n}\n\n\nfunction splitTextSmart(text, minLen = 20, maxLen = 30) {\n  const punctuationRegex = /([.!?])/g;\n\n\n  const chunks = text\n    .replace(punctuationRegex, '$1|')\n    .split('|')\n    .map(t => t.trim())\n    .filter(Boolean);\n\n  let result = [];\n\n  for (const chunk of chunks) {\n    if (chunk.length <= maxLen) {\n      result.push(chunk);\n    } else {\n    \n      const words = chunk.split(' ');\n      let line = '';\n\n      for (const word of words) {\n        if ((line + ' ' + word).trim().length > maxLen) {\n          result.push(line.trim());\n          line = word;\n        } else {\n          line += ' ' + word;\n        }\n      }\n      if (line.trim()) result.push(line.trim());\n    }\n  }\n\n  return result;\n}\n\n\nlet input = items[0].json;\nif (!Array.isArray(input)) input = [input];\n\nlet srt = '';\nlet index = 1;\n\nfor (const segment of input) {\n  if (!segment.words || !Array.isArray(segment.words)) continue;\n\n  const words = segment.words.filter(w => w.type === 'word');\n  if (!words.length) continue;\n\n  const fullText = segment.text?.trim();\n  if (!fullText) continue;\n\n  const start = words[0].start;\n  const end = words[words.length - 1].end;\n  const totalDuration = end - start;\n\n\n  const textParts = splitTextSmart(fullText);\n\n  const partDuration = totalDuration / textParts.length;\n\n  textParts.forEach((part, i) => {\n    const partStart = start + partDuration * i;\n    const partEnd = partStart + partDuration;\n\n    srt += `${index}\\n`;\n    srt += `${secondsToSrtTime(partStart)} --> ${secondsToSrtTime(partEnd)}\\n`;\n    srt += `${part}\\n\\n`;\n    index++;\n  });\n}\n\nreturn [\n  {\n    json: {\n      srt\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "da044094-31fe-4280-b658-4726072bd41c",
      "name": "From Json to Binary",
      "type": "n8n-nodes-base.code",
      "position": [
        -96,
        304
      ],
      "parameters": {
        "jsCode": "const srt = items[0].json.srt;\n\nconst buffer = Buffer.from(srt, 'utf-8');\n\nreturn [\n  {\n    json: {},\n    binary: {\n      data: {\n        data: buffer.toString('base64'),\n        mimeType: 'application/x-subrip',\n        fileName: 'subtitles.srt'\n      }\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5ddc5a1f-d60d-4394-aa0c-797e71f21d04",
      "name": "Upload file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        240,
        304
      ],
      "parameters": {
        "name": "={{ $binary.data.fileName }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1tkCr7xdraoZwsHqeLm7FZ4aRWY94oLbZ",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1tkCr7xdraoZwsHqeLm7FZ4aRWY94oLbZ",
          "cachedResultName": "n8n"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "b037f599-4c2f-4910-b988-20def895e46f",
      "name": "Set Video Url",
      "type": "n8n-nodes-base.set",
      "position": [
        -432,
        -16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "49612b7f-a2f8-4464-a1a1-340edae1d419",
              "name": "video_url",
              "type": "string",
              "value": "=XXX"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "bc436d21-ec7e-489b-933d-b1d8de4da533",
      "name": "Get Video",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -96,
        -16
      ],
      "parameters": {
        "url": "={{ $json.video_url }}",
        "options": {}
      },
      "typeVersion": 4.3
    },
    {
      "id": "8ab36b8d-bd0f-49d4-8983-1314d4d4e4d3",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -944
      ],
      "parameters": {
        "width": 736,
        "height": 752,
        "content": "## Automated YouTube Video to SRT Subtitles via ElevenLabs & Google Drive\nThis workflow is a simple yet brilliant automation designed to **generate time-coded SRT** subtitles starting directly from a video URL using [ElevenLabs](https://try.elevenlabs.io/ahkbf00hocnu).\n\nWith just a single video link, the workflow automatically extracts the audio, transcribes it using AI speech recognition, and converts the transcription into a properly formatted SRT subtitle file with accurate timestamps.\n\nThis workflow automates the creation of **SRT subtitle files for YouTube** videos using **AI speech recognition**, eliminating the need for manual captioning and saving creators hours of work.\n\n### **How it works:**\n\nThe workflow automates the process of generating **SRT subtitle files** directly from a YouTube video URL. It starts by fetching the video content and extracting its audio, which is then transcribed using **ElevenLabs AI speech recognition**. The resulting text, complete with timestamps, is split into natural, readable subtitle segments based on punctuation and length. These segments are then formatted into a standard **SRT (SubRip)** file, ensuring accurate synchronization with the video. Finally, the workflow uploads the completed SRT file to a specified **Google Drive folder**, providing a ready-to-use subtitle file for video creators and editors.\n\n\n### **Setup steps:**\n\n1. **Video Input:** Update the \u201cSet Video Url\u201d node with a valid YouTube link or configure it to receive URLs dynamically.\n2. **API Credentials:** Add your **ElevenLabs API key** in the transcription node and configure **Google Drive OAuth2** credentials for file uploads.\n3. **Subtitle Formatting:** Adjust SRT generation settings in the \u201cFrom Elevenlabs to Srt\u201d node if custom formatting is needed.\n4. **Destination Folder:** Confirm the correct **Google Drive folder ID** in the upload node.\n5. **Run the Workflow:** Manually trigger the process, input a video URL, and let the workflow automatically create and upload the subtitle file.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4d6a8b10-03b0-4efe-8a94-3192f753304f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 304,
        "content": "## STEP 1 - Set Video URL\nUpdate the \u201cSet Video Url\u201d node with a valid YouTube link or configure it to receive URLs dynamically."
      },
      "typeVersion": 1
    },
    {
      "id": "ca09a16e-d440-4022-b41e-6afe8f1dc374",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -176,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## STEP 1 - Speech to Text\nAdd your [ElevenLabs API ](https://try.elevenlabs.io/ahkbf00hocnu) key in the transcription node and configure Google Drive OAuth2 credentials for file uploads"
      },
      "typeVersion": 1
    },
    {
      "id": "0124cb41-3144-4abf-aacb-d95ac9cf48d9",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 288,
        "content": "## STEP 3 - Subtitle Formatting\nThese segments are formatted into standard SRT (SubRip) format with precise timing, converted to a binary file,"
      },
      "typeVersion": 1
    },
    {
      "id": "800592c0-bb0d-42ef-9c74-aaf95239dde4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 288,
        "content": "## STEP 4 - Google Drive\nUpload file to Google Drive"
      },
      "typeVersion": 1
    },
    {
      "id": "0ddff6ee-4c0d-43f6-a263-08e4ef99ec23",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        -928
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 736,
        "content": "## MY NEW YOUTUBE CHANNEL\n\ud83d\udc49 [Subscribe to my new **YouTube channel**](https://youtube.com/@n3witalia). Here I\u2019ll share videos and Shorts with practical tutorials and **FREE templates for n8n**.\n\n[![image](https://n3wstorage.b-cdn.net/n3witalia/youtube-n8n-cover.jpg)](https://youtube.com/@n3witalia)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "24c2f42c-349c-42da-a38a-e5f35a44a579",
  "connections": {
    "Get Video": {
      "main": [
        [
          {
            "node": "Transcribe audio or video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload file": {
      "main": [
        []
      ]
    },
    "Set Video Url": {
      "main": [
        [
          {
            "node": "Get Video",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "From Json to Binary": {
      "main": [
        [
          {
            "node": "Upload file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "From Elevenlabs to Srt": {
      "main": [
        [
          {
            "node": "From Json to Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe audio or video": {
      "main": [
        [
          {
            "node": "From Elevenlabs to Srt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Set Video Url",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}