Publish Container / docker (push) Successful in 15m27s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
51 lines
2.1 KiB
JavaScript
51 lines
2.1 KiB
JavaScript
// Shared podcast-RSS parsing, used by both the main show (episodes.js) and
|
|
// any sub-show fed by its own feed URL (nuggets.js).
|
|
|
|
export function extractCdata(raw) {
|
|
const cdata = /^<!\[CDATA\[([\s\S]*?)\]\]>$/.exec(raw.trim())
|
|
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 = /<item>([\s\S]*?)<\/item>/g
|
|
let match
|
|
while ((match = itemRegex.exec(xml)) !== null && items.length < limit) {
|
|
const block = match[1]
|
|
const titleRaw = /<title>([\s\S]*?)<\/title>/.exec(block)?.[1] ?? ''
|
|
const title = extractCdata(titleRaw)
|
|
if (!title) continue
|
|
|
|
const pubDate = (/<pubDate>([\s\S]*?)<\/pubDate>/.exec(block)?.[1] ?? '').trim()
|
|
const guidRaw = /<guid[^>]*>([\s\S]*?)<\/guid>/.exec(block)?.[1] ?? ''
|
|
const guid = extractCdata(guidRaw)
|
|
const enclosureUrl = /<enclosure[^>]+url="([^"]+)"/.exec(block)?.[1] ?? ''
|
|
const link = guid.startsWith('http') ? guid : enclosureUrl
|
|
const descRaw = /<description>([\s\S]*?)<\/description>/.exec(block)?.[1] ?? ''
|
|
const descText = decodeEntities(extractCdata(descRaw).replace(/<[^>]+>/g, '')).replace(/\s+/g, ' ').trim()
|
|
const duration = (/<itunes:duration>([\s\S]*?)<\/itunes:duration>/.exec(block)?.[1] ?? '').trim()
|
|
const episode = (/<itunes:episode>([\s\S]*?)<\/itunes:episode>/.exec(block)?.[1] ?? '').trim()
|
|
const season = (/<itunes:season>([\s\S]*?)<\/itunes:season>/.exec(block)?.[1] ?? '').trim()
|
|
items.push({
|
|
title, pubDate, link,
|
|
audioUrl: enclosureUrl,
|
|
description: descText.slice(0, 220) + (descText.length > 220 ? '…' : ''),
|
|
duration, episode, season,
|
|
})
|
|
}
|
|
return items
|
|
}
|