{ "nodes": [ { "id": "aa0c62d1-2a5e-4336-8783-a8a21cb23374", "name": "English Translation Model", "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", "position": [ 1180, 760 ], "parameters": { "model": { "__rl": true, "mode": "id", "value": "gpt-4o-mini-2024-07-18" }, "options": { "temperature": 0, "maxRetries": 2 } }, "typeVersion": 1.2 }, { "id": "0c7d21e6-5bf6-4927-ad23-008b22e2ffde", "name": "When clicking \"Execute Workflow\"", "type": "n8n-nodes-base.manualTrigger", "position": [ 280, 560 ], "parameters": {}, "typeVersion": 1 }, { "id": "352de912-3a36-4bf2-b013-b46e0ace38e9", "name": "Generate French Audio", "type": "n8n-nodes-base.httpRequest", "position": [ 720, 560 ], "parameters": { "method": "POST", "url": "=https://api.elevenlabs.io/v1/text-to-speech/{{ encodeURIComponent($('Validate Text and Voice').first().json.voiceId) }}", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "optimize_streaming_latency", "value": "1" }, { "name": "output_format", "value": "mp3_44100_128" } ] }, "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "accept", "value": "audio/mpeg" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={{ {text:$json.frenchText,model_id:\"eleven_multilingual_v2\",voice_settings:{stability:0.5,similarity_boost:0.5}} }}", "options": { "response": { "response": { "responseFormat": "file", "outputPropertyName": "data" } } } }, "typeVersion": 4.2 }, { "id": "0cde2e89-0669-41b4-8fe1-1a6aff14792f", "name": "Configure French Text and Voice", "type": "n8n-nodes-base.set", "position": [ 500, 560 ], "parameters": { "assignments": { "assignments": [ { "id": "french-text", "name": "french_text", "type": "string", "value": "Bonjour. Notre équipe prépare un guide public pour expliquer son travail." }, { "id": "voice-id", "name": "voice_id", "type": "string", "value": "YOUR_ELEVENLABS_VOICE_ID" } ] }, "options": {} }, "typeVersion": 3.4 }, { "id": "38aa323e-a899-4018-afb9-4d4682ac8ff1", "name": "Translate Text to English", "type": "@n8n/n8n-nodes-langchain.chainLlm", "position": [ 1180, 560 ], "parameters": { "promptType": "define", "text": "=Translate to English:\n{{ $json.frenchTranscript }}", "batching": { "batchSize": 1 } }, "typeVersion": 1.7 }, { "id": "f0b7adad-fa0b-4764-96e0-0883bbcc02d6", "name": "Translate English text to speech", "type": "n8n-nodes-base.httpRequest", "position": [ 1540, 560 ], "parameters": { "method": "POST", "url": "=https://api.elevenlabs.io/v1/text-to-speech/{{ encodeURIComponent($('Validate Text and Voice').first().json.voiceId) }}", "authentication": "genericCredentialType", "genericAuthType": "httpHeaderAuth", "sendQuery": true, "queryParameters": { "parameters": [ { "name": "optimize_streaming_latency", "value": "1" }, { "name": "output_format", "value": "mp3_44100_128" } ] }, "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "accept", "value": "audio/mpeg" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={{ {text:$json.englishText,model_id:\"eleven_multilingual_v2\",voice_settings:{stability:0.5,similarity_boost:0.5}} }}", "options": { "response": { "response": { "responseFormat": "file", "outputPropertyName": "data" } } } }, "typeVersion": 4.2 }, { "id": "f8700266-5491-4ca7-b29a-3f5ec1e9b66f", "name": "Transcribe Audio", "type": "n8n-nodes-base.httpRequest", "position": [ 960, 560 ], "parameters": { "method": "POST", "url": "https://api.openai.com/v1/audio/transcriptions", "authentication": "predefinedCredentialType", "nodeCredentialType": "openAiApi", "sendBody": true, "contentType": "multipart-form-data", "bodyParameters": { "parameters": [ { "name": "file", "parameterType": "formBinaryData", "inputDataFieldName": "data" }, { "name": "model", "value": "whisper-1" }, { "name": "response_format", "value": "json" } ] }, "options": { "response": { "response": { "responseFormat": "json" } } } }, "typeVersion": 4.2 }, { "id": "25630b45-3827-4ee0-a77e-c30cadefe999", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ 449.2637232176971, 319.7947500318393 ], "parameters": { "color": 7, "width": 199.37543798209555, "height": 420.623805972039, "content": "## Configuration\n\nSet your own ElevenLabs voice ID and public French text. Use a French/English-capable voice with eleven_multilingual_v2. Both speech requests use stability0.5,similarity_boost0.5,MP3 and source latency optimization1. Connect Header Auth xi-api-key on both ElevenLabs nodes. See README." }, "typeVersion": 1 }, { "id": "a41d2622-4476-44c2-bac6-212be237aa4b", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [ 680, 320 ], "parameters": { "color": 7, "width": 192.21792012722693, "height": 418.3754668433847, "content": "## Models\n\nConnect OpenAI on Transcribe Audio and English Translation Model. Source Whisper1 stays until announced2027-02-26 shutdown. Translator explicitly selects gpt-4o-mini-2024-07-18,temperature0; replace before2027-01-14. No workflow retries; model adapter may internally retry transient failures." }, "typeVersion": 1 }, { "id": "58143bb1-816f-4ff6-9cac-9ce7765e02be", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [ 920, 320 ], "parameters": { "color": 7, "width": 192.21792012722693, "height": 414.59045768149747, "content": "## Outputs and storage\n\nNative execution returns English MP3 in binary data and French MP3 in binary french, plus transcripts. Open Flow counterpart stores both audio files as public-read Connector download URLs: anyone with URLs can read them; no automatic expiry verified. Use only text/audio that can be public. Failed later steps can leave earlier hosted audio. See README." }, "typeVersion": 1 }, { "id": "bd2ef5d2-c27d-45e4-a66e-a73168f94087", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ 160, 273.1221160672591 ], "parameters": { "color": 7, "width": 230.39134868652621, "height": 233.3354221029769, "content": "## Full audio chain\n\nFrench text ->French speech ->Whisper transcription ->English translation ->English speech. French synthesis/transcription are retained. JSON request objects safely carry quotes/newlines. The workflow does not verify translation or pronunciation accuracy." }, "typeVersion": 1 }, { "id": "a1f207d4-dbed-4dfa-aad5-2b2f6e4e6271", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ 440, 272.42998167622557 ], "parameters": { "color": 7, "width": 685.8541178336201, "height": 478.0993479050163, "content": "## Usage\n\nUse Execute Workflow after configuring accounts,voice and text. Both speech texts must fit10000characters and Whisper files25MB. Files are generated by ElevenLabs; describe shared recordings as AI-generated." }, "typeVersion": 1 }, { "id": "validate-french-text-voice", "name": "Validate Text and Voice", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 600, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const prepare = function prepareSpeechInput({french_text,voice_id}) {\n if(typeof french_text!=='string'||!french_text.trim()||Array.from(french_text).length>10000) throw new Error('Supply French text with 1–10000 characters.');\n if(typeof voice_id!=='string'||!voice_id.trim()) throw new Error('Supply an ElevenLabs voice ID.');\n return {frenchText:french_text,voiceId:voice_id.trim()};\n}\n\nreturn [{json:prepare($input.first().json),pairedItem:{item:0}}];" } }, { "id": "prepare-french-audio", "name": "Prepare French Audio", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1000, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const item=$input.first(),audio=item.binary?.data;\nconst mime=typeof audio?.mimeType==='string'?audio.mimeType.split(';')[0].trim().toLowerCase():'';\nif(!['audio/mpeg','audio/mp3','application/octet-stream'].includes(mime))throw new Error('The speech response is not MP3 audio.');\nreturn [{json:{},binary:{...item.binary,data:{...audio,fileName:'french.mp3'}},pairedItem:{item:0}}];" } }, { "id": "read-french-transcript", "name": "Read French Transcript", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1400, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const prepare = function readFrenchTranscript({transcription}) {\n if(!transcription||typeof transcription.text!=='string'||!transcription.text.trim()) throw new Error('The French audio returned no transcript.');\n return {frenchTranscript:transcription.text};\n}\n\nreturn [{json:prepare({transcription:$input.first().json}),pairedItem:{item:0}}];" } }, { "id": "validate-english-translation", "name": "Validate English Translation", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1800, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const prepare = function validateEnglishSpeechText({text}) {\n if(typeof text!=='string'||!text.trim()) throw new Error('The translation returned no English text.');\n const englishText=text.trim();\n if(Array.from(englishText).length>10000) throw new Error('The English translation exceeds the 10000-character speech limit.');\n return {englishText};\n}\n\nreturn [{json:prepare({text:$input.first().json.text}),pairedItem:{item:0}}];" } }, { "id": "return-bilingual-audio", "name": "French and English Audio", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2200, 0 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "const item=$input.first(),audio=item.binary?.data;\nconst mime=typeof audio?.mimeType==='string'?audio.mimeType.split(';')[0].trim().toLowerCase():'';\nif(!['audio/mpeg','audio/mp3','application/octet-stream'].includes(mime))throw new Error('The speech response is not MP3 audio.');\nreturn [{json:{frenchTranscript:$('Read French Transcript').first().json.frenchTranscript,englishText:$('Validate English Translation').first().json.englishText},binary:{data:{...audio,fileName:'english.mp3'},french:{...$('Prepare French Audio').first().binary.data,fileName:'french.mp3'}},pairedItem:{item:0}}];" } } ], "pinData": {}, "connections": { "When clicking \"Execute Workflow\"": { "main": [ [ { "node": "Configure French Text and Voice", "type": "main", "index": 0 } ] ] }, "Configure French Text and Voice": { "main": [ [ { "node": "Validate Text and Voice", "type": "main", "index": 0 } ] ] }, "Validate Text and Voice": { "main": [ [ { "node": "Generate French Audio", "type": "main", "index": 0 } ] ] }, "Generate French Audio": { "main": [ [ { "node": "Prepare French Audio", "type": "main", "index": 0 } ] ] }, "Prepare French Audio": { "main": [ [ { "node": "Transcribe Audio", "type": "main", "index": 0 } ] ] }, "Transcribe Audio": { "main": [ [ { "node": "Read French Transcript", "type": "main", "index": 0 } ] ] }, "Read French Transcript": { "main": [ [ { "node": "Translate Text to English", "type": "main", "index": 0 } ] ] }, "Translate Text to English": { "main": [ [ { "node": "Validate English Translation", "type": "main", "index": 0 } ] ] }, "Validate English Translation": { "main": [ [ { "node": "Translate English text to speech", "type": "main", "index": 0 } ] ] }, "Translate English text to speech": { "main": [ [ { "node": "French and English Audio", "type": "main", "index": 0 } ] ] }, "English Translation Model": { "ai_languageModel": [ [ { "node": "Translate Text to English", "type": "ai_languageModel", "index": 0 } ] ] } }, "active": false }