51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

NeoDB API 创建观影页面

前言

#

几个月之前在 长毛象联邦宇宙 里问过 NeoDB 官方有没有 API,得到肯定回答后,我就着手计划把观影页面的 API 搬到 NeoDB 了。前几天豆瓣的图片挂掉之后,加快了这一进程。
感谢豆瓣以前提供的无偿服务。不过这也印证了 SaaS 服务不可信 的观点。

有很多吐槽,但是算了,直接开始写备忘录。
我没有使用通过 API 获取动态数据的方式,而是把数据都下载到本地。静态化后性能会更好。

1. 注册 NeoDB 账号

#

注册 NeoDB 账号前,需要注册一个 Mastodon 长毛象宇宙的账号,有很多实例可以注册。然后用 Mastodon 账号就可以登录 NeoDB 了。最新的 NeoDB 似乎已经可以绑定邮箱登录了。
注册 Mastodon 和 NeoDB 这些都是小事情,暂时略过,默认任何人都会了。
比如我就注册在 mastodon.social ,我以前还自建过 Mastodon,不过没必要。

2. 生成 NeoDB 的 Token

#

参考:《 NeoDB 获取 Access Token 》一文。

3. 标记影音

#

3.1 在 NeoDB 标记:

3.2 在 NeoDB 数据 设置里导入其他平台标记的数据:

4. 下载 NeoDB 数据

#

因为 NeoDB 限制分页,需要按页数下载,不能一次下载所有数据。
就写了个 Shell Script 脚本下载:

注意替换 QuhZZpr8bE711111111111X2OPaSRKUAccess Token

#! /bin/sh

curl -X 'GET' 'https://neodb.social/api/me/shelf/complete?category=movie&page=1'
-H 'accept: application/json'
-H 'Authorization: Bearer QuhZZpr8bE711111111111X2OPaSRKU' > movie1.json

curl -X 'GET' 'https://neodb.social/api/me/shelf/complete?category=tv&page=1'
-H 'accept: application/json'
-H 'Authorization: Bearer QuhZZpr8bE711111111111X2OPaSRKU' > tv1.json

pages=$(jq '.pages' movie1.json) tv_pages=$(jq '.pages' tv1.json)

下载 Movie 分类

循环下载文件,因为 page 1 已经下载过了,从 2 开始

for ((i=2; i<=$pages; i++)); do url="https://neodb.social/api/me/shelf/complete?category=movie&page=$i" filename="movie$i.json"

下载文件并保存为对应的文件名

curl -X 'GET' "$url"
-H 'accept: application/json'
-H 'Authorization: Bearer QuhZZpr8bE711111111111X2OPaSRKU' > "$filename" done

下载 TV 分类

for ((i=2; i<=$tv_pages; i++)); do tv_url="https://neodb.social/api/me/shelf/complete?category=tv&page=$i" tv_filename="tv$i.json"

curl -X 'GET' "$tv_url"
-H 'accept: application/json'
-H 'Authorization: Bearer ${{ secrets.NEODB_ACCESS_TOKEN }}' > "$tv_filename" done

把所有数据合并成一个文件

jq -c -s '{data: map(.data[]) | unique | sort_by(.created_time) | reverse, pages: map(.pages)[0], count: map(.count)[0]}' *.json > movie.json

然后就会得到一个包含所有标记数据的文件------movie.json

movie.json 文件复制到目录 data/neodb/movie.json



5. 新建 movie.html 模板

#

在 Hugo 根目录或者主题目录 layouts/_default 新建一个 movie.html 模板。
如果不知道模板长什么样,可以复制正在使用的主题下其他 Page 在用的模板,然后改下名字。
核心代码:

<!-- 其他代码 -->

<!-- 引入 Style 。注意路径,放在 static 目录--> <link rel="stylesheet" href="/movie.css">

<!-- 获取本地 Json 数据 --> {{ $movies := getJSON "data/neodb/movie.json" }}

<div class="yourContent">

