Hugo系列(五):添加豆瓣观影页面

通过 GitHub doumark-action 保存豆瓣观影数据,并给 Hugo 博客添加豆瓣观影页面。

Tip

已将 data.GetCSV 函数替换,Hugo 会在 0.136 版本移除,目前最新版本是 0.135。 取而代之的是 resources.Gettransform.Unmarshal,由于 resource 函数获取全局文件路径是 ~/assets/,因此也替换了对应文件的存放路径。

前言

Hexohexo-douban 项目,可以添加观影数据到页面,更换 Hugo 后也想添加一个观影页面,查找资料后发现有一个 doumark-action 项目可以将观影数据保存为本地文件,然后可以通过 Hugo 的函数获取数据,再前端展示,前面的文章中已经添加了豆瓣条目的短代码,这篇文章再补充两个,一个是近期观影的短代码,另外是添加一个海报墙页面。 最终实现的样式布局参考了一些博主的文章和仓库代码:

保存本地数据

由于豆瓣 API 的限制,之前的一些在线获取数据方式已经失效了,可以通过在线爬取标记的数据到本地,然后操作本地的数据文件,再前端展示数据。 用到的项目是 doumark-action。 首先在博客仓库的根目录新建一个 Workflow,新建 .github/workflows/douban.yml 文件,在其中写入自己想要保存的数据,影音、书籍,也可以保存到 Notion,项目有详细的说明,这里贴一下我的配置,其中将 id 改为自己的豆瓣 ID

 1# .github/workflows/douban.yml
 2name: douban
 3on: 
 4  schedule:
 5  - cron: "30 * * * *"
 6  workflow_dispatch:
 7
 8jobs:
 9  douban:
10    name: Douban mark data sync
11    runs-on: ubuntu-latest
12    steps:
13    - name: Checkout
14      uses: actions/checkout@v2
15
16    - name: movie
17      uses: lizheming/doumark-action@master
18      with:
19        id: 222317686
20        type: movie
21        format: csv
22        dir: ./assets/data/douban
23    
24    - name: book
25      uses: lizheming/doumark-action@master
26      with:
27        id: 222317686
28        type: book
29        format: csv
30        dir: ./assets/data/douban
31  
32    - name: Commit
33      uses: EndBug/add-and-commit@v8
34      with:
35        message: 'chore: update douban data'
36        add: './assets/data/douban'
Tip

如果使用 data.GetCSV 函数,不能使用站点根目录下的 data 目录,之前也有人出现同类型的错误,更改目录后就正常了。

When working with local data, the file path is relative to the working directory. You must not place CSV files in the project’s data directory. 来自 Hugo 文档

之后可以在仓库手动执行一下 action,完成后会在 ~/assets/data/douban/ 生成 movie.csvbook.csv ,这就是用到的数据文件。

近期观影短代码

配置

要添加短代码需要在 ~/layouts/shortcodes/ 目录下新建短代码模板文件,这里为 recent-douban.html,在其中添加模板元素:

 1{{ $type := .Get "type" }}
 2{{ $count := .Get "count" | default 4 }}
 3{{ $count = add $count 1 }}
 4{{ $items := slice }}
 5{{ $csvPath := "" }}
 6{{ if eq $type "movies" }}
 7    {{ $csvPath = "data/douban/movie.csv" }}
 8{{ else if eq $type "books" }}
 9    {{ $csvPath = "data/douban/book.csv" }}
10{{ end }}
11
12{{ with resources.Get $csvPath }}
13    {{ $opts := dict "delimiter" "," }}
14    {{ $items = . | transform.Unmarshal $opts }}
15{{ else }}
16    {{ errorf "无法获取资源 %q" $csvPath }}
17{{ end }}
18
19<div class="recent-items">
20    {{ range $idx, $item := first $count $items }}
21        {{ if ne $idx 0 }}
22            {{ $rating := float (index $item 6) }}
23            <div class="recent-item">
24                <div class="recent-item-cover">
25                    <img class="avatar" src="{{ index $item 3 }}" referrer-policy="no-referrer" loading="lazy" alt="{{ index $item 9 }}" title="{{ index $item 1 }}" width="150" height="220">
26                </div>
27                <div class="recent-douban-rating">
28                    <div class="rating">
29                        <span class="allstardark">
30                            <span class="allstarlight" style="width:{{ mul 10 $rating }}%"></span>
31                        </span>
32                        <span class="rating_nums">{{ $rating }}</span>
33                    </div>
34                </div>
35                <div class="recent-item-title">
36                    <a rel="noreferrer" href="{{ index $item 5 }}" target="_blank">{{ index $item 1 }}</a>
37                </div>
38            </div>
39        {{ end }}
40    {{ end }}
41</div>

