Automating your Now page with Hugo
A week ago on Hacker News someone shared their setup for an automated /now
page. Their idea was nice but I felt like they didn’t take full advantage of Hugo. This is how I reimplemented some of that functionality in an alternative way.
Fun fact: Hugo supports downloading JSON, CSV, XML, Images and is also capable of processing them at render time.
Movies
Like the original blog I also used letterboxd becuase it’s free. (Usually I use Trakt + Showly but it requires a subscription). We create a simple shortcode and then call it from the markdown.
markdown
{{< letterboxd >}}
go-html-template
{{ $data := dict }}
{{ $url := "https://letterboxd.com/yourusername/rss/" }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "%s" . }}
{{ else }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ end }}
{{ with $data.channel.item }}
<ul class="consumed-list">
{{ range first 3 . }}
{{ $title := .filmTitle }}
{{ $year := .filmYear }}
{{ $posterUrl := index (findRE `(https://.+\.jpg)` .description ) 0 }}
<li class="consumed-element">
<a href="https://www.themoviedb.org/movie/{{ .movieId }}" >
{{ with $thumbnail := resources.GetRemote $posterUrl }}
{{ with .Err }}
{{ warnf "%s" . }}
<img src="$posterUrl"
width="100"
loading="lazy"
alt="Poster of {{ $title }}">
{{ else }}
{{ $filter := images.Process "resize 100x q90 webp" }}
{{ with . | images.Filter $filter }}
<img src="{{ .RelPermalink }}"
width="{{ .Width }}"
height="{{ .Height }}"
alt="Poster Image of {{ $title }}"
loading="lazy">
{{ end }}
{{end}}
{{end}}
</a>
<p><a href="https://www.themoviedb.org/movie/{{ .movieId }}" >{{ $title }}</a> ({{$year}})</p>
</li>
{{ end }}
</ul>
{{ end }}
{{/* <pre>{{ debug.Dump $data }}</pre> */}}
This will automatically download and parse the RSS, download the posters from TMDB and optimize them.
Books
This time instead I used OpenLibrary because it has a slightly more complete collection.
markdown
{{< openlibrary "currently-reading" >}}
{{< openlibrary "already-read" >}}
go-html-template
{{ $type := (index .Params 0) }}
{{/* types: already-read currently-reading want-to-read https://openlibrary.org/dev/docs/api/mybooks */}}
{{ $data := dict }}
{{ $url := printf "https://openlibrary.org/people/mekBot/books/%s.json" $type }}
{{ with resources.GetRemote $url }}
{{ with .Err }}
{{ errorf "%s" . }}
{{ else }}
{{ $data = . | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf "Unable to get remote resource %q" $url }}
{{ end }}
{{/* {{ $data.reading_log_entries }} */}}
{{ with $data.reading_log_entries }}
<ul class="consumed-list">
{{ range first 3 . }}
{{ $date := index . "logged_date" }}
{{ $title := index . "work" "title" }}
{{ $url := index . "work" "key" }}
{{ $year := index . "work" "first_publish_year" }}
{{ $cover := index . "work" "cover_id" | string}}
{{ $thumbnailUrl := printf "https://covers.openlibrary.org/b/id/%s-L.jpg" $cover }}
{{ $authors := index . "work" "author_names" }}
<li class="consumed-element">
<a href="https://openlibrary.org{{ $url }}">
{{ with $thumbnail := resources.GetRemote $thumbnailUrl }}
{{ with .Err }}
{{ warnf "%s" . }}
<img
src="https://covers.openlibrary.org/b/id/{{ string $cover }}-S.jpg"
width="100"
loading="lazy"
alt="Cover Image of {{$title}}">
{{ else }}
{{ $filter := images.Process "resize 100x q90 webp" }}
{{ with . | images.Filter $filter }}
<img src="{{ .RelPermalink }}"
width="{{ .Width }}"
height="{{ .Height }}"
alt="Cover Image of {{$title}}"
loading="lazy">
{{ end }}
{{end}}
{{end}}
</a>
<div>
<a href="https://openlibrary.org{{ $url }}">{{ $title }}</a> ({{$year}})
<p>written by <em>{{ delimit $authors ", " " and "}}</em></p>
</div>
</li>
{{ end }}
</ul>
{{ end }}
Like before, it downloads the file, parses the JSON, looks up the cover and embeddeds it into the page.
It has some quirks like: the JSON orders the entries based the logged date when it was added in your “general” collection and not when it was moved in a specific collection, so when you change a book from “reading” to “finished reading” it won’t be in the first entry.
Games
I don’t play that many games tbh so I didn’t bother.
TV Shows
markdown
{{< serializd "yourusername" >}}
go-html-template
{{ $username := (index .Params 0) }}
{{ $refurl := printf "https://www.serializd.com/user/%s/currently_watching" $username }}
{{ $requrl := printf "https://www.serializd.com/api/user/%s/currently_watching_page/1?sort_by=date_added_desc" $username }}
{{ $data := dict }}
{{ $opts := dict
"method" "GET"
"body" `{"complete": true}`
"headers" (dict
"Accept" "application/json, text/plain, */*"
"Accept-Encoding" "deflate, br, zstd"
"Accept-Language" "en-US,en;q=0.9"
"Dnt" "1"
"Referer" $refurl
"Sec-Ch-Ua" `"Chromium";v="123", "Not:A-Brand";v="8"`
"Sec-Ch-Ua-Mobile" "?1"
"Sec-Ch-Ua-Platform" `"Android"`
"Sec-Fetch-Dest" "empty"
"Sec-Fetch-Mode" "cors"
"Sec-Fetch-Site" "same-origin"
"user-agent" "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36"
"x-requested-with" "serializd_vercel"
)
}}
{{ with resources.GetRemote $requrl $opts }}
{{ with .Err }}
{{debug.Dump $opts}}
{{debug.Dump .}}
{{ warnf "line 23 error: %s" . }}
<p>(Error occured while retrieving the data)</p>
{{ else }}
{{ $data = .Content | transform.Unmarshal }}
{{ end }}
{{ else }}
{{ errorf " Line 28 Unable to get remote resource %q" $url }}
{{ end }}
{{ with $data.items }}
<ul class="consumed-list">
{{ range first 3 . }}
{{ $title := index . "showName" }}
{{ $showid := index . "showId" | string }}
{{ $filename := index . "bannerImage" }}
{{ $posterUrl := printf "https://serializd-tmdb-images.b-cdn.net/t/p/w400%s" $filename }}
<li class="consumed-element">
<a href="https://www.serializd.com/show/{{ $showid }}" >
{{ with $thumbnail := resources.GetRemote $posterUrl }}
{{ with .Err }}
{{ warnf "%s" . }}
<img src="$posterUrl" width="100" loading="lazy" alt="Poster of {{ $title }}">
{{ else }}
{{ $filter := images.Process "resize 100x q90 webp" }}
{{ with . | images.Filter $filter }}
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="Poster Image of {{ $title }}" loading="lazy">
{{ end }}
{{end}}
{{end}}
</a>
<p><a href="https://www.serializd.com/show/{{ $showid }}" >{{ $title }}</a></p>
</li>
{{ end }}
</ul>
{{ end }}
Final result
My now page is visible here.