前言

之前一直使用印象笔记作为主力的笔记软件,但印象笔记这几年广告加的越来越离谱,遂开始寻找其替代品,几经搜索,最终确定将 Obsidian 作为自己的主力笔记软件。

Obsidian 只是一个本地笔记,如果需要多端同步,那么需要借助 Remotely Save 等插件实现。由于自己有 NAS,因此在 NAS 上部署了 WebDAV 服务,在 Remotely Save 插件中配置完毕后,实现了家、手机、公司三端的笔记同步。

除了使用 Obsidian 记录笔记外,自己还有一个对外的博客,用于发布一些内容上相对还比较完整的文章。

博客基于 Hugo 静态网站工具生成,Hugo 元数据使用 Git 管理,并托管在 Github 上,这与大部分静态博客的维护方式相同,唯一的区别在于自己的博客没有使用 Github pages,而是部署在了自己的海外服务器上,以便获得相对更好的国内访问体验。

过去,自己的发布博客的流程是,

  1. 创建 Hugo 文章目录和 index.md 文件。
  2. 将 Obsidian 笔记中的内容拷贝到 index.md 文件中。
  3. 将 obsidna 中依赖的图片(图片存储在本地)拷贝到 Hugo 文章目录下(自己大部分情况下未使用图床,图片直接放在 Hugo 文章目录下,并最终同步到自己的服务器上)。
  4. 修改 index.md 文章的图片引用方式(Obsidian 基于 ![[]],而 Hugo 则使用标准 Markdown ![]() 的方式)。
  5. 提交 Hugo 博客文章到 Github,之后基于 WebHook 自动构建博客并同步到自己的服务器上。

如果文章内容有更改,那么则,

  1. 更改 Obsidian 笔记的内容。
  2. 复制拷贝到 Hugo 文章目录下。
  3. 提交 Hugo 博客内容到 Github。

整个过程相当的繁琐,尤其是涉及文章的内容更新时,很容易出现 Hugo 博客内容改了,但 Obsidian 没改,或是 Obsidian 内容改了,而 Hugo 博客忘了改的情况。

近段实在不能忍,遂打算将整个流程自动化起来,实现 Obsidian 到 Hugo 博客的自动更新同步。

方案

网上参考了几篇文章,看到了两种方案。

  1. 将整个 Hugo 仓库当成一个 Obsidian 库,在 Obsidian 编辑后,同步到 Git,然后通过 Git actions 自动将文章构建发布。其中通过 draft 参数控制文章是否发布。
    1. 相关文章
      1. Obsidian 配合 Hugo、cloudflare:让发布博客简单到不可思议
      2. Hugo 博客写作最佳实践 | 胡说
    2. 优点:整个方案确实比较流畅自动化。
    3. 缺点:Obsidian 目录结构需要按照 Hugo 规范,不能自定义目录结果。
  2. 方案 2,使用 hugo-publish 等插件,将 Obsidian 文章转换后同步到 Hugo 目录里。
    1. 相关文章:
      1. 写了一个 Obsidian 插件,能够把 Obsidian 笔记转换为 Hugo blog - V2EX
    2. 优点:笔记是笔记,博客是博客,两个目录隔离开。
    3. 缺点:同步到博客后,还需手动提交博客目录,才能发布。整个过程不流畅。

以上两个方案我都不是很满意,期望实现方式是将两者结合起来。

依旧分为 Obsidian 笔记库和 Hugo 博客库,在 Obsidian 里编辑笔记并无误后,添加 #blog 或是其他标志,会自动的将对笔记进行格式转换并把笔记及相关的资源拷贝到 Hugo 库目录,之后自动提交 Hugo 仓库触发静态页面构建,进而更新博客。

综合上述流程、笔记管理方式以及我的能力,我决定这样实现。

  1. 家、公司、手机上的 Obsidian 笔记现在通过 WebDAV 的方式同步到 NAS 上。
  2. 在 NAS 上创建 Hugo 博客仓库。
  3. 实现脚本并部署在 NAS 上,脚本循环执行以下逻辑。
    1. 遍历 Obsidian 库中的所有笔记,检测笔记是否有 #blog 标签。
    2. 如果有,那么将笔记的格式转换为 Hugo 博客形式,包括头、引用的链接、图片等。
    3. 将转换后的文章生成到 Hugo 目录下。
    4. 如果文章有引用图片,将图片拷贝到 Hugo 库的文章目录下。
    5. 记录笔记的 md5 信息,用于后续比对笔记内容是否有更新。
    6. 提交 Hugo 仓库
  4. Hugo 仓库更新后,触发 Github 上配置的 WebHook,最终触发博客的更新。

通过以上流程,只需要在我写好的笔记中添加 #blog 标签,那么等一会,博客就会自动更新。而当我更新笔记后,这个流程也会将更新后的笔记自动更新到我的博客上。实属懒人必备。

实现

整个流程使用 Python 实现也比较简单,我已经提交到 Github 仓库 中,可自行阅读调整。

针对脚本的功能,这里说明几点。

  1. 脚本执行需要 -op-hp-i-b 这四个参数,其中,
    1. -op 用来执行 Obsidian 库的根目录。
    2. -hp 指定 Hugo 库的根目录。
    3. -i 指定脚本每次循环执行的间隔,单位秒。
    4. -b 指定 Bark 通知的 URL 地址。
  2. 脚本会在当前目录下生成 obsidian2hugo_cfg.json 配置文件,存储已经生成博客的笔记的 MD5 信息,用于检测博客内容是否有更新。
  3. 脚本解析 Markdown 使用了 mistletoe 库,主要用于解析获取 #blog![[]] 等关键字,没有使用正则替换,避免这些关键字写在代码段中。
  4. 脚本只处理了 #![[]] 关键字,对 [[]] 关键字没有做任何替换处理,原因是自己没想好该怎么做。一种做法是递归将引用的文章都发表为博客,由于我目前很少使用 [[]],因此暂时没有处理。
  5. 脚本使用 Bark 进行了异常通知,如果脚本执行出现异常,那么会将异常的堆栈通过 Bark 进行通知。如果需要使用 Bark,那么就指定 -b 参数,否则无需指定。如果想要其他通知方式,可自行修改代码。

效果

尴尬的最后

在我脚本写完并开始写整理这篇文章时,我发现已经有现成的工具可以更好的实现我这个需求,而且也是我理想中的形式。

使用 Obsidian 免费建个人博客 | PrintLove 这篇文章中,介绍了 Github Publisher 插件,这个插件可以将 Obsidian 库中的指定文章 (基于标签) 做一些替换操作后发布到 Github 的指定目录中,而且不需要 NAS、WebDAV 同步中转,对大多数人来说更为友好。

在自己造轮子前果然还是应该多找一些资料的😭。不过既然代码都写完了,还是整理下自己的方案,给大家一些参考。