tag - same color as links but no underline and not clickable
+ return `${escapedDisplay}`;
});
// Convert WIKILINK:dtag|display placeholder format to HTML
@@ -68,6 +67,12 @@ export function postProcessHtml(html: string, options: PostProcessOptions = {}):
}
});
+ // Process media URLs (YouTube, Spotify, video, audio)
+ processed = processMedia(processed);
+
+ // Process OpenGraph links (external links that should have rich previews)
+ processed = processOpenGraphLinks(processed, options.linkBaseURL);
+
// Process images: add max-width styling and data attributes
processed = processImages(processed);
@@ -100,6 +105,180 @@ function getNostrType(id: string): 'npub' | 'nprofile' | 'nevent' | 'naddr' | 'n
return null;
}
+/**
+ * Process media URLs (YouTube, Spotify, video, audio)
+ * Converts MEDIA: placeholders to HTML embeds/players
+ */
+function processMedia(html: string): string {
+ let processed = html;
+
+ // Process YouTube embeds
+ processed = processed.replace(/MEDIA:youtube:([a-zA-Z0-9_-]+)/g, (_match, videoId) => {
+ const escapedId = videoId.replace(/"/g, '"');
+ return `
+
+
`;
+ });
+
+ // Process Spotify embeds
+ processed = processed.replace(/MEDIA:spotify:(track|album|playlist|artist|episode|show):([a-zA-Z0-9]+)/g, (_match, type, id) => {
+ const escapedType = type.replace(/"/g, '"');
+ const escapedId = id.replace(/"/g, '"');
+ return `
+
+
`;
+ });
+
+ // Process video files
+ processed = processed.replace(/MEDIA:video:(https?:\/\/[^\s<>"{}|\\^`\[\]()]+)/g, (_match, url) => {
+ const escapedUrl = url
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ return `
+
+
`;
+ });
+
+ // Process audio files
+ processed = processed.replace(/MEDIA:audio:(https?:\/\/[^\s<>"{}|\\^`\[\]()]+)/g, (_match, url) => {
+ const escapedUrl = url
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ return ``;
+ });
+
+ return processed;
+}
+
+/**
+ * Process OpenGraph links - mark external links for OpenGraph preview fetching
+ */
+function processOpenGraphLinks(html: string, linkBaseURL?: string): string {
+ let processed = html;
+
+ // Extract base domain from linkBaseURL if provided
+ let baseDomain: string | null = null;
+ if (linkBaseURL) {
+ try {
+ const urlMatch = linkBaseURL.match(/^https?:\/\/([^\/]+)/);
+ if (urlMatch) {
+ baseDomain = urlMatch[1];
+ }
+ } catch {
+ // Ignore parsing errors
+ }
+ }
+
+ // Match external links (http/https) that aren't media, nostr, or wikilinks
+ // Skip links that are already in media embeds or special containers
+ // Use a more flexible regex that handles attributes in any order
+ processed = processed.replace(/