【Hugo】RSSの自動画像設定を作ったけどやめた

【Hugo】RSSの自動画像設定を作ったけどやめた

Hugo 2024/10/27

概要

全ての記事で毎回サムネイル画像を用意するのが面倒だったので、記事内に画像が無い場合は、Zenn のような自動でタイトルが書かれたサムネイル画像生成するようにした。

  • 自動サムネイルの出力イメージ

最初は満足していたのだが、RSS の xml をみたときに、サムネイルのリンクが含まれていないことに気づいた。 最初は簡単に追加できると思ったが、実装してみると無理やりな実装が多くなってしまい、一応実装できたが美しく無いのでやめた。

この記事では、実際には実装しなかった RSS への画像追加コードをメモとして残す。

サムネイル画像を自動で生成する

作成方法は下記のサイトを参考にさせていただいた。
チラシの表の反対側

下記の方法のすごいところは、Go Template にコードを記載するだけで勝手にサムネイル画像が生成とOGPの設定がされる点である。
※ Hugo の images.Text の機能を利用しており、 {{ images }}public/img 配下に画像が生成される。

layouts/partials/ogp-card.html
{{/* もし ogp.png というファイルがあれば、それを使う */}}
{{- $image := .Resources.Get "ogp.png" }}
{{- if not $image -}}
  {{/* ogp.png がないので、生成する */}}

  {{/* 文字のレンダリングに使うフォントをダウンロードする */}}
  {{- $font := resources.Get "fonts/NotoSansJP-Bold.ttf" }}

  {{- if .IsPage -}}
    {{/* 記事ページの場合。記事タイトルの画像を生成する */}}

    {{/* タイトルの 18 文字ごとに改行を入れる */}}
    {{- $maxLen := 18 }}
    {{- $len := strings.RuneCount .Title -}}
    {{- $titleText := "" -}}
    {{- range $i := seq 0 (div $len $maxLen) }}
      {{- $line := substr $.Title (mul $i $maxLen) $maxLen }}
      {{- $titleText = printf "%s%s\n" $titleText $line }}
    {{- end -}}

    {{/* 記事とサイトのタイトルをベースの画像の上にレンダリングする */}}
    {{- $articleTitle := images.Text $titleText (dict "font" $font "color" "#333" "size" 60 "x" 100 "y" 100) }}
    {{- $image = (resources.Get "img/ogp.png" | images.Filter $articleTitle) }}
  {{- else }}
    {{/* 記事以外のページの場合。サイトのタイトルの画像を生成する */}}
    {{- $title := images.Text site.Title (dict "font" $font "color" "#333" "size" 100 "x" 140 "y" 220) }}
    {{- $image = (resources.Get "img/ogp_title.png") }}
  {{- end -}}
{{- end -}}
<meta property="og:image" content="{{ $image.Permalink }}" />

下記のコードを head.html などに記載し、どのページでもサムネイル画像が生成されるようにする。

layouts/partials/head.html
<head>
 <!-- もともとのコード -->
    {{ partial "ogp-card.html" . }}
</head>

問題点:サムネイル画像の再利用(RSSへの反映)ができない

サムネイル画像だけを生成するだけであれば、上記の方法で十分満足だった。
しかし、RSS の xml にもサムネイル情報を追加したいという気持ちが出てきた。 簡単に RSS にサムネイルの画像情報を追加できると思ったら、これが沼だった。

実装上の主な問題点は下記の2つ。

生成された画像を Go Template から利用できない

この方法で生成された画像は Public 配下に保存されるため、 Go Template からアクセスができない。 正確には別の方法でアクセスができるのだが、それは解決策に書く。

生成された画像のパスがわからない

生成される画像のファイル名を指定することができない。
ogp_huae1e778bbe54732beded77b6d19b26b7_50070_filter_508557149237022477.png といった感じでランダムな文字列が生成される。
そのためサムネイルとして設定されている画像の URL を知りたい場合は、Public 配下の該当記事の html ファイルの meta og:image の src を見る必要がある。

解決策(非推奨)

サムネイルの URL を resources.GetRemote で抜き出す

Go Template からサムネイル画像の情報を取得するために、リモートからアクセスする方法を行った。
具体的には、{{ resources.GetRemote $url }} で記事の html を読み込み、meta og:image の src を抜き取ることで、サムネイル画像の URL を特定することができる。

list.rss.xml に サムネリンクを入れ込む

RSS に画像リンクを埋め込むために、下記のコードで xml 内に <enclosure /> タグを入れ込み、画像情報を付与した。

layouts/_default/list.rss.xml
{{ $u := urls.Parse .Permalink }}
{{ $url := printf "%s%s" "https://www.cyamax.com" $u.RequestURI }}
{{ with $result := resources.GetRemote $url }}
{{- $image := "" -}}
{{- with $head := index (findRE `<head>(.|\n)*?</head>` $result.Content) 0 -}}
    {{- range $meta := findRE `<meta.*?>` $head -}}
        {{- $name := replaceRE `<.*name=\"(.*?)\".*>` "$1" $meta -}}
        {{- $property := replaceRE `<.*property=\"(.*?)\".*>` "$1" $meta -}}
        {{- $content := replaceRE `<.*content=\"(.*?)\".*>` "$1" $meta -}}
        {{- if eq $property "og:image" -}}
            {{- $image = $content -}}
            <enclosure url="{{ $image }}" length="0" type="image/png"/>
        {{- end -}}
    {{- end -}}
{{- end -}}
{{- end -}}

追加の問題点

上記の方法で動くのだが、色々と問題点が残る。

hugo server -D の起動が遅くなる

Hugo 起動時に rss で記載されているリンクすべてに対して {{ resources.GetRemote $url }} を行うので、起動が遅くなってしまった。

鶏と卵問題

Hugo を起動すると、list.rss.xml が読み込まれ、 {{ resources.GetRemote $url }} で html の情報を取りに行くが、そのアクセス先の Hugo がまだ起動していない。起動するためには Hugo が起動している必要があるので、矛盾してしまっている。
今回は、ローカルでの Hugo 起動時は本番サーバ向けに Request を投げるようにしたが、仕様としてはかなりイケていない。
また、コード内に https://www.cyamax.com がハードコーディングされているのもイケてない。 イケてないとわかっているのに実装している理由は、本来利用できる.Site.baseURL の値が機能しないためである。
ローカルで起動するとデフォルトでは http://local:1313 になってしまい、現在起動中の URL にアクセスしてしまうからである。
.Site.Params などで別で設定すれば良いかもしれないが、この機能のためだけに考慮が増えること自体が良くない。

仕様が美しくない

こんなにおかしなことになってしまう理由はわかっている。 それは、 Public 配下の画像の情報を取得しようという発想自体がナンセンスだからだ。 本来は生成された public 配下の html を再利用することはない。 今回は、それを再利用しようとしたからおかしなことになってしまった。

  • 通常:本来の Hugo の使い方
flowchart LR;
    md,GoTemplate-->public/html;
  • 今回の問題:本来は一方通行な仕様なのに、戻りが発生している
flowchart LR;
    md,GoTemplate-->public/html-->md,GoTemplate;