ブログ自動化プロセスと体験最適化——第二弾

✍🏼 作成日 2022年08月14日    💡 更新日 2022年08月25日
❗️ 注意:この記事が作成されてから既に 日が経過しています。情報の鮮度にご注意ください
🖥  説明:ブログ体験がさらにレベルアップしました!
📚  Craft にも公開: https://www.craft.do/s/BJ8gIm5fmtdOCB

はじめに

以前、ブログ自動化プロセス](https://example.com)とその後2022年ブログ自動化プロセス](https://example.com)を作成しましたが、全体的に以下の課題がありました:

  1. Github CI経由でCraftから取得した画像をTencent Cloudにアップロードする際、頻繁にネットワークエラーが発生し、プロセスが不安定。

  2. ブログの画像をTencent Cloudにアップロードすると、強制的にpng形式に変換されるが、実際はgifなどより多くの形式をサポートすべき。

  3. CraftからMarkdownへの変換結果には、標準Markdownと異なる点が多数存在:

    1. ネストされたリストをサポートしない
    2. 引用ブロック内にネストされたリストがある場合、レンダリング結果ではリストが外側に出てしまう
    3. Craftの「フォーカス」(block)と「ブロック」(focus)のうち、後者のみがblockquote(引用ブロック)として認識される。本来は両方とも認識されるべき
    4. Craftのbookmarkにはページ説明やタイトルなど多くの情報が含まれるが、Markdown変換後はリンクを囲む段落だけになり、情報が失われる
    5. Craftでは画像自体にキャプションを付ける機能がない
    6. Craftが生成するコードブロックは、バッククォートの後に言語指定が自動追加されないため、一部の言語がplaintextと認識されハイライトされない

そこで今回の最適化では、これらの課題に対処するため、再度改良を加えました。

最適化1:独自のCraftBlockToMarkdown関数を使用

Craftにcraft.markdown.craftBlockToMarkdownが正しく変換されない問題を報告しました:

Wrong render of markdown with Decoration focus and image question Decoration block 'focus'は'blockquote'としてレンダリングされるべきですが、現在のmarkdow API craftBlockToMarkdown(flavorは'common')はこのブロックタイプを無視しプレーンテキストとしてレンダリングします。markdow API craftBlockToMarkdownが返すImageのURIはあらゆる場所で有効であり、問題を引き起こす可能性があります。サイトホワイトリスト設定を導入し、リスト内のサイトのみがドキュメント内の画像を使用できるようにすることを希望します。この戦略はAWSのデータ転送コスト削減にもつながります。 https://forum.developer.craft.do/t/wrong-render-of-markdown-with-decoration-focus-and-image-question/235

しかし公式には解決する気配が全くないため、手動変換用のシンプルな関数を自作しました:

craftBlockToMarkdown GitHub Gist: コード、メモ、スニペットを即座に共有 https://gist.github.com/Xheldon/036d9b187bd83303205001e8af97eda7

注意点として、この関数はJekyllに適応させるため(後述の最適化3参照)に特別にimageBlockurlBlockタイプのBlockを処理しています:

Image

この関数は公式プラグイン開発者フォーラムに公開していますので、ぜひご意見を:

A better implementation of craft blocks to Markdown transformation methods Craftのmarkdown APIには以下の問題があります:1.ネストされたリストがサポートされない 2.ネストされたblockquoteの順序が誤っている 3.後者のみが参照ブロックとして認識される 4.urlBlock('bookmark'と呼ばれる?)はリンクとして認識される(この関数ではJekyllのレンダリングタグをカスタマイズ可能)注:1.toggle/formulaはサポートしない(Markdownもサポートしていないため)2.私はこの関数をJekyllブログ用に使用しています... https://forum.developer.craft.do/t/a-better-implementation-of-craft-blocks-to-markdown-transformation-methods/554

最適化2:Github CIプロセスをローカルに移行

以前のプロセスでは、プラグインの公開ボタンをクリック(もちろんGitHub Tokenの入力やCOS関連情報の設定が必要)し、待つだけでした。

今回の最適化では、以前のプロセスとの一貫性を保つため、Craftプラグインを新たに作成しました。これをクリックするとドキュメントの内容を取得し、簡単な処理(例:headerImgの画像著作権情報をmetaに追加)を行った後、特定のリンクを呼び出します。リンクのパラメータにはドキュメント情報が含まれます:

1
2
3
craft.editorApi.openURL(
`xhelper://${btoa(unescape(encodeURIComponent(content)))}`
);

この特定のリンクの役割は、Apple Scriptで書かれたApplicationを呼び出すことです。Applicationの作成方法やリンク呼び出しへの対応方法については、私が書いたこのドキュメント]を参照してください。内部ではnodeが呼び出され、実際には以前Github CIで実行していたコードと同じ処理が行われます。ここに簡単なスクリーンショットを掲載します:

Xhelper截图👆🏻

