From 2b308e6d2a19bc4d94deb31553999b528dce8599 Mon Sep 17 00:00:00 2001 From: "Carlos (Almeira)" Date: Thu, 20 Aug 2026 09:55:59 -0500 Subject: [PATCH] design: extract video from post content to display at top as hero media replacing static featured image --- src/app/[slug]/page.tsx | 115 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx index 52e5b1f..62045ce 100644 --- a/src/app/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -25,6 +25,101 @@ function formatTextToHtml(text: string) { .join(''); } +function extractMedia(content: string, videoUrlField?: string) { + let videoHtml: string | null = null; + let cleanContent = content; + + // 1. Check if there's a video_url field (YouTube/Vimeo) + if (videoUrlField) { + if (videoUrlField.includes('youtube.com') || videoUrlField.includes('youtu.be')) { + let videoId = ''; + const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/; + const match = videoUrlField.match(regExp); + if (match && match[2].length === 11) { + videoId = match[2]; + } + if (videoId) { + videoHtml = ``; + } + } else if (videoUrlField.includes('vimeo.com')) { + const match = videoUrlField.match(/vimeo\.com\/(\d+)/); + if (match) { + const videoId = match[1]; + videoHtml = ``; + } + } else { + videoHtml = ``; + } + } + + // 2. Look for video tag inside content (first occurrence) + if (!videoHtml && content) { + const videoRegex = /]*>([\s\S]*?)<\/video>/i; + const match = videoRegex.exec(content); + if (match) { + videoHtml = match[0]; + cleanContent = content.replace(videoRegex, ''); + } + } + + // 3. Look for youtube iframe/div inside content (first occurrence) + if (!videoHtml && content) { + const ytRegex = /]*data-youtube-video[^>]*>([\s\S]*?)<\/div>/i; + const ytMatch = ytRegex.exec(content); + if (ytMatch) { + videoHtml = ytMatch[0]; + cleanContent = content.replace(ytRegex, ''); + } else { + const iframeRegex = /]*src=["'][^"']*(?:youtube\.com|youtu\.be|vimeo\.com)[^"']*["'][^>]*>([\s\S]*?)<\/iframe>/i; + const iframeMatch = iframeRegex.exec(content); + if (iframeMatch) { + videoHtml = iframeMatch[0]; + cleanContent = content.replace(iframeRegex, ''); + } + } + } + + return { videoHtml, cleanContent }; +} + +function styleHeroMedia(html: string, posterUrl?: string): string { + if (!html) return ''; + + if (html.includes(']*>[\s\S]*?<\/iframe>/i); + if (iframeMatch) { + return styleHeroMedia(iframeMatch[0]); + } + } + + return html; +} + async function fetchPostData(slugOrId: string) { if (!slugOrId || typeof slugOrId !== 'string' || slugOrId.includes('.')) return null; @@ -141,6 +236,11 @@ export default async function NoticiaDetail({ params }: { params: Promise<{ slug const fechaObj = new Date(post.fecha_publicacion || post.$createdAt); const fechaFormateada = fechaObj.toLocaleDateString('es-ES', { day: '2-digit', month: 'long', year: 'numeric' }); + // Extract video media if present + const rawContent = post.contenido || ''; + const { videoHtml, cleanContent } = extractMedia(rawContent, post.video_url); + const styledHeroMedia = videoHtml ? styleHeroMedia(videoHtml, imagenUrl || undefined) : null; + return (
@@ -173,19 +273,28 @@ export default async function NoticiaDetail({ params }: { params: Promise<{ slug )} - {imagenUrl && ( + {styledHeroMedia ? ( +
+
+
+ ) : imagenUrl ? (
{post.titulo}
- )} + ) : null}