然后添加一下样式代码,这里和之前的文章保持一致,添加到 shortcodes.scss 中:

1.recent-items {
2    display: flex;
3    flex-wrap: wrap;
4    justify-content: space-between;
5    margin: 15px;
6}
7.recent-items .recent-item, .recent-items .recent-item img {
8    margin-bottom: 10px;
9}
Important

之前的豆瓣条目短代码样式文件已经添加了评分星级的样式 allstarlight,所以这里不添加,如果之前没添加过的话需要添加相关的样式,在之前的文章可以找到。

使用

使用方法如下:

1{\{< recent-douban type="movies" count=8 >}\}
2{\{< recent-douban type="books" >}\}

默认展示四条数据,可以通过 count 参数指定显示的数量。

观影页面

配置

添加一个海报墙页面展示所有的观影信息,默认展示 18 条信息。

首先在 ~/lauouts/_default/ 文件夹下新建模板文件,这里为 posterwall.html,然后在其中添加页面元素:

 1{{- define "title" -}}
 2  {{- title .Title -}}
 3  {{- if .Site.Params.withSiteTitle }} {{ .Site.Params.titleDelimiter }} {{ .Site.Title }}{{- end -}}
 4{{- end -}}
 5
 6{{- define "content" -}}
 7  {{- $title := title .Title -}}
 8  {{- $params := partial "function/params.html" -}}
 9  {{- $toc := .Scratch.Get "toc" -}}
10  {{- $tocEmpty := eq .TableOfContents `<nav id="TableOfContents"></nav>` -}}
11<div class="posterwall-wrapper">
12  <div class="posterwall-description">
13    {{ .Params.description | markdownify }}
14  </div>
15  <div class="movie-wall">
16    {{ $csvPath := "data/douban/movie.csv" }}
17    {{ $items := slice }}
18    {{ with resources.Get $csvPath }}
19        {{ $opts := dict "delimiter" "," }}
20        {{ $items = . | transform.Unmarshal $opts }}
21    {{ else }}
22        {{ errorf "无法获取资源 %q" $csvPath }}
23    {{ end }}
24    {{ range $idx, $item := $items }}
25        {{ if ne $idx 0 }}
26        {{ $rating := float (index $item 6) }}
27        <div class="movie-item" style="display: none;">
28            <div class="movie-cover">
29            <img src="{{ index $item 3 }}" alt="{{ index $item 1 }}" loading="lazy" width="200" referrer-policy="no-referrer">
30            <div class="movie-info">
31                <div class="movie-title"><a rel="noreferrer" href="{{ index $item 5 }}" target="_blank">{{ index $item 1 }}</a></div>
32                <div class="movie-rating">
33                    <div class="rating">
34                        <span class="allstardark">
35                            <span class="allstarlight" style="width:{{ mul 10 $rating }}%"></span>
36                        </span>
37                        <span class="rating_nums">{{ index $item 6 }}</span>
38                    </div>
39                </div>
40                <div class="movie-card">{{ index $item 12 }}</div>
41                <div class="movie-comment">{{ index $item 9 }}</div>
42            </div>
43            </div>
44        </div>
45        {{ end }}
46    {{ end }}
47  </div>
48  <button id="loadMore">加载更多</button>
49</div>
50
51<script>
52  let visibleMovies = 20;
53  const movieItems = document.querySelectorAll('.movie-item');
54  const loadMoreButton = document.getElementById('loadMore');
55
56  function updateVisibility() {
57    movieItems.forEach((movie, idx) => {
58      movie.style.display = idx < visibleMovies ? 'block' : 'none';
59    });
60
61    if (visibleMovies >= movieItems.length) {
62      loadMoreButton.style.display = 'none';
63    }
64  }
65
66  loadMoreButton.addEventListener('click', () => {
67    visibleMovies += 20;
68    updateVisibility();
69  });
70
71  updateVisibility();
72</script>
73{{ end }}
Important

  • 代码中包括了页面元素和加载更多数据的 js 代码,并且在页面标题下添加了一个页面描述,方便在 markdown 文件中更改描述信息,也便于调整样式。
  • 同样页面中评分星级的命名 allstarlight 也是与之前豆瓣条目短代码一致,方便一起调整。
  • 顶部的元素适应主题 Fixit,其他主题自行更改。