効果:

Image

以前のプロセスでは簡便さを優先し、Craftにアップロードされた画像に必ずしも拡張子が付いていない(ドラッグ&ドロップや画像アップロードの場合は拡張子あり、コピペやWebアップロードの場合は拡張子なし)ため、全ての画像をpng形式に変換していました。今回はこのロジックを削除し、まず元の拡張子がある場合はそれを使用し、拡張子がない場合のみpngに強制変換するようにしました。Sharpというライブラリが非常に便利でおすすめです:

sharp - High performance Node.js image processing "一般的な形式の大きな画像を、ウェブ向けの小さなJPEG、PNG、WebP、GIF、AVIF画像にリサイズ" https://sharp.pixelplumbing.com/

というわけで…Gifをご覧ください!

支持gif啦

注:以前は手軽さ(認証不要、固定IPサーバー不要)を優先し、微信云托管]を使用していました。ついでに要約を微信公众号に送信するロジックも実装していました。しかし困ったことに、このサーバーは単に微信公众号公開を楽にするために用意したものですが、私の性格上3ヶ月に1回しか使わず、使うたびにコールドスタート(リクエストが30分ないとインスタンスが自動停止、実行時間課金のため)になり、頻繁に失敗します。そのトラブルシューティングにかかる時間を考えると、手動で微信公众号のエディタにコピペする方が早いです。というわけで、微信公众号は諦めて手動貼り付け…これも悪くない!

最適化その3:CraftのBookmarkと画像Captionの実現

Jekyllベースのブログシステムでこの効果を実現できたのは、GithubのJekyllサービスを直接使わず、自分でJekyll buildしてHTMLを生成し、それをリポジトリにpushしているためです。理由と過程はこちら]をご覧ください。

Bookmarkと画像Captionの実現には、前段階で生成されたMarkdown内の特殊Jekyllタグを利用し、カスタムJekyllプラグインで処理しています。Bookmarkのレンダリングにはrender_bookmarkを、画像captionのレンダリングにはrender_captionを使用しました(2時間前に学んだrubyスクリプト言語で書いています):

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
module Jekyll
class RenderBookMarkBlock < Liquid::Block
def initialize(tag_name, attr, tokens)
super
attrs = attr.scan(/url\=\"(.*)\"\stitle\=\"(.*)\"\simg\=\"(.*)\"/)
@url = attrs[0][0]
@title = attrs[0][1]
@img = attrs[0][2]
@error = ""
end
def render(context)
@desc = super
"<p><a class='link-bookmark' href='#{@url}'><span><img src='#{@img}'/></span><span><span>#{@title}</span><span>#{@desc}</span><span>#{@url}</span></span></a></p>"
end

end
end

module Jekyll
class RenderImageCaptionBlock < Liquid::Block
def initialize(tag_name, attr, tokens)
super
attrs = attr.scan(/caption\=\"(.*)\"\simg\=\"(.*)\"/)
@caption = attrs[0][0]
@img = attrs[0][1]
end
def render(context)
text = super
"<p caption='#{@caption}'><img src='#{@img}' alt='#{@caption}' title='#{@caption}' /></p>"
end

end
end

Liquid::Template.register_tag('render_caption', Jekyll::RenderImageCaptionBlock)
Liquid::Template.register_tag('render_bookmark', Jekyll::RenderBookMarkBlock)

Jekyllでプラグインを使うのは非常に簡単です。ルートディレクトリに_pluginsディレクトリを作成し、rubyで書かれたJekyllプラグインを配置するだけで、Jekyll Build時にプラグインがロードされます。

その他の細かい調整

以前の内容は標準Markdownから生成されたHTMLだったため、RSSリーダーで内容を取得してもフォーマットが崩れませんでした。しかし今回Bookmarkを追加したことで、RSSのフォーマットが乱れるようになりました(Reeder 5でのスクリーンショット):

Reeder5中Bookmark格式错乱

そこで再びrubyプラグインを作成し、このタグをフィルタリングして通常のHTMLリンクに変換しました:

1
2
3
4
5
6
7
8
9
module Jekyll
module BookmarkFilter
def bookmark_filter(input)
input.gsub(/^\<p\>\<a\s+class=\"link-bookmark\"\shref=(.*)\starget=\"_blank\"\>\<span\>(.*)\<\/span\>\<span\>\<span\>(.*)\<\/span\>\<span\>\n(.*)\n\<\/span\>\<span\>(.*)\<\/span\>\<\/span\>\<\/a\>\<\/p\>$/, '<p><a href=\1 target="_blank">\4</a></p>');
end
end
end

Liquid::Template.register_filter(Jekyll::BookmarkFilter)

その後、feed.xmlで使用します:post.content | bookmark_filter

ご意見・ご感想お待ちしています!

- EOF -
この記事の初出: ブログ自動化プロセスと体験最適化——第二弾 - Xheldon Blog