From 460753244070306ad09f8cb1f9011a8327f7b94c Mon Sep 17 00:00:00 2001 From: nmemmert Date: Wed, 9 Sep 2026 19:16:25 -0400 Subject: [PATCH] Decode HTML entities in RSS episode descriptions; v2.0.20 Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 +- server/rss-helpers.js | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 56f373e..3299e85 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "siteforge", "private": true, - "version": "2.0.19", + "version": "2.0.20", "type": "module", "scripts": { "dev": "vite", diff --git a/server/rss-helpers.js b/server/rss-helpers.js index 6310642..eb34868 100644 --- a/server/rss-helpers.js +++ b/server/rss-helpers.js @@ -6,6 +6,19 @@ export function extractCdata(raw) { return cdata ? cdata[1].trim() : raw.trim() } +function decodeEntities(str) { + return str + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/'/g, "'") + .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n))) + .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCharCode(parseInt(h, 16))) + .replace(/ /g, ' ') +} + export function parseRssItems(xml, limit = Infinity) { const items = [] const itemRegex = /([\s\S]*?)<\/item>/g @@ -22,7 +35,7 @@ export function parseRssItems(xml, limit = Infinity) { const enclosureUrl = /]+url="([^"]+)"/.exec(block)?.[1] ?? '' const link = guid.startsWith('http') ? guid : enclosureUrl const descRaw = /([\s\S]*?)<\/description>/.exec(block)?.[1] ?? '' - const descText = extractCdata(descRaw).replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim() + const descText = decodeEntities(extractCdata(descRaw).replace(/<[^>]+>/g, '')).replace(/\s+/g, ' ').trim() const duration = (/([\s\S]*?)<\/itunes:duration>/.exec(block)?.[1] ?? '').trim() const episode = (/([\s\S]*?)<\/itunes:episode>/.exec(block)?.[1] ?? '').trim() const season = (/([\s\S]*?)<\/itunes:season>/.exec(block)?.[1] ?? '').trim()