&lt;div class=&quot;sort-by-items&quot;&gt;
    &lt;a href=&quot;javascript:void 0;&quot; class=&quot;sort-by-item active&quot; data-order=&quot;time&quot;&gt;&lt;i
            class=&quot;fas fa-sort-amount-down&quot;&gt;&lt;/i&gt; 观影时间排序&lt;/a&gt;
    &lt;a href=&quot;javascript:void 0;&quot; class=&quot;sort-by-item&quot; data-order=&quot;rating&quot;&gt;&lt;i
            class=&quot;fas fa-sort-numeric-down-alt&quot;&gt;&lt;/i&gt; 评分排序&lt;/a&gt;
    &lt;a href=&quot;javascript:void 0;&quot; class=&quot;sort-by-item&quot; data-order=&quot;count&quot;&gt;&lt;i class=&quot;fas fa-sort-alpha-down-alt&quot;&gt;&lt;/i&gt;
        评分人数排序&lt;/a&gt;
&lt;/div&gt;
&amp;lt;div class=&amp;quot;movie&amp;quot;&amp;gt;

    {{ range $movies.data }}
    {{ $title := .item.display_title }}
    {{ $rating := .item.rating }}
    {{ $movie_url := .item.url }}
    {{ $cover := .item.cover_image_url }}
    {{ $cover_name := path.Base $cover }}
    {{ $cate_movie := &amp;quot;movie&amp;quot; }}
    {{ $cate_tv := &amp;quot;tv&amp;quot; }}

    &amp;lt;div class=&amp;quot;movies sorting&amp;quot; data-marked=&amp;quot;{{ .created_time }}&amp;quot; data-year='{{  dateFormat &amp;quot;2006-01-02 15:04:05&amp;quot; .created_time }}' data-star=&amp;quot;{{ .rating_grade }}&amp;quot; data-rating=&amp;quot;{{ .item.rating }}&amp;quot; data-count=&amp;quot;{{ .item.rating_count }}&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;cover&amp;quot;&amp;gt;
                &amp;lt;div class=&amp;quot;cover__container&amp;quot;&amp;gt;
                    {{ range .item.external_resources }}
                        {{ if (in .url &amp;quot;douban&amp;quot;) }}
                            &amp;lt;a href=&amp;quot;{{ .url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;&amp;lt;img alt=&amp;quot;{{ $title }}&amp;quot; class=&amp;quot;lazy&amp;quot; loading=&amp;quot;lazy&amp;quot; data-src=&amp;quot;/assets/images/posts/neodb/{{ $cover_name }}&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;
                        {{ end }}
                    {{ end }}
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;title&amp;quot;&amp;gt;
                {{ $hasDouban := false }}
                {{ range .item.external_resources }}
                    {{ if (in .url &amp;quot;douban&amp;quot;) }}
                        {{ $hasDouban = true }}
                        &amp;lt;a href=&amp;quot;{{ .url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;
                            {{ $title }}
                        &amp;lt;/a&amp;gt;
                        {{ end }}
                    {{ end }}

                    {{ if not $hasDouban }}
                        &amp;lt;a href=&amp;quot;https://neodb.social{{ $movie_url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;
                            {{ $title }}
                        &amp;lt;/a&amp;gt;
                    {{ end }}

            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;rating&amp;quot;&amp;gt;

                {{ range $star := (seq 0 2 8) }}
                {{ if gt $rating $star }}
                &amp;lt;span class=&amp;quot;rating_star&amp;quot;&amp;gt;
                    &amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; width=&amp;quot;24&amp;quot; height=&amp;quot;24&amp;quot; class=&amp;quot;stars&amp;quot;&amp;gt;
                        &amp;lt;path fill=&amp;quot;none&amp;quot; d=&amp;quot;M0 0h24v24H0z&amp;quot;&amp;gt;&amp;lt;/path&amp;gt;
                        &amp;lt;path fill=&amp;quot;currentColor&amp;quot; d=&amp;quot;M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z&amp;quot;&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/span&amp;gt;
                {{ else }}
                &amp;lt;span class=&amp;quot;rating_star&amp;quot;&amp;gt;
                    &amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; width=&amp;quot;24&amp;quot; height=&amp;quot;24&amp;quot; class=&amp;quot;stars white&amp;quot;&amp;gt;
                        &amp;lt;path fill=&amp;quot;none&amp;quot; d=&amp;quot;M0 0h24v24H0z&amp;quot;&amp;gt;&amp;lt;/path&amp;gt;
                        &amp;lt;path fill=&amp;quot;currentcolor&amp;quot; d=&amp;quot;M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z&amp;quot;&amp;gt;&amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/span&amp;gt;
                {{ end }}
                &amp;lt;span class=&amp;quot;rating_star&amp;quot;&amp;gt;{{ $rating }}&amp;lt;/span&amp;gt;
            &amp;lt;div class=&amp;quot;rating_count hidden&amp;quot;&amp;gt;
                &amp;lt;span&amp;gt;
                    &amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; width=&amp;quot;24&amp;quot; height=&amp;quot;24&amp;quot; class=&amp;quot;stars&amp;quot;&amp;gt;
                        &amp;lt;path fill=&amp;quot;none&amp;quot; d=&amp;quot;M0 0h24v24H0z&amp;quot;&amp;gt;&amp;lt;/path&amp;gt;
                        &amp;lt;path fill=&amp;quot;currentColor&amp;quot; d=&amp;quot;M12 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z&amp;quot;&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/span&amp;gt;
                &amp;lt;span&amp;gt;&amp;lt;a href=&amp;quot;https://neodb.social{{ $movie_url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;{{ .item.rating_count }} {{ T `movie_count_text` }}&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&amp;quot;referrer&amp;quot;&amp;gt;
                {{ if eq .item.category $cate_movie }}
                &amp;lt;i class=&amp;quot;fas fa-film fa-fw&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
                {{ else if eq .item.category $cate_tv }}
                &amp;lt;i class=&amp;quot;fas fa-tv fa-xs&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
                {{ end }}
                &amp;lt;span class=&amp;quot;neodb&amp;quot;&amp;gt;
                    &amp;lt;a href=&amp;quot;https://neodb.social{{ $movie_url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;
                        &amp;lt;img src=&amp;quot;/assets/images/movie/neodbsocial.jpg&amp;quot; loading=&amp;quot;lazy&amp;quot; alt=&amp;quot;NeoDB&amp;quot;&amp;gt;
                    &amp;lt;/a&amp;gt;
                &amp;lt;/span&amp;gt;

                {{ range .item.external_resources }}
                    {{ $parsedURL := urls.Parse .url }}
                    {{ $host := $parsedURL.Hostname }}
                    {{ $title := .title }}
                        &amp;lt;span class=&amp;quot;external-resource&amp;quot;&amp;gt;
                            &amp;lt;a href=&amp;quot;{{ .url }}&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;
                                &amp;lt;img src=&amp;quot;/assets/images/movie/{{ $host }}.png&amp;quot; loading=&amp;quot;lazy&amp;quot; alt=&amp;quot;{{ $title }}&amp;quot;&amp;gt;
                            &amp;lt;/a&amp;gt;
                        &amp;lt;/span&amp;gt;
                {{ end }}

                &amp;lt;!-- &amp;lt;span class=&amp;quot;rottentomatoes&amp;quot;&amp;gt;
                    &amp;lt;a href=&amp;quot;http://www.google.com/search?hl=en&amp;amp;q={{ $title }}+rotten+tomatoes&amp;amp;btnI=I&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noreferrer noopener nofollow&amp;quot;&amp;gt;
                        &amp;lt;img src=&amp;quot;/assets/images/movie/www.rottentomatoes.com.png&amp;quot; loading=&amp;quot;lazy&amp;quot; alt=&amp;quot;NeoDB&amp;quot;&amp;gt;
                    &amp;lt;/a&amp;gt;
                &amp;lt;/span&amp;gt; --&amp;gt;
            &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    {{ end }}

&amp;lt;/div&amp;gt;

&lt;/div&gt;

</article>

<script type="text/javascript" src="/assets/lazyload.iife.min.js?v=17.8.3"></script> <script type="text/javascript" src="/assets/movie.min.js?v=2023.07.11"></script>

<script> var lazyLoadInstance = new LazyLoad({ // Your custom settings go here }); </script>

<!-- 其他代码 -->

6. CSS 样式

#

这是一些必要的 CSS,只会影响到观影页面,没有侵入性。
可把 CSS 放入 Hugo 的 static 目录

.movie {
    display: grid;
    width: 100%;
    gap: 10px;
    margin-top: 1rem;
}

@media (min-width: 1000px) { .movie { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

@media only screen and (max-width: 1000px) {

.movie {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

}

@media only screen and (max-width: 680px) { .movie { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

@media (max-width: 359px) { .movie { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.movie .cover { position: relative; border-radius: 0.25rem; width: 100%; height: 100%; }

.movie .cover .cover__container { position: relative; border-radius: 0.25rem; background-image: linear-gradient(to bottom, #ddd, #f5f5f5); overflow: hidden; padding-top: 177.78%; /* 9:16 竖屏宽高比的容器 / padding-top: 133.33%; / 3:4 宽高比的容器 / padding-top: 150%; / 豆瓣常见的宽高比的容器 */ }

.movie .cover .cover__container img { position: absolute; top: 0; left: 0; display: block; width: 100%; height: 100%; cursor: pointer; -o-object-fit: cover; object-fit: cover; transition: all 0.6s ease; }

.movie .cover .cover__container img:hover { transform: scale(1.1); }

.movie .movies { display: flex; flex-flow: column; justify-content: center; align-items: center; margin: 0; margin-bottom: 2rem; }

.movie .title { margin-top: 0.25rem; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-bottom: 0.5rem; }

.movie .title, .movie .rating, .movie .referrer { margin-right: auto; }

.movie .rating { display: flex; -webkit-box-align: center; align-items: center; font-size: 0.875rem; }

.movie .rating span:last-child { margin-right: 0.5rem; }

.movie .rating .stars { margin-right: 1px; width: 0.875rem; height: 0.875rem; color: #fccd59; }

.movie .rating .stars.white { color: #eee; }

.movie .movies .referrer { display: flex; justify-content: center; align-items: center; margin-top: 0.5rem; gap: 5px; }

.movie .movies .referrer img { width: 1rem; height: 1rem; opacity: 0.7; }

.movie .movies .referrer img:hover { opacity: 0.95; transition: all 0.6s ease; }

.sort-by-items { text-align: left; }

.rating_count { display: flex; text-align: left; justify-content: flex-start; align-items: center; white-space: nowrap; overflow: hidden; }

.rating_star.hidden, .rating_count.hidden { display: none; }

.sort-by-item.active { background: rgba(85,85,85,.1); }

.sort-by-item { padding: 0 5px; }

7. JS 代码

#

其实可以不需要 JS。所有数据都通过脚本和 Hugo 程序处理好了。这一段 JS 主要是用于排序。

function search(e) {
    // 隐藏所有 .sorting 元素
    document.querySelectorAll('.sorting').forEach(item => item.classList.add('hide'));
// 移除之前处于活动状态的 .dvtjjf 元素
document.querySelector(`.dvtjjf.active[data-search=&quot;${e.target.dataset.search}&quot;]`)?.classList.remove('active');

if (e.target.dataset.value) { // 将当前点击的 .dvtjjf 元素设为活动状态 e.target.classList.add('active'); }

// 构建属性选择器数组 const searchItems = document.querySelectorAll('.dvtjjf.active'); const attributes = Array.from(searchItems, searchItem =&gt; { const property = data-${searchItem.dataset.search}; const logic = searchItem.dataset.method === 'contain' ? '*' : '^'; const value = searchItem.dataset.method === 'contain' ? ${searchItem.dataset.value} : searchItem.dataset.value; return [${property}${logic}='${value}']; });

// 构建选择器字符串 const selector = .sorting${attributes.join('')};

// 显示匹配选择器的元素 document.querySelectorAll(selector).forEach(item =&gt; item.classList.remove('hide'));

}

window.addEventListener('click', function (e) { if (e.target.classList.contains('sc-gtsrHT')) { e.preventDefault(); search(e); } });

function sort(e) { const sortBy = e.target.dataset.order; const style = document.createElement('style'); style.classList.add('sort-order-style');

// 移除之前的排序样式
document.querySelector('style.sort-order-style')?.remove();

// 移除之前处于活动状态的 .sort-by-item 元素 document.querySelector('.sort-by-item.active')?.classList.remove('active');

// 将当前点击的 .sort-by-item 元素设为活动状态 e.target.classList.add('active');

if (sortBy === 'rating') { const movies = Array.from(document.querySelectorAll('.sorting'));

// 根据评分进行排序
movies.sort((movieA, movieB) =&amp;gt; {
    const ratingA = parseFloat(movieA.dataset.rating) || 0;
    const ratingB = parseFloat(movieB.dataset.rating) || 0;
    if (ratingA === ratingB) {
        return 0;
    }
    return ratingA &amp;gt; ratingB ? -1 : 1;
});

// 生成排序样式表
const stylesheet = movies.map((movie, idx) =&amp;gt; `.sorting[data-rating=&amp;quot;${movie.dataset.rating}&amp;quot;] { order: ${idx}; }`).join('\r\n');
style.innerHTML = stylesheet;
document.body.appendChild(style);

} else if (sortBy === 'count') { const movies = Array.from(document.querySelectorAll('.sorting'));

// 根据评分人数进行排序
movies.sort((movieA, movieB) =&amp;gt; {
    const countA = parseInt(movieA.dataset.count) || 0;
    const countB = parseInt(movieB.dataset.count) || 0;
    if (countA === countB) {
        return 0;
    }
    return countA &amp;gt; countB ? -1 : 1;
});

// 生成排序样式表
const stylesheet = movies.map((movie, idx) =&amp;gt; `.sorting[data-count=&amp;quot;${movie.dataset.count}&amp;quot;] { order: ${idx}; }`).join('\r\n');
style.innerHTML = stylesheet;
document.body.appendChild(style);

}

}

window.addEventListener('click', function (e) { if (e.target.classList.contains('sort-by-item')) { e.preventDefault(); sort(e); } });

8. 附加 GitHub Actions

#

GitHub Actions 处理 Json 数据的好处是不用每次都手动下载更新,而且 Access Token 可以保存在 GitHub 仓库的 Secrets Setting 里。



然后填入前面步骤得到的 Access Token

  • Name *NEODB_ACCESS_TOKEN
  • Secret *QuhZZpr111111111111111110X2OPaSRKU

:secret:


下面是具体的 GitHub Actions neodb.yml 代码。不需要用到的步骤直接删除即可。

# .github/workflows/douban.yml
name: Sync NeoDB Data
on:
  schedule:
  - cron: "0 17 * * *"
#  watch:
#    types: [started]

workflow_dispatch:

jobs: douban: name: Sync NeoDB Data runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3

# 检查是否安装了 JQ
- name: Check JQ
  run: |
    if ! command -v jq &amp;&gt; /dev/null; then
      echo &quot;jq is not installed. Installing...&quot;
      sudo apt-get update
      sudo apt-get install -y jq
    else
      echo &quot;jq is already installed.&quot;
    fi
    # 把当前目录保存到环境变量中
    echo &quot;WORK_DIR=$(pwd)&quot; &gt;&gt; $GITHUB_ENV

获取本地现有文件的标记数

  • name: Get Current Count run: | CURRENT_COUNT() { jq '.count' data/neodb/movie.json } echo &quot;CURRENT_COUNT=$(CURRENT_COUNT)&quot; &gt;&gt; $GITHUB_ENV

  • name: Get NeoDB JSON and Count run: | curl -X 'GET'
    'https://neodb.social/api/me/shelf/complete?category=movie&amp;page=1'
    -H 'accept: application/json'
    -H 'Authorization: Bearer ${{ secrets.NEODB_ACCESS_TOKEN }}' &gt; movie1.json

    获取 NeoDB 上电影的标记数

    MOVIE_COUNT() { jq '.count' movie1.json } echo &quot;MOVIE_COUNT=$(MOVIE_COUNT)&quot; &gt;&gt; $GITHUB_ENV

    curl -X 'GET'
    'https://neodb.social/api/me/shelf/complete?category=tv&amp;page=1'
    -H 'accept: application/json'
    -H 'Authorization: Bearer ${{ secrets.NEODB_ACCESS_TOKEN }}' &gt; tv1.json

    获取 NeoDB 上电视剧的标记数

    TV_COUNT() { jq '.count' tv1.json }

    REMOTE_COUNT=$(($(MOVIE_COUNT) + $(TV_COUNT))) echo &quot;REMOTE_COUNT=$REMOTE_COUNT&quot; &gt;&gt; $GITHUB_ENV

对比本地的标记数和远程标记数,相等就跳过,不相等就下载新数据

  • name: Count Compare run: | if [ &quot;${{ env.REMOTE_COUNT }}&quot; = &quot;${{ env.CURRENT_COUNT }}&quot; ]; then echo &quot;Variables are equal. Skipping the next steps.&quot; exit 0 else echo &quot;Variables are not equal. Running the next steps.&quot; fi

下载所有数据

  • name: Get All NeoDB Count if: ${{ env.REMOTE_COUNT != env.CURRENT_COUNT }} run: | #从 json 中提取 pages 字段的值 pages=$(jq '.pages' movie1.json) tv_pages=$(jq '.pages' tv1.json)

    个人使用,新建 WorkDIR ,排除 vercel.json 和 package.json 等

    mkdir neodb cd neodb

    下载 Movie 分类

    for ((i=1; i&lt;=$pages; i++)); do url=&quot;https://neodb.social/api/me/shelf/complete?category=movie&amp;page=$i&quot; filename=&quot;movie$i.json&quot;

    下载文件并保存为对应的文件名

    curl -X 'GET' &quot;$url&quot;
    -H 'accept: application/json'
    -H 'Authorization: Bearer ${{ secrets.NEODB_ACCESS_TOKEN }}' &gt; &quot;$filename&quot; done

    下载 TV 分类

    for ((i=1; i&lt;=$tv_pages; i++)); do tv_url=&quot;https://neodb.social/api/me/shelf/complete?category=tv&amp;page=$i&quot; tv_filename=&quot;tv$i.json&quot;

    curl -X 'GET' &amp;quot;$tv_url&amp;quot; \
      -H 'accept: application/json' \
      -H 'Authorization: Bearer ${{ secrets.NEODB_ACCESS_TOKEN }}' &amp;gt; &amp;quot;$tv_filename&amp;quot;
    done
    

    把所有数据合并成一个文件

    jq -c -s '{data: map(.data[]) | unique | sort_by(.created_time) | reverse, pages: map(.pages)[0], count: map(.count)[0]}' *.json &gt; movie.json

    更新 NeoDB 数据

    cp -f movie.json ${{ env.WORK_DIR }}/data/neodb/

  • name: Download NeoDB Cover run: |

    检查 movie 目录是否存在,如果不存在则创建

    if [ ! -d &quot;movie&quot; ]; then mkdir movie fi

    读取本地的 movie.json 文件内容

    json=$(cat data/neodb/movie.json)

    提取图片 URL

    image_urls=$(echo &quot;$json&quot; | jq -r '.data[].item.cover_image_url')

    遍历图片 URL 并下载图片

    for url in $image_urls; do filename=$(basename &quot;$url&quot;) filepath=&quot;data/neodb/cover/$filename&quot; # 检查文件是否已存在 if [ -f &quot;$filepath&quot; ]; then echo &quot;Skipping $filename - File already exists&quot; else # 使用 curl 命令下载图片 curl -o &quot;$filepath&quot; &quot;$url&quot; echo &quot;Downloaded $filename&quot; echo &quot;REMOTE_COUNT=''&quot; &gt;&gt; $GITHUB_ENV fi done

把修改后的数据提交到 GitHub 仓库

  • name: Git Add and Commit if: ${{ env.REMOTE_COUNT != env.CURRENT_COUNT }} uses: EndBug/add-and-commit@v9 with: message: 'chore(data): update neodb data' add: './data/neodb'

调用另外的 GitHub Actions 构建 Hugo

  • name: Build Hugo and Deploy if: ${{ env.REMOTE_COUNT != env.CURRENT_COUNT }} uses: peter-evans/repository-dispatch@v2 with: event-type: &quot;Build Hugo and Deploy&quot;

把海报上传到腾讯云

  • name: Upload Cover to Tencent COS if: ${{ env.REMOTE_COUNT != env.CURRENT_COUNT }} uses: zkqiang/tencent-cos-action@v0.1.0 with: args: upload -rs ./data/neodb/cover/ /images/neodb/ secret_id: ${{ secrets.SECRET_COS_ID }} secret_key: ${{ secrets.SECRET_COS_KEY }} bucket: ${{ secrets.COS_CDN_BUCKET }} region: ap-shanghai

赞(3)
未经允许不得转载:工具盒子 » NeoDB API 创建观影页面