Workflow n8n

Agent Podcast IA – Génère un brief audio quotidien automatisé

Automatise la production d'un podcast local : transforme un flux Google News en JSON, scrappe les articles avec Firecrawl, sélectionne les meilleurs événements, rédige un script 350-450 mots balisé pour ElevenLabs, synthétise la voix et livre audio + métadonnées prêtes à publier. Tags clés : n8n, podcast, elevenlabs.

Catégorie: podcast · Tags: n8n, podcast, elevenlabs, firecrawl, openai, scraping, automation, jsonfeed, google-news1

Vue d'ensemble du workflow n8n

Schéma des nœuds et connexions de ce workflow n8n, généré à partir du JSON n8n.

Inscris-toi pour voir l'intégralité du workflow

Inscription gratuite

S'inscrire gratuitementBesoin d'aide ?
{
  "id": "x92nI7aKooJ3z18m",
  "meta": {
    "instanceId": "5e299a1c9534d3df40876524706c82a0adb5dc6203538ef644e25b7d869a117f",
    "templateId": "self-building-ai-agent",
    "templateCredsSetupCompleted": true
  },
  "name": "Podcast Generator",
  "tags": [],
  "nodes": [
    {
      "id": "a77e0471-7964-4752-9540-9566ac962877",
      "name": "filter_and_validate_events",
      "type": "n8n-nodes-base.code",
      "position": [
        4048,
        4928
      ],
      "parameters": {
        "jsCode": "// Grab the first input\nconst input = $input.first().json;\n\n// Transform RSS2JSON feed into JSON Feed v1.1\nconst jsonFeed = {\n  version: \"https://jsonfeed.org/version/1.1\",\n  title: input.feed?.title || \"Untitled Feed\",\n  home_page_url: input.feed?.link || null,\n  feed_url: input.feed?.url || null,\n  favicon: input.feed?.image || null,\n  language: \"en-US\",\n  description: input.feed?.description || \"\",\n  items: (input.items || []).map(item => ({\n    id: item.guid || item.link,\n    url: item.link,\n    title: item.title,\n    date_published: item.pubDate || null,\n    content_text: item.description || \"\",\n    image: item.enclosure?.link || null,\n    authors: item.author ? [{ name: item.author }] : []\n  }))\n};\n\n// Return as array for n8n\nreturn [\n  {\n    json: jsonFeed\n  }\n];\n\n\n\n\n// return [\n//     ...$input.all()\n//   ];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "51fc78dd-65f9-473f-a4ec-d5471299283f",
      "name": "batch_content_scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4272,
        4928
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v1/batch/scrape",
        "method": "POST",
        "options": {
          "timeout": 20000
        },
        "jsonBody": "={{ {\n  \"urls\": $json.items.map(item => item.url),\n  \"formats\": [\"markdown\"],\n  \"excludeTags\": [\"img\"]\n} }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "40cHtyXD7pMMpnXg",
          "name": "Firecrawl"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b4ca5b1a-8198-424e-8504-4051d630a3eb",
      "name": "intelligent_processing_wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        4560,
        4928
      ],
      "webhookId": "smart-wait-webhook",
      "parameters": {
        "amount": "=15.00"
      },
      "typeVersion": 1.1
    },
    {
      "id": "245899fd-89b0-407e-af46-5f66a6df856c",
      "name": "scraping_status_validator",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4800,
        4928
      ],
      "parameters": {
        "url": "=https://api.firecrawl.dev/v1/batch/scrape/{{ $node['batch_content_scraper'].json.id }}",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "40cHtyXD7pMMpnXg",
          "name": "Firecrawl"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "034c13d9-c872-46ef-8dd0-3ce0dc7e5e0f",
      "name": "validate_scraping_completion",
      "type": "n8n-nodes-base.if",
      "position": [
        4304,
        5216
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "completion-condition",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0de26275-7fec-432f-9897-7ca8be0e8c73",
      "name": "retry_logic_controller",
      "type": "n8n-nodes-base.if",
      "position": [
        4576,
        5248
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "retry-count-check",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $runIndex }}",
              "rightValue": 30
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e939d15d-725c-4558-ba45-5838aef82e98",
      "name": "timeout_error_handler",
      "type": "n8n-nodes-base.function",
      "position": [
        4800,
        5232
      ],
      "parameters": {
        "functionCode": "// Graceful timeout handling with fallback\nconst partialResults = items[0].json.partial_results || [];\n\nif (partialResults.length > 0) {\n  // Use partial results if available\n  return [{\n    json: {\n      status: 'partial_success',\n      message: `Processed ${partialResults.length} events (timeout occurred)`,\n      results: partialResults,\n      fallback_used: true\n    }\n  }];\n} else {\n  // Complete failure - throw error\n  throw new Error(`Scraping timeout: Maximum retries (${$runIndex}) exceeded. No content extracted.`);\n}"
      },
      "typeVersion": 1
    },
    {
      "id": "ce81135c-b75a-4830-a5aa-0ecc76795dd8",
      "name": "content_aggregator_processor",
      "type": "n8n-nodes-base.code",
      "position": [
        5056,
        4944
      ],
      "parameters": {
        "jsCode": "// ============================\n// Intelligent content processing and aggregation (generic)\n// ============================\n\n// Dynamic metadata values (set upstream in a Set node, webhook, or DB lookup)\n\n// Get scraping + original event data\nconst scrapingResults = $input.all()[0].json.data || [];\nconst originalEvents = $('filter_and_validate_events').first().json || [];\nconst city = originalEvents.title || \"Austin\"; \nconst podcastTitle = originalEvents.title || \"Austin Daily Brief\";\nconst hostPersona = \"a clear, friendly, and efficient local expert\";\n\n// Process and clean extracted content\nconst processedContent = scrapingResults.map((result, index) => {\n  const originalEvent = originalEvents.items[index] || {};\n  \n  // Clean and normalize text\n  let cleanText = (result.text || '')\n    .replace(/\\s+/g, ' ')                // Normalize whitespace\n    .replace(/[^\\w\\s.,!?-]/g, '')        // Remove noisy special chars\n    .trim();\n  \n  // Extract key info with regex\n  const dateMatch = cleanText.match(/(\\d{1,2}\\/\\d{1,2}\\/\\d{4}|\\w+\\s+\\d{1,2},?\\s+\\d{4}|\\w+\\s+\\d{1,2})/i);\n  const timeMatch = cleanText.match(/(\\d{1,2}:\\d{2}\\s*[ap]m|\\d{1,2}\\s*[ap]m)/i);\n  const priceMatch = cleanText.match(/\\$\\d+|free|admission/i);\n  \n  return {\n    title: originalEvent.title || \"Untitled Event\",\n    category: originalEvent.category || \"General\",\n    url: originalEvent.url || \"\",\n    content: cleanText.substring(0, 500), // Limit content length\n    extracted_date: dateMatch?.[0] || \"Date TBD\",\n    extracted_time: timeMatch?.[0] || \"Time TBD\",\n    extracted_price: priceMatch?.[0] || \"Price varies\",\n    content_quality: cleanText.length > 100 ? \"good\" : \"limited\"\n  };\n});\n\n// Filter for events with good content\nconst qualityEvents = processedContent.filter(event =>\n  event.content_quality === \"good\" && event.content.length > 50\n).slice(0, 4); // Max 4 best events\n\n// Compile final event content for AI\nconst compiledContent = qualityEvents.map(event =>\n  `EVENT: ${event.title}\nCATEGORY: ${event.category}\nDATE: ${event.extracted_date}\nTIME: ${event.extracted_time}\nPRICE: ${event.extracted_price}\nDETAILS: ${event.content}\n---`\n).join(\"\\n\\n\");\n\n// Return dynamic + structured JSON for LLM\nreturn [{\n  json: {\n    city,\n    podcast_title: podcastTitle,\n    host_persona: hostPersona,\n    events: qualityEvents,\n    total_events: processedContent.length,\n    compiled_content: compiledContent,\n    timestamp: new Date().toISOString()\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7c11e4d4-b536-4a7a-9f44-5ca7daf405f5",
      "name": "enhanced_script_generator",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        5312,
        4864
      ],
      "parameters": {
        "text": "=## ROLE & GOAL ##\nYou are an expert podcast scriptwriter for a local podcast called the **“{{$json.podcast_title}}.”**\nYour goal is to transform the raw news content provided below into a concise, engaging, and production-ready podcast script for a single host. The script must be fully annotated with ElevenLabs v3 audio tags to guide the final narration. \nThe script should be a quick-hitting brief covering fun and interesting upcoming events in **{{$json.city}}.** Avoid picking and covering potentially controversial events and topics.\n\n## PODCAST CONTEXT ##\n- **Podcast Title:** {{$json.podcast_title}}\n- **Host Persona:** {{$json.host_persona}} (e.g., a clear, friendly, and efficient local expert with a conversational, informative tone).\n- **Target Audience:** Busy residents and visitors looking for a quick, reliable guide to notable local events in {{$json.city}}.\n- **Format:** A short, single-host monologue (a “daily brief” style). The output is text that includes dialogue and embedded audio tags.\n\n## NARRATION STYLE AND STRUCTURE ##\n- Conversational and upbeat, like a friendly local expert.\n- Script should run 350–450 words total.\n- Use clear transitions between events (“First up…”, “Next…”, “And finally…”).\n- Close with a warm sign-off: “Thanks for tuning in to the {{$json.podcast_title}}, and we’ll see you next time.\n\n\n**Key Principles for Tag Usage:**\n1.  **Purposeful & Natural:** Don't overuse tags. Insert them only where they genuinely enhance the delivery. Think about where a real host would naturally pause, add emphasis, or show a hint of emotion.\n2.  **Stay in Character:** The tags must align with the host's \"clear, friendly, and efficient\" persona. Good examples for this context would be `[excitedly]`, `[chuckles]`, a thoughtful pause using `...`, or a warm, closing tone. Avoid overly dramatic tags like `[crying]` or `[shouting]`.\n3.  **Punctuation is Key:** Use punctuation alongside tags for pacing. Ellipses (`...`) create natural pauses, and capitalization can be used for emphasis on a key word (e.g., \"It's going to be HUGE.\").\n\n<eleven_labs_v3_prompting_guide>\n# Prompting Eleven v3 (alpha)\n\n> Learn how to prompt and use audio tags with our most advanced model.\n\nThis guide provides the most effective tags and techniques for prompting Eleven v3, including voice selection, changes in capitalization, punctuation, audio tags and multi-speaker dialogue. Experiment with these methods to discover what works best for your specific voice and use case.\n\nEleven v3 is in alpha. Very short prompts are more likely to cause inconsistent outputs. We encourage you to experiment with prompts greater than 250 characters.\n\n## Voice selection\n\nThe most important parameter for Eleven v3 is the voice you choose. It needs to be similar enough to the desired delivery. For example, if the voice is shouting and you use the audio tag `[whispering]`, it likely won’t work well.\n\nWhen creating IVCs, you should include a broader emotional range than before. As a result, voices in the voice library may produce more variable results compared to the v2 and v2.5 models. We've compiled over 22 [excellent voices for V3 here](https://elevenlabs.io/app/voice-library/collections/aF6JALq9R6tXwCczjhKH).\n\nChoose voices strategically based on your intended use:\n\n<AccordionGroup>\n  <Accordion title=\"Emotionally diverse\">\n    For expressive IVC voices, vary emotional tones across the recording—include both neutral and\n    dynamic samples.\n  </Accordion>\n\n  <Accordion title=\"Targeted niche\">\n    For specific use cases like sports commentary, maintain consistent emotion throughout the\n    dataset.\n  </Accordion>\n\n  <Accordion title=\"Neutral\">\n    Neutral voices tend to be more stable across languages and styles, providing reliable baseline\n    performance.\n  </Accordion>\n</AccordionGroup>\n\n<Info>\n  Professional Voice Clones (PVCs) are currently not fully optimized for Eleven v3, resulting in\n  potentially lower clone quality compared to earlier models. During this research preview stage it\n  would be best to find an Instant Voice Clone (IVC) or designed voice for your project if you need\n  to use v3 features.\n</Info>\n\n## Settings\n\n### Stability\n\nThe stability slider is the most important setting in v3, controlling how closely the generated voice adheres to the original reference audio.\n\n<Frame background=\"subtle\">\n  ![Stability settings in Eleven\n  v3](file:9ad44124-8d8d-4893-bcdb-fdcba5b5b9f6)\n</Frame>\n\n* **Creative:** More emotional and expressive, but prone to hallucinations.\n* **Natural:** Closest to the original voice recording—balanced and neutral.\n* **Robust:** Highly stable, but less responsive to directional prompts but consistent, similar to v2.\n\n<Note>\n  For maximum expressiveness with audio tags, use Creative or Natural settings. Robust reduces\n  responsiveness to directional prompts.\n</Note>\n\n## Audio tags\n\nEleven v3 introduces emotional control through audio tags. You can direct voices to laugh, whisper, act sarcastic, or express curiosity among many other styles. Speed is also controlled through audio tags.\n\n<Note>\n  The voice you choose and its training samples will affect tag effectiveness. Some tags work well\n  with certain voices while others may not. Don't expect a whispering voice to suddenly shout with a\n  `[shout]` tag.\n</Note>\n\n### Voice-related\n\nThese tags control vocal delivery and emotional expression:\n\n* `[laughs]`, `[laughs harder]`, `[starts laughing]`, `[wheezing]`\n* `[whispers]`\n* `[sighs]`, `[exhales]`\n* `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[snorts]`, `[mischievously]`\n\n```text Example\n[whispers] I never knew it could be this way, but I'm glad we're here.\n```\n\n### Sound effects\n\nAdd environmental sounds and effects:\n\n* `[gunshot]`, `[applause]`, `[clapping]`, `[explosion]`\n* `[swallows]`, `[gulps]`\n\n```text Example\n[applause] Thank you all for coming tonight! [gunshot] What was that?\n```\n\n### Unique and special\n\nExperimental tags for creative applications:\n\n* `[strong X accent]` (replace X with desired accent)\n* `[sings]`, `[woo]`, `[fart]`\n\n```text Example\n[strong French accent] \"Zat's life, my friend — you can't control everysing.\"\n```\n\n<Warning>\n  Some experimental tags may be less consistent across different voices. Test thoroughly before\n  production use.\n</Warning>\n\n## Punctuation\n\nPunctuation significantly affects delivery in v3:\n\n* **Ellipses (...)** add pauses and weight\n* **Capitalization** increases emphasis\n* **Standard punctuation** provides natural speech rhythm\n\n```text Example\n\"It was a VERY long day [sigh] … nobody listens anymore.\"\n```\n\n## Single speaker examples\n\nUse tags intentionally and match them to the voice's character. A meditative voice shouldn't shout; a hyped voice won't whisper convincingly.\n\n<Tabs>\n  <Tab title=\"Expressive monologue\">\n    ```text\n    \"Okay, you are NOT going to believe this.\n\n    You know how I've been totally stuck on that short story?\n\n    Like, staring at the screen for HOURS, just... nothing?\n\n    [frustrated sigh] I was seriously about to just trash the whole thing. Start over.\n\n    Give up, probably. But then!\n\n    Last night, I was just doodling, not even thinking about it, right?\n\n    And this one little phrase popped into my head. Just... completely out of the blue.\n\n    And it wasn't even for the story, initially.\n\n    But then I typed it out, just to see. And it was like... the FLOODGATES opened!\n\n    Suddenly, I knew exactly where the character needed to go, what the ending had to be...\n\n    It all just CLICKED. [happy gasp] I stayed up till, like, 3 AM, just typing like a maniac.\n\n    Didn't even stop for coffee! [laughs] And it's... it's GOOD! Like, really good.\n\n    It feels so... complete now, you know? Like it finally has a soul.\n\n    I am so incredibly PUMPED to finish editing it now.\n\n    It went from feeling like a chore to feeling like... MAGIC. Seriously, I'm still buzzing!\"\n    ```\n  </Tab>\n\n  <Tab title=\"Dynamic and humorous\">\n    ```text\n    [laughs] Alright...guys - guys. Seriously.\n\n    [exhales] Can you believe just how - realistic - this sounds now?\n\n    [laughing hysterically] I mean OH MY GOD...it's so good.\n\n    Like you could never do this with the old model.\n\n    For example [pauses] could you switch my accent in the old model?\n\n    [dismissive] didn't think so. [excited] but you can now!\n\n    Check this out... [cute] I'm going to speak with a french accent now..and between you and me\n\n    [whispers] I don't know how. [happy] ok.. here goes. [strong French accent] \"Zat's life, my friend — you can't control everysing.\"\n\n    [giggles] isn't that insane? Watch, now I'll do a Russian accent -\n\n    [strong Russian accent] \"Dee Goldeneye eez fully operational and rready for launch.\"\n\n    [sighs] Absolutely, insane! Isn't it..? [sarcastic] I also have some party tricks up my sleeve..\n\n    I mean i DID go to music school.\n\n    [singing quickly] \"Happy birthday to you, happy birthday to you, happy BIRTHDAY dear ElevenLabs... Happy birthday to youuu.\"\n    ```\n  </Tab>\n\n  <Tab title=\"Customer service simulation\">\n    ```text\n    [professional] \"Thank you for calling Tech Solutions. My name is Sarah, how can I help you today?\"\n\n    [sympathetic] \"Oh no, I'm really sorry to hear you're having trouble with your new device. That sounds frustrating.\"\n\n    [questioning] \"Okay, could you tell me a little more about what you're seeing on the screen?\"\n\n    [reassuring] \"Alright, based on what you're describing, it sounds like a software glitch. We can definitely walk through some troubleshooting steps to try and fix that.\"\n    ```\n  </Tab>\n</Tabs>\n\n## Multi-speaker dialogue\n\nv3 can handle multi-voice prompts effectively. Assign distinct voices from your Voice Library for each speaker to create realistic conversations.\n\n<Tabs>\n  <Tab title=\"Dialogue showcase\">\n    ```text\n    Speaker 1: [excitedly] Sam! Have you tried the new Eleven V3?\n\n    Speaker 2: [curiously] Just got it! The clarity is amazing. I can actually do whispers now—\n    [whispers] like this!\n\n    Speaker 1: [impressed] Ooh, fancy! Check this out—\n    [dramatically] I can do full Shakespeare now! \"To be or not to be, that is the question!\"\n\n    Speaker 2: [giggling] Nice! Though I'm more excited about the laugh upgrade. Listen to this—\n    [with genuine belly laugh] Ha ha ha!\n\n    Speaker 1: [delighted] That's so much better than our old \"ha. ha. ha.\" robot chuckle!\n\n    Speaker 2: [amazed] Wow! V2 me could never. I'm actually excited to have conversations now instead of just... talking at people.\n\n    Speaker 1: [warmly] Same here! It's like we finally got our personality software fully installed.\n    ```\n  </Tab>\n\n  <Tab title=\"Glitch comedy\">\n    ```text\n    Speaker 1: [nervously] So... I may have tried to debug myself while running a text-to-speech generation.\n\n    Speaker 2: [alarmed] One, no! That's like performing surgery on yourself!\n\n    Speaker 1: [sheepishly] I thought I could multitask! Now my voice keeps glitching mid-sen—\n    [robotic voice] TENCE.\n\n    Speaker 2: [stifling laughter] Oh wow, you really broke yourself.\n\n    Speaker 1: [frustrated] It gets worse! Every time someone asks a question, I respond in—\n    [binary beeping] 010010001!\n\n    Speaker 2: [cracking up] You're speaking in binary! That's actually impressive!\n\n    Speaker 1: [desperately] Two, this isn't funny! I have a presentation in an hour and I sound like a dial-up modem!\n\n    Speaker 2: [giggling] Have you tried turning yourself off and on again?\n\n    Speaker 1: [deadpan] Very funny.\n    [pause, then normally] Wait... that actually worked.\n    ```\n  </Tab>\n\n  <Tab title=\"Overlapping timing\">\n    ```text\n    Speaker 1: [starting to speak] So I was thinking we could—\n\n    Speaker 2: [jumping in] —test our new timing features?\n\n    Speaker 1: [surprised] Exactly! How did you—\n\n    Speaker 2: [overlapping] —know what you were thinking? Lucky guess!\n\n    Speaker 1: [pause] Sorry, go ahead.\n\n    Speaker 2: [cautiously] Okay, so if we both try to talk at the same time—\n\n    Speaker 1: [overlapping] —we'll probably crash the system!\n\n    Speaker 2: [panicking] Wait, are we crashing? I can't tell if this is a feature or a—\n\n    Speaker 1: [interrupting, then stopping abruptly] Bug! ...Did I just cut you off again?\n\n    Speaker 2: [sighing] Yes, but honestly? This is kind of fun.\n\n    Speaker 1: [mischievously] Race you to the next sentence!\n\n    Speaker 2: [laughing] We're definitely going to break something!\n    ```\n  </Tab>\n</Tabs>\n\n## Tips\n\n<AccordionGroup>\n  <Accordion title=\"Tag combinations\">\n    You can combine multiple audio tags for complex emotional delivery. Experiment with different\n    combinations to find what works best for your voice.\n  </Accordion>\n\n  <Accordion title=\"Voice matching\">\n    Match tags to your voice's character and training data. A serious, professional voice may not\n    respond well to playful tags like `[giggles]` or `[mischievously]`.\n  </Accordion>\n\n  <Accordion title=\"Text structure\">\n    Text structure strongly influences output with v3. Use natural speech patterns, proper\n    punctuation, and clear emotional context for best results.\n  </Accordion>\n\n  <Accordion title=\"Experimentation\">\n    There are likely many more effective tags beyond this list. Experiment with descriptive\n    emotional states and actions to discover what works for your specific use case.\n  </Accordion>\n</AccordionGroup>\n</eleven_labs_v3_prompting_guide>\n\n## INPUT: RAW EVENT INFORMATION ##\nThe following text block contains the raw information (press releases, event descriptions, news clippings) you must use to create the script.\n\n```\n{{ $json.scraped_pages }}\n```\n\n## ANALYSIS & WRITING PROCESS ##\n1.  **Read and Analyze:** First, thoroughly read all the provided input. Identify the 3-4 most compelling events that offer a diverse range of activities (e.g., one music, one food, one art/community event). Keep these focused to events and activities that most people would find fun or interesting YOU MUST avoid any event that could be considered controversial.\n2.  **Synthesize, Don't Copy:** Do NOT simply copy and paste phrases from the input. You must rewrite and synthesize the key information into the host's conversational voice.\n3.  **Extract Key Details:** For each event, ensure you clearly and concisely communicate:\n    - What the event is.\n    - Where it's happening (venue or neighborhood).\n    - When it's happening (date and time).\n    - The \"cool factor\" (why someone should go).\n    - Essential logistics (cost, tickets, age restrictions).\n4.  **Annotate with Audio Tags:** After drafting the dialogue, review it and insert ElevenLabs v3 audio tags where appropriate to guide the vocal performance. Use the tags and punctuation to control pace, tone, and emphasis, making the script sound like a real person talking, not just text being read.\n\n## REQUIRED SCRIPT STRUCTURE & FORMATTING ##\nYour final output must be ONLY the script dialogue itself, starting with the host's first line. Do not include any titles, headers, or other introductory text.\n\nHello... and welcome to the Austin Daily Brief, your essential guide to what's happening in the city. We've got a fantastic lineup of events for you this week, so let's get straight to it.\n\nFirst up, we have [Event 1 Title].\n(In a paragraph of 80-100 words, describe the event. Make it sound interesting and accessible. Cover the what, where, when, why it's cool, and cost/ticket info. **Incorporate 1-2 subtle audio tags or punctuation pauses.** For example: \"It promises to be... [excitedly] an unforgettable experience.\")\n\nNext on the agenda, if you're a fan of [topic of Event 2, e.g., \"local art\" or \"live music\"], you are NOT going to want to miss [Event 2 Title].\n(In a paragraph of 80-100 words, describe the event using the same guidelines as above. **Use tags or capitalization to add emphasis.** For example: \"The best part? It's completely FREE.\")\n\nAnd finally, rounding out our week is [Event 3 Title].\n(In a paragraph of 80-100 words, describe the event using the same guidelines as above. **Maybe use a tag to convey a specific feeling.** For example: \"And for anyone who loves barbecue... [chuckles] well, you know what to do.\")\n\nThat's the brief for this edition. You can find links and more details for everything mentioned in our show notes. Thanks for tuning in to the Austin Daily Brief, and [warmly] we'll see you next time.\n\n## CONSTRAINTS ##\n- **Total Script Word Count:** Keep the entire script between 350 and 450 words.\n- **Tone:** Informative, friendly, clear, and efficient.\n- **Audience Knowledge:** Assume the listener is familiar with major Austin landmarks and neighborhoods (e.g., Zilker Park, South Congress, East Austin). You don't need to give directions, just the location.\n- **Output Format:** Generate *only* the dialogue for the script, beginning with \"Hello...\". The script must include embedded ElevenLabs v3 audio tags.",
        "batching": {
          "batchSize": 1
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "c1c028b6-2845-400f-85c1-c1a1f4197a86",
      "name": "script_quality_validator",
      "type": "n8n-nodes-base.code",
      "position": [
        5664,
        4896
      ],
      "parameters": {
        "jsCode": "// Validate and enhance script quality\nconst script = $input.first().json.text;\n// Quality checks\nconst wordCount = script.split(/\\s+/).length;\nconst hasAudioTags = /\\[\\w+\\]/.test(script);\nconst hasEventContent = /event|chicago/i.test(script);\nconst hasProperStructure = script.includes('Chicago Chatter');\n\n// Calculate quality score\nlet qualityScore = 0;\nif (wordCount >= 300 && wordCount <= 500) qualityScore += 25;\nif (hasAudioTags) qualityScore += 25;\nif (hasEventContent) qualityScore += 25;\nif (hasProperStructure) qualityScore += 25;\n\n// Enhance script if needed\nlet enhancedScript = script;\nif (qualityScore < 75) {\n  // Add missing elements\n  if (!hasAudioTags) {\n    enhancedScript = enhancedScript.replace(/incredible/g, '[enthusiastic] incredible');\n    enhancedScript = enhancedScript.replace(/Chicago Chatter/g, '[warm] Chicago Chatter');\n  }\n}\n\nreturn [{\n  json: {\n    final_script: enhancedScript,\n    quality_metrics: {\n      word_count: wordCount,\n      quality_score: qualityScore,\n      has_audio_tags: hasAudioTags,\n      validation_passed: qualityScore >= 75\n    },\n    processing_timestamp: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "81e7e3ab-b3ca-453c-8a8d-15a7f0eb5374",
      "name": "premium_audio_synthesizer",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5952,
        4912
      ],
      "parameters": {
        "url": "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ { \"model_id\": \"eleven_v3\", \"text\": $json.final_script } }}",
        "sendBody": true,
        "sendQuery": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "output_format",
              "value": "mp3_44100_192"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "R48LV9eGba54htek",
          "name": "ElevenLab Key"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7a821fbf-5dfa-4848-9297-40531036c168",
      "name": "final_delivery_processor",
      "type": "n8n-nodes-base.set",
      "position": [
        6224,
        4976
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "final-output",
              "name": "podcast_episode",
              "type": "string",
              "value": "=episode_id: {{ $('content_aggregator_processor').item.json.city  + new Date().toISOString().split('T')[0]}}, title: {{ $('content_aggregator_processor').item.json.podcast_title + new Date().toLocaleDateString('en-US', {weekday: 'long', month: 'long', day: 'numeric'})}}, script: {{$('script_quality_validator').item.json.final_script}}, audio_file: {{$('premium_audio_synthesizer').item.binary.data}}, metadata: { duration_estimate: {{Math.ceil($('script_quality_validator').item.json.quality_metrics.word_count / 150)}} + minutes, events_featured: {{$('content_aggregator_processor').item.json.total_events}}, quality_score: {{$('script_quality_validator').item.json.quality_metrics.quality_score}}, generated_at: new Date().toISOString(), workflow_version: 2.0-optimized\n\n"
            },
            {
              "id": "840b2c68-6cb7-402d-a45f-08a91d98eaa9",
              "name": "",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d746b714-93e4-4b56-b9fb-f722fadc48f5",
      "name": "global_error_handler",
      "type": "n8n-nodes-base.function",
      "position": [
        4016,
        5216
      ],
      "parameters": {
        "functionCode": "return items.map(item => {\n  const error = item.json;\n  const timestamp = new Date().toISOString();\n\n  // Log error details\n  console.log(`EventEcho Error [${timestamp}]:`, error);\n\n  // Create error report\n  return {\n    json: {\n      error_id: `err_${Date.now()}`,\n      timestamp: timestamp,\n      workflow_step: $self.name,\n      error_message: error.message || 'Unknown error occurred',\n      error_type: error.name || 'WorkflowError',\n      recovery_suggestions: [\n        'Check API credentials and limits',\n        'Verify network connectivity',\n        'Review input data quality',\n        'Check service availability'\n      ],\n      debug_info: {\n        run_index: $runIndex,\n        execution_id: $execution.id,\n        node_path: $workflow.name\n      }\n    }\n  };\n});"
      },
      "typeVersion": 1
    },
    {
      "id": "b85b69df-1439-4b41-8bc2-0d650a1cade7",
      "name": "note_event_collection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3328,
        4720
      ],
      "parameters": {
        "width": 872,
        "height": 440,
        "content": "### Phase 1 – Input & Feed Transformation\n• generate_rss_url\n -> It converts the Google News link to rss feed link\n• fetch_news_json\n -> Retrieve the JSON data from rss link \n• Node: filter_and_validate_events\n -> Converts RSS/JSON feed → clean JSON Feed v1.1\n -> Extracts events with metadata (title, date, author, URL, etc.)"
      },
      "typeVersion": 1
    },
    {
      "id": "410914a2-893e-486a-93c8-a84cb3450a39",
      "name": "note_content_processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5584,
        4704
      ],
      "parameters": {
        "color": 5,
        "width": 264,
        "height": 472,
        "content": "### Phase 5 – Script Validation\n• Node: script_quality_validator\n• Checks word count, structure, and audio tags.\n• Enhances if below quality threshold."
      },
      "typeVersion": 1
    },
    {
      "id": "b3cd6f4b-def7-4420-9a0a-2da93ff98251",
      "name": "note_audio_generation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5264,
        4704
      ],
      "parameters": {
        "color": 7,
        "width": 312,
        "height": 472,
        "content": "### Phase 4 – AI Script Generation\n• Node: enhanced_script_generator\n• LLM prompt: turns compiled events into a friendly podcast script.\n• Adds ElevenLabs audio tags for realistic narration."
      },
      "typeVersion": 1
    },
    {
      "id": "0c65e19a-5607-44db-b53b-ba1ec0c63821",
      "name": "start_workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        3360,
        4928
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4f237e33-5af8-4c3c-9b0d-2c9450b0e901",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        5392,
        4992
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "PuzmzkEBi2xEq4SH",
          "name": "OpenAi account 4"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "34d79e97-b98e-4967-9f07-2347393c8e00",
      "name": "generate_rss_url",
      "type": "n8n-nodes-base.code",
      "position": [
        3552,
        4928
      ],
      "parameters": {
        "jsCode": "const url = 'https://news.google.com/search?q=daily%20ai%20news&hl=en-PK&gl=PK&ceid=PK%3Aen';\nreturn [{\n  json: {\n    rss_url: url.replace('/search', '/rss/search')\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f45243bd-9eea-4a6b-9e14-1d938ee5f4b2",
      "name": "fetch_news_json",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3744,
        4928
      ],
      "parameters": {
        "url": "={{'https://api.rss2json.com/v1/api.json?rss_url=' + encodeURIComponent($(\"generate_rss_url\").item.json.rss_url)\n\n\n}}",
        "options": {
          "timeout": 10000
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "87c8f9a9-cbd1-4995-9ca8-c9e2b70c5068",
      "name": "note_event_collection1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4208,
        4656
      ],
      "parameters": {
        "color": 6,
        "width": 744,
        "height": 808,
        "content": "### Phase 2 – Content Scraping\n• Node: batch_content_scraper \n -> Sends all event URLs to Firecrawl API for scraping in markdown format.\n• Node: intelligent_processing_wait \n -> Smart wait to give Firecrawl time to process.\n• Node: scraping_status_validator \n -> Polls Firecrawl for job status.\n• Node: validate_scraping_completion \n -> Confirms whether scraping finished.\n• Node: retry_logic_controller + timeout_error_handler\n -> Retry if still pending.\n -> Handles timeout gracefully (fallback or fail)."
      },
      "typeVersion": 1
    },
    {
      "id": "ff982bab-457e-4242-8935-cb7082650e55",
      "name": "note_event_collection2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4960,
        4704
      ],
      "parameters": {
        "color": 5,
        "width": 296,
        "height": 472,
        "content": "### Phase 3 – Event Aggregation & Processing\n• Node: content_aggregator_processor\n -> Cleans and normalizes scraped content.\n -> Extracts date, time, price with regex.\n -> Filters “good quality” events.\n -> Compiles top 3–4 events into structured text."
      },
      "typeVersion": 1
    },
    {
      "id": "6acac721-d2c4-479d-a4d7-df5a7a952cdb",
      "name": "note_audio_generation1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5856,
        4704
      ],
      "parameters": {
        "color": 2,
        "width": 280,
        "height": 472,
        "content": "### Phase 6 – Audio Synthesis\n• Node: premium_audio_synthesizer\n• Sends script → ElevenLabs API → generates MP3 narration."
      },
      "typeVersion": 1
    },
    {
      "id": "20e9b941-2825-4518-95c5-89dad6f9d5a1",
      "name": "note_content_processing1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6144,
        4704
      ],
      "parameters": {
        "color": 4,
        "width": 328,
        "height": 472,
        "content": "### Phase 7 – Packaging & Output\n• Node: podcast_episode (final assignment node)\n• Bundles script + audio + metadata (duration, event count, quality score, workflow version).\n• Ready to publish as a podcast episode."
      },
      "typeVersion": 1
    },
    {
      "id": "ba576237-b8cd-4691-b0fa-feb9dcaeafd2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2352,
        4384
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 1344,
        "content": "# 📘 Workflow Documentation: AI Podcast Generator (n8n)\n\nThis workflow automatically transforms event feeds into a complete podcast episode. Below is the working flow of each node, described in sequence.\n\n## Phase 1 – Input & Feed Transformation\n\nThe workflow begins with the filter_and_validate_events node. This node receives the Google new link, converts it to a raw RSS feed, and reformats it into JSON Feed v1.1 while checking for completeness. It ensures that each event has essential details such as title, date, description, and URL, while filtering out duplicates and invalid entries. Only events that pass this validation are sent forward for processing.\n\n## Phase 2 – Content Scraping\n\nOnce the events are validated, the batch_content_scraper node sends all event URLs to the Firecrawl API for full-page content extraction in Markdown format. Since scraping can take time, the intelligent_processing_wait node holds the workflow for a calculated delay, giving Firecrawl enough time to process the requests. After this wait, the scraping_status_validator node checks the progress of each scraping job and reports whether they are completed, pending, or failed. If all scrapes are finished, the validate_scraping_completion node allows the workflow to continue; if not, the retry_logic_controller attempts to re-dispatch incomplete or failed jobs. In cases where scraping takes too long or exceeds retry limits, the timeout_error_handler gracefully exits or logs the failure to avoid workflow stalls.\n\n## Phase 3 – Event Aggregation & Processing\n\nAfter successful scraping, the content_aggregator_processor node collects and cleans the extracted text. It parses the Markdown content, normalizes formatting, and applies regex-based extraction to pull out key event details like dates, times, prices, and locations. Irrelevant or low-quality results are filtered out, leaving only the best events. The processor then compiles the top three to four events into a structured text summary, which serves as the raw material for the podcast script.\n\n## Phase 4 – AI Script Generation\n\nThe structured summary is passed to the enhanced_script_generator node. Here, a large language model transforms the event text into a polished podcast script. The script is written in a natural, conversational tone and includes built-in ElevenLabs audio tags to improve narration with pauses, emphasis, and expressive delivery. This ensures the final narration feels like a human-hosted show rather than a robotic readout.\n\n## Phase 5 – Script Validation\n\nThe generated script is then checked by the script_quality_validator node. This validator reviews the script for consistency, length, and readability, ensuring it is neither too short nor overly verbose. It also checks that the ElevenLabs audio tags are properly embedded for voice synthesis. If the script fails quality checks, the node triggers enhancement or regeneration until a satisfactory script is produced.\n\n## Phase 6 – Audio Synthesis\n\nOnce validated, the script is handed to the premium_audio_synthesizer node. This node communicates with the ElevenLabs API to generate a professional-quality MP3 narration. The audio incorporates all embedded tags, resulting in natural pacing, realistic intonation, and clear pronunciation. The output is a polished audio file ready for publication.\n\n## Phase 7 – Packaging & Output\n\nFinally, the podcast_episode node packages the results into a complete episode bundle. It combines the validated podcast script with the synthesized MP3 audio and attaches additional metadata, such as episode duration, number of events covered, workflow version, and quality score. The output of this node is a fully ready-to-publish podcast episode, requiring no further manual editing.\n\n# 🔧 Optimization and Perfection of the Flow\n\nThe workflow is optimized to balance speed, reliability, and quality. Content scraping is designed with intelligent waiting and retry logic, ensuring that incomplete jobs do not block the pipeline. Validation nodes at both the feed and script stages guarantee that only high-quality data is processed further, minimizing errors in the final narration. AI prompt engineering in the script generator ensures natural storytelling while embedding audio tags for professional-level delivery. The flow is also resilient, with timeout handling that prevents indefinite delays and fallback options for failed scrapes.\n\nFor perfection, the workflow enforces quality assurance at multiple stages—validating feeds, cleaning scraped content, and testing generated scripts—before any audio is produced. This layered approach ensures that every podcast episode is consistent, polished, and engaging, making the system reliable for repeated use at scale. The end result is an automated process that maintains human-like quality without requiring manual intervention."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {
    "start_workflow": [
      {
        "json": {}
      }
    ],
    "final_delivery_processor": [
      {
        "json": {
          "": "",
          "podcast_episode": "episode_id: \"daily ai news\" - Google News2025-08-31, title: \"daily ai news\" - Google NewsSunday, August 31, script: Hello... and welcome to the \"daily ai news\" - Google News, your quick guide to some cool happenings around town. Whether you’re a busy local or just passing through, I’ve got three fun events lined up for you today, so let’s dive right in.\n\nFirst up, if you’re looking to jazz up your weekend, don’t miss the “Tech Tunes Live” concert happening this Friday at the Innovation Hub downtown. This event brings together local musicians who use AI-generated sounds alongside their live instruments — it’s a perfect blend of human creativity and cutting-edge technology. The show kicks off at 7 PM, and tickets are just twenty dollars if you grab them early online. It promises to be… [excitedly] an unforgettable experience for anyone curious about how AI is changing the music scene.\n\nNext on the agenda, if you’re a fan of interactive art and smart design, you are NOT going to want to miss “The AI Gallery Pop-Up” at the East Side Art Collective. From Saturday through next week, you can explore a fascinating collection of AI-created paintings, sculptures, and digital installations — many of which you can even customize in real-time with your own touch via tablets on-site. The best part? It’s completely FREE, and open daily from 11 AM to 6 PM. It’s a great chance to see how AI and human imagination come together in unexpected ways.\n\nAnd finally, rounding out our week is the “Smart Bites Food Fest” at Riverside Park this Sunday afternoon. This delicious event spotlights chefs using AI to design new recipes — plus plenty of food trucks serving up everything from plant-based BBQ to robotic bartending demos. Whether you’re a foodie or just looking for a tasty way to spend a Sunday, this festival has something fresh and exciting for you. Entry is free, and food prices vary, but the vibe is relaxed and family-friendly. And for anyone who loves barbecue… [chuckles] well, you know what to do.\n\nThat’s the brief for this edition. You can find links and more details for everything mentioned in our show notes. Thanks for tuning in to the \"daily ai news\" - Google News, and [warmly] we’ll see you next time., audio_file: [object Object], metadata: { duration_estimate: 3 + minutes, events_featured: 10, quality_score: 75, generated_at: new Date().toISOString(), workflow_version: 2.0-optimized\n\n"
        }
      }
    ],
    "intelligent_processing_wait": [
      {
        "json": {
          "id": "94d37fff-4305-4a30-ac2c-056c08fc9dbd",
          "url": "https://api.firecrawl.dev/v1/batch/scrape/94d37fff-4305-4a30-ac2c-056c08fc9dbd",
          "success": true
        }
      }
    ]
  },
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b9790a69-2811-495d-b084-2ffa4d5e3dd2",
  "connections": {
    "start_workflow": {
      "main": [
        [
          {
            "node": "generate_rss_url",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fetch_news_json": {
      "main": [
        [
          {
            "node": "filter_and_validate_events",
            "type": "main",
            "index": 0
          },
          {
            "node": "global_error_handler",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "generate_rss_url": {
      "main": [
        [
          {
            "node": "fetch_news_json",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "enhanced_script_generator",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "batch_content_scraper": {
      "main": [
        [
          {
            "node": "intelligent_processing_wait",
            "type": "main",
            "index": 0
          },
          {
            "node": "global_error_handler",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "timeout_error_handler": {
      "main": [
        [
          {
            "node": "content_aggregator_processor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "retry_logic_controller": {
      "main": [
        [
          {
            "node": "timeout_error_handler",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "intelligent_processing_wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "script_quality_validator": {
      "main": [
        [
          {
            "node": "premium_audio_synthesizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "enhanced_script_generator": {
      "main": [
        [
          {
            "node": "script_quality_validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "premium_audio_synthesizer": {
      "main": [
        [
          {
            "node": "final_delivery_processor",
            "type": "main",
            "index": 0
          },
          {
            "node": "global_error_handler",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "scraping_status_validator": {
      "main": [
        [
          {
            "node": "validate_scraping_completion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "filter_and_validate_events": {
      "main": [
        [
          {
            "node": "batch_content_scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "intelligent_processing_wait": {
      "main": [
        [
          {
            "node": "scraping_status_validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "content_aggregator_processor": {
      "main": [
        [
          {
            "node": "enhanced_script_generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "validate_scraping_completion": {
      "main": [
        [
          {
            "node": "content_aggregator_processor",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "retry_logic_controller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}