This workflow corresponds to n8n.io template #7595 — we link there as the canonical source.
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": "W69y1dllbQpuNZQ0",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Automate Video Upload \u2192 Auto-Thumbnail \u2192 Google Drive",
"tags": [],
"nodes": [
{
"id": "0f0dc32f-cadc-4001-9ded-4049186ed556",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-570,
-180
],
"parameters": {
"width": 1600,
"height": 340,
"content": "## Video Upload \u2192 Auto-Thumbnail \u2192 Google Drive\n\n### **What it does (high level)**\nAccepts a video via HTTP upload, validates it\u2019s a real video file, extracts a thumbnail at the 5-second mark using FFmpeg (auto-installs if missing), saves the thumbnail to a designated Google Drive folder, and returns a JSON response with the new file\u2019s details."
},
"typeVersion": 1
},
{
"id": "9e6b7fcd-2c7a-4762-8dd7-efa102ad9599",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
180
],
"parameters": {
"width": 1580,
"height": 660,
"content": "# **Node Breakdown & Descriptions:**\n\n### \\* The workflow starts with a **Webhook** node named **\u201cAccept Video Upload\u201d**, which activates on an HTTP **POST** to `/mediaUpload`. It expects `multipart/form-data` with the file in the `media` field and defers responding until the last node, so the API reply can include the uploaded thumbnail\u2019s metadata.\n\n### \\* The next node, **IF** named **\u201cValidate Upload is Video\u201d**, checks `{{$binary.media.mimeType}}` contains `video/`. Only true (video) items proceed. If it ever receives a non-video, the false branch can return a **400** (optional improvement).\n\n### \\* The **Write Binary File** node named **\u201cPersist Upload to /tmp\u201d** writes the incoming `media` binary to disk at `/tmp/<originalFilename or input.mp4>`. This produces a stable file path for FFmpeg to read from.\n\n### \\* The **Execute Command** node named **\u201cExtract Thumbnail with FFmpeg (Auto-Install)\u201d** runs FFmpeg to grab a single frame at **5 seconds**:\n\n* If FFmpeg exists in the environment, it uses it.\n* Otherwise it **downloads a static FFmpeg** build into `/tmp/ffmpeg` (no root needed) and uses that.\n* Command outputs `/tmp/thumbnail.jpg` (`-ss 5 -frames:v 1 -q:v 2`).\n\n### \\* The **Read Binary File** node named **\u201cLoad Thumbnail from Disk\u201d** reads `/tmp/thumbnail.jpg` back into the item\u2019s binary data, preparing it for upload (thumbnail is now available as the node\u2019s binary output).\n\n### \\* The **Google Drive** node named **\u201cUpload Thumbnail to Drive\u201d** uploads the thumbnail image to your target folder (folder ID you configured). The file name is derived from the original video name with a `-thumb.jpg` suffix. On success, it returns Drive metadata such as `id` and `name`.\n\n### \\* Finally, the **Respond to Webhook** node named **\u201cReturn API Response\u201d** sends a JSON response to the caller indicating success, including the Drive `thumbnailFileId` and `thumbnailName`, so the client can reference or share the thumbnail immediately."
},
"typeVersion": 1
},
{
"id": "eb68ca4d-93d6-4535-a009-be7a129e18f3",
"name": "Accept Video Upload (Webhook)",
"type": "n8n-nodes-base.webhook",
"position": [
-480,
0
],
"parameters": {
"path": "mediaUpload",
"options": {},
"httpMethod": "POST",
"responseData": "allEntries",
"responseMode": "lastNode"
},
"typeVersion": 1
},
{
"id": "22cf7a78-1f4b-4dac-83b6-718476d74c11",
"name": "Validate Upload is Video (IF)",
"type": "n8n-nodes-base.if",
"position": [
-260,
0
],
"parameters": {
"options": {},
"conditions": {
"string": [
{
"value1": "={{$binary.file.mimeType}}",
"value2": "video/",
"operation": "contains"
}
],
"boolean": []
}
},
"typeVersion": 2
},
{
"id": "9bc950c0-4ffd-4c86-9345-7aa2bb682625",
"name": "Persist Upload to /tmp (Write Binary File)",
"type": "n8n-nodes-base.writeBinaryFile",
"position": [
-40,
0
],
"parameters": {
"options": {},
"fileName": "={{$('Accept Video Upload (Webhook)').item.binary.media.fileName ? \"/tmp/\" + $('Accept Video Upload (Webhook)').item.binary.media.fileName : \"/tmp/input.mp4\"}}",
"dataPropertyName": "media"
},
"typeVersion": 1
},
{
"id": "b8ecc2ac-ddca-42cb-a735-ec3e79e02f1c",
"name": "Extract Thumbnail with FFmpeg (Auto-Install) (Execute Command)",
"type": "n8n-nodes-base.executeCommand",
"position": [
180,
0
],
"parameters": {
"command": "=set -e\n\nVIDEO=\"{{ $('Persist Upload to /tmp (Write Binary File)').item.json.fileName }}\"\nOUT=\"/tmp/{{ $('Accept Video Upload (Webhook)').item.binary.media.fileName }}_thumbnail.jpg\"\n\n# 1) locate ffmpeg\nif command -v ffmpeg >/dev/null 2>&1; then\n FFMPEG_BIN=\"$(command -v ffmpeg)\"\nelif [ -x /tmp/ffmpeg/ffmpeg ]; then\n FFMPEG_BIN=\"/tmp/ffmpeg/ffmpeg\"\nelse\n # 2) fetch a static ffmpeg (no root). Works on most Linux hosts/containers.\n ARCH=\"$(uname -m)\"\n case \"$ARCH\" in\n x86_64|amd64) URL=\"https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz\" ;;\n aarch64|arm64) URL=\"https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz\" ;;\n armv7l|armhf) URL=\"https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armhf-static.tar.xz\" ;;\n i686|i386) URL=\"https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-i686-static.tar.xz\" ;;\n *) echo \"Unsupported CPU arch: $ARCH\" >&2; exit 1 ;;\n esac\n\n mkdir -p /tmp/ffmpeg && cd /tmp/ffmpeg\n\n # downloader: prefer curl, else wget\n if command -v curl >/dev/null 2>&1; then\n curl -fsSL \"$URL\" -o ffmpeg.tar.xz\n elif command -v wget >/dev/null 2>&1; then\n wget -qO ffmpeg.tar.xz \"$URL\"\n else\n echo \"Need curl or wget to fetch ffmpeg\" >&2\n exit 1\n fi\n\n # extract (xz support required by most tars on Linux)\n tar -xJf ffmpeg.tar.xz --strip-components=1\n chmod +x ./ffmpeg ./ffprobe\n FFMPEG_BIN=\"/tmp/ffmpeg/ffmpeg\"\nfi\n\n# 3) do the work\n\"$FFMPEG_BIN\" -y -ss 5 -i \"$VIDEO\" -frames:v 1 -q:v 2 \"$OUT\"\n",
"executeOnce": false
},
"typeVersion": 1
},
{
"id": "736a61c2-bfeb-4821-8ca2-a0e54ddfb04d",
"name": "Load Thumbnail from Disk (Read Binary File)",
"type": "n8n-nodes-base.readBinaryFile",
"position": [
400,
0
],
"parameters": {
"filePath": "=/tmp/{{ $('Persist Upload to /tmp (Write Binary File)').item.binary.media.fileName }}_thumbnail.jpg"
},
"typeVersion": 1
},
{
"id": "8b2760d0-22f8-4291-9663-71f01e3c397b",
"name": "Upload Thumbnail to Drive (Google Drive)",
"type": "n8n-nodes-base.googleDrive",
"position": [
620,
0
],
"parameters": {
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1AXMk_yyq3sD_Oc9f5eRLuqtJevbX-d1i",
"cachedResultUrl": "",
"cachedResultName": "Video_Thumbnail"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "418127d5-2902-472c-b4dc-5f9628c7c6b7",
"name": "Return API Response (Respond to Webhook)",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
840,
0
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "json",
"responseBody": "={\n \"ok\": true,\n \"fileName\": \"{{ $json.name || $json.originalFilename }}\",\n \"type\": \"{{ $json.mimeType || $json.fullFileExtension || $json.fileExtension }}\",\n \"sizeBytes\": \"{{ $json.size }}\",\n \"sizeHuman\": \"={{ (Number($json.size) >= 1048576 ? (Number($json.size)/1048576).toFixed(2) + ' MB' : (Number($json.size)/1024).toFixed(2) + ' KB') }}\",\n \"url\": \"={{ $json.webViewLink || ('https://drive.google.com/file/d/' + $json.id + '/view') }}\",\n \"downloadUrl\": \"={{ $json.webContentLink || ('https://drive.google.com/uc?id=' + $json.id + '&export=download') }}\",\n \"createdTime\": \"{{ $json.createdTime }}\",\n \"modifiedTime\": \"{{ $json.modifiedTime }}\",\n \"image\": {\n \"width\": \"{{ $json.imageMediaMetadata && $json.imageMediaMetadata.width }}\",\n \"height\": \"{{ $json.imageMediaMetadata && $json.imageMediaMetadata.height }}\",\n \"rotation\": \"{{ $json.imageMediaMetadata && $json.imageMediaMetadata.rotation }}\"\n },\n \"owner\": \"{{ $json.owners && $json.owners[0] && $json.owners[0].displayName }}\"\n}"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "8797b44a-f6aa-4043-9997-84a2147ae0aa",
"connections": {
"Accept Video Upload (Webhook)": {
"main": [
[
{
"node": "Validate Upload is Video (IF)",
"type": "main",
"index": 0
}
]
]
},
"Validate Upload is Video (IF)": {
"main": [
[
{
"node": "Persist Upload to /tmp (Write Binary File)",
"type": "main",
"index": 0
}
]
]
},
"Upload Thumbnail to Drive (Google Drive)": {
"main": [
[
{
"node": "Return API Response (Respond to Webhook)",
"type": "main",
"index": 0
}
]
]
},
"Persist Upload to /tmp (Write Binary File)": {
"main": [
[
{
"node": "Extract Thumbnail with FFmpeg (Auto-Install) (Execute Command)",
"type": "main",
"index": 0
}
]
]
},
"Load Thumbnail from Disk (Read Binary File)": {
"main": [
[
{
"node": "Upload Thumbnail to Drive (Google Drive)",
"type": "main",
"index": 0
}
]
]
},
"Extract Thumbnail with FFmpeg (Auto-Install) (Execute Command)": {
"main": [
[
{
"node": "Load Thumbnail from Disk (Read Binary File)",
"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.
googleDriveOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow accepts a video via HTTP upload, verifies it’s a valid video, extracts a thumbnail frame at the 5-second mark using FFmpeg (auto-installs a static build if missing), uploads the image to a specified Google Drive folder and returns a structured JSON response…
Source: https://n8n.io/workflows/7595/ — 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.
Automate Video Upload → Auto-Thumbnail → Google Drive. Uses writeBinaryFile, executeCommand, readBinaryFile, googleDrive. Webhook trigger; 9 nodes.
This n8n workflow automatically transfers files from Google Drive to an FTP server on a scheduled basis. It includes comprehensive logging, email notifications, and error handling. Automated Schedulin
This n8n workflow template uses community nodes and is only compatible with the self-hosted version of n8n.
This workflow enables seamless, privacy-first capture of meeting notes from your iPhone. It pairs with an iOS Shortcut that leverages Apple’s on-device transcription from the Voice Memos app and optio