1. はじめに
このブログは astro-notion-blog で記述しています。これは Notion で記述したページを自動的にブログの形に変換してくれるフレームワークです。このフレームワークは astro という技術を用いており、静的なページを作成してくれます。
過去の middleman のブログを使っていたときは、画像は Gyazo というサービスに置き、外部リンクにしていました。astro-notion-blog では外部リンクはそのまま取り込んでくれるので、問題なく表示されていました。また、ビルド自体もかなり高速でした。
一方、astro-notion-blog に移行後は普通に Notion に画像をアップロードしていました。astro-notion-blog はこのようなアップロード画像に対しては以下のような処理をしてくれます。
- ファイルをローカルにダウンロードする
- リンクをダウンロードフォルダに向ける
この結果、リンクが1時間で切れてしまう Notion 画像に対しても静的なページからアクセスできるようになります。
しかし、ページが増えるに従い、astro-notion-blog に時間がかかるようになってしまいました。画像自体はほとんど変わらないにも関わらず、このダウンロードなどの処理に時間がかかっているようです。今回は、この Notion 内のページを middleman 時代のように外部リンクに変換することを目的とします。
2. 実装
私の場合には、職場の単なるWebサーバに rsync でフォルダごと展開しています。一度でも astro-notion-blog がビルドされていれば、Web サーバ上にその画像が存在しているはずです。そこで、Notion page の Image block の URL を外部リンクに変更すればいいはずです。ここでは、astro-notion-blog のフォルダに image_export.rb
というスクリプトを実装することにします。
2.1 NotionRubyMapping の設定と定数定義
最初に NotionRubyMapping の読み込みとインテグレーションキーの設定を行います。また、外部サーバの URL と astro-notion-blog のデータベース ID を定数にしておきます。
#! /usr/bin/env ruby
require "notion_ruby_mapping"
include NotionRubyMapping
NotionRubyMapping.configure { |c| c.token = ENV["NOTION_API_KEY"] }
EXPORT_URL = "https://www2.metro-cit.ac.jp/~hkob/notion/"
ASTRO_DB_ID = "c9cfd957e44443c59bc67173401f6c6b"
2.2 object URL の差し替え
次に object を渡して、その URL を差し替えるメソッド export_image を作成します。ここで object は ImageBlock のオブジェクトだけでなく、OG 画像用の File Object をとることもあります。どちらのブロックもポリモーフィズムにより url=
メソッドで書き換えが可能となっています。この辺りは型を気にしなくていい Ruby のいいところですね。url が amazonaws だった場合に、ファイル名の部分を取り出し、URL を外部 Web サーバのものに置き換えるだけです。ImageBlock と File object では更新方法が異なるので、ここでは Ruby インスタンス変数の置き換えまでを実施することになります。置き換えがあった場合には、true が返されるので、呼び出し側で保存処理を実施します。
def export_image(object)
url = object.url
ans = false
if url =~ /https:\/\/s3.us-west-2.amazonaws.com\/secure.notion-static.com\/([^?]+)?/
filename = $1
if File.exist?("public/notion/#{filename}")
object.url = "#{EXPORT_URL}#{filename}"
print "## Exporting #{filename}\n"
ans = true
else
puts "File not found: #{filename}"
exit 1
end
end
ans
end
2.3 ページ全体の書き換え
オブジェクトに対する URL の書き換えはできているので、実際にページ内のすべての画像に対して、置き換えを実施します。書き換えが終わったページを再処理しないように ImageExported
というチェックボックスプロパティを用意して、ここにチェックが入っていないもののみを処理するようにします。
Notion のブロックには、CalloutBlock や ToggleBlock など子ブロックを持つブロックも存在します。そのため、再起的な処理ができるように blocks という処理バッファを用意し、子ブロック情報を処理バッファに追記する処理を行っています。ここでは再起的にすべてのブロックをスキャンし、ImageBlock があった時のみ、export_image
を実施します。置き換えがあった場合には、true が帰るので、その時に block.save
を実施します。
ブロックの処理が終わったら、FeaturedImage プロパティにある og image の書き換えも実施します。こちらはプロパティ内の最初の File object を取得し、export_image
を呼び出します。NotionRubyMapping では File object の書き換えでプロパティの書き換えを検知できないので、プロパティ自体の files を上書きしています。また、外部書き出しが終わったので、ImageExported
のチェックボックスも true に変更しておきます。その後、page を保存することで画像 URL の変更及び処理完了フラグも書き換えも行われます。
def export_page(page)
pp = page.properties["ImageExported"]
unless pp.checkbox
print "# Exporting page #{page.title}\n"
blocks = page.children.to_a
until blocks.empty?
block = blocks.shift
if block.is_a? ImageBlock
block.save if export_image block
elsif block.has_children
blocks += block.children.to_a
end
end
pf = page.properties["FeaturedImage"]
file = pf.files[0]
if file
pf.files = [file] if export_image file
end
pp.checkbox = true
page.save
print "#### Done\n\n"
end
end
2.4 ブログ全体の書き換え
最後にこのプログラムの呼び出し部を記載します。ページ ID を一つ設定した場合には、そのページのみを処理します。一方、何も指示しなかった場合には、ブログ全体のページをすべて処理します。
if ARGV.length < 1
db = Database.find ASTRO_DB_ID
dp = db.properties["ImageExported"]
db.query_database(dp.filter_equals(false)).each do |page|
export_page page
end
else
page = Page.find ARGV[0]
export_page page
end
3. ハマったところ
3.1 URL の書き換えに失敗した
NotionRubyMapping の FileObject には internal と external の二種類があります。Ver 0.8.0 では、Internal URL を書き換えようとするとエラーになるように設定していました。今回の処理は internal の URL を書き換えたかったので、url の書き換えとともに external に変更する処理に変えました。これを NotionRubyMapping 0.8.1 としてリリースしました。
def url=(url)
@url = url
@type = "external"
@expiry_time = nil
@will_update = true
end
3.2 OG image が消えた
実際に Notion データベースは変更されたものの、astro-notion-blog を実行したところ OG image がデフォルトのものに変わってしまいました。今日の昼休みに、おとよさんとやりとりをしたところ、external の処理が抜けてしまっていたことがわかりました。私の方でもパッチを作り確認をしましたが、その後おとよさんも早速パッチを作ってくれました。
https://github.com/otoyo/astro-notion-blog/pull/143
中身はこんな感じ
4. 実行
astro-notion-blog でビルドすると、こんな感じでちゃんと外部 URL になっています。
これを受けて今回のスクリプトを実行すると FeaturedImage の URL がちゃんと書きかわりました。
5. おわりに
この変更によりブログのビルドが格段に高速化しました。ビルドが遅くてちょっと躊躇していたのですが、これからもっと頻繁に記事を投稿していきたいと思います。