feat: add YouTube and media embed support to RSS feed
- Support video nodes with YouTube URL detection - Support urlEmbed nodes for rich media previews - Convert YouTube URLs to embedded iframes in RSS - Add Twitter/X embed preview support - Support generic iframe embeds - Provide fallback links for better RSS reader compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
be1da5aec7
commit
7acd366751
1 changed files with 77 additions and 0 deletions
|
|
@ -58,6 +58,83 @@ function convertContentToHTML(content: any): string {
|
||||||
const src = node.attrs?.src || ''
|
const src = node.attrs?.src || ''
|
||||||
const alt = node.attrs?.alt || ''
|
const alt = node.attrs?.alt || ''
|
||||||
return src ? `<figure><img src="${escapeXML(src)}" alt="${escapeXML(alt)}" /></figure>` : ''
|
return src ? `<figure><img src="${escapeXML(src)}" alt="${escapeXML(alt)}" /></figure>` : ''
|
||||||
|
case 'video':
|
||||||
|
const videoSrc = node.attrs?.src || ''
|
||||||
|
if (!videoSrc) return ''
|
||||||
|
// Check if it's a YouTube URL
|
||||||
|
const youtubeMatch = videoSrc.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/)
|
||||||
|
if (youtubeMatch) {
|
||||||
|
const videoId = youtubeMatch[1]
|
||||||
|
return `<div class="video-embed"><iframe width="560" height="315" src="https://www.youtube.com/embed/${escapeXML(videoId)}" frameborder="0" allowfullscreen></iframe><p><a href="${escapeXML(videoSrc)}">Watch on YouTube</a></p></div>`
|
||||||
|
}
|
||||||
|
// For other video sources, include a video tag
|
||||||
|
return `<video controls><source src="${escapeXML(videoSrc)}" type="video/mp4">Your browser does not support the video tag. <a href="${escapeXML(videoSrc)}">Download video</a></video>`
|
||||||
|
case 'urlEmbed':
|
||||||
|
const embedUrl = node.attrs?.url || ''
|
||||||
|
const embedTitle = node.attrs?.title || ''
|
||||||
|
const embedDescription = node.attrs?.description || ''
|
||||||
|
const embedImage = node.attrs?.image || ''
|
||||||
|
const embedSiteName = node.attrs?.siteName || ''
|
||||||
|
|
||||||
|
if (!embedUrl) return ''
|
||||||
|
|
||||||
|
// Check if it's a YouTube URL
|
||||||
|
const ytMatch = embedUrl.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/)
|
||||||
|
if (ytMatch) {
|
||||||
|
const videoId = ytMatch[1]
|
||||||
|
let html = '<div class="url-embed youtube-embed">'
|
||||||
|
html += `<iframe width="560" height="315" src="https://www.youtube.com/embed/${escapeXML(videoId)}" frameborder="0" allowfullscreen></iframe>`
|
||||||
|
if (embedTitle) {
|
||||||
|
html += `<h3><a href="${escapeXML(embedUrl)}">${escapeXML(embedTitle)}</a></h3>`
|
||||||
|
}
|
||||||
|
if (embedDescription) {
|
||||||
|
html += `<p>${escapeXML(embedDescription)}</p>`
|
||||||
|
}
|
||||||
|
html += '</div>'
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a Twitter/X URL
|
||||||
|
const twitterMatch = embedUrl.match(/(?:twitter\.com|x\.com)\/\w+\/status\/(\d+)/)
|
||||||
|
if (twitterMatch) {
|
||||||
|
// For Twitter/X, we can't embed the actual tweet in RSS, but we can provide a nice preview
|
||||||
|
let html = '<div class="url-embed twitter-embed">'
|
||||||
|
if (embedImage) {
|
||||||
|
html += `<img src="${escapeXML(embedImage)}" alt="Tweet preview" />`
|
||||||
|
}
|
||||||
|
html += '<div class="embed-content">'
|
||||||
|
html += `<span class="site-name">𝕏 (Twitter)</span>`
|
||||||
|
if (embedTitle || embedDescription) {
|
||||||
|
html += `<p>${escapeXML(embedDescription || embedTitle || '')}</p>`
|
||||||
|
}
|
||||||
|
html += `<p><a href="${escapeXML(embedUrl)}">View on 𝕏</a></p>`
|
||||||
|
html += '</div></div>'
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other URL embeds, create a rich preview
|
||||||
|
let html = '<div class="url-embed">'
|
||||||
|
if (embedImage) {
|
||||||
|
html += `<img src="${escapeXML(embedImage)}" alt="${escapeXML(embedTitle || '')}" />`
|
||||||
|
}
|
||||||
|
html += '<div class="embed-content">'
|
||||||
|
if (embedSiteName) {
|
||||||
|
html += `<span class="site-name">${escapeXML(embedSiteName)}</span>`
|
||||||
|
}
|
||||||
|
if (embedTitle) {
|
||||||
|
html += `<h3><a href="${escapeXML(embedUrl)}">${escapeXML(embedTitle)}</a></h3>`
|
||||||
|
}
|
||||||
|
if (embedDescription) {
|
||||||
|
html += `<p>${escapeXML(embedDescription)}</p>`
|
||||||
|
}
|
||||||
|
html += '</div></div>'
|
||||||
|
return html
|
||||||
|
case 'iframe':
|
||||||
|
const iframeSrc = node.attrs?.src || ''
|
||||||
|
const iframeWidth = node.attrs?.width || 560
|
||||||
|
const iframeHeight = node.attrs?.height || 315
|
||||||
|
if (!iframeSrc) return ''
|
||||||
|
return `<iframe src="${escapeXML(iframeSrc)}" width="${iframeWidth}" height="${iframeHeight}" frameborder="0" allowfullscreen></iframe>`
|
||||||
default:
|
default:
|
||||||
const defaultText = extractTextFromNode(node)
|
const defaultText = extractTextFromNode(node)
|
||||||
return defaultText ? `<p>${defaultText}</p>` : ''
|
return defaultText ? `<p>${defaultText}</p>` : ''
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue