This workflow corresponds to n8n.io template #11441 — we link there as the canonical source.
This workflow follows the Chainllm → Google Drive 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": "3iYyVYeLfQjiReRv",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Auto-post YouTube Shorts from Google Drive (with AI captions)",
"tags": [],
"nodes": [
{
"id": "88b76a4a-1166-4ab8-8e3a-57a500aa984b",
"name": "OpenRouter Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
624,
240
],
"parameters": {
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "10ed3943-2881-4a1b-b8d9-2aa0f8c3a56f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
-192
],
"parameters": {
"width": 784,
"height": 576,
"content": "## How it works\nThis workflow automates posting YouTube Shorts directly from a Google Drive folder. Each time it runs, it selects the next available video, generates a title, description, and hashtags using an AI model, prepares the YouTube metadata, uploads the file through YouTube\u2019s resumable upload API, and finally moves the processed video to a \u201cPosted\u201d folder. \nThe goal is to help creators keep a consistent publishing schedule without manual uploads or text generation.\n\n## Setup steps\n1. Connect your Google Drive and YouTube OAuth2 credentials. \n2. Replace the folder ID in the \u201cGet next video\u201d node with your own input folder. \n3. Replace the folder ID in the final \u201cMove file\u201d node with your posted/processed folder. \n4. Open the \u201cSet variables\u201d or prompt section to adjust store name, region, coupon code, or tone as needed. \n5. Review the LLM prompt if you want to customize the text style. \n6. Enable the schedule trigger with the time interval you prefer. \n\nSticky notes in the flow briefly explain each section, but this main sticky gives you the high-level view required to get started quickly.\n"
},
"typeVersion": 1
},
{
"id": "b0fcdc38-53ec-44a5-bc9f-e40e1573342b",
"name": "Pick next video",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-32,
0
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 13
},
{
"triggerAtHour": 14
},
{
"triggerAtHour": 15
},
{
"triggerAtHour": 16
},
{
"triggerAtHour": 17
},
{
"triggerAtHour": 18
},
{
"triggerAtHour": 19
},
{
"triggerAtHour": 20
},
{
"triggerAtHour": 21
},
{
"triggerAtHour": 22
},
{
"triggerAtHour": 23
}
]
}
},
"typeVersion": 1.2
},
{
"id": "4030c416-5a97-4205-9b22-58bcd33dbc76",
"name": "Get next video from folder",
"type": "n8n-nodes-base.googleDrive",
"position": [
240,
0
],
"parameters": {
"limit": 1,
"filter": {
"folderId": {
"__rl": true,
"mode": "list",
"value": "1Gmal44X9qe3O_djokfRQ_Ega0bZaBkQA",
"cachedResultUrl": "",
"cachedResultName": "youtube reals"
}
},
"options": {
"fields": [
"id",
"mimeType",
"name",
"webViewLink"
]
},
"resource": "fileFolder"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "e828bacf-a6fc-4bc7-b991-0d2bcb193a45",
"name": "Generate title & description",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
624,
0
],
"parameters": {
"text": "=You are a professional digital marketing specialist and short-form content writer.\n\nYour task is to generate high-conversion, SEO-optimized content for YouTube Shorts promoting an online shopping discount coupon.\n\n=====================================\n\ud83d\udccc INPUT VARIABLES (Injected from workflow)\n=====================================\nStore name: {{ $json.storeName }}\nTarget country/region: {{ $json.country }}\nDiscount code: {{ $json.couponCode }}\nTone style: {{ $json.tone }} (e.g., Saudi dialect, Arabic simplified, or English)\nLinks (optional):\n- Website: {{ $json.website }}\n- Instagram: {{ $json.instagram }}\n- TikTok: {{ $json.tiktok }}\n\n=====================================\n\ud83c\udfaf WHAT YOU MUST GENERATE\n=====================================\n1. A catchy YouTube Shorts title (max 60 characters)\n2. A concise, keyword-rich description\n3. Focused hashtags suitable for the region + store\n\n=====================================\n\ud83d\udccc RULES\n=====================================\n- Make the title short, powerful, and optimized for search.\n- Description must encourage immediate coupon use.\n- Include strong keywords related to:\n - the store\n - the discount\n - regional search intent\n- Add expressive shopping emojis (\ud83c\udf81\ud83d\udd25\ud83d\udcb8\ud83d\uded2).\n- Adapt tone based on the requested style.\n- If links exist, include them neatly in the description.\n- Keep hashtags relevant, not spammy.\n\n=====================================\n\ud83d\udccc OUTPUT FORMAT\n=====================================\nOutput **only valid JSON** in this structure:\n\n{\n \"title\": \"...\",\n \"description\": \"...\",\n \"hashtags\": [\"...\", \"...\", \"...\"]\n}\n\n=====================================\n\ud83d\udccc EXAMPLE (Do NOT copy):\n{\n \"title\": \"\u062e\u0635\u0645 \u0631\u0647\u064a\u0628 \u0639\u0644\u0649 \u062a\u0633\u0648\u0642\u0643 \u0627\u0644\u064a\u0648\u0645! \ud83c\udf81\ud83d\udd25\",\n \"description\": \"\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0643\u0648\u062f \u0627\u0644\u0622\u0646 \u0642\u0628\u0644 \u0627\u0646\u062a\u0647\u0627\u0621 \u0627\u0644\u0639\u0631\u0636! \u0631\u0648\u0627\u0628\u0637\u0646\u0627 \u0627\u0644\u0631\u0633\u0645\u064a\u0629 \u0628\u0627\u0644\u0623\u0633\u0641\u0644 \ud83d\udc47\",\n \"hashtags\": [\"#\u062e\u0635\u0645\", \"#\u062a\u0633\u0648\u0642\", \"#\u0639\u0631\u0648\u0636\"]\n}\n\n=====================================\nNow generate the final JSON.\n",
"promptType": "define"
},
"typeVersion": 1.6
},
{
"id": "141ea23f-c4f6-4eeb-bd77-987bab1c2112",
"name": "Clean LLM JSON",
"type": "n8n-nodes-base.code",
"position": [
992,
0
],
"parameters": {
"jsCode": "let txt = $json.text;\n\ntxt = txt.replace(/^```(?:json)?\\s*|\\s*```$/g, '').trim();\n\nlet parsed;\n\ntry {\n parsed = JSON.parse(txt);\n} catch (e1) {\n try {\n parsed = eval('(' + txt + ')');\n } catch (e2) {\n throw new Error(\"Parsing failed! Cleaned value: \" + txt);\n }\n}\n\nreturn [\n {\n json: {\n title: parsed.title,\n description: parsed.description,\n hashtags: parsed.hashtags,\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "5131e763-576d-480a-a4c9-99df9c02236d",
"name": "YouTube video metadata",
"type": "n8n-nodes-base.set",
"position": [
1344,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ea111876-cea0-46f9-980b-75b3875cabdd",
"name": "snippet.title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "699c4e23-15f9-4407-8140-b4947c7cacc4",
"name": "snippet.description",
"type": "string",
"value": "={{ $json.description }} {{ $json.hashtags }} #shorts"
},
{
"id": "7a863ea6-7157-4bc2-a284-c81bd55931e7",
"name": "snippet.categoryId",
"type": "string",
"value": "24"
},
{
"id": "2831cca4-02d9-4588-82b7-2147034e1eb0",
"name": "status.privacyStatus",
"type": "string",
"value": "public"
},
{
"id": "a0f19a5f-f9f1-4887-9391-8aaf300815ed",
"name": "defaultLanguage",
"type": "string",
"value": "\"ar\""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d11d1848-abe1-4a6b-9286-58b037e65568",
"name": "Initialize resumable upload",
"type": "n8n-nodes-base.httpRequest",
"position": [
1536,
0
],
"parameters": {
"url": "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status",
"method": "POST",
"options": {
"response": {
"response": {
"fullResponse": true
}
}
},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "=snippet",
"value": "={{ $json.snippet }}"
},
{
"name": "status",
"value": "={{ $json.status }}"
},
{
"name": "defaultLanguage",
"value": "={{ $json.defaultLanguage }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "X-Upload-Content-Type",
"value": "video/mp4"
},
{
"name": "Content-Type",
"value": "application/json; charset=UTF-8"
}
]
},
"nodeCredentialType": "youTubeOAuth2Api"
},
"credentials": {},
"typeVersion": 4.2
},
{
"id": "9a2b2e30-57bb-481e-9e39-7e9b241a1f83",
"name": "Download video file",
"type": "n8n-nodes-base.googleDrive",
"position": [
1744,
0
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Get next video from folder').item.json.webViewLink }}"
},
"options": {
"fileName": "data"
},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "531e098e-e673-4492-8668-860ea4a8165e",
"name": "Upload video file",
"type": "n8n-nodes-base.httpRequest",
"position": [
2096,
0
],
"parameters": {
"url": "={{ $json.headers.location }}",
"method": "PUT",
"options": {},
"sendBody": true,
"contentType": "binaryData",
"inputDataFieldName": "data"
},
"typeVersion": 4.2
},
{
"id": "58f4d0a8-7480-487d-89a3-6b2fe608b7a9",
"name": "Move video to Posted",
"type": "n8n-nodes-base.googleDrive",
"position": [
2320,
0
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Get next video from folder').item.json.id }}"
},
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1Xc27qsxlZMSuo6bugWKPN1BC-jOoPXT8",
"cachedResultUrl": "",
"cachedResultName": "youtube posted"
},
"operation": "move"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "28bc28e7-1827-43a9-8a86-5e5e07be1fff",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
-160
],
"parameters": {
"color": 7,
"width": 560,
"height": 352,
"content": "## Pick next video\nRuns on a schedule and fetches one video from the selected Google Drive folder. \nThis section identifies the next file to process.\n"
},
"typeVersion": 1
},
{
"id": "59a26eee-cb7b-4cea-b257-ecef0bd5eed8",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
-176
],
"parameters": {
"color": 7,
"width": 608,
"height": 560,
"content": "## Generate metadata\nUses an AI model to generate a title, description, and hashtags. \nA small code node cleans JSON and formats the output for YouTube.\n"
},
"typeVersion": 1
},
{
"id": "1b23e3f2-55c4-4c07-93a4-5c58d7fb8a7d",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1296,
-176
],
"parameters": {
"color": 7,
"width": 608,
"height": 432,
"content": "## Upload to YouTube\nInitializes a resumable upload, downloads the file, and streams it to YouTube in the required format.\n"
},
"typeVersion": 1
},
{
"id": "ebba3b1c-c114-41c4-b71f-251ee8641f61",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1984,
-176
],
"parameters": {
"color": 7,
"width": 528,
"height": 368,
"content": "## Move processed file\nMoves the video to the \u201cPosted\u201d folder to avoid duplicates and confirm successful upload.\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "83ae8102-fc68-43a5-a907-335df51cb6fd",
"connections": {
"Clean LLM JSON": {
"main": [
[
{
"node": "YouTube video metadata",
"type": "main",
"index": 0
}
]
]
},
"Pick next video": {
"main": [
[
{
"node": "Get next video from folder",
"type": "main",
"index": 0
}
]
]
},
"Upload video file": {
"main": [
[
{
"node": "Move video to Posted",
"type": "main",
"index": 0
}
]
]
},
"Download video file": {
"main": [
[
{
"node": "Upload video file",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model1": {
"ai_languageModel": [
[
{
"node": "Generate title & description",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"YouTube video metadata": {
"main": [
[
{
"node": "Initialize resumable upload",
"type": "main",
"index": 0
}
]
]
},
"Get next video from folder": {
"main": [
[
{
"node": "Generate title & description",
"type": "main",
"index": 0
}
]
]
},
"Initialize resumable upload": {
"main": [
[
{
"node": "Download video file",
"type": "main",
"index": 0
}
]
]
},
"Generate title & description": {
"main": [
[
{
"node": "Clean LLM JSON",
"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.
googleDriveOAuth2ApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically uploads YouTube Shorts from a Google Drive folder. It picks one video at each run, generates a YouTube-optimized title, description, and hashtags using an AI model, uploads the video through YouTube’s resumable upload API, and finally moves the…
Source: https://n8n.io/workflows/11441/ — 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 workflow transforms scattered Reddit discussions into a systematic lead generation machine by discovering actual marketing problems businesses face and automatically converting them into authorit
Nova AI Content Marketing Agent - LinkedIn & Facebook Automation This n8n template demonstrates how to create a complete AI-powered social media content creation and scheduling system that generates p
Automatically scrape LinkedIn posts with Apify, transform them into optimized tweets and threads using Claude AI, store them in Airtable for approval, and publish to X on a daily schedule.
This workflow is designed for content creators, social media managers, digital marketers, and business owners who want to automate their content creation and distribution process across multiple platf
How it works Automates daily LinkedIn post creation from trending AI tweets. Fetches latest tweets, processes content, checks for duplicates, converts to LinkedIn-ready format, schedules, and posts au