Hugo で URLをカード形式で表示するcustom short code を作った
·
Konboi
これもやっていることは二番煎じだけど、記録として残しておく。
はじめに
はてなブログURLをカード形式に整形して表示してくれる機能があるが、hugoにはそれがない。(と記憶している
以前は Iframary というサービスを使って埋め込んでいた
Embeds codes for today
Iframely gives you simple APIs to have all the Web
ただ、都度作成すのが面倒だったり、Iframary でリダイレクトさせるのも微妙だなと思っていた。そんな折に hugo に resources.GetRemote と言う HTTP Request 的なことができるメソッド追加されているのを発見した。
resources.GetRemote
Returns a remote resource from the given URL, or nil if none found.

また、他の人もこの機能を使ってカード形式でURLを埋め込むカスタムコードを作っている人がいたので、自分が使っている hugo layout - paper にあったカスタムコードを作ってみた
コード
カスタムショートコードのコードは以下。
これを layouts/shortcodes/card.html において
{{ <card "URL" > }}
と言う形で使っている
{{- $url := .Get 0 -}}
{{- $title := "" -}}
{{- $description := "" -}}
{{- $image := "" -}}
{{- $siteName := "" -}}
{{- $result := try (resources.GetRemote $url) -}}
{{- if $result.Err -}}
{{- warnf "Unable to fetch %s: %s" $url $result.Err -}}
{{- else -}}
{{- with $result.Value -}}
{{- $html := .Content -}}
{{/* og:title */}}
{{- $ogTitle := findRE `<meta[^>]*property=["']og:title["'][^>]*content=["']([^"']+)["'][^>]*>` $html 1 -}}
{{- if not $ogTitle -}}
{{- $ogTitle = findRE `<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:title["'][^>]*>` $html 1 -}}
{{- end -}}
{{- with $ogTitle -}}
{{- $title = index . 0 | replaceRE `.*content=["']([^"']+)["'].*` "$1" -}}
{{- end -}}
{{/* og:description */}}
{{- $ogDesc := findRE `<meta[^>]*property=["']og:description["'][^>]*content=["']([^"']+)["'][^>]*>` $html 1 -}}
{{- if not $ogDesc -}}
{{- $ogDesc = findRE `<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:description["'][^>]*>` $html 1 -}}
{{- end -}}
{{- with $ogDesc -}}
{{- $description = index . 0 | replaceRE `.*content=["']([^"']+)["'].*` "$1" -}}
{{- end -}}
{{/* og:image */}}
{{- $ogImage := findRE `<meta[^>]*property=["']og:image["'][^>]*content=["']([^"']+)["'][^>]*>` $html 1 -}}
{{- if not $ogImage -}}
{{- $ogImage = findRE `<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:image["'][^>]*>` $html 1 -}}
{{- end -}}
{{- with $ogImage -}}
{{- $image = index . 0 | replaceRE `.*content=["']([^"']+)["'].*` "$1" -}}
{{- end -}}
{{/* og:site_name */}}
{{- $ogSite := findRE `<meta[^>]*property=["']og:site_name["'][^>]*content=["']([^"']+)["'][^>]*>` $html 1 -}}
{{- if not $ogSite -}}
{{- $ogSite = findRE `<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:site_name["'][^>]*>` $html 1 -}}
{{- end -}}
{{- with $ogSite -}}
{{- $siteName = index . 0 | replaceRE `.*content=["']([^"']+)["'].*` "$1" -}}
{{- end -}}
{{/* fallback: <title> */}}
{{- if not $title -}}
{{- $htmlTitle := findRE `<title[^>]*>([^<]+)</title>` $html 1 -}}
{{- with $htmlTitle -}}
{{- $title = index . 0 | replaceRE `<title[^>]*>([^<]+)</title>` "$1" -}}
{{- end -}}
{{- end -}}
{{/* fallback: meta description */}}
{{- if not $description -}}
{{- $metaDesc := findRE `<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["'][^>]*>` $html 1 -}}
{{- if not $metaDesc -}}
{{- $metaDesc = findRE `<meta[^>]*content=["']([^"']+)["'][^>]*name=["']description["'][^>]*>` $html 1 -}}
{{- end -}}
{{- with $metaDesc -}}
{{- $description = index . 0 | replaceRE `.*content=["']([^"']+)["'].*` "$1" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $domain := $url | replaceRE `^https?://([^/]+).*` "$1" -}}
{{- if not $title -}}
{{- $title = $domain -}}
{{- end -}}
<a href="{{ $url }}" target="_blank" rel="noopener noreferrer" class="paper-card not-prose">
<div class="paper-card__body">
<div class="paper-card__title">{{ $title | htmlUnescape }}</div>
{{- if $description }}
<div class="paper-card__desc">{{ $description | htmlUnescape }}</div>
{{- end }}
<div class="paper-card__meta">{{ if $siteName }}{{ $siteName }} · {{ end }}{{ $domain }}</div>
</div>
{{- if $image }}
<div class="paper-card__thumb">
<img src="{{ $image }}" alt="{{ $title }}" loading="lazy" />
</div>
{{- end }}
</a>
Designは css/style.css を 使っている テーマが読み込む設定なので追加のスタイルをあてるようにしている。
おわりに
OGP 画像の表示部分がいい感じに表示できない時があるが、概ね満足。
もう少し使ってみて問題なさそうなら、既存の記事で Iframely を使っているところがあれば置き換えていく。