完成后在 custom.scss 中添加样式代码:

 1// 电影海报墙样式
 2.posterwall-description {
 3  margin: 10px;
 4  text-align: center;
 5}
 6.movie-wall {
 7  display: grid;
 8  grid-template-columns: repeat(3, 1fr);
 9  grid-gap: 3px;
10  padding: 3px;
11}
12
13.movie-item {
14  width: 100%;
15  margin-bottom: 0;
16}
17
18.movie-cover {
19  position: relative;
20  overflow: hidden;
21  aspect-ratio: 2 / 3;
22}
23
24.movie-cover img {
25  width: 100%;
26  height: 100%;
27  object-fit: cover;
28  transition: transform 0.3s ease;
29}
30
31.movie-info {
32  position: absolute;
33  top: 0;
34  left: 0;
35  right: 0;
36  bottom: 0;
37  background: rgba(0, 0, 0, 0.8);
38  color: #fff;
39  padding: 10px;
40  opacity: 0;
41  transition: opacity 0.3s ease;
42  overflow: auto;
43  display: flex;
44  flex-direction: column;
45  justify-content: center;
46}
47
48.movie-cover:hover img {
49  transform: scale(1.1);
50}
51
52.movie-cover:hover .movie-info {
53  opacity: 1;
54}
55
56#loadMore {
57  display: block;
58  margin: 20px auto;
59  padding: 10px 20px;
60  background-color: lighten($color-accent,0.1);
61  color: white;
62  border: none;
63  cursor: pointer;
64}
65
66#loadMore:hover {
67  background-color: gray;
68}
69
70.movie-info .movie-rating {
71  display: flex;
72  align-items: center;
73  margin-top: 3px;
74  margin-bottom: 3px;
75}
76.movie-info .movie-rating .rating .rating_nums {
77  font-size: 1.0em;
78  color: #fff;
79}
80
81.movie-info .movie-card {
82  margin: 5px 0;
83  font-size: 0.7em;
84  color: #fff;
85}
86
87.movie-info .movie-comment {
88  margin: 5px 0;
89  font-size: 0.7em;
90  color: #fff;
91}

使用

content 目录下创建一个 posterwall.md 文件,Front Matter 信息填写如下,标题可描述可以自由更改:

1---
2title: 海报墙
3layout: "posterwall"
4description: "这里是我已观看电影的海报墙,数据来源于豆瓣。"
5---

添加后就可以在配置文件中添加一个菜单页面来指向海报墙,最终效果可以访问 👉 观影页面

图片地址替换

CSV 文件中的图片地址是 douban-action 作者反代的地址,近期图片挂了,而 Neodb 提供 API,所以这里将图片替换为 Neodb 地址,方法如下, 海报墙页面代码和位置都一致:

{{ $rating := float (index $item 6) }} 这行代码下添加下面两行:

1{{ $dbUrl := printf "https://neodb.social/api/catalog/fetch?url=%s" (index $item 5) }}
2{{ $dbFetch := (resources.GetRemote $dbUrl) | transform.Unmarshal }}

然后将图片地址更换:

1-src=" {{ index $item 3 }} " 
2+src=" {{ $dbFetch.cover_image_url }} "

参考

Hugo系列(五):添加豆瓣观影页面

https://blog.grew.cc/posts/hugo-douban/

作者

Tom

创建时间

2024-08-19

最后更新时间

2024-08-19

许可协议

CC BY 4.0