{
  "id": "f9X48gqgIUwyseMM",
  "meta": {
    "instanceId": "d47f3738b860eed937a1b18d7345fa2c65cf4b4957554e29477cb064a7039870"
  },
  "name": "Obsidian Notes Read Aloud: Available as a Podcast Feed",
  "tags": [],
  "nodes": [
    {
      "id": "a44b5cb3-6c9f-4227-a45f-a21765ea120c",
      "name": "OpenAI1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -660,
        -180
      ],
      "parameters": {
        "input": "={{ $json.body.content }}",
        "options": {
          "response_format": "mp3"
        },
        "resource": "audio"
      },
      "credentials": {
        "openAiApi": {
          "id": "q8L9oWVM7QyzYEE5",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.7
    },
    {
      "id": "9ca589b6-f1c7-44a9-8ff7-4abb979a71c3",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1200,
        -400
      ],
      "parameters": {
        "width": 440,
        "height": 540,
        "content": "## Send Notes to Webhookn**Setup:**n- Install [Post Webhook Plugin](https://github.com/Masterb1234/obsidian-post-webhook/) in Obsidiann- Enter n8n Webhook URL and name in plugin settingsnn**Usage:**n- Select text or use full noten- Open Command Palette (Ctrl+P)n- Choose 'Send Note/Selection to [name]'n- Audio file appears in Podcast Feed and note"
      },
      "typeVersion": 1
    },
    {
      "id": "3ea132e5-8c67-4140-a9b2-607ea256e90f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1200,
        240
      ],
      "parameters": {
        "width": 440,
        "height": 440,
        "content": "## Generic Podcast Feed ModulenA reusable module for any 'X-to-Podcast' workflow. Generates standard RSS feed from:n- Source data (Google Sheets)n- Podcast metadatannCompatible with all major podcast platforms (Apple, Google, Spotify, etc.).nnnnnnnnnnnnnnnn"
      },
      "typeVersion": 1
    },
    {
      "id": "92d6a6df-0e4e-423b-8447-dce10d5373ae",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        -400
      ],
      "parameters": {
        "color": 3,
        "width": 440,
        "height": 540,
        "content": "## Create Audio and Write DescriptionnOpenAI TTS converts notes to audio while the messaging model generates concise descriptions for podcast apps."
      },
      "typeVersion": 1
    },
    {
      "id": "b950b0ab-e27e-473d-9891-d5551a44ed17",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        -400
      ],
      "parameters": {
        "color": 4,
        "width": 380,
        "height": 540,
        "content": "## Append Row to Google SheetsnSaves essential podcast parameters (<title>, , , ) to Google Sheets for Feed generation."
      },
      "typeVersion": 1
    },
    {
      "id": "02fda37f-77a5-47f5-81bc-b59486704386",
      "name": "Webhook GET Note",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1040,
        -120
      ],
      "webhookId": "64fac784-9b98-4bbc-aaf2-dd45763d3362",
      "parameters": {
        "path": "64fac784-9b98-4bbc-aaf2-dd45763d3362",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "845d04ea-d221-4034-b5e1-75061e5f351c",
      "name": "Webhook GET Podcast Feed",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1040,
        460
      ],
      "webhookId": "2f0a6706-54da-4b89-91f4-5e147b393bd8",
      "parameters": {
        "path": "2f0a6706-54da-4b89-91f4-5e147b393bd8h",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "ce6d766c-89e6-4d62-9d48-d6715a28592f",
      "name": "Upload Audio to Cloudinary",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -220,
        -120
      ],
      "parameters": {
        "url": "https://api.cloudinary.com/v1_1/CLOUDINARY_ENV/upload",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "data"
            },
            {
              "name": "upload_preset",
              "value": "rb_preset"
            },
            {
              "name": "resource_type",
              "value": "auto"
            }
          ]
        },
        "genericAuthType": "httpCustomAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "multipart/form-data"
            }
          ]
        }
      },
      "credentials": {
        "httpCustomAuth": {
          "id": "DHmR14pD9rTrd3nS",
          "name": "Cloudinary API"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "1f86c18d-8197-4671-9c41-726a02108c4e",
      "name": "OpenAI",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -660,
        -20
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "={{ $json.body.content }}"
            },
            {
              "role": "system",
              "content": "Based on the user input text, write a concise and engaging description of 50u2013150 characters. Highlight the key idea or takeaway while making it compelling and easy to understand. Avoid unnecessary details or repetition."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "id": "q8L9oWVM7QyzYEE5",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.7
    },
    {
      "id": "0942959c-2231-4055-b196-4483c210a39d",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        320,
        -40
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "ee7ba6a7-f8dd-4863-bf5c-6ec8eb2329ea",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        460,
        -180
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "f403d045-08e9-400e-9988-c8f55a5aa609",
      "name": "Give Audio Unique Name",
      "type": "n8n-nodes-base.set",
      "position": [
        -460,
        -180
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "97f0fe66-7ddf-4eff-a3cf-3104e74dbfac",
              "name": "fileName",
              "type": "string",
              "value": "={{ $('Webhook GET Note').item.json.body.timestamp }}.mp3"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "2dbff0f5-f359-43b7-b0de-4b9d657c69c0",
      "name": "Send Audio to Obsidian",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        80,
        -180
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "content-type",
                "value": "=audio/mpeg"
              }
            ]
          }
        },
        "respondWith": "binary",
        "responseDataSource": "set"
      },
      "typeVersion": 1
    },
    {
      "id": "ede7c038-b210-4b29-8557-7530ea4cf63e",
      "name": "Rename Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        620,
        -180
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3a7d01f4-7448-40e0-9f46-e6edea971b72",
              "name": "title",
              "type": "string",
              "value": "={{ $('Webhook GET Note').item.json.body.filename.split('.md')[0] }}"
            },
            {
              "id": "f49446df-3975-4133-a964-ebdcc0d904dd",
              "name": "link",
              "type": "string",
              "value": "={{ $json.data[0].url }}"
            },
            {
              "id": "8be5df35-ec79-45b1-94c3-306d58100fd2",
              "name": "description",
              "type": "string",
              "value": "={{ $json.data[1].message.content }}"
            },
            {
              "id": "231d0ee2-13d2-4a28-a19c-adc4920130fd",
              "name": "date",
              "type": "string",
              "value": "={{ $json.data[0].created_at }}"
            },
            {
              "id": "cd2748b3-999a-4514-9b31-49b7d045101f",
              "name": "duration",
              "type": "number",
              "value": "={{ $json.data[0].duration }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "10a35ef9-ab86-4010-9fcc-3cd765384e93",
      "name": "Append Item to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        940,
        -180
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "title",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "duration",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "duration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA/edit#gid=0",
          "cachedResultName": "Blad1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA/edit?usp=drivesdk",
          "cachedResultName": "obsidian-n8n"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3Pu0wlfxgNYzVqY6",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "62dd3faf-22db-40f9-892c-2cf9368a9496",
      "name": "Get Items from Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -660,
        460
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA/edit#gid=0",
          "cachedResultName": "Blad1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1F73a7uuzLAq916w2JFndumv0JhnCAvOTN-Cn_OOP3uA/edit?usp=drivesdk",
          "cachedResultName": "obsidian-n8n"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "3Pu0wlfxgNYzVqY6",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "7b465ed0-d2cc-4862-b0e6-4bd6215f3945",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        320
      ],
      "parameters": {
        "color": 3,
        "width": 440,
        "height": 360,
        "content": "## Podcast Feed Configurationn- Static: Configure podcast metadata in 'Edit Fields'n- Dynamic: Episodes automatically pulled from Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "1608ce65-bf1f-4dce-b4c7-b85b72ecb8c7",
      "name": "Write RSS Feed",
      "type": "n8n-nodes-base.code",
      "position": [
        -120,
        460
      ],
      "parameters": {
        "jsCode": "// Variables from a separate edit nodenconst baseUrl = $node["Manually Enter Other Data for Podcast Feed"].data.baseUrl; nconst podcastTitle = $node["Manually Enter Other Data for Podcast Feed"].data.podcastTitle;nconst podcastDescription = $node["Manually Enter Other Data for Podcast Feed"].data.podcastDescription;nconst authorName = $node["Manually Enter Other Data for Podcast Feed"].data.authorName;nconst ownerName = $node["Manually Enter Other Data for Podcast Feed"].data.ownerName;nconst ownerEmail = $node["Manually Enter Other Data for Podcast Feed"].data.ownerEmail;nconst coverImageUrl = $node["Manually Enter Other Data for Podcast Feed"].data.coverImageUrl;nconst language = $node["Manually Enter Other Data for Podcast Feed"].data.language || 'en-us';nconst explicitContent = $node["Manually Enter Other Data for Podcast Feed"].data.explicitContent || false;nconst itunesCategory = $node["Manually Enter Other Data for Podcast Feed"].data.itunesCategory;nconst webhookUrl = $node["Webhook GET Podcast Feed"].data.webhookUrlnn// Get the input itemsnconst inputItems = items;nn// Function to format date to RFC 822 formatnfunction formatDate(dateString) {n  return new Date(dateString || new Date()).toUTCString();n}nn// Function to convert duration from seconds to HH:MM:SSnfunction formatDuration(seconds = 0) {n  const hours = Math.floor(seconds / 3600);n  const minutes = Math.floor((seconds % 3600) / 60);n  const remainingSeconds = Math.floor(seconds % 60);nn  const minutesStr = minutes.toString().padStart(2, '0');n  const secondsStr = remainingSeconds.toString().padStart(2, '0');n  n  if (hours &gt; 0) {n    return `${hours}:${minutesStr}:${secondsStr}`;n  }n  return `${minutesStr}:${secondsStr}`;n}nn// Function to safely sanitize textnfunction sanitizeText(text) {n  if (text === undefined || text === null) {n    return '';n  }n  return String(text)n    .replace(/&amp;/g, '&amp;')n    .replace(//g, '&gt;')n    .replace(/"/g, '&quot;')n    .replace(/'/g, '&apos;');n}nn// Generate the RSS feed headernlet rssFeed = `nn  n    <title>${sanitizeText(podcastTitle)}</title>n    ${sanitizeText(podcastDescription)}n    ${sanitizeText(baseUrl)}n    n    ${sanitizeText(language)}n    u00a9 ${new Date().getFullYear()} ${sanitizeText(authorName)}n    ${new Date().toUTCString()}n    ${sanitizeText(authorName)}n    n      ${sanitizeText(ownerName)}n      ${sanitizeText(ownerEmail)}n    n    n    n    ${explicitContent}n    episodic\n`;nn// Generate itemsnfor (const item of inputItems) {n  const json = item.json;n  n  // Extract values from the json objectn  const title = sanitizeText(json.title);n  const description = sanitizeText(json.description);n  const link = sanitizeText(json.link);n  const date = json.date;n  const duration = json.duration;n  n  // Assign episode and season numbers dynamically based on row_numbern  const episodeNumber = json.row_number; // Use row_number for the episode numbern  const seasonNumber = 1; // You can adjust this logic if your episodes span multiple seasonsnn  rssFeed += `    n    <title>${title}</title>n    ${description}n    ${link}n    ${link}n    ${formatDate(date)}n    n    ${formatDuration(duration)}n    ${description}n    fulln    ${episodeNumber}n    ${seasonNumber}n    ${explicitContent}n    n      &lt;![CDATA[n      <p>${description}</p>n      ]]&gt;n    n  \n`;n}nn// Close the RSS feednrssFeed += `  n`;nn// Return the complete RSS feednreturn [{n  json: {n    rssFeedn  }n}];n"
      },
      "typeVersion": 2
    },
    {
      "id": "c8c7fbfc-c408-438e-af7e-5c384cfce4a5",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        320
      ],
      "parameters": {
        "color": 5,
        "width": 340,
        "height": 360,
        "content": "## Write Podcast FeednGenerates RSS feed XML from collected data."
      },
      "typeVersion": 1
    },
    {
      "id": "b5962e24-49eb-423a-ab8c-cb04daf5e1a0",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -400
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 540,
        "content": "## Audio to Cloudinary and ObsidiannCloudinary stores audio files and provides duration metadata for podcast feed.nnSetup:n- Create Custom Auth credentialsn- Set CLOUDINARY_ENV to your environment"
      },
      "typeVersion": 1
    },
    {
      "id": "e0f18eda-13fc-4771-8ce0-11574a4469ad",
      "name": "Return Podcast Feed to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        200,
        460
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/xml"
              }
            ]
          }
        },
        "respondWith": "text",
        "responseBody": "={{ $json.rssFeed }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "d3afe3f0-79e4-48c1-a0d6-356b462156c7",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        260,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 500,
        "height": 540,
        "content": "## Prepare Relevant DatanConsolidates and formats data for Google Sheets storage."
      },
      "typeVersion": 1
    },
    {
      "id": "f77ff10c-e4e3-4761-b4db-4c42d5831f5c",
      "name": "Manually Enter Other Data for Podcast Feed",
      "type": "n8n-nodes-base.set",
      "position": [
        -460,
        460
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "05d1c4f7-ebe7-4df8-925b-0e0d5539f172",
              "name": "baseUrl",
              "type": "string",
              "value": "https://n8n.io"
            },
            {
              "id": "e8c6845e-887f-49e9-8336-ca2cb2a2fd29",
              "name": "podcastTitle",
              "type": "string",
              "value": "My Notes to Podcast"
            },
            {
              "id": "bf2948ed-cffa-4d3f-9bab-5fb008d83b4c",
              "name": "podcastDescription",
              "type": "string",
              "value": "My Notes Read Aloud"
            },
            {
              "id": "f5008697-3e52-4ae2-94da-c059b60a6de9",
              "name": "authorName",
              "type": "string",
              "value": "Your Name"
            },
            {
              "id": "6595bf45-e054-4e18-ade9-13e38e6efedb",
              "name": "ownerName",
              "type": "string",
              "value": "Owner Name"
            },
            {
              "id": "b21efe1c-e5b5-4bb3-bf07-a52859c7a607",
              "name": "ownerEmail",
              "type": "string",
              "value": "owner@email.com"
            },
            {
              "id": "3f0b090c-0b5e-41cb-9841-05b7b8f83126",
              "name": "coverImageUrl",
              "type": "string",
              "value": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRPDcMnpgGkzIFxDpDaHEIFVg_D6nVG5Z0pPA&amp;s"
            },
            {
              "id": "1fb27792-1f2b-4a9a-a353-a64e31bb4747",
              "name": "language",
              "type": "string",
              "value": "en-us"
            },
            {
              "id": "7c3d868a-f3c0-4fd0-8909-e4172f8a4b18",
              "name": "explicitContent",
              "type": "string",
              "value": "false"
            },
            {
              "id": "6aa041b4-554c-4540-889c-e37a314d5842",
              "name": "itunesCategory",
              "type": "string",
              "value": "Technology"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4eb1c404-4e77-45ea-b413-4b79d8f40b1d",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "OpenAI1": {
      "main": [
        [
          {
            "node": "Give Audio Unique Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Rename Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rename Fields": {
      "main": [
        [
          {
            "node": "Append Item to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write RSS Feed": {
      "main": [
        [
          {
            "node": "Return Podcast Feed to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook GET Note": {
      "main": [
        [
          {
            "node": "OpenAI1",
            "type": "main",
            "index": 0
          },
          {
            "node": "OpenAI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Give Audio Unique Name": {
      "main": [
        [
          {
            "node": "Upload Audio to Cloudinary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Audio to Obsidian",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook GET Podcast Feed": {
      "main": [
        [
          {
            "node": "Get Items from Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Audio to Cloudinary": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Item to Google Sheet": {
      "main": [
        []
      ]
    },
    "Get Items from Google Sheets": {
      "main": [
        [
          {
            "node": "Manually Enter Other Data for Podcast Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manually Enter Other Data for Podcast Feed": {
      "main": [
        [
          {
            "node": "Write RSS Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}