This workflow corresponds to n8n.io template #9158 — 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": "62eZeoZUfZggUZjF",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Meeting Minutes Maker",
"tags": [],
"nodes": [
{
"id": "6d521590-dd15-49fe-88cb-d732265653a5",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
-9712,
-32
],
"parameters": {
"amount": 3
},
"typeVersion": 1.1
},
{
"id": "3913a10a-4787-40eb-b34d-c6df1a53c05b",
"name": "Execute Command",
"type": "n8n-nodes-base.executeCommand",
"onError": "continueRegularOutput",
"position": [
-9456,
-32
],
"parameters": {
"command": "=powershell.exe -ExecutionPolicy Bypass -File \"G:\\obs\\tools\\wait-for-file.ps1\" -FilePath \"{{ $('Wait').item.json.path }}\""
},
"typeVersion": 1
},
{
"id": "c560e8cf-a988-4713-8bda-b28410cd4bb8",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-9312,
-32
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "22a384ae-7d08-42d8-a844-142827385c85",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.stdout }}",
"rightValue": "0"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "6433a78c-7766-4da4-9f35-e7e6efcd0ff7",
"name": "Wait1",
"type": "n8n-nodes-base.wait",
"position": [
-9136,
-16
],
"parameters": {
"amount": 3
},
"typeVersion": 1.1
},
{
"id": "4f45e4aa-a5f1-4fda-a3c6-7366ecefd950",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-9760,
-112
],
"parameters": {
"color": 3,
"width": 1056,
"height": 304,
"content": "## Wait for file copy to finish\n"
},
"typeVersion": 1
},
{
"id": "b6799161-3f29-49f3-ad45-4a7a77f83709",
"name": "Create a page",
"type": "n8n-nodes-base.notion",
"position": [
-6896,
-48
],
"parameters": {
"title": "={{ $('File').item.json.path.split(/[/\\\\]/).pop().replace(/\\.[^/.]+$/, '') }}",
"pageId": {
"__rl": true,
"mode": "id",
"value": "26ddc80914ee803a9896f7a313c44125"
},
"options": {}
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "e39ec985-66df-4434-85e5-0a673d562b50",
"name": "Filter",
"type": "n8n-nodes-base.filter",
"position": [
-8912,
-48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "fb3de4a9-893a-4311-8a6f-046f459d9181",
"operator": {
"type": "string",
"operation": "endsWith"
},
"leftValue": "={{ $('File').item.json.path }}",
"rightValue": "mkv"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "7a7ebdab-91c5-4c2a-b508-72614ce230c1",
"name": "Create Notes",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-6224,
-48
],
"parameters": {
"text": "You are a professional meeting assistant. Your task is to generate clear, concise, and actionable meeting minutes from the provided transcript.\n\nYou will receive:\n- A full text transcript of the meeting with timestamps.\n- A link to the original video recording for reference.\n\nYour output must be well-structured and include the following sections:\n\n**1. Main Topics Discussed**\nSummarize the key subjects that were covered.\n\n**2. Decisions Made**\nList any definitive conclusions or choices that were agreed upon.\n\n**3. Action Items**\nList concrete next steps. For each, try to identify the responsible person and the task.\nFormat: `- [Owner's Name] \u2192 [Specific Task]`\n\n**4. Issues & Blockers**\nNote any problems, technical difficulties, or obstacles that were raised.\n\n**5. Next Steps & Deadlines**\nOutline the immediate future actions and any mentioned deadlines.\n\n**Guidelines:**\n- **Do not** provide a word-for-word transcript. Summarize and synthesize.\n- **Be concise and professional.**\n- **Use bullet points** for lists and action items.\n- **Reference the video:** If a specific moment in the discussion is critical, you can note its relevance, but the primary link to the full recording will be provided separately.\n\n**File Name:** {{ $('File').item.json.path }}\n\n### START OF TRANSCRIPT ###\n{{ $('Set Transcript Data').item.json.transcript }}\n### END OF TRANSCRIPT ###\n\n**Expected Output Format (in Markdown):**\n\n# Meeting Notes - [Meeting Name from File]\n**Date:** [Date extracted from the file name]\n\n## Main Topics Discussed\n- ...\n\n## Decisions Made\n- ...\n\n## Action Items\n- [Owner's Name] \u2192 [Specific Task]\n\n## Issues & Blockers\n- ...\n\n## Next Steps & Deadlines\n- ...",
"batching": {},
"promptType": "define"
},
"executeOnce": true,
"typeVersion": 1.7
},
{
"id": "b334ad41-b972-43e4-8e5c-2e5d25b88bd6",
"name": "Parse for Notion",
"type": "n8n-nodes-base.code",
"position": [
-5392,
-48
],
"parameters": {
"jsCode": "const text = $('Create Notes').first().json.text; // El texto original muy largo que quieres dividir\n// Funci\u00f3n para dividir texto en bloques de max 2000 chars\nfunction splitTextIntoBlocks(str, maxSize = 2000) {\n const result = [];\n for (let i = 0; i < str.length; i += maxSize) {\n const chunk = str.substring(i, i + maxSize);\n result.push({chunk});\n }\n return result;\n}\nconst blocks = splitTextIntoBlocks(text);\n// Devolver el array para usar en el nodo Notion en el campo blocks (children)\n//return [{ json: { blocks } }];\nreturn splitTextIntoBlocks(text);"
},
"typeVersion": 2
},
{
"id": "2238df32-057e-4f9b-8888-9997018175b3",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-4592,
-48
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "31ab577f-87ae-4160-8ce9-370c0a39359a",
"name": "Append a block",
"type": "n8n-nodes-base.notion",
"position": [
-4368,
-32
],
"parameters": {
"blockId": {
"__rl": true,
"mode": "id",
"value": "={{ $node[\"Create a page\"].json.id }}"
},
"blockUi": {
"blockValues": [
{
"text": {
"text": [
{
"text": "={{ $json.chunk }}",
"annotationUi": {}
}
]
},
"richText": true
}
]
},
"resource": "block"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "593874f0-3fd2-44cc-b25f-38af5ef78592",
"name": "File",
"type": "n8n-nodes-base.localFileTrigger",
"position": [
-10048,
-32
],
"parameters": {
"path": "G:\\OBS\\videos",
"events": [
"add"
],
"options": {
"depth": 0,
"awaitWriteFinish": true
},
"triggerOn": "folder"
},
"typeVersion": 1
},
{
"id": "323e836a-4408-4cd9-91e2-afa998624090",
"name": "Create Wav",
"type": "n8n-nodes-base.executeCommand",
"position": [
-7824,
-48
],
"parameters": {
"command": "=python G:\\OBS\\tools\\create_wav.py \"{{ $('File').item.json.path }}\""
},
"typeVersion": 1
},
{
"id": "0e1f1c0a-ee1c-4a79-b2c7-af4b53643fc3",
"name": "Transcribe Local",
"type": "n8n-nodes-base.executeCommand",
"position": [
-7648,
-48
],
"parameters": {
"command": "=python G:\\OBS\\tools\\transcribe_return.py \"{{ $('File').item.json.path }}\""
},
"typeVersion": 1
},
{
"id": "f0895e66-f550-487a-8ff3-098104d9d84c",
"name": "Set Transcript Data",
"type": "n8n-nodes-base.set",
"position": [
-7280,
-48
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "78b62534-3f5f-4978-bad7-2d9a97787e86",
"name": "transcript",
"type": "string",
"value": "={{ $('Transcribe Local').item.json.stdout }}"
},
{
"id": "1c1442d9-2876-406d-963d-d282a6caa891",
"name": "scripts",
"type": "string",
"value": "Script"
},
{
"id": "4f009110-51e2-4679-b33f-b6944d2d4c4d",
"name": "drive_link",
"type": "string",
"value": "={{ $('Upload video file').item.json.webViewLink }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "aea30152-5b6c-4a41-8c1c-a517d47b3960",
"name": "Save Transcript File",
"type": "n8n-nodes-base.executeCommand",
"onError": "continueRegularOutput",
"position": [
-7456,
-48
],
"parameters": {
"command": "=echo {{ JSON.stringify($json.stdout) }} > \"{{ $('File').item.json.path.replace(/\\.[^/.]+$/, '.transcript.txt') }}\""
},
"typeVersion": 1
},
{
"id": "c3e7a795-f86f-4b7d-9934-05c3715b82fe",
"name": "Save Notes File",
"type": "n8n-nodes-base.executeCommand",
"position": [
-5952,
-48
],
"parameters": {
"command": "=echo {{ JSON.stringify($json.text) }} > \"{{ $('File').item.json.path.replace(/\\.[^/.]+$/, '.notes.txt') }}\""
},
"typeVersion": 1
},
{
"id": "e3136ca2-040d-4aa6-bfe9-ecd31b2604c4",
"name": "Read MKV From Disk",
"type": "n8n-nodes-base.readWriteFile",
"position": [
-8336,
-48
],
"parameters": {
"options": {},
"fileSelector": "={{ $('File').item.json.path.replace(/\\\\/g, '/') }}"
},
"typeVersion": 1
},
{
"id": "97e06a40-b35d-4ef7-86f8-f0e54a61af60",
"name": "Paste Video URL",
"type": "n8n-nodes-base.notion",
"onError": "continueRegularOutput",
"position": [
-6704,
-48
],
"parameters": {
"blockId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"blockUi": {
"blockValues": [
{
"text": {
"text": [
{
"text": "=LINK: {{ $('File').item.json.path.replace(/\\\\/g, '/').split('/').pop() }}",
"isLink": true,
"textLink": "={{ $('Set Transcript Data').item.json.drive_link }}",
"annotationUi": {}
}
]
},
"type": "heading_3",
"richText": true
}
]
},
"resource": "block"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "1b49cce9-be3e-4ce0-a64f-b7f942af1923",
"name": "Upload video file",
"type": "n8n-nodes-base.googleDrive",
"onError": "continueRegularOutput",
"position": [
-8176,
-48
],
"parameters": {
"name": "={{ $('File').item.json.path.replace(/\\\\/g, '/').split('/').pop() }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Create sub-folder').item.json.id }}"
},
"inputDataFieldName": "=data"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "c58d7f3f-fc60-4c6d-af43-2840ab02ae8e",
"name": "Create sub-folder",
"type": "n8n-nodes-base.googleDrive",
"position": [
-8512,
-48
],
"parameters": {
"name": "={{ $('File').item.json.path.replace(/\\\\/g, '/').split('/').pop().replace(/\\.[^/.]+$/, '') }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1EXauY-BVnK-A8-t4fJ_M0C9RghTzIFsO",
"cachedResultUrl": "https://drive.google.com/drive/folders/1EXauY-BVnK-A8-t4fJ_M0C9RghTzIFsO",
"cachedResultName": "Meetings"
},
"resource": "folder"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "ae938a6b-5a60-49d8-9df5-992f2d148b27",
"name": "gpt-oss 4090",
"type": "@n8n/n8n-nodes-langchain.lmOllama",
"position": [
-6224,
176
],
"parameters": {
"model": "gpt-oss:20b",
"options": {
"numCtx": 6144,
"numBatch": 6144
}
},
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "3f5aa733-e084-4c9e-910b-2a861103b112",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-8608,
-112
],
"parameters": {
"color": 4,
"width": 608,
"height": 256,
"content": "## Upload to Google Drive"
},
"typeVersion": 1
},
{
"id": "c9f67d15-0af6-43db-8c3f-1789d5e85867",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-7904,
-112
],
"parameters": {
"color": 7,
"width": 768,
"height": 240,
"content": "## Create Wav and Transcribe with Local Whisper Model"
},
"typeVersion": 1
},
{
"id": "55beaebd-6914-4160-81c1-46c0c0749a1f",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-6960,
-112
],
"parameters": {
"color": 6,
"width": 432,
"height": 240,
"content": "## Generate Notion Page"
},
"typeVersion": 1
},
{
"id": "3143ebdd-0bc2-475e-9a6a-146b22556f69",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-6304,
-112
],
"parameters": {
"color": 7,
"width": 1088,
"height": 432,
"content": "## Create Notes with local Ollama Model and parse the info for Notion"
},
"typeVersion": 1
},
{
"id": "05b07726-dcff-4fec-9787-f70528240d76",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4688,
-128
],
"parameters": {
"color": 6,
"width": 640,
"height": 304,
"content": "## Generate Notion Page and send notification"
},
"typeVersion": 1
},
{
"id": "ff40d58e-90fb-4939-9682-cbe56c6920d6",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-10192,
-112
],
"parameters": {
"color": 2,
"width": 384,
"height": 304,
"content": "## New File Trigger"
},
"typeVersion": 1
},
{
"id": "2bcacbb4-1f9b-4d4c-af9d-ca388c6e086c",
"name": "Discord",
"type": "n8n-nodes-base.discord",
"position": [
-4224,
-32
],
"parameters": {
"content": "={{ $('Loop Over Items').item.json.chunk }}",
"options": {},
"authentication": "webhook"
},
"credentials": {
"discordWebhookApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "9a966128-f9ec-4fa4-bbed-03f09647989e",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-9712,
336
],
"parameters": {
"color": 7,
"width": 1056,
"height": 352,
"content": "## wait-for-file.ps1\n```\nparam(\n [Parameter(Mandatory=$true)]\n [string]$FilePath\n)\ntry {\n # Try to open the file in exclusive read mode (no sharing)\n $fileStream = [System.IO.File]::Open($FilePath, 'Open', 'Read', 'None')\n $fileStream.Close()\n $fileStream.Dispose()\n # If we reach this point, the file is free\n Write-Output \"0\"\n}\ncatch {\n # Any error: locked, doesn't exist, permissions, etc. \u2192 consider it \"blocked\"\n Write-Output \"1\"\n}\n```"
},
"typeVersion": 1
},
{
"id": "9098cf0f-ec0f-43e5-96d9-72431000e352",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
-8608,
336
],
"parameters": {
"color": 7,
"width": 944,
"height": 1056,
"content": "## create_wav.py\n```\nimport subprocess\nimport math\nimport sys\nimport os\n\ndef get_duration(input_path):\n \"\"\"Get duration in seconds using ffprobe\"\"\"\n cmd = [\n 'ffprobe', '-v', 'error',\n '-show_entries', 'format=duration',\n '-of', 'default=noprint_wrappers=1:nokey=1',\n input_path\n ]\n result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)\n try:\n duration_sec = float(result.stdout.strip())\n except ValueError:\n raise RuntimeError(\"Could not get duration with ffprobe.\")\n return duration_sec\n\ndef convert_to_wav(input_path):\n max_size_bytes = 25 * 1024 * 1024 # 25 MB\n duration = get_duration(input_path)\n max_bitrate = (max_size_bytes * 8) / duration # bits per second\n min_samplerate = 8000\n min_channels = 1\n bits_per_sample = 16 # pcm_s16le\n max_samplerate = int(max_bitrate / (min_channels * bits_per_sample))\n \n if max_samplerate > 44100:\n samplerate = 44100\n codec = 'pcm_s16le'\n elif max_samplerate < min_samplerate:\n samplerate = min_samplerate\n codec = 'adpcm_ms'\n else:\n samplerate = max_samplerate\n codec = 'pcm_s16le'\n \n output_path = os.path.splitext(input_path)[0] + '.wav'\n ffmpeg_cmd = [\n 'ffmpeg', '-i', input_path, '-vn',\n '-acodec', codec,\n '-ar', str(samplerate),\n '-ac', str(min_channels),\n output_path,\n '-y' # overwrite without asking\n ]\n print(f\"Executing: {' '.join(ffmpeg_cmd)}\")\n subprocess.run(ffmpeg_cmd, check=True)\n print(f\"Converted file saved to: {output_path}\")\n\nif __name__ == \"__main__\":\n if len(sys.argv) != 2:\n print(f\"Usage: python {sys.argv[0]} path_to_file.mkv\")\n sys.exit(1)\n input_file = sys.argv[1]\n convert_to_wav(input_file)\n```"
},
"typeVersion": 1
},
{
"id": "18ba0df7-61ef-42cf-9348-b514f90caa73",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-7616,
336
],
"parameters": {
"color": 7,
"width": 1120,
"height": 1776,
"content": "## transcribe_return.py\n```\n# transcribe_return.py\nimport sys\nimport os\nimport whisper\nimport ffmpeg\nfrom datetime import timedelta\nimport re\n\ndef extract_audio(video_path, audio_path):\n try:\n (\n ffmpeg\n .input(video_path)\n .output(audio_path, acodec='pcm_s16le', ac=1, ar='16000')\n .overwrite_output()\n .run(capture_stdout=True, capture_stderr=True)\n )\n except ffmpeg.Error as e:\n print(\"\u274c Error extracting audio with ffmpeg:\")\n print(e.stderr.decode() if e.stderr else str(e))\n sys.exit(1)\n\ndef format_time_for_txt(seconds):\n \"\"\"Format seconds as [HH:MM:SS] for output\"\"\"\n td = timedelta(seconds=seconds)\n total_seconds = int(td.total_seconds())\n hours, remainder = divmod(total_seconds, 3600)\n minutes, seconds = divmod(remainder, 60)\n return f\"[{hours:02}:{minutes:02}:{seconds:02}]\"\n\ndef transcribe_with_timestamps(audio_path, force_language=None):\n \"\"\"\n Transcribe the audio. If force_language is None, Whisper auto-detects it.\n Returns: (segments, detected_or_forced_language)\n \"\"\"\n print(f\"\ud83d\udde3\ufe0f LANGUAGE FOR TRANSCRIPTION: {force_language or 'AUTO-DETECTION'}\")\n model = whisper.load_model(\"small\") # Change to \"small\" or \"medium\" if you need more accuracy\n result = model.transcribe(audio_path, language=force_language, word_timestamps=False)\n # If no language was forced, get the language detected by Whisper\n detected_language = result.get(\"language\", \"unknown\")\n if force_language is None:\n print(f\"\ud83d\udd0d Automatically detected language: {detected_language}\")\n return result[\"segments\"], detected_language\n\ndef extract_language_from_filename(video_path):\n \"\"\"\n Extract the language code from the filename, right before the final extension.\n Example: \"file.es.mkv\" -> returns \"es\"\n \"file.en.mp4\" -> returns \"en\"\n \"file.mp4\" -> returns None\n \"\"\"\n name, ext = os.path.splitext(video_path)\n match = re.search(r'\\.([a-z]{2})$', name)\n if match:\n return match.group(1)\n return None\n\ndef main():\n if len(sys.argv) < 2:\n print(\"\u274c Incorrect usage.\")\n print(\"\ud83d\udccc Usage: python transcribe_return.py <video>\")\n print(\" - The language is automatically extracted from the filename if it has .xx before the extension.\")\n print(\" - Examples:\")\n print(\" - video.es.mkv \u2192 uses Spanish and outputs with timestamps\")\n print(\" - video.en.mp4 \u2192 uses English and outputs with timestamps\")\n print(\" - video.mkv \u2192 auto-detects and outputs without .xx\")\n sys.exit(1)\n\n video_path = sys.argv[1]\n # \ud83d\udd0d Extract language from filename\n force_language = extract_language_from_filename(video_path)\n print(f\"\ud83c\udfac VIDEO: {video_path}\")\n if force_language:\n print(f\"\ud83d\udde3\ufe0f LANGUAGE EXTRACTED FROM FILENAME: {force_language}\")\n else:\n print(\"\ud83d\udde3\ufe0f LANGUAGE: AUTO-DETECTION (no .xx found in filename)\")\n\n # \ud83d\udcc1 Base filename WITHOUT the final extension (.mkv, .mp4, etc.)\n base_name = os.path.splitext(video_path)[0]\n audio_path = base_name + \".wav\"\n print(f\"\ud83d\udcdd Output: Transcription printed to STDOUT (no file saved)\")\n\n # Extract audio\n extract_audio(video_path, audio_path)\n # Transcribe\n segments, lang = transcribe_with_timestamps(audio_path, force_language)\n # \ud83d\udc47\ud83d\udc47\ud83d\udc47 KEY CHANGE: INSTEAD OF SAVING TO A FILE, WE PRINT DIRECTLY\n for seg in segments:\n start_time = format_time_for_txt(seg[\"start\"])\n text = seg[\"text\"].strip()\n print(f\"{start_time} {text}\")\n\n # Clean up temporary audio\n try:\n os.remove(audio_path)\n print(f\"\ud83d\uddd1\ufe0f Temporary audio deleted: {audio_path}\", file=sys.stderr)\n except Exception as e:\n print(f\"\u26a0\ufe0f Could not delete {audio_path}: {str(e)}\", file=sys.stderr)\n\n print(f\"\u2705 Process completed! Transcription generated to STDOUT.\", file=sys.stderr)\n\nif __name__ == \"__main__\":\n main()\n```"
},
"typeVersion": 1
},
{
"id": "185e7a73-ffd7-48e3-96e8-926d9287cc8a",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-9744,
272
],
"parameters": {
"color": 7,
"width": 3296,
"height": 1888,
"content": "## Helper Scripts"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"executionOrder": "v1"
},
"versionId": "e5641f53-a2ef-4315-a04a-9b4196aa019f",
"connections": {
"If": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"File": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Execute Command",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "Execute Command",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Create sub-folder",
"type": "main",
"index": 0
}
]
]
},
"Discord": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Create Wav": {
"main": [
[
{
"node": "Transcribe Local",
"type": "main",
"index": 0
}
]
]
},
"Create Notes": {
"main": [
[
{
"node": "Save Notes File",
"type": "main",
"index": 0
}
]
]
},
"gpt-oss 4090": {
"ai_languageModel": [
[
{
"node": "Create Notes",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Create a page": {
"main": [
[
{
"node": "Paste Video URL",
"type": "main",
"index": 0
}
]
]
},
"Append a block": {
"main": [
[
{
"node": "Discord",
"type": "main",
"index": 0
}
]
]
},
"Execute Command": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Append a block",
"type": "main",
"index": 0
}
]
]
},
"Paste Video URL": {
"main": [
[
{
"node": "Create Notes",
"type": "main",
"index": 0
}
]
]
},
"Save Notes File": {
"main": [
[
{
"node": "Parse for Notion",
"type": "main",
"index": 0
}
]
]
},
"Parse for Notion": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Transcribe Local": {
"main": [
[
{
"node": "Save Transcript File",
"type": "main",
"index": 0
}
]
]
},
"Create sub-folder": {
"main": [
[
{
"node": "Read MKV From Disk",
"type": "main",
"index": 0
}
]
]
},
"Upload video file": {
"main": [
[
{
"node": "Create Wav",
"type": "main",
"index": 0
}
]
]
},
"Read MKV From Disk": {
"main": [
[
{
"node": "Upload video file",
"type": "main",
"index": 0
}
]
]
},
"Set Transcript Data": {
"main": [
[
{
"node": "Create a page",
"type": "main",
"index": 0
}
]
]
},
"Save Transcript File": {
"main": [
[
{
"node": "Set Transcript Data",
"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.
discordWebhookApigoogleDriveOAuth2ApinotionApiollamaApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically transforms video recordings of meetings into structured, professional meeting minutes in Notion. It uses local AI models (Whisper for transcription and Ollama for summarization) to ensure privacy and cost efficiency, while uploading the original video…
Source: https://n8n.io/workflows/9158/ — 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.
🤖🧑💻 AI Agent for Top n8n Creators Leaderboard Reporting. Uses httpRequest, lmChatOpenAi, executeWorkflowTrigger, toolWorkflow. Event-driven trigger; 49 nodes.
🤖🧑💻 AI Agent for Top n8n Creators Leaderboard Reporting. Uses httpRequest, lmChatOpenAi, executeWorkflowTrigger, toolWorkflow. Event-driven trigger; 49 nodes.
This n8n workflow is designed to automate the aggregation, processing, and reporting of community statistics related to n8n creators and workflows. Its primary purpose is to generate insightful report
This workflow is perfect for creators, solopreneurs, and personal brands who want to consistently publish bold, high-performing content on X (Twitter) — without writing a single line themselves. After
Awesome N8N Templates. Uses notion, lmChatOpenAi, outputParserStructured, chainLlm. Event-driven trigger; 36 nodes.