NA of Moles

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.


Tags: