Una propuesta que hice de un shortcode para Hugo usando el tema Blowfish para leer información Open Graph desde una URL.
Sigue los estándares del protocolo Open Graph de Facebook Platform - Wikipedia.
Este código crea una tarjeta vertical como la siguiente:

En caso de fallback (por ejemplo, error 403/404), mostrará un pequeño banner.
El código que debes añadir en tu entorno en layouts/shortcodes/og-card.html:
{{/* layouts/shortcodes/og-card.html */}}
{{/* Provide the URL to fetch Open Graph data from */}}
{{ $url := .Get "url" | default (.Get 0) }}
{{/* Multiple additional parameters */}}
{{ $add_link := .Get "add_link" | default (.Get 1) | default true }}
{{ $add_margin_bottom := .Get "add_margin_bottom" | default (.Get 2) | default true }}
{{ $add_image := .Get "add_image" | default (.Get 3) | default true }}
{{ $only_text := .Get "only_text" | default (.Get 4) | default false }}
{{ $ogTitle := "" }}
{{ $ogDesc := "" }}
{{ $ogImage := "" }}
{{/* Regex (más tolerantes y reutilizables) */}}
{{ $reOgImage := `(?i)<meta[^>]+(?:property|name)=["'](?:og:image|twitter:image)["'][^>]+content=["']([^"']+)["']` }}
{{ $reOgTitle := `(?i)<meta[^>]+property=["']og:title["'][^>]+content=["']([^"']+)["']` }}
{{ $reOgDesc := `(?i)<meta[^>]+property=["']og:description["'][^>]+content=["']([^"']+)["']` }}
{{ $reTitleTag := `(?i)<title>(.*?)</title>` }}
{{/* Parse del URL para host/scheme */}}
{{ $u := urls.Parse $url }}
{{ $host := "" }}
{{ with $u }}{{ $host = .Host }}{{ end }}
{{/* Configuración de la petición */}}
{{ $opts := dict "headers" (dict
"User-Agent" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
"Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
"Accept-Language" "en-US,en;q=0.5"
"Referer" "https://www.google.com/"
"Upgrade-Insecure-Requests" "1"
"Sec-Ch-Ua" "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\""
"Sec-Ch-Ua-Mobile" "?0"
"Sec-Ch-Ua-Platform" "\"Windows\""
"Sec-Fetch-Dest" "document"
"Sec-Fetch-Mode" "navigate"
"Sec-Fetch-Site" "none"
"Sec-Fetch-User" "?1"
) }}
{{ $add_margin_bottom_class := cond $add_margin_bottom " mb-6" "" }}
{{/* Recurso remoto. NOTA: Para activar el fallback en caso de error (403/404), agrega ignoreErrors = ["error-remote-get"] en hugo.toml */}}
{{ $resource := false }}
{{ if not $only_text }}
{{ $resource = resources.GetRemote $url $opts }}
{{ end }}
{{ if $add_link }}
{{ $content := printf (i18n "external_reference_text.add_link") $url }}
<p>{{- $content | markdownify -}}</p>
{{ end }}
{{ if and $resource (not $only_text) }}
{{ $content := $resource.Content }}
{{/* 1 IMAGEN */}}
{{ if $add_image }}
{{ with (findRE $reOgImage $content 1) }}
{{ $ogImage = index . 0 | replaceRE $reOgImage "$1" }}
{{ end }}
{{/* Normaliza og:image relativo -> absoluto */}}
{{ if and $ogImage $u }}
{{ if hasPrefix $ogImage "//" }}
{{ $ogImage = printf "%s:%s" $u.Scheme $ogImage }}
{{ else if hasPrefix $ogImage "/" }}
{{ $ogImage = printf "%s://%s%s" $u.Scheme $u.Host $ogImage }}
{{ end }}
{{ end }}
{{ end }}
{{/* 2 TÍTULO */}}
{{ with (findRE $reOgTitle $content 1) }}
{{ $ogTitle = index . 0 | replaceRE $reOgTitle "$1" }}
{{ else }}
{{ with (findRE $reTitleTag $content 1) }}
{{ $ogTitle = index . 0 | replaceRE $reTitleTag "$1" }}
{{ end }}
{{ end }}
{{/* 3 DESCRIPCIÓN */}}
{{ with (findRE $reOgDesc $content 1) }}
{{ $ogDesc = index . 0 | replaceRE $reOgDesc "$1" }}
{{ end }}
<div class="my-6 w-full max-w-prose mx-auto{{ $add_margin_bottom_class }}">
<a href="{{ $url }}" target="_blank" rel="noopener noreferrer" class="not-prose block group relative overflow-hidden rounded-2xl bg-neutral-100 dark:bg-neutral-800 hover:shadow-xl transition-all duration-300 border border-neutral-200 dark:border-neutral-700/50">
{{ if $ogImage }}
<div class="aspect-video w-full overflow-hidden relative bg-neutral-200 dark:bg-neutral-700">
<img src="{{ $ogImage | safeURL }}" alt="{{ ($ogTitle | default "Preview") | htmlUnescape }}" class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" loading="lazy" />
</div>
{{ end }}
<div class="p-5">
<h3 class="text-lg font-bold text-neutral-900 dark:text-neutral-100 group-hover:text-primary-600 dark:group-hover:text-primary-400 leading-tight mb-2">
{{ $ogTitle | default $url | htmlUnescape }}
</h3>
{{ if $ogDesc }}
<p class="text-sm text-neutral-600 dark:text-neutral-400 line-clamp-2 mb-3">
{{ $ogDesc | htmlUnescape | truncate 140 }}
</p>
{{ end }}
<div class="flex items-center gap-2 mt-2 pt-2 border-t border-neutral-200 dark:border-neutral-700/50">
<img src="https://www.google.com/s2/favicons?domain={{ $host | default $url }}&sz=32" alt="" class="w-4 h-4 opacity-70" />
<div class="text-xs font-mono text-neutral-500">
{{ $host | default ($url | replaceRE `^https?://(www\.)?([^/]+).*` "$2") }} ↗
</div>
</div>
</div>
</a>
</div>
{{ end }}
{{ if not $resource }}
<div class="my-6{{ $add_margin_bottom_class }}">
{{ partial "components/alert.html" (dict
"icon" "link"
"content" (printf (i18n "external_reference_text.alternative") $url)
) }}
</div>
{{ end }}Para que lo muestre horizontal, como alternativa al anterior vertical, por ejemplo:

Usar el siguiente código:
{{/* layouts/shortcodes/og-card.html */}}
{{/* Provide the URL to fetch Open Graph data from */}}
{{ $url := .Get "url" | default (.Get 0) }}
{{/* Multiple additional parameters */}}
{{ $add_link := .Get "add_link" | default (.Get 1) | default true }}
{{ $add_margin_bottom := .Get "add_margin_bottom" | default (.Get 2) | default true }}
{{ $add_image := .Get "add_image" | default (.Get 3) | default true }}
{{ $only_text := .Get "only_text" | default (.Get 4) | default false }}
{{ $ogTitle := "" }}
{{ $ogDesc := "" }}
{{ $ogImage := "" }}
{{/* Regex (más tolerantes y reutilizables) */}}
{{ $reOgImage := `(?i)<meta[^>]+(?:property|name)=["'](?:og:image|twitter:image)["'][^>]+content=["']([^"']+)["']` }}
{{ $reOgTitle := `(?i)<meta[^>]+property=["']og:title["'][^>]+content=["']([^"']+)["']` }}
{{ $reOgDesc := `(?i)<meta[^>]+property=["']og:description["'][^>]+content=["']([^"']+)["']` }}
{{ $reTitleTag := `(?i)<title>(.*?)</title>` }}
{{/* Parse del URL para host/scheme */}}
{{ $u := urls.Parse $url }}
{{ $host := "" }}
{{ with $u }}{{ $host = .Host }}{{ end }}
{{/* Configuración de la petición */}}
{{ $opts := dict "headers" (dict
"User-Agent" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
"Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
"Accept-Language" "en-US,en;q=0.5"
"Referer" "https://www.google.com/"
"Upgrade-Insecure-Requests" "1"
"Sec-Ch-Ua" "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\""
"Sec-Ch-Ua-Mobile" "?0"
"Sec-Ch-Ua-Platform" "\"Windows\""
"Sec-Fetch-Dest" "document"
"Sec-Fetch-Mode" "navigate"
"Sec-Fetch-Site" "none"
"Sec-Fetch-User" "?1"
) }}
{{ $add_margin_bottom_class := cond $add_margin_bottom " mb-6" "" }}
{{/* Recurso remoto. NOTA: Para activar el fallback en caso de error (403/404), agrega ignoreErrors = ["error-remote-get"] en hugo.toml */}}
{{ $resource := false }}
{{ if not $only_text }}
{{ $resource = resources.GetRemote $url $opts }}
{{ end }}
{{ if $add_link }}
{{ $content := printf (i18n "external_reference_text.add_link") $url }}
<p>{{- $content | markdownify -}}</p>
{{ end }}
{{ if and $resource (not $only_text) }}
{{ $content := $resource.Content }}
{{/* 1 IMAGEN */}}
{{ if $add_image }}
{{ with (findRE $reOgImage $content 1) }}
{{ $ogImage = index . 0 | replaceRE $reOgImage "$1" }}
{{ end }}
{{/* Normaliza og:image relativo -> absoluto */}}
{{ if and $ogImage $u }}
{{ if hasPrefix $ogImage "//" }}
{{ $ogImage = printf "%s:%s" $u.Scheme $ogImage }}
{{ else if hasPrefix $ogImage "/" }}
{{ $ogImage = printf "%s://%s%s" $u.Scheme $u.Host $ogImage }}
{{ end }}
{{ end }}
{{ end }}
{{/* 2 TÍTULO */}}
{{ with (findRE $reOgTitle $content 1) }}
{{ $ogTitle = index . 0 | replaceRE $reOgTitle "$1" }}
{{ else }}
{{ with (findRE $reTitleTag $content 1) }}
{{ $ogTitle = index . 0 | replaceRE $reTitleTag "$1" }}
{{ end }}
{{ end }}
{{/* 3 DESCRIPCIÓN */}}
{{ with (findRE $reOgDesc $content 1) }}
{{ $ogDesc = index . 0 | replaceRE $reOgDesc "$1" }}
{{ end }}
<div class="my-6 w-full max-w-prose mx-auto{{ $add_margin_bottom_class }}">
<a href="{{ $url }}" target="_blank" rel="noopener noreferrer" class="not-prose flex flex-row group relative overflow-hidden rounded-lg bg-neutral-100 dark:bg-neutral-800 hover:shadow-xl transition-all duration-300 border border-neutral-300 dark:border-neutral-600">
{{ if $ogImage }}
<div class="flex-none relative overflow-hidden bg-neutral-200 dark:bg-neutral-700" style="width:10rem; min-height:6rem; align-self:stretch;">
<img src="{{ $ogImage | safeURL }}" alt="{{ ($ogTitle | default "Preview") | htmlUnescape }}" class="not-prose" style="position:absolute; inset:0; width:100%; height:100%; object-fit:cover;" loading="lazy" />
</div>
{{ end }}
<div class="flex flex-col justify-between p-4 min-w-0">
<div>
<h3 class="text-base font-bold text-neutral-900 dark:text-neutral-100 group-hover:text-primary-600 dark:group-hover:text-primary-400 leading-snug mb-1 line-clamp-2">
{{ $ogTitle | default $url | htmlUnescape }}
</h3>
{{ if $ogDesc }}
<p class="text-sm text-neutral-600 dark:text-neutral-400 line-clamp-3">
{{ $ogDesc | htmlUnescape | truncate 140 }}
</p>
{{ end }}
</div>
<div class="flex items-center gap-2 mt-3 pt-2 border-t border-neutral-200 dark:border-neutral-700/50">
<img src="https://www.google.com/s2/favicons?domain={{ $host | default $url }}&sz=32" alt="" class="w-4 h-4 opacity-70 flex-none" />
<div class="text-xs font-mono text-neutral-500 truncate">
{{ $host | default ($url | replaceRE `^https?://(www\.)?([^/]+).*` "$2") }} ↗
</div>
</div>
</div>
</a>
</div>
{{ end }}
{{ if not $resource }}
<div class="my-6{{ $add_margin_bottom_class }}">
{{ partial "components/alert.html" (dict
"icon" "link"
"content" (printf (i18n "external_reference_text.alternative") $url)
) }}
</div>
{{ end }}Además, añade las traducciones en la carpeta i18n: i18n/en.yaml y i18n/es.yaml.
global:
language: "EN"
external_reference_text:
add_link: "More information at the [link](%s) 👇"
alternative: "More in the following [external reference](%s)."global:
language: "ES"
external_reference_text:
add_link: "Más información en el [link](%s) 👇"
alternative: "Más en la siguiente [referencia externa](%s)."Para usar este shortcode, escribe lo siguiente en tu post en markdown:
(observa que debes eliminar los * en el siguiente ejemplo)
{{*< og-card url="http://www.sqlalchemy.org" add_link=true >*}}El shortcode tiene los siguientes parámetros:
- url: La URL de la página a enlazar.
- add_link: Añade el texto de referencia definido en la variable i18n
add_link. - add_margin_bottom: Deja un espacio o margen al pie de la tarjeta Open Graph.
- add_image: Añade la imagen Open Graph.
- only_text: Muestra únicamente el texto
alternativede i18n. Sin imagen ni información adicional.
Esta idea fue publicada en los siguientes foros 👇

