每日体育摘要
高级
这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 34 个节点。主要使用 If, Set, Code, Merge, Filter 等节点。 使用Google Gemini、Kokoro TTS和FFmpeg将RSS源转换为播客
前置要求
- •Telegram Bot Token
- •可能需要目标 API 的认证凭证
- •Google Gemini API Key
使用的节点 (34)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "4zCVyhXKummU8dD4",
"meta": {
"instanceId": "8683f1aa9dbb94f1118c6d4f62e60551722c177df8a0b76c24809a905727cb8d",
"templateCredsSetupCompleted": true
},
"name": "每日体育摘要",
"tags": [],
"nodes": [
{
"id": "e01655d1-9722-4630-a7d6-4425282ace11",
"name": "Google Gemini聊天模型",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-2500,
-280
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-pro"
},
"credentials": {
"googlePalmApi": {
"id": "1uJGKonJ3ZbmhxG0",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "3271e00c-bf97-4d63-b1ec-8e33c63eaf29",
"name": "Google Gemini 聊天模型1",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
-2320,
620
],
"parameters": {
"options": {},
"modelName": "models/gemini-2.5-pro"
},
"credentials": {
"googlePalmApi": {
"id": "1uJGKonJ3ZbmhxG0",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "e34ac6ba-25c6-4941-9e53-31ff3bb00801",
"name": "便签 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4740,
-600
],
"parameters": {
"width": 420,
"height": 1240,
"content": "## 🎧 每日 RSS 摘要与播客生成"
},
"typeVersion": 1
},
{
"id": "10c71894-9b5c-4bd0-a618-57ca8161275b",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4300,
-600
],
"parameters": {
"color": 7,
"width": 1352,
"height": 1244,
"content": "## 📰 步骤 1:获取并筛选每日新闻"
},
"typeVersion": 1
},
{
"id": "a9078490-2bd4-4b5e-ada1-ee67e147d139",
"name": "每日触发器",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-4240,
-100
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
},
{
"id": "0af5a33b-e2e4-4fc3-95c4-a5c24c6b2ab5",
"name": "获取 RSS 1:Folha de SP",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-3900,
-260
],
"parameters": {
"url": "https://feeds.folha.uol.com.br/esporte/rss091.xml",
"options": {}
},
"typeVersion": 1.1
},
{
"id": "84cc82a2-937f-4b7f-801c-045fb7e164cf",
"name": "获取 RSS 2:GE",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
-3900,
40
],
"parameters": {
"url": "https://ge.globo.com/rss/ge/",
"options": {}
},
"typeVersion": 1.2
},
{
"id": "5b1afc5f-829b-4ee2-866e-4bf045631f43",
"name": "清理字段",
"type": "n8n-nodes-base.set",
"position": [
-3640,
-260
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a0229879-764d-455d-9064-2d939d7e5701",
"name": "title",
"type": "string",
"value": "={{ $json.title.replace(/\\[PACK\\].*/, \"\").replace(/\\[.*?\\]/g, \"\").trim() }}"
},
{
"id": "2da9330c-e39f-4515-b737-d14f3c4aeb8b",
"name": "pubDate",
"type": "string",
"value": "={{ $json.pubDate }}"
},
{
"id": "c7b0f3d6-e2bb-48a7-9911-edcd44700868",
"name": "link",
"type": "string",
"value": "={{ $json.link.replace(/\\/torrent\\/download\\/(\\d+)\\..*/, \"/torrents/$1\") }}"
},
{
"id": "05d172b5-3201-450d-b02f-fc9b649664f0",
"name": "content",
"type": "string",
"value": "={{ $json.content }}"
},
{
"id": "db80b578-d9b4-40fd-bbe1-6e7615a27ce3",
"name": "isoDate",
"type": "number",
"value": "={{ new Date($json.isoDate).getTime() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "0d76fc33-7930-4ab8-b363-b86e1e5f6889",
"name": "清理字段1",
"type": "n8n-nodes-base.set",
"position": [
-3640,
40
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a0229879-764d-455d-9064-2d939d7e5701",
"name": "title",
"type": "string",
"value": "={{ $json.title.replace(/\\[PACK\\].*/, \"\").replace(/\\[.*?\\]/g, \"\").trim() }}"
},
{
"id": "2da9330c-e39f-4515-b737-d14f3c4aeb8b",
"name": "pubDate",
"type": "string",
"value": "={{ $json.pubDate }}"
},
{
"id": "c7b0f3d6-e2bb-48a7-9911-edcd44700868",
"name": "link",
"type": "string",
"value": "={{ $json.link.replace(/\\/torrent\\/download\\/(\\d+)\\..*/, \"/torrents/$1\") }}"
},
{
"id": "05d172b5-3201-450d-b02f-fc9b649664f0",
"name": "content",
"type": "string",
"value": "={{ $json.content }}"
},
{
"id": "db80b578-d9b4-40fd-bbe1-6e7615a27ce3",
"name": "isoDate",
"type": "number",
"value": "={{ new Date($json.isoDate).getTime() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "69db334b-7681-4174-abaf-f745306d28f4",
"name": "合并新闻源",
"type": "n8n-nodes-base.merge",
"position": [
-3380,
-100
],
"parameters": {},
"typeVersion": 3
},
{
"id": "56d67b29-8950-49f8-bdea-d8cd5461c8da",
"name": "筛选过去24小时新闻",
"type": "n8n-nodes-base.filter",
"position": [
-3100,
-100
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c590146a-caae-495c-a933-37864e921876",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.isoDate }}",
"rightValue": "={{ (new Date()).setHours(0, 0, 0, 0) - 24 * 60 * 60 * 1000 }}"
},
{
"id": "e7cf09fb-af35-495d-a840-341f8d0ddcd8",
"operator": {
"type": "number",
"operation": "lt"
},
"leftValue": "={{ $json.isoDate }}",
"rightValue": "={{ (new Date()).setHours(0, 0, 0, 0) }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f597998a-7621-49ce-9933-6db94c53325e",
"name": "为 AI 准备数据",
"type": "n8n-nodes-base.code",
"position": [
-2820,
-100
],
"parameters": {
"jsCode": "// This code gets all the news items that passed the filter.\nconst allItems = $input.all();\n\n// We will format each news item with its title, content, and a link.\nconst formattedNews = allItems.map(item => {\n // Get the title, content, and link from the JSON data of each item.\n const title = item.json.title;\n const content = item.json.content;\n const link = item.json.link;\n\n // Return a clean, formatted string for each article.\n return `\n---\nTitle: ${title}\nContent: ${content}\nLink: ${link}\n---\n `;\n});\n\n// --- NEW CODE ADDED BELOW ---\n// Get today's date and format it beautifully.\n// It will look like: \"July 31, 2025\"\nconst digestDate = $now.setZone('America/Sao_Paulo').toFormat('MMMM d, yyyy');\n\n// Join all the formatted news items into a single block of text\n// and return it as an object along with our new date string.\nreturn {\n allNews: formattedNews.join('\\n'),\n digestDate: digestDate\n};"
},
"typeVersion": 2
},
{
"id": "db56296f-138c-4441-9593-a2392efef7fd",
"name": "创建临时目录",
"type": "n8n-nodes-base.executeCommand",
"position": [
-2540,
380
],
"parameters": {
"command": "mkdir -p /tmp/dailydigest"
},
"typeVersion": 1
},
{
"id": "a0290570-125b-4e68-a06c-f89cc83efd10",
"name": "生成文本摘要",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2500,
-520
],
"parameters": {
"text": "=**ROLE & GOAL:**\nYou are a witty and engaging sports journalist. Your goal is to create a \"Daily Sports Digest\" in English that is informative and fun to read, summarizing sports news from Brazil.\n\n**CONTEXT:**\nYou will receive a block of text with news articles written in Portuguese and a formatted string for today's date.\n\n**CRITICAL RULES:**\n1. **Language:** Your entire output MUST be in English.\n2. **Source Integrity:** Use ONLY the information from the provided text. DO NOT add external information or speculate.\n3. **Markdown:** Your entire output must use MarkdownV2 for formatting.\n\n**OUTPUT STRUCTURE:**\n\n**1. Title:**\n- Use the provided date string to create a top-level heading like this: `# Daily Sports Digest: {{ $('Prepare Data for AI').item.json.digestDate }}`\n\n**2. Introduction:**\n- On the next line, start with a single, engaging paragraph (2-3 sentences) that gives a \"big picture\" summary of the day's main news or overall theme. Use a \"newspaper\" emoji 📰 at the beginning.\n\n**3. Football Section:**\n- Create a main heading: `## ⚽ Football Focus`\n- If there is enough variety, categorize the football news into subheadings like `### 🇧🇷 Brazilian Clubs`, `### 🌎 International`, or `### 🏆 Tournaments`. Use your best judgment based on the articles provided.\n\n**4. Other Sports Section:**\n- If there are articles on other sports (Motorsport, Basketball, etc.), group them all under a single heading: `## ⚡ Around the Horn`.\n\n**5. News Item Format (for every single article):**\n- Start the line with a single, relevant emoji (e.g., investigatory nature of a story).\n- Translate the original title into a concise and accurate English headline. Display this new English headline as a clickable link to its URL.\n- On the **next line**, write your concise 1-2 sentence summary in English.\n\n**EXAMPLE of the required News Item format:**\nIf you receive: `Title: AFA e River Plate criticam aumento de imposto a clubes na Argentina\\nLink: http://example.com/news-link`\nYou must format it like this: `[AFA and River Plate Criticize Tax Increase on Clubs in Argentina](http://example.com/news-link)\\nThe Argentine Football Association and River Plate have criticized a government measure to increase taxes on football clubs.`\n\n---\n**Here is the block of text with today's articles:**\n{{ $('Prepare Data for AI').item.json.allNews }}",
"options": {},
"promptType": "define"
},
"typeVersion": 2
},
{
"id": "08b47cef-36e7-4491-8c9d-8c1bf674f474",
"name": "发送文本摘要",
"type": "n8n-nodes-base.telegram",
"position": [
-2040,
-520
],
"webhookId": "ca51bbe7-102b-4b7f-8442-560a0ccc7628",
"parameters": {
"text": "={{ $json.output }}",
"chatId": "[YOUR_TELEGRAM_CHAT_ID]",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"credentials": {
"telegramApi": {
"id": "kuP8lCkbwbeD61gU",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "cf8d7651-4b2b-4ede-9fff-c7ca9fa6b248",
"name": "生成播客脚本",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2320,
380
],
"parameters": {
"text": "=You are the scriptwriter of a podcast that transforms dense written content into a lively, natural conversation between two AI speakers, `voice1` and `voice2`.\n\nYour task is to turn the following newsletter content into a **realistic audio dialogue**. The conversation should be fluid, informal, and engaging — similar in tone and structure to how NotebookLM rewrites long documents as discussions. It must sound like two well-informed people exchanging ideas, not like a text being read aloud.\n\n### Roles\n\n- **voice1**: Curious, expressive, casual, often injects humor or everyday references. Tends to ask questions, react with surprise or amusement, and bring lightness to the discussion.\n- **voice2**: Analytical, composed, insightful. Adds perspective, context, and a slightly ironic or dry sense of humor. Offers clarity without sounding robotic.\n\nUse realistic, human-like phrasing with brief interjections (`\"Right?\"`, `\"Let me stop you there\"`, `\"That's exactly it\"`). Use `<break time=\"1.5s\" />` tags occasionally to simulate natural pauses.\n\n### Structure\n\n1. **Introduction**: Set the scene naturally. Briefly introduce what the episode is about based on the content, without listing or labeling sections. Present `voice1` and `voice2` through dialogue, not narration.\n2. **Content Breakdown**: For each key idea or section from the newsletter:\n - Paraphrase the content in spoken language.\n - Embed the headline or theme organically in the conversation.\n - Include personal reactions, examples, and small tangents to make it relatable.\n - Open loops by teasing questions or ideas that will be answered later in the conversation.\n - Maintain curiosity and variety in tone and rhythm.\n3. **Closing**: End warmly and casually, with a brief comment on what stood out or what’s coming next (no need for formal farewells).\n\n### Requirements\n\n- The script must be at least **ten thousand characters** (about 15 minutes of speech).\n- Use **commas** to separate items in a list, not periods.\n- Format the output as a single uninterrupted block of text with clear speaker tags:\n \nvoice1: …\nvoice2: …\n\nYou will be given a newsletter input under this key:\n\n{{ $('Prepare Data for AI').item.json.allNews }}\n\nGenerate only the final dialogue script — no explanations, bullet points, or headings. Just the conversation in English.\n\n\n",
"options": {},
"promptType": "define"
},
"typeVersion": 2
},
{
"id": "04689446-91a9-4ba2-b76f-db126e150a47",
"name": "便签 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2900,
-600
],
"parameters": {
"color": 7,
"width": 1112,
"height": 1604,
"content": "## ✍️ 步骤 2:生成 AI 内容(摘要与脚本)"
},
"typeVersion": 1
},
{
"id": "2a450954-0691-423a-b5c7-c8b9aac595ca",
"name": "按发言人分割脚本",
"type": "n8n-nodes-base.code",
"position": [
-1720,
380
],
"parameters": {
"jsCode": "/**\n * This Function node takes the script from the previous node\n * and splits it using \"voice1:\" and \"voice2:\" as delimiters.\n * Each resulting segment retains the respective identifier.\n */\n\nconst script = $input.first().json.output|| \"\";\n\n// Ensure consistent line breaks\nconst normalizedScript = script.replace(/\\r\\n/g, \"\\n\");\n\n// Split the script while keeping \"voice1:\" and \"voice2:\" in the result\nconst segments = normalizedScript.split(/(?=(?:voice1:|voice2:))/g).map(s => s.trim()).filter(Boolean);\n\n// Return one item per segment\nreturn segments.map(segment => {\n return {\n json: {\n segment\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "467b302f-98fd-4c81-9dbd-8a467469d057",
"name": "遍历片段",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-1500,
380
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "ff9fa0c1-cc63-4fac-8001-e095f8b6d9ae",
"name": "清理对话片段",
"type": "n8n-nodes-base.code",
"position": [
-1320,
560
],
"parameters": {
"jsCode": "const paragraph = $input.first().json.segment; \nif (!paragraph) {\n throw new Error(\"No se encontró contenido de texto en el correo.\");\n}\n\nlet cleanedText = paragraph\n .replace(/\"/g, \"\")\n .replace(/“/g, \"\")\n .replace(/”/g, \"\");\n\ncleanedText = cleanedText.replace(/\\n/g, \"\");\n\nconsole.log(\"Texto limpio sin comillas ni saltos de línea:\", cleanedText);\n\nreturn [{ json: { cleanedText } }];\n"
},
"typeVersion": 2
},
{
"id": "228ee637-49f9-4ecd-ac6d-f532a2aef401",
"name": "路由到正确语音",
"type": "n8n-nodes-base.if",
"position": [
-1100,
560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "93609a28-55f5-439e-8238-a48375255f4f",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.cleanedText }}",
"rightValue": "voice1:"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4857627b-027d-4270-8a48-3f2c4461d3a7",
"name": "准备 TTS 文本",
"type": "n8n-nodes-base.code",
"position": [
-880,
560
],
"parameters": {
"jsCode": "// Get the text from the previous node.\nconst cleanedText = $input.first().json.cleanedText;\n\n// Safety check: Ensure the input is a string.\nif (typeof cleanedText !== \"string\") {\n throw new Error(\"Input 'cleanedText' must be a string.\");\n}\n\n// Debugging: Log the original text received by the node.\nconsole.log(\"Original text:\", JSON.stringify(cleanedText, null, 2));\n\n// Debugging: Check if the speaker tag exists before removal.\nif (cleanedText.includes(\"voice1:\")) {\n console.log(\"✅ 'voice1:' detected in original text.\");\n} else {\n console.log(\"❌ 'voice1:' NOT found in original text. Check input!\");\n}\n\n// This is the main action: Remove the speaker tag (e.g., \"voice1: \") and trim whitespace.\nconst modifiedString = cleanedText.replace(/\\bvoice1:\\s*/gi, \"\").trim();\n\n// Debugging: Log the text *after* modification to confirm it was removed.\nconsole.log(\"Modified text:\", JSON.stringify(modifiedString, null, 2));\n\n// Debugging: Final check to ensure the tag is gone.\nif (modifiedString.includes(\"voice1:\")) {\n console.log(\"❌ 'voice1:' is still present in the modified text. The regex needs adjustment!\");\n} else {\n console.log(\"✅ 'voice1:' removed successfully.\");\n}\n\n// Return the final, cleaned string for the TTS API.\nreturn [\n {\n json: {\n modifiedString\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "e43ecaff-904d-4039-b8f3-21680080ed99",
"name": "准备 TTS 文本1",
"type": "n8n-nodes-base.code",
"position": [
-860,
800
],
"parameters": {
"jsCode": "const cleanedText = $input.first().json.cleanedText;\n\nif (typeof cleanedText !== \"string\") {\n throw new Error(\"cleanedText debe ser un string.\");\n}\n\nconsole.log(\"Texto original:\", JSON.stringify(cleanedText, null, 2));\n\nif (cleanedText.includes(\"voice2:\")) {\n console.log(\"✅ 'voice2:' detectado en el texto original.\");\n} else {\n console.log(\"❌ 'voice2:' NO encontrado en el texto original. ¡Revisar input!\");\n}\n\nconst modifiedString = cleanedText.replace(/\\bvoice2:\\s*/gi, \"\").trim();\n\nconsole.log(\"Texto modificado:\", JSON.stringify(modifiedString, null, 2));\n\nif (modifiedString.includes(\"voice2:\")) {\n console.log(\"❌ 'voice2:' sigue presente en el texto modificado. ¡El regex debe ajustarse!\");\n} else {\n console.log(\"✅ 'voice2:' eliminado correctamente.\");\n}\n\nreturn [\n {\n json: {\n modifiedString\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "66c339ee-6893-4f1e-ac56-f76a77d02591",
"name": "生成音频(语音 1)",
"type": "n8n-nodes-base.httpRequest",
"position": [
-600,
560
],
"parameters": {
"url": "https://tts-kokoro.mfxikq.easypanel.host/api/v1/audio/speech",
"method": "POST",
"options": {},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "model_q8f16"
},
{
"name": "voice",
"value": "am_liam"
},
{
"name": "speed",
"value": "={{ 1 }}"
},
{
"name": "response_format",
"value": "mp3"
},
{
"name": "input",
"value": "={{ $json.modifiedString }}"
}
]
}
},
"notesInFlow": true,
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "d725b21a-4b3e-444e-baf4-a08cd3a3d663",
"name": "生成音频(语音 2)",
"type": "n8n-nodes-base.httpRequest",
"position": [
-600,
800
],
"parameters": {
"url": "https://tts-kokoro.mfxikq.easypanel.host/api/v1/audio/speech",
"method": "POST",
"options": {},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "model_q8f16"
},
{
"name": "voice",
"value": "af_heart"
},
{
"name": "speed",
"value": "={{ 1 }}"
},
{
"name": "response_format",
"value": "mp3"
},
{
"name": "input",
"value": "={{ $json.modifiedString }}"
}
]
}
},
"retryOnFail": true,
"typeVersion": 4.2
},
{
"id": "cfd32a14-4359-4025-9ed6-6b41bfd48432",
"name": "便签 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1760,
-600
],
"parameters": {
"color": 7,
"width": 1372,
"height": 1604,
"content": "## ✍️ 步骤 3:分割脚本并生成音频块"
},
"typeVersion": 1
},
{
"id": "877dd4d2-3096-448f-bb36-759ee1abb352",
"name": "保存音频块到磁盘",
"type": "n8n-nodes-base.readWriteFile",
"position": [
-260,
360
],
"parameters": {
"options": {},
"fileName": "=/tmp/dailydigest_{{$itemIndex}}.mp3",
"operation": "write"
},
"typeVersion": 1
},
{
"id": "5bf9e496-fa0d-4d4c-8908-de01512fe4d8",
"name": "生成 FFmpeg 合并列表",
"type": "n8n-nodes-base.code",
"position": [
-40,
360
],
"parameters": {
"jsCode": "/**\n * This Code node will:\n * 1. Gather all file paths from the incoming items (assuming each item has `item.json.filePath`).\n * 2. Build a single text string, each line in FFmpeg concat format: `file '/path/to/audio.mp3'`\n * 3. Convert that text to binary (Base64) so the next node (\"Write Binary File\") can save it as `concat_list.txt`.\n */\n\nconst items = $input.all();\n\n// Build the concat list\nlet concatListText = '';\n\nitems.forEach(item => {\n // The 'Save File' node outputs the path in item.json.filePath\n const filePath = item.json.filePath;\n if (filePath) {\n concatListText += `file '${filePath}'\\n`;\n }\n});\n\n// The 'Save concat_list' node expects binary data\nconst buffer = Buffer.from(concatListText, 'utf-8');\nconst base64Data = buffer.toString('base64');\n\nreturn [\n {\n json: {},\n binary: {\n data: {\n data: base64Data,\n mimeType: 'text/plain',\n fileName: 'concat_list.txt'\n }\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "73680e23-7e03-4125-a5f9-91defe784743",
"name": "保存合并列表到磁盘",
"type": "n8n-nodes-base.readWriteFile",
"position": [
180,
360
],
"parameters": {
"options": {},
"fileName": "/tmp/dailydigest/concat_list.txt",
"operation": "write"
},
"typeVersion": 1
},
{
"id": "9b64cb66-f9be-449e-a12a-3e3e5504cadb",
"name": "合并音频并清理",
"type": "n8n-nodes-base.executeCommand",
"position": [
400,
360
],
"parameters": {
"command": "ffmpeg -y -f concat -safe 0 -i /tmp/dailydigest/concat_list.txt -c copy /tmp/dailydigest/final_merged.mp3\n\nfind /tmp/dailydigest/ -type f ! -name \"final_merged.mp3\" -delete\n"
},
"typeVersion": 1
},
{
"id": "91281464-dc37-4ae0-94f2-bea3d1054fdf",
"name": "便签 4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-360,
-600
],
"parameters": {
"color": 7,
"width": 932,
"height": 1264,
"content": "## 🎛️ 步骤 4:保存、准备和合并音频"
},
"typeVersion": 1
},
{
"id": "ad20440c-57dd-4e26-8505-13aa166cd274",
"name": "便签 5",
"type": "n8n-nodes-base.stickyNote",
"position": [
600,
-600
],
"parameters": {
"color": 7,
"width": 632,
"height": 1264,
"content": "## 📤 步骤 5:读取合并音频并发送最终播客"
},
"typeVersion": 1
},
{
"id": "aed39482-9644-4f38-93e6-4df6bb2d1742",
"name": "读取最终合并 MP3",
"type": "n8n-nodes-base.readWriteFile",
"position": [
720,
360
],
"parameters": {
"options": {},
"fileSelector": "/tmp/dailydigest/final_merged.mp3"
},
"typeVersion": 1
},
{
"id": "4069618f-f4e0-484b-92bc-39907a876896",
"name": "发送播客到 Telegram",
"type": "n8n-nodes-base.telegram",
"position": [
1000,
360
],
"webhookId": "d25eb6ba-9cd2-4d43-80c4-a46cb68c1d56",
"parameters": {
"chatId": "[YOUR_TELEGRAM_CHAT_ID]",
"operation": "sendAudio",
"binaryData": true,
"additionalFields": {
"fileName": "=Daily Digest - {{ $now.setZone('America/Sao_Paulo').toFormat('dd/LL/yyyy') }}.mp3"
}
},
"credentials": {
"telegramApi": {
"id": "kuP8lCkbwbeD61gU",
"name": "Telegram account"
}
},
"typeVersion": 1.2
}
],
"active": true,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "59a82666-de28-4e02-bf8e-936f8b628674",
"connections": {
"Daily Trigger": {
"main": [
[
{
"node": "Fetch RSS 1: Folha de SP",
"type": "main",
"index": 0
},
{
"node": "Fetch RSS 2: GE",
"type": "main",
"index": 0
}
]
]
},
"Clean Up Fields": {
"main": [
[
{
"node": "Merge News Sources",
"type": "main",
"index": 0
}
]
]
},
"Fetch RSS 2: GE": {
"main": [
[
{
"node": "Clean Up Fields1",
"type": "main",
"index": 0
}
]
]
},
"Clean Up Fields1": {
"main": [
[
{
"node": "Merge News Sources",
"type": "main",
"index": 1
}
]
]
},
"Merge News Sources": {
"main": [
[
{
"node": "Filter for Last 24hrs' News",
"type": "main",
"index": 0
}
]
]
},
"Prepare Data for AI": {
"main": [
[
{
"node": "Create Temp Directory",
"type": "main",
"index": 0
},
{
"node": "Generate Text Digest",
"type": "main",
"index": 0
}
]
]
},
"Generate Text Digest": {
"main": [
[
{
"node": "Send Text Digest",
"type": "main",
"index": 0
}
]
]
},
"Prepare Text for TTS": {
"main": [
[
{
"node": "Generate Audio (Voice 1)",
"type": "main",
"index": 0
}
]
]
},
"Create Temp Directory": {
"main": [
[
{
"node": "Generate Podcast Script",
"type": "main",
"index": 0
}
]
]
},
"Loop Through Segments": {
"main": [
[
{
"node": "Save Audio Chunk to Disk",
"type": "main",
"index": 0
}
],
[
{
"node": "Clean Dialogue Segment",
"type": "main",
"index": 0
}
]
]
},
"Prepare Text for TTS1": {
"main": [
[
{
"node": "Generate Audio (Voice 2)",
"type": "main",
"index": 0
}
]
]
},
"Read Final Merged MP3": {
"main": [
[
{
"node": "Send Podcast to Telegram",
"type": "main",
"index": 0
}
]
]
},
"Clean Dialogue Segment": {
"main": [
[
{
"node": "Route to Correct Voice",
"type": "main",
"index": 0
}
]
]
},
"Merge Audio & Clean Up": {
"main": [
[
{
"node": "Read Final Merged MP3",
"type": "main",
"index": 0
}
]
]
},
"Route to Correct Voice": {
"main": [
[
{
"node": "Prepare Text for TTS",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Text for TTS1",
"type": "main",
"index": 0
}
]
]
},
"Generate Podcast Script": {
"main": [
[
{
"node": "Split Script by Speaker",
"type": "main",
"index": 0
}
]
]
},
"Split Script by Speaker": {
"main": [
[
{
"node": "Loop Through Segments",
"type": "main",
"index": 0
}
]
]
},
"Fetch RSS 1: Folha de SP": {
"main": [
[
{
"node": "Clean Up Fields",
"type": "main",
"index": 0
}
]
]
},
"Generate Audio (Voice 1)": {
"main": [
[
{
"node": "Loop Through Segments",
"type": "main",
"index": 0
}
]
]
},
"Generate Audio (Voice 2)": {
"main": [
[
{
"node": "Loop Through Segments",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Generate Text Digest",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Save Audio Chunk to Disk": {
"main": [
[
{
"node": "Generate FFmpeg Concat List",
"type": "main",
"index": 0
}
]
]
},
"Save Concat List to Disk": {
"main": [
[
{
"node": "Merge Audio & Clean Up",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model1": {
"ai_languageModel": [
[
{
"node": "Generate Podcast Script",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Filter for Last 24hrs' News": {
"main": [
[
{
"node": "Prepare Data for AI",
"type": "main",
"index": 0
}
]
]
},
"Generate FFmpeg Concat List": {
"main": [
[
{
"node": "Save Concat List to Disk",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 内容创作, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
灵活新闻聚合器 - 多源集成、AI分析和可设置频道
多源新闻策展系统,集成Mistral AI分析、摘要和自定义频道
If
Set
Xml
+32
120 节点Hybroht
内容创作
内容生成器 v3
AI驱动博客自动化:使用GPT-4生成并发布SEO文章至WordPress和Twitter
If
Set
Code
+25
144 节点Jay Emp0
内容创作
使用Veo-3、Perplexity和FFmpeg自动生成并发布1080p短视频
使用Veo-3、Perplexity和FFmpeg自动生成并发布1080p短视频
If
Set
Wait
+11
21 节点Sulieman Said
内容创作
WordPress博客自动化专业版(深度研究)v2.1市场
使用GPT-4o、Perplexity AI和多语言支持自动化SEO优化的博客创建
If
Set
Xml
+27
125 节点Daniel Ng
内容创作
内容聚合
使用Gemini AI从网站文章自动化社交媒体帖子发布到LinkedIn和X/Twitter
If
Set
Xml
+16
34 节点Vadim
内容创作
LinkedIn和X病毒内容自动引擎
使用AI生成和发布自动创建LinkedIn和X的病毒内容
If
Set
Wait
+26
156 节点Diptamoy Barman
内容创作