<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Firekylin 系统</title>
    <link>http://localhost:8360</link>
    <description>A Simple &amp; Fast Node Bloging Platform Base On ThinkJS 2.0 &amp; ReactJS &amp; ES6/7</description>
    <atom:link href="http://localhost:8360/rss.html" rel="self" />
    <language>zh-cn</language>
    <lastBuildDate>Tue, 06 Oct 2026 03:49:44 GMT</lastBuildDate>
        <item>
      <title>豆瓣书影音同步 GitHub Action</title>
      <link>http://localhost:8360/post/doumark.html</link>
      <description><![CDATA[
            <div class="toc"><ul><li><a href="#toc-e05">简介</a></li><li><a href="#toc-ecf">使用</a></li><li><a href="#notion">Notion</a></li><li><a href="#toc-e83">渲染</a></li><li><a href="#toc-e5f">搜索</a></li><li><a href="#toc-c36">排序</a></li><li><a href="#toc-968">起因</a></li></ul></div><blockquote>
<p>2023-07-12 更新：<a href="https://github.com/lizheming/doumark-action/issues/8">《关于豆瓣图片无法直接使用的说明》</a></p>
</blockquote>
<h2><a id="toc-e05" class="anchor" href="#toc-e05"></a>简介</h2><p><a href="https://github.com/lizheming/doumark-action">doumark-action</a> 是我前段时间造的一个轮子。它是一款 GitHub Action，支持在 GitHub 中同步你的豆瓣书影音数据到本地的文件或者 Notion 中。我利用它，定时同步我的豆瓣观影数据到我的博客仓库中，并利用 Hugo 读取文件数据渲染成页面，<a href="https://imnerd.org/douban.html">观影</a> 是最终的效果。</p>
<h2><a id="toc-ecf" class="anchor" href="#toc-ecf"></a>使用</h2><p>使用其实很简单，在你的博客仓库中新建 <code>.github/workflows/douban.yml</code> 文件，以观影为例添加如下内容。它实现了每小时自动抓取你的豆瓣观影记录并更新到文件中，如果发现文件有更新则触发 commit 提交。</p>
<pre><code class="hljs lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">douban</span>
<span class="hljs-attr">on:</span> 
  <span class="hljs-attr">schedule:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span> <span class="hljs-string">&quot;30 * * * *&quot;</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">douban:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Douban</span> <span class="hljs-string">mark</span> <span class="hljs-string">data</span> <span class="hljs-string">sync</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">movie</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">lizheming/doumark-action@master</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">lizheming</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">movie</span>
        <span class="hljs-attr">format:</span> <span class="hljs-string">csv</span>
        <span class="hljs-attr">dir:</span> <span class="hljs-string">./douban</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Commit</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">EndBug/add-and-commit@v8</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">message:</span> <span class="hljs-string">&#x27;chore: update douban data&#x27;</span>
        <span class="hljs-attr">add:</span> <span class="hljs-string">&#x27;./douban&#x27;</span>
</code></pre><p>该 workflow 总共分为三步，第一步初始化 Git 仓库；第二步调用 <a href="https://github.com/lizheming/doumark-action">doumark-action</a> 同步豆瓣账号 <code>lizheming</code> 的 <code>movie</code> 类型数据到 <code>./douban</code> 文件夹下，并保存为 <code>csv</code> 格式文件；最后一步则是当 <code>./douban</code> 文件夹下有更新则调用插件提交修改。</p>
<h2><a id="notion" class="anchor" href="#notion"></a>Notion</h2><p>如果是要同步到 Notion 中会稍微复杂一点。需要先准备好 Notion Token 并初始化好页面。</p>
<ol>
<li>我们可以在 <a href="https://www.notion.so/my-integrations">My Integrations</a> 里创建机器人得到 <code>NOTION_TOKEN</code>。</li>
<li><a href="https://lizheming.notion.site/d8a363df3ca84ca89ef52208ad874e3b">电影</a> | <a href="https://lizheming.notion.site/488c17fd89fb424591f68f7cfb029020">阅读</a> | <a href="https://lizheming.notion.site/d80ca60213c54ab99c4376caec0be9d7">音乐</a> 基于这三个模板点击右上角的 <kbd>Duplicate</kbd> 按钮渲染复制页面。</li>
<li>复制后的页面右上角选择右上角的 <kbd>Share</kbd> - <kbd>Invite</kbd> 将第一步创建的机器人加入，这样机器人就有权限更新你的页面数据。</li>
</ol>
<pre><code class="hljs lang-yml"><span class="hljs-comment"># .github/workflows/douban.yml</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">douban</span>
<span class="hljs-attr">on:</span> 
  <span class="hljs-attr">schedule:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span> <span class="hljs-string">&quot;30 * * * *&quot;</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">douban:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Douban</span> <span class="hljs-string">mark</span> <span class="hljs-string">data</span> <span class="hljs-string">sync</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">movie</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">lizheming/doumark-action@master</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">lizheming</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">movie</span>
        <span class="hljs-attr">format:</span> <span class="hljs-string">notion</span>
        <span class="hljs-attr">dir:</span> <span class="hljs-string">xxxx</span>
        <span class="hljs-attr">notion_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.notion_token</span> <span class="hljs-string">}}</span>
</code></pre><p>其中 <code>format</code> 需要为 <code>notion</code>，<code>dir</code> 为 Notion 页面 ID，Notion 页面 URL 第一个随机字符即为页面的 ID。</p>
<h2><a id="toc-e83" class="anchor" href="#toc-e83"></a>渲染</h2><p>数据已经有了，剩下的就是我们需要读取该数据源的数据，并渲染出页面。除了数据渲染之外，我还给自己增加了筛选查找的需求，所以我在头部还渲染了一些筛选项。</p>
<pre><code class="hljs lang-html">{{$movies := getCSV &quot;,&quot; &quot;douban/movie.csv&quot; }}
{{$scratch := newScratch}}
{{$scratch.Add &quot;genres&quot; slice}}
{{range $idx, $movie := $movies}}
  {{if ne $idx 0}}
    {{$scratch.Set &quot;genres&quot; (union ($scratch.Get &quot;genres&quot;) (split (index $movie 7) &quot;,&quot;))}}
  {{end}}
{{end}}
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-ksluID gFnzgG&quot;</span>&gt;</span>
  <span class="hljs-comment">&lt;!--分类筛选--&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-bdnxRM jvCTkj&quot;</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT kEoOHR&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;genres&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;contain&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;&quot;</span>&gt;</span>全部<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    {{range $genre := $scratch.Get &quot;genres&quot;}}
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;genres&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;contain&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;{{$genre}}&quot;</span>&gt;</span>{{$genre}}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    {{end}}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  
  <span class="hljs-comment">&lt;!--时间筛选--&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-bdnxRM jvCTkj&quot;</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT kEoOHR&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;year&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;&quot;</span>&gt;</span>全部<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    {{range $year := (seq 2022 -1 2009)}}
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;year&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;{{$year}}&quot;</span>&gt;</span>{{$year}}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    {{end}}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  
  <span class="hljs-comment">&lt;!--评分筛选--&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-bdnxRM jvCTkj&quot;</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT kEoOHR&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;&quot;</span>&gt;</span>全部<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;5&quot;</span>&gt;</span>五星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;4&quot;</span>&gt;</span>四星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;3&quot;</span>&gt;</span>三星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;2&quot;</span>&gt;</span>二星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;1&quot;</span>&gt;</span>一星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gtsrHT dvtjjf&quot;</span> <span class="hljs-attr">data-search</span>=<span class="hljs-string">&quot;star&quot;</span> <span class="hljs-attr">data-method</span>=<span class="hljs-string">&quot;equal&quot;</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">&quot;0&quot;</span>&gt;</span>零星<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-comment">&lt;!--排序规则--&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-bdnxRM jvCTkj sort-by&quot;</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sort-by-item active&quot;</span> <span class="hljs-attr">data-order</span>=<span class="hljs-string">&quot;time&quot;</span>&gt;</span>
      观影时间排序
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;javascript:void 0;&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sort-by-item&quot;</span> <span class="hljs-attr">data-order</span>=<span class="hljs-string">&quot;rating&quot;</span>&gt;</span>
      网友评分排序
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  &lt;!-影片列表--&gt;
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-dIsUp fIuTG&quot;</span>&gt;</span>
    {{range $idx, $movie := $movies}}
    <span class="hljs-comment">&lt;!--排除第一行表头--&gt;</span>
    {{if ne $idx 0 }}
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> 
      <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-gKAaRy dfdORB&quot;</span> 
      <span class="hljs-attr">data-year</span>=<span class="hljs-string">&quot;{{index $movie 9}}&quot;</span> 
      <span class="hljs-attr">data-star</span>=<span class="hljs-string">&quot;{{index $movie 8}}&quot;</span>
      <span class="hljs-attr">data-rating</span>=<span class="hljs-string">&quot;{{index $movie 6}}&quot;</span>
      <span class="hljs-attr">data-genres</span>=<span class="hljs-string">&quot;{{index $movie 7}}&quot;</span>  
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;{{index $movie 5}}&quot;</span> <span class="hljs-attr">target</span>=<span class="hljs-string">&quot;_blank&quot;</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-hKFxyN HPRth&quot;</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;lazyload-wrapper &quot;</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;lazy&quot;</span> <span class="hljs-attr">data-src</span>=<span class="hljs-string">&quot;https://dou.img.lithub.cc/movie/{{ index (findRE `\d+` (index $movie 5)) 0 }}.jpg&quot;</span> <span class="hljs-attr">referrer-policy</span>=<span class="hljs-string">&quot;no-referrer&quot;</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">&quot;lazy&quot;</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">&quot;{{index $movie 1}}&quot;</span> <span class="hljs-attr">width</span>=<span class="hljs-string">&quot;150&quot;</span> <span class="hljs-attr">height</span>=<span class="hljs-string">&quot;220&quot;</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-iCoGMd kMthTr&quot;</span>&gt;</span>{{index $movie 1}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-fujyAs eysHZq&quot;</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-jSFjdj jcTaHb&quot;</span>&gt;</span>
            {{range $star := (seq 0 2 8)}}
            <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">&quot;0 0 24 24&quot;</span> <span class="hljs-attr">width</span>=<span class="hljs-string">&quot;24&quot;</span> <span class="hljs-attr">height</span>=<span class="hljs-string">&quot;24&quot;</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-dlnjwi {{if gt (index $movie 6) $star}}lhtmRw{{else}}gaztka{{end}}&quot;</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">&quot;none&quot;</span> <span class="hljs-attr">d</span>=<span class="hljs-string">&quot;M0 0h24v24H0z&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">&quot;currentColor&quot;</span> <span class="hljs-attr">d</span>=<span class="hljs-string">&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&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
            {{end}}
          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;sc-pNWdM iibjPt&quot;</span>&gt;</span>{{index $movie 6}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    {{end}}
    {{end}}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre><p>整体的布局我使用了 Flex 布局，增加了图片懒加载。</p>
<h2><a id="toc-e5f" class="anchor" href="#toc-e5f"></a>搜索</h2><p>使用 CSS 的属性选择器，可以非常简单的实现搜索的功能。事先将数据通过属性挂载在 DIV 上，通过 <code>[data-year^=2022][data-genres*=喜剧]</code> 就可以查询到 2022 年看过的喜剧片了！</p>
<pre><code class="hljs lang-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">search</span>(<span class="hljs-params">e</span>) {
  <span class="hljs-comment">// 隐藏全部电影</span>
  <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">&#x27;.dfdORB&#x27;</span>).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> item.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">&#x27;hide&#x27;</span>));
  <span class="hljs-comment">// 移除当前筛选项之前的选项</span>
  <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">`.dvtjjf.active[data-search=&quot;<span class="hljs-subst">${e.target.dataset.search}</span>&quot;]`</span>)?.<span class="hljs-property">classList</span>.<span class="hljs-title function_">remove</span>(<span class="hljs-string">&#x27;active&#x27;</span>);
  <span class="hljs-comment">// 如果选择的是非全部选项，则高亮该选项</span>
  <span class="hljs-keyword">if</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">dataset</span>.<span class="hljs-property">value</span>) {
    e.<span class="hljs-property">target</span>.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">&#x27;active&#x27;</span>);
  }

  <span class="hljs-comment">// 找到所有筛选项的值</span>
  <span class="hljs-keyword">const</span> searchItems = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">&#x27;.dvtjjf.active&#x27;</span>);
  <span class="hljs-comment">// 根据筛选值拼接 CSS 选择器，JSON 数据类型的需要使用 *=，其它的需要使用 ^=</span>
  <span class="hljs-keyword">const</span> attributes = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>(searchItems, <span class="hljs-function"><span class="hljs-params">searchItem</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> property = <span class="hljs-string">`data-<span class="hljs-subst">${searchItem.dataset.search}</span>`</span>;
    <span class="hljs-keyword">const</span> logic = searchItem.<span class="hljs-property">dataset</span>.<span class="hljs-property">method</span> === <span class="hljs-string">&#x27;contain&#x27;</span> ? <span class="hljs-string">&#x27;*&#x27;</span> : <span class="hljs-string">&#x27;^&#x27;</span>;
    <span class="hljs-keyword">const</span> value = searchItem.<span class="hljs-property">dataset</span>.<span class="hljs-property">method</span> === <span class="hljs-string">&#x27;contain&#x27;</span> ? <span class="hljs-string">`<span class="hljs-subst">${searchItem.dataset.value}</span>`</span> : searchItem.<span class="hljs-property">dataset</span>.<span class="hljs-property">value</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[<span class="hljs-subst">${property}</span><span class="hljs-subst">${logic}</span>=&#x27;<span class="hljs-subst">${value}</span>&#x27;]`</span>;
  });
  <span class="hljs-keyword">const</span> selector = <span class="hljs-string">`.dfdORB<span class="hljs-subst">${attributes.join(<span class="hljs-string">&#x27;&#x27;</span>)}</span>`</span>;
  <span class="hljs-comment">// 找到目标元素对其进行展现操作</span>
  <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(selector).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> item.<span class="hljs-property">classList</span>.<span class="hljs-title function_">remove</span>(<span class="hljs-string">&#x27;hide&#x27;</span>));
}

<span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) {
  <span class="hljs-keyword">if</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">classList</span>.<span class="hljs-title function_">contains</span>(<span class="hljs-string">&#x27;sc-gtsrHT&#x27;</span>)) {
    e.<span class="hljs-title function_">preventDefault</span>();
    <span class="hljs-title function_">search</span>(e);
  }
});
</code></pre><h2><a id="toc-c36" class="anchor" href="#toc-c36"></a>排序</h2><p>由于我使用了 Flex 布局，所以排序这个实行实际上是可以通过 Flex 的 <code>order</code> 属性来实现的。这样做的好处就是我不需要真的去修改 DOM 结构，只需要生成或者删除 CSS 就好了。</p>
<pre><code class="hljs lang-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">sort</span>(<span class="hljs-params">e</span>) {
  <span class="hljs-keyword">const</span> sortBy = e.<span class="hljs-property">target</span>.<span class="hljs-property">dataset</span>.<span class="hljs-property">order</span>;
  <span class="hljs-keyword">const</span> style = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">&#x27;style&#x27;</span>);
  style.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">&#x27;sort-order-style&#x27;</span>);
  <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;style.sort-order-style&#x27;</span>)?.<span class="hljs-title function_">remove</span>();
  <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">&#x27;.sort-by-item.active&#x27;</span>)?.<span class="hljs-property">classList</span>.<span class="hljs-title function_">remove</span>(<span class="hljs-string">&#x27;active&#x27;</span>);
  e.<span class="hljs-property">target</span>.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">&#x27;active&#x27;</span>);
  <span class="hljs-keyword">if</span>(sortBy === <span class="hljs-string">&#x27;rating&#x27;</span>) {
    <span class="hljs-keyword">const</span> movies = <span class="hljs-title class_">Array</span>.<span class="hljs-title function_">from</span>(<span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">&#x27;.dfdORB&#x27;</span>));
    movies.<span class="hljs-title function_">sort</span>(<span class="hljs-function">(<span class="hljs-params">movieA, movieB</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> ratingA = <span class="hljs-built_in">parseFloat</span>(movieA.<span class="hljs-property">dataset</span>.<span class="hljs-property">rating</span>) || <span class="hljs-number">0</span>;
      <span class="hljs-keyword">const</span> ratingB = <span class="hljs-built_in">parseFloat</span>(movieB.<span class="hljs-property">dataset</span>.<span class="hljs-property">rating</span>) || <span class="hljs-number">0</span>;
      <span class="hljs-keyword">if</span>(ratingA === ratingB) {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
      }
      <span class="hljs-keyword">return</span> ratingA &gt; ratingB ? -<span class="hljs-number">1</span> : <span class="hljs-number">1</span>;
    });
    <span class="hljs-keyword">const</span> stylesheet = movies.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">movie, idx</span>) =&gt;</span> <span class="hljs-string">`.dfdORB[data-rating=&quot;<span class="hljs-subst">${movie.dataset.rating}</span>&quot;] { order: <span class="hljs-subst">${idx}</span>; }`</span>).<span class="hljs-title function_">join</span>(<span class="hljs-string">&#x27;\r\n&#x27;</span>);
    style.<span class="hljs-property">innerHTML</span> = stylesheet;
    <span class="hljs-variable language_">document</span>.<span class="hljs-property">body</span>.<span class="hljs-title function_">appendChild</span>(style);
  }
}
<span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&#x27;click&#x27;</span>, <span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) {
  <span class="hljs-keyword">if</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">classList</span>.<span class="hljs-title function_">contains</span>(<span class="hljs-string">&#x27;sort-by-item&#x27;</span>)) {
    e.<span class="hljs-title function_">preventDefault</span>();
    <span class="hljs-title function_">sort</span>(e);
  }
});
</code></pre><h2><a id="toc-968" class="anchor" href="#toc-968"></a>起因</h2><p>很早以前我就养成了看完电影就要上豆瓣上标记一下的习惯，并在每年年末的时候统计一下。为了满足自己的需求，很早之前我写过一款 Chrome 插件，用于统计豆瓣电影记录，具体可以看这篇文章<a href="/douban-movie-track-for-chrome.html">《豆瓣电影统计插件For Chrome》</a>。</p>
<p>在后来无意间知道了<a href="https://mufeng.me/">牧风老师</a>开发的<a href="https://bm.weajs.com/">布克牧为</a>，用户同步豆瓣记录数据并支持在第三方网站中挂件展示。所以我为我的博客增加了<a href="/douban.html">观影</a>页面，用来展示我看过的电影。后来，每当我和朋友聊电影，想要推荐之前看过的电影给他们的时候，它也成为了重要的查找入口。</p>
<p>布克牧为的第三方挂件样式很好看，但筛选功能偏弱，仅支持分类的筛选。对于我有搜索和统计的需求其实没办法很好的满足。再加之最近布克牧为时长不出数据，变的不太稳定，导致我又有了重新造轮子的想法。</p>
<p>自从博客切换成 Hugo 之后，我对 SSG(Server Side Generate) 就非常的痴迷，连评论都是使用 SSG 的方式渲染到页面上的，具体可以查看我之前写的这篇文章<a href="/hugo-ssr-comment.html">《静态博客如何高性能插入评论》</a>。于是关于这次的功能理所当然我也想使用类似的方式。</p>
<p>所以最开始我是写了个独立的服务，该服务会定时抓取数据并更新到数据库中，同时提供了 API 用于获取数据。在博客中则去调用该接口获取到数据后渲染页面。后来因为需要找一个第三方定义任务服务，用于定时触发抓取任务接口。更新数据后还需要调用博客的构建触发器，同时又觉得每次构建的时候都需要花时间去请求一次接口有点浪费，就一直在思考有没有其它的方式。</p>
<p>其实 Hugo 除了支持 JSON 接口的数据读取之外，也支持本地 CSV 文件的数据读取。直接读取从库中的表格文件获取到数据能减少不必要的网络请求，而表格文件更新的时候会自动触发 Git 操作从何触发博客的构建任务。所以最终就想到了 GitHub Action 的方案，通过免费的 GitHub Action 触发 CSV 文件的更新，最终触发构建更新。</p>
<p>于是乎「<a href="(https://github.com/lizheming/doumark-action)">doumark-action</a>」这个项目就诞生了！</p>
            ]]></description>
      <pubDate>Mon, 14 Mar 2022 22:36:27 GMT</pubDate>
      <guid>http://localhost:8360/post/doumark.html</guid>
    </item>
      </channel>
</rss>
