Hugo系列(五):添加豆瓣观影页面
通过 GitHub doumark-action 保存豆瓣观影数据,并给 Hugo 博客添加豆瓣观影页面。
Tip已将
data.GetCSV
函数替换,Hugo 会在0.136
版本移除,目前最新版本是0.135
。 取而代之的是resources.Get
和transform.Unmarshal
,由于resource
函数获取全局文件路径是~/assets/
,因此也替换了对应文件的存放路径。
前言
Hexo
有 hexo-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.csv
和 book.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 }} "