This workflow corresponds to n8n.io template #10649 — we link there as the canonical source.
This workflow follows the Executecommand → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "sJP7yYyMUuFkBxhP",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Generate AI Voiceovers from Scripts and Upload to Google Drive",
"tags": [],
"nodes": [
{
"id": "b77d0479-251b-4dde-a8db-70dd5c1dcde9",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-656,
0
],
"parameters": {},
"typeVersion": 1
},
{
"id": "420b8504-2cc1-4947-b53d-29fd0ae53d4b",
"name": "Convert to File",
"type": "n8n-nodes-base.convertToFile",
"position": [
592,
32
],
"parameters": {
"options": {
"fileName": "={{ $('Code1').item.json.safeFilename }}.pcm",
"mimeType": "={{ $json.candidates[0].content.parts[0].inlineData.mimeType }}"
},
"operation": "toBinary",
"sourceProperty": "candidates[0].content.parts[0].inlineData.data"
},
"typeVersion": 1.1
},
{
"id": "3905f225-4b9d-4e47-bda4-b08002ed635b",
"name": "Read/Write Files from Disk",
"type": "n8n-nodes-base.readWriteFile",
"position": [
784,
32
],
"parameters": {
"options": {},
"fileName": "=/Users/INSERT_YOUR_LOCAL_STORAGE_HERE/{{ $('Code1').item.json.safeFilename }}.pcm",
"operation": "write"
},
"typeVersion": 1
},
{
"id": "88b59cbf-7949-4fb4-84cb-d36a866bbf43",
"name": "Code1",
"type": "n8n-nodes-base.code",
"position": [
176,
32
],
"parameters": {
"jsCode": "// Get all the items coming into this node\nconst items = $items();\n\n// Loop through each item (this code works even if there's only one)\nfor (const item of items) {\n \n // 1. Get the original script text from the 'full_script_text' field.\n const originalScript = item.json.full_script_text;\n \n // Get the row number to make the filename unique\n const rowNumber = item.json.row_number;\n\n // 2. Create the clean filename\n // - Convert to lowercase for consistency\n // - Replace any character that is NOT a letter or number with an underscore\n // - Shorten it to the first 60 characters to prevent it from being too long\n const sanitizedScriptPart = originalScript\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '_') \n .substring(0, 60);\n\n // 3. Combine the row number and the sanitized text to create a final, unique filename\n const safeFilename = `${rowNumber}_${sanitizedScriptPart}`;\n\n // 4. Add the new, safe filename back to the data\n item.json.safeFilename = safeFilename;\n}\n\n// Return the modified data to the next node\nreturn items;"
},
"typeVersion": 2
},
{
"id": "383050a4-5d1a-4f80-9ac3-8ab99dd6c121",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-64,
0
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "8f194775-656e-4687-afbb-507226a8fdb3",
"name": "Filter",
"type": "n8n-nodes-base.filter",
"position": [
-272,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "51e34efc-ba22-404f-835a-4324f403bcbe",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json['Voice Generated?'] }}",
"rightValue": "Yes"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "be5453fc-c5e8-4591-b7d9-cd5a35b9582a",
"name": "Getting Video Scripts",
"type": "n8n-nodes-base.googleSheets",
"position": [
-464,
0
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 539540410,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tFC1EC863ZwJB0GffCM9BzU5kYo1wITwFoESZ130oP4/edit#gid=539540410",
"cachedResultName": "Text Overlays"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1tFC1EC863ZwJB0GffCM9BzU5kYo1wITwFoESZ130oP4/edit?gid=539540410#gid=539540410"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "1decede0-d000-4a68-838c-ce708fc49b55",
"name": "HTTP Request To Generate Voice",
"type": "n8n-nodes-base.httpRequest",
"position": [
384,
32
],
"parameters": {
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-tts:generateContent\n",
"method": "POST",
"options": {},
"jsonBody": "={\n \"contents\": [\n {\n \"parts\": [\n {\n \"text\": \"Say this script in a neutral <INSERT YOUR DESIRED ACCENT HERE> professional accent at a face pace enthusiastically: {{ $json.full_script_text }}\"\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"responseModalities\": [\n \"AUDIO\"\n ],\n \"speechConfig\": {\n \"voiceConfig\": {\n \"prebuiltVoiceConfig\": {\n \"voiceName\": \"Kore\"\n }\n }\n }\n },\n \"model\": \"gemini-2.5-flash-preview-tts\"\n}",
"sendBody": true,
"sendQuery": true,
"specifyBody": "json",
"queryParameters": {
"parameters": [
{
"name": "key",
"value": "INSERT YOUR API KEY HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "3323cc73-9026-48bd-b951-763101a44280",
"name": "Extracting fileName",
"type": "n8n-nodes-base.set",
"position": [
960,
32
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "fe124702-e115-4be8-b5da-df6d01e1ddbe",
"name": "fileName",
"type": "string",
"value": "={{ $json.fileName }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "648b7a61-af32-43d9-97f1-2189560cc47e",
"name": "Running FFMPEG Local Code To Change Format Of File",
"type": "n8n-nodes-base.code",
"position": [
1152,
32
],
"parameters": {
"jsCode": "// Get the input data from the previous node\nconst inputData = $input.all()[0].json;\n\n// Use the EXISTING PCM file path from the input\nconst pcmFilePath = inputData.fileName; // \"/Users/jjtham/3_dream_of_a_marriage_free_from_painful_surprises__imagine_a_f.pcm\"\n\n// Generate the WAV output path by replacing .pcm with .wav\nconst wavFilePath = pcmFilePath.replace('.pcm', '.wav');\n\n// Build the ffmpeg command (quotes handle spaces in paths)\nconst ffmpegCommand = `ffmpeg -y -f s16le -ar 24000 -ac 1 -i \"${pcmFilePath}\" \"${wavFilePath}\"`;\n\nreturn {\n json: {\n pcmPath: pcmFilePath,\n wavPath: wavFilePath,\n command: ffmpegCommand\n }\n};"
},
"typeVersion": 2
},
{
"id": "da306259-3612-41bd-99b0-91408c63e400",
"name": "Running FFMPEG Code",
"type": "n8n-nodes-base.executeCommand",
"position": [
1328,
32
],
"parameters": {
"command": "={{ $json.command }}"
},
"typeVersion": 1
},
{
"id": "2fabed55-983e-4420-b16c-f822697cadfa",
"name": "Writing File Output Onto Disk",
"type": "n8n-nodes-base.readWriteFile",
"position": [
1520,
32
],
"parameters": {
"options": {},
"fileSelector": "={{ $('Running FFMPEG Local Code To Change Format Of File').item.json.wavPath }}"
},
"typeVersion": 1
},
{
"id": "bbc88d97-1edb-4430-8749-2df5594c9dcd",
"name": "Uploading Wav File",
"type": "n8n-nodes-base.googleDrive",
"position": [
1712,
32
],
"parameters": {
"name": "={{ $('Code1').item.json.safeFilename }}.wav",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive",
"cachedResultUrl": "https://drive.google.com/drive/my-drive",
"cachedResultName": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "root",
"cachedResultUrl": "https://drive.google.com/drive",
"cachedResultName": "/ (Root folder)"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "d38711cb-420b-4a70-8ec5-b6df27207caf",
"name": "Uploading Google Drive Link of File To Google Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1920,
32
],
"parameters": {
"columns": {
"value": {
"Voice Link": "={{ $json.webViewLink }}",
"Google Drive ID": "={{ $json.id }}",
"Voice Generated?": "Yes",
"full_script_text": "={{ $('Loop Over Items').item.json.full_script_text }}",
"Computer File Name": "={{ $('Writing File Output Onto Disk').item.json.fileName }}",
"Google Drive Download Link": "={{ $json.webContentLink }}"
},
"schema": [
{
"id": "script_name",
"type": "string",
"display": true,
"required": false,
"displayName": "script_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "psychological_angle",
"type": "string",
"display": true,
"required": false,
"displayName": "psychological_angle",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "full_script_text",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "full_script_text",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "script_breakdown",
"type": "string",
"display": true,
"required": false,
"displayName": "script_breakdown",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Bucket",
"type": "string",
"display": true,
"required": false,
"displayName": "Bucket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "strategyPillar",
"type": "string",
"display": true,
"required": false,
"displayName": "strategyPillar",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "psychologicalAngle",
"type": "string",
"display": true,
"required": false,
"displayName": "psychologicalAngle",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Generated?",
"type": "string",
"display": true,
"required": false,
"displayName": "Voice Generated?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Voice Link",
"type": "string",
"display": true,
"required": false,
"displayName": "Voice Link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Google Drive ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Google Drive ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Google Drive Download Link",
"type": "string",
"display": true,
"required": false,
"displayName": "Google Drive Download Link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "File Location",
"type": "string",
"display": true,
"required": false,
"displayName": "File Location",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Computer File Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Computer File Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Editing Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Editing Status",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"full_script_text"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 539540410,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tFC1EC863ZwJB0GffCM9BzU5kYo1wITwFoESZ130oP4/edit#gid=539540410",
"cachedResultName": "Text Overlays"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "https://docs.google.com/spreadsheets/d/1tFC1EC863ZwJB0GffCM9BzU5kYo1wITwFoESZ130oP4/edit?gid=539540410#gid=539540410"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "39ec1f9e-a5be-4e9c-b1cf-aa2f958694d7",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1408,
-224
],
"parameters": {
"width": 704,
"height": 1120,
"content": "## Generate AI Voiceovers from Scripts and Upload to Google Drive\n\nThis is the final piece of the AI content factory. This workflow takes your text-based video scripts and automatically generates high-quality audio voiceovers for each one, turning your text into ready-to-use audio assets for your video ads.\n\nGo from a spreadsheet of text to a folder of audio files, completely on autopilot.\n\n## \u26a0\ufe0f CRITICAL REQUIREMENTS (Read First!)\nThis is an advanced, self-hosted workflow that requires specific local setup:\n\n* **Self-Hosted n8n Only:** This workflow uses the `Execute Command` and `Read/Write Files` nodes, which requires you to run your own instance of n8n. It will not work on n8n Cloud.\n* **FFmpeg Installation:** You must have [FFmpeg](https://ffmpeg.org/download.html) installed on the same machine where your n8n instance is running. This is used to convert the audio files to a standard format.\n\n## What it does\nThis is Part 3 of the AI marketing series. It connects to the Google Sheet where you generated your video scripts (in Part 2). For each script that hasn't been processed, it:\n\n1. Uses the Google Gemini Text-to-Speech (TTS) API to generate a voiceover.\n2. Saves the audio file to your local computer.\n3. Uses FFmpeg to convert the raw audio into a standard `.wav` file.\n4. Uploads the final `.wav` file to your Google Drive.\n5. Updates the original Google Sheet with a link to the audio file in Drive and marks the script as complete.\n\n## How to set up\n**IMPORTANT:** This workflow is Part 3 of a series and requires the output from Part 2 (\"Generate AI Video Ad Scripts\").\n\n**If you need Part 1 or Part 2 of this workflow series, you can find them for free on [my n8n Creator Profile](https://n8n.io/creators/jj-tham/).**\n\n1. **Connect to Your Scripts Sheet:** In the \"Getting Video Scripts\" node, connect your Google Sheets account and provide the URL to the sheet containing your generated video scripts from Part 2.\n2. **Configure AI Voice Generation (HTTP Request):**\n * In the \"HTTP Request To Generate Voice\" node, go to the **Query Parameters** and replace `INSERT YOUR API KEY HERE` with your Google Gemini API key.\n * In the **JSON Body**, you can customize the voice prompt (e.g., change `<INSERT YOUR DESIRED ACCENT HERE>`).\n3. **Set Your Local File Path:** In the first \"Read/Write Files from Disk\" node, update the **File Name** field to a valid directory on your local machine where n8n has permission to write files. Replace `/Users/INSERT_YOUR_LOCAL_STORAGE_HERE/`.\n4. **Connect Google Drive:** In the \"Uploading Wav File\" node, connect your Google Drive account and choose the folder where your audio files will be saved.\n5. **Update Your Tracking Sheet:** In the final \"Uploading Google Drive Link...\" node, ensure it's connected to the same Google Sheet from Step 1. This node will update your sheet with the results."
},
"typeVersion": 1
},
{
"id": "1cc5dc7d-55c4-4d0a-b356-a8458169dd96",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
-256
],
"parameters": {
"color": 2,
"width": 432,
"height": 240,
"content": "**\ud83d\udc47 STEP 1: CONNECT TO YOUR SCRIPTS**\n\nThis workflow reads the scripts you generated in Part 2.\n\n**Action Required:**\n1. Connect your Google Sheets account.\n2. Paste the URL of the Google Sheet that was the **output** of the \"Generate AI Video Ad Scripts\" workflow.\n\nThe **Filter** node ensures we only process scripts that don't have a voiceover yet."
},
"typeVersion": 1
},
{
"id": "a7250930-1359-44ad-865f-48c21f641cf9",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
-192
],
"parameters": {
"color": 4,
"width": 496,
"height": 176,
"content": "**\ud83e\udde0 STEP 2: CONFIGURE AI VOICE**\n\nThis node generates the audio using Google Gemini's TTS API.\n\n**Action Required:**\n1. Go to **Query Parameters** and insert your Gemini API Key.\n2. (Optional) In the **JSON Body**, edit the prompt to change the accent or delivery style."
},
"typeVersion": 1
},
{
"id": "c53a760c-aaaf-4c39-a564-6b30b8bf52d3",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
272
],
"parameters": {
"color": 5,
"width": 432,
"height": 224,
"content": "**\u26a0\ufe0f STEP 3: CONFIGURE LOCAL FILE PROCESSING**\n\n**This section is for SELF-HOSTED n8n ONLY and requires FFmpeg to be installed on your machine.**\n\n**Action Required:**\n1. Click the first **Read/Write Files from Disk** node.\n2. Update the **File Name** with a real folder path on your computer (e.g., `/home/n8n/audio/` or `C:\\n8n\\audio\\`)."
},
"typeVersion": 1
},
{
"id": "7b792b51-908d-485e-8b9b-7066bf344898",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1216,
-192
],
"parameters": {
"color": 6,
"width": 576,
"height": 192,
"content": "**\u2705 STEP 4: SET UPLOAD DESTINATION**\n\nThis node uploads your final `.wav` audio files.\n\n**Action Required:**\n1. Connect your Google Drive account.\n2. (Optional) Choose a specific folder to keep your voiceovers organized."
},
"typeVersion": 1
},
{
"id": "6ccd0f26-1155-420b-9cad-f140b2d82e13",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1840,
240
],
"parameters": {
"width": 432,
"height": 128,
"content": "**\ud83d\ude80 STEP 5: UPDATE TRACKING SHEET**\n\nThis final step updates your script sheet with links to the new audio files in Google Drive and marks them as \"Yes\" so they don't get processed again."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "8d66a2a2-0e5f-47ba-883d-1a4c6da2d14d",
"connections": {
"Code1": {
"main": [
[
{
"node": "HTTP Request To Generate Voice",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Read/Write Files from Disk",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Code1",
"type": "main",
"index": 0
}
]
]
},
"Uploading Wav File": {
"main": [
[
{
"node": "Uploading Google Drive Link of File To Google Sheet",
"type": "main",
"index": 0
}
]
]
},
"Extracting fileName": {
"main": [
[
{
"node": "Running FFMPEG Local Code To Change Format Of File",
"type": "main",
"index": 0
}
]
]
},
"Running FFMPEG Code": {
"main": [
[
{
"node": "Writing File Output Onto Disk",
"type": "main",
"index": 0
}
]
]
},
"Getting Video Scripts": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
]
]
},
"Read/Write Files from Disk": {
"main": [
[
{
"node": "Extracting fileName",
"type": "main",
"index": 0
}
]
]
},
"Writing File Output Onto Disk": {
"main": [
[
{
"node": "Uploading Wav File",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request To Generate Voice": {
"main": [
[
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Getting Video Scripts",
"type": "main",
"index": 0
}
]
]
},
"Running FFMPEG Local Code To Change Format Of File": {
"main": [
[
{
"node": "Running FFMPEG Code",
"type": "main",
"index": 0
}
]
]
},
"Uploading Google Drive Link of File To Google Sheet": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleDriveOAuth2ApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This is the final piece of the AI content factory. This workflow takes your text-based video scripts and automatically generates high-quality audio voiceovers for each one, turning your text into ready-to-use audio assets for your video ads.
Source: https://n8n.io/workflows/10649/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n workflow automatically generates presentation-style "screen recording" videos with AI-generated slides and a talking head avatar overlay. You provide a topic and intention, and the workflow h
Monitor Google Drive folder, parsing PDF, DOCX and image file into a destination folder, ready for further processing (e.g. RAG ingestion, translation, etc.) Keep processing log in Google Sheet and se
Transcribe audio messages from Telegram using Google Gemini for free.
This workflow turns your n8n into an automated product-video generator powered by Google Sheets. When a new row is added with status = run, it: Downloads the product image from Google Drive. Converts
Generate realistic, high-quality images from text prompts using the Flux AI Text-to-Image Generator API via RapidAPI, and seamlessly store the results in Google Drive and log them in Google Sheets — a