Doradx/notion2markdown-action
Doradx
Created: 2023-04-12T02:04:29Z

[Updates]

  • 2023.04.21:添加对audio的支持,建议使用外部 MP3 源文件。
  • 2023.04.20:添加pic_compress可选配置项,可配置为true开启图片自动压缩,默认为false
  • 2023.04.18:添加Notion一些特性支持,包括bookmarklink_previewvideopdfembed
  • 2023.04.17:添加pic_base_url可选配置项,可配置为自己图床的基础链接。图床上传过程中,如检测到此链接,将会自动查询要上传的图片是否已在图床(上传的文件名为 md5,据此检测)。此项可极大提高文章更新效率,节省宝贵的GitHub Action运行时间和CDN流量。

本项目受notion-blog-action项目启发,fork 后深度修改而得,在此感谢@Mo Huishou

概览

本方案主要是为了将 Notion 中的各种格式导出为 Markdown 格式,并在此基础上添加对 Notion 各类特性的支持,包括bookmarklink_previewvideopdfembed。符合原生 Markdown 格式的将转为纯 Markdown 语法,其它部分将转为 html 格式。渲染效果

方案主要分为三部分:

  • Notion database:创建写作, 进行稿件管理
  • notion2markdown-action:GitHub Actions 将 notion 转为 Markdown,并将图片上传图床
  • GitHub Actions: 编译部署 Hexo, 推送到 COS

实现原理

  1. 采用Notion API,从Notion中同步Dataset中的Pages,并转换为Markdown,将其中的图片上传到图床中;
  2. Hexo/Huge部署。
  3. 以上均通过Github Action实现。

食用方法

Notion 配置

  1. Blog - Template 复制Dataset模板
  2. 参考Notion官方教程Create an integration (notion.com),创建Notion integration
    应用,并获取**Secrets**,直达链接:My integrations | Notion Developers
  3. 将创建的Dataset数据库授权给刚创建的Integration应用,如下图:

在Notion给Integration应用授权

GitHub 配置

workflows 配置

  1. 切换到自己在 GitHub 上托管的仓库目录
  2. 分别添加notion_sync.ymldeploy.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
name: Automatic sync pages from notion

on:
# push:
# branches: master
workflow_dispatch:
schedule:
- cron: "5 */1 * * *"

permissions:
contents: write

jobs:
notionSyncTask:
name: Ubuntu and node ${{ matrix.node_version }} and ${{ matrix.os }}
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest]
node_version: [16.x]
outputs:
HAS_CHANGES: ${{ steps.checkStep.outputs.HAS_CHANGES }}

steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node_version }}
- name: Convert notion to markdown
uses: Doradx/notion2markdown-action@latest
with:
notion_secret: ${{ secrets.NOTION_TOKEN }} # NOTION授权码
database_id: ${{ secrets.NOTION_DATABASE_ID }} # Dataset ID
migrate_image: true
picBedConfig: ${{ secrets.PICBED_CONFIG}} # picBed的配置,JSON格式,建议为minify JSON, 否则可能报错. 不同图床的配置可参考:https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#picbed
# 图床基础链接, 可留空。如填写,在图片上传图床前,会检查该图片是否已经上传过,如已上传则跳过,提升效率。
pic_base_url: "" # 改成你的链接,例如你图床的图片链接为:https://i.blog.com/image/xxxx.jpg, 则此处填写https://i.blog.com/image/
pic_compress: false # 是否开启图片压缩, 节约图床空间
page_output_dir: 'source' # page 类文章的输出目录,例如about。
post_output_dir: 'source/_posts/notion' # post 的输出目录,切记当clean_unpublished_post为true时,勿设置为 'source/_posts', 可能会删除你原目录下的文章!!!
clean_unpublished_post: true # 是否清除未发表的文章,例如之前发表了,你又在notion中给移到草稿箱了.
- name: Check if there is anything changed
id: checkStep
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "HAS_CHANGES=true" >> $GITHUB_OUTPUT;
echo "Updates available & redeployment required."
else
echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT;
echo "Nothing to commit & deploy."
fi
- name: Commit & Push
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Automatic sync from notion.
callDepolyTask:
name: Call the depoly workflow
needs: notionSyncTask
if: ${{ needs.notionSyncTask.outputs.HAS_CHANGES=='true' }}
uses: Doradx/hexo-blog/.github/workflows/deploy.yml@master # 根据自身Github地址修改即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
name: Automatic deployment of Dorad's Blog -> Tencent COS

on:
push:
branches: master
paths:
- "source/**"
- "**.yml"
workflow_dispatch:
workflow_call:

env:
GIT_USER: Dorad
GIT_EMAIL: ddxid@outlook.com

jobs:
build:
name: Ubuntu and node ${{ matrix.node_version }} and ${{ matrix.os }}
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest]
node_version: [14.x]

steps:
- name: Checkout blog and theme
uses: actions/checkout@v3
with:
submodules: "recursive"
- name: Use Node.js ${{ matrix.node_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node_version }}
- name: Install dependencies
run: |
git pull
npm install
- name: Depoly
run: |
npm run clean
gulp
npm run build
npm run deploy
- name: Commit & Push
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Automatic deployment.

GitHub Action 环境变量配置

前往博客仓库的settings/secrets/actions中添加环境变量,核心为NOTION_DATABASE_ID, NOTION_TOKENPICBED_CONFIG

配置完成的GITHUB ACTION环境变量

其中,

以腾讯云 COS 为例,PICBED_CONFIG的格式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"uploader": "tcyun", // 代表当前的默认上传图床为,
"tcyun":
{
"secretId": "",
"secretKey": "",
"bucket": "", // 存储桶名,v4 和 v5 版本不一样
"appId": "",
"area": "", // 存储区域,例如 ap-beijing-1
"path": "", // 自定义存储路径,比如 img/
"customUrl": "", // 自定义域名,注意要加 http://或者 https://
"version": "v5" | "v4" // COS 版本,v4 或者 v5
}
}

如需使用不同图床,直接修改uploader字段,并配置相应的参数即可。以下提供几种常见图床的配置文件案例:

  • qiniu
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"uploader": "qiniu", // 代表当前的默认上传图床为,
"qiniu":
{
"accessKey": "",
"secretKey": "",
"bucket": "", // 存储空间名
"url": "", // 自定义域名
"area": "z0" | "z1" | "z2" | "na0" | "as0", // 存储区域编号
"options": "", // 网址后缀,比如?imgslim
"path": "" // 自定义存储路径,比如 img/
}
}
  • aliyun
1
2
3
4
5
6
7
8
9
10
11
12
{
"uploader": "aliyun", // 代表当前的默认上传图床,
"aliyun": {
"accessKeyId": "",
"accessKeySecret": "",
"bucket": "", // 存储空间名
"area": "", // 存储区域代号
"path": "", // 自定义存储路径
"customUrl": "", // 自定义域名,注意要加 http://或者 https://
"options": "" // 针对图片的一些后缀处理参数 PicGo 2.2.0+ PicGo-Core 1.4.0+
}
}
  • github
1
2
3
4
5
6
7
8
9
10
{
"uploader": "github", // 代表当前的默认上传图床,
"github": {
"repo": "", // 仓库名,格式是 username/reponame
"token": "", // github token
"path": "", // 自定义存储路径,比如 img/
"customUrl": "", // 自定义域名,注意要加 http://或者 https://
"branch": "" // 分支名,默认是 main
}
}

测试效果

前往仓库的Actions页面,选择Automatic sync pages from notion,进行测试手动部署,并查看运行情况。

手动触发,查看运行结果

搞定!尽情享受写作吧!

完整版配置文件及说明

notion2markdown提供了众多配置参数,以满足各类需求,以下为根据此 action 的配置文件,撰写的参数说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
name: "notion2markdown-action"
description: |
将 notion database 中的 page 转换为 markdown 文档,可以用于 hexo、hugo 等静态博客构建, 内置picgo-core,使用picBed上传图床。
inputs:
notion_secret: # id of input
description: notion app token,建议最好放到 Action Secret
required: true
database_id:
required: true
description: |
notion 中的数据库 id
- 假设你的数据库页面链接是 `https://www.notion.so/you-name/0f3d856498ca4db3b457c5b4eeaxxxxx`
- 那么 `database_id=0f3d856498ca4db3b457c5b4eeaxxxxx`
status_name:
description: notion database 状态字段的字段名,支持自定义
default: "status"
status_published:
description: notion database 文章已发布状态的字段值
default: "已发布"
status_unpublish:
description: |
notion database 文章待发布状态的字段值
触发 action 后会自动拉去所有该状态的文章,成功导出之后会把这篇文章的状态修改为上面设置的已发布状态
default: "待发布"
migrate_image:
required: false
description: |
是否迁移图片到图床
注意: 如果不迁移图片默认导出图片链接是 notion 的自带链接,有访问时效
目前支持迁移图片到多类图床中,采用的是PicGO-Core.
default: "true"
picBedConfig:
description: |
picgo-core中picBed配置文件, 支持多类型图床。
example:
```
"current": "smms",
"uploader": "smms", // 代表当前的默认上传图床为 SM.MS,
"smms": {
"token": "" // 从 https://sm.ms/home/apitoken 获取的 token
}
"aliyun":{
"accessKeyId": "",
"accessKeySecret": "",
"bucket": "", // 存储空间名
"area": "", // 存储区域代号
"path": "", // 自定义存储路径
"customUrl": "", // 自定义域名,注意要加 http://或者 https://
"options": "" // 针对图片的一些后缀处理参数 PicGo 2.2.0+ PicGo-Core 1.4.0+
},
"tcyun":{
"secretId": "",
"secretKey": "",
"bucket": "", // 存储桶名,v4 和 v5 版本不一样
"appId": "",
"area": "", // 存储区域,例如 ap-beijing-1
"path": "", // 自定义存储路径,比如 img/
"customUrl": "", // 自定义域名,注意要加 http://或者 https://
"version": "v5" | "v4" // COS 版本,v4 或者 v5
}
```
详见: https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#%E6%89%8B%E5%8A%A8%E7%94%9F%E6%88%90
default: "{}"
pic_base_url:
description: |
图床的基础链接, 例如: https://i.blog.com/image/,如填写此链接,在图片上传图床前,会检查该图片是否已经上传过,如已上传则跳过,提升效率。
default: ""
pic_compress:
description: |
是否开启图片压缩?true为开启,默认不开启
default: ""
page_output_dir:
description: page类型页面的输出文件夹
default: "source/"
post_output_dir:
description: post类型页面的输出文件夹
default: "source/_posts/notion"
clean_unpublished_post:
description: 是否清除未发表的post
default: "false"
timezone:
description: 设置的时区
default: "Asia/Shanghai"

Notion 文章属性说明

属性列表

Nation Dataset中,每篇文章都预设了属性字段,字段说明如下:

  • status: 文章状态
  • type:页面类型,post/page, 分别对应 hexo 中的pagepost, 留空则默认为post
  • date: 发表日期
  • categories: 分类
  • tags: 标签
  • filename: Markdown文件最终的文件名,可留空,默认为文章的标题
  • description: 文章简介
  • abbrlink: 静态文章链接, 此项需搭配hexo-abbrlink使用,如果你不知道该属性的意义,可留空
  • Created time: 创建时间,此属性自动更新,将会被同步到*.md文件的 created属性中
  • Last edit time:最后修改时间,此属性自动更新,将会被同步到*.md文件的updated属性中

Tips

  • 只有状态为待发布的文章才会被Github Action转为Markdown
  • Notion上设置的文章封面会被用作文章的cover字段,并上传图床
  • Notion中每个页面会有一个独一无二的 UID, 为方便管理,此 ID 会被同步到*.md文件的id属性
  • Notion中每个页面会有created_timelast_edited_time, 会被分别同步到*.md文件的createdupdated属性
  • Notion Dataset中,使用status进行阶段管理,其中只有处于待发布状态的文章会被自动处理。所以,只需要保证待发布已发布两个状态存在即可,其它几个状态可以根据喜好修改或删除;
  • Notion Dataset中,每篇文章都设置了众多属性,其均会写入到最终Markdown文件头部的 YAML 配置中,可根据自身需要添加属性,但注意字段名最好不要有特殊字符,否则可能出错;
  • 使用hexo的童鞋,如部署过慢,建议升级hexo版本,并清理项目依赖(在package.json中),可极大提升部署效率。博主升级后时间缩短了一半,目前约90s内能完成部署。

Actions 耗时分析

notion_sync.yml

对于此任务,最耗时的部分在于图床上传,具体耗时与图片数量和GitHub Actions的具体执行机器有关(每次机器都不一样)。

由于是每小时轮询,当查到 Notion 无需要发布的文献时候

本文处理耗时约17s,各任务为并行,取决于执行机器网速和图片数量。此步骤主要瓶颈在于 GitHub 上传图床速度,可优化面较小。。。或许用 GitHub 做图床会非常快?利好图床采用外网平台的童鞋。

deploy.yml

此任务最大耗时在依赖安装(Install dependencies)和部署(Deploy):

  • 升级Hexo到最新版v6.3.0,并清理package.json中无用的包后,依赖安装约耗时20s
  • 部署部分主要还是网络问题,速度取决于你的部署类型,博主使用腾讯云 COS,单次部署上传约30s

单次部署的典型耗时约1min

渲染效果

bookmark

支持撰写标题

GitHub 页面

自动抓取页面概览,并生成图标、概览图

Google Map [link_preview]

百度地图 [link_preview]

从网页抓取信息,目前暂只支持 GitHub 站点。

souvikinator/notion-to-md
souvikinator
Created: 2021-12-04T13:11:08Z
ant-design/ant-design Issues
ant-design
Created: 2015-04-24T15:37:24Z
App.useApp().message 动态自定义主题样式有误 #41885Closed
#41885onlyling
Created: 2023-04-19T08:03:13Z
ant-design/ant-design Pulls
ant-design
Created: 2015-04-24T15:37:24Z
site: time changed to dynamic Closed
ayangweb
Created: 2023-04-19T09:36:14Z

audio

邓紫棋-偶尔.MP3

Rocky - 那就这样吧.MP3

video

YouTube

Bilibili

腾讯视频

embed

Twitter

Google Map [embed]

高德地图

百度地图

pdf

在线PDF阅读

Q&A

  • 如何更新已发布的文章?

撰写修改后,将文章状态修改为待发布即可,会自动更新。

  • 如何删除已发布的文章?

直接将文章状态修改为正在写/计划中,或其它任何不是已发布/待发布的状态即可。

注意:需要notion_sync.yml中配置clean_unpublished_post: true

  • 更新已发布的文章,如何保证文章链接不变?

方案 1(手动):给Notion的文章手动设置abbrlink,会同步到Markdown文件中

方案 2(自动):采用rozbo/hexo-abbrlink插件的童鞋,Notion文章的 abbrlink 留空即可,会自动处理;当然如果想自定义abbrlink,直接填写即可

rozbo/hexo-abbrlink会在Deploy阶段自动给文章生成abbrlink,当将已发布文章修改后重新发布时,会读取原 Markfown 文件中的abbrlink并同步到 Notion 文章。

  • 如何修改同步频次?

notion_sync.yml配置中修改 schedule配置即可,格式为 cron,可利用Crontab.guru进行可视化调整。

注意:不要改那么高频!GitHub 对于私有仓库,调用 GitHub Actions 的时间是有限的,Pro 用户每个月 3000 分钟。小心 GitHub 找你收钱。根据推算,建议触发间隔不小于 10 分钟!

  • 如何嵌入 HTML 等代码?

Notion中直接粘贴即可,需要确保在粘贴过程中 Notion 没有给你的 URL 链接自动加上超链接,否则可能渲染出错,例如:

1
2
3
4
5
6
7
8
9
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/r7iLI8vW4bE"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>

复制后直接粘贴到 Notion 的文章,它会自动给 URL 加上超链接:

1
2
3
4
5
6
7
8
9
10
<iframe
width="560"
height="315"
src="[https://www.youtube.com/embed/r7iLI8vW4bE](https://www.youtube.com/embed/r7iLI8vW4bE)
"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>

会导致最终的Markdown渲染出错,出现以下情况:

  • 什么图片会被上传到图床?

为节省空间,本插件只将 Notion 存储的图片转到图床中,外链图片和原本就属于自己图床的图片链接不会被上传。

注意:插入外链图片时请检查是否允许跨域,否则可能图片不显示。

总结

优势

  • 功能完备,虽然有一些小坑,但NotionMarkdown功能完备
  • 自动化部署,只需要在Notion编辑即可,且方便管理,剩余流程全自动
  • 利用Notion可在多端同步编辑,图床自动上传,无需关心其它问题,转注写作即可
  • 理论上支持各类静态博客,因为原理就是Notion to Markdown,通用型极高
  • 可设置指定的目录存储 Notion 导出的 post, 例如: source/_posts/notion,不影响原先使用其它编辑器撰写的文章
  • 多处备份,Notion+GitHub,暂不用担心数据丢失

不足

  • Notion的有些特性,Markdown可能不支持,所以写作时的结果和发表后的结果可能有差异。

例如如何嵌入HTML代码…添加Bilibili/YouTube/163等视频或音乐iframe媒体文件,Markdown导入Notion时,Notion给转成了embed类型,再导出Markdown的时候就全成链接了!!!麻了。。。解决方案也很简单,将iframe代码直接复制粘贴到Notion中粘贴为纯文本即可,记得检查Notion是否把链接给转成了引用。

Note: The scheduleevent can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. If the load is sufficiently high enough, some queued jobs may be dropped. To decrease the chance of delay, schedule your workflow to run at a different time of the hour.

  • 后期可考虑换为Webhook(更及时、节省资源) - 已支持,详情见:
Doradx/notion2markdown-vercel
Doradx
Created: 2023-04-18T02:53:33Z

特别鸣谢@王云子同学协助debug并完善此项目。

后记

为了折腾自动化部署,fork 了notion-blog-action后,捣鼓了半天,重写 debug…已麻…近期肯定是不想再折腾了 😂

博客更大的意义在于记录,而不是折腾各种工具。

目前对于这套流程较为满意,希望能加油产出内容!

主要参考