2017年11月17日金曜日

Raspberry Pi+Raspbian stretchでwkhtmltopdfを使ったホームページ画像化しGoogleフォトにアップロードする

事の発端は毎週のダイエットサマリーブログ

毎週掲載しているダイエットサマリーブログで、体組成データや筋トレチェック表を画像化して掲載しています。
以前ご紹介した画像化の方法でしばらく使っていたのですが、OSアップデートによる変更で動作しなくなったり、SDカードの破損で運用そのものが危ぶまれたり、踏んだり蹴ったりのトラブル続出で使用中止していました。
代替案で、他の端末からスクリーンショットをアップロードして使っていたのですが、その作業がとにかく面倒くさい😭
Raspbian stretchで強化されたセキュリティ関連のクセが、やっとここに来て飲み込めて来たので楽したくて再チャレンジしました😤

wkhtmltopdfに巡り合う

あれこれ探した末に見つけたのがwkhtmltopdfでした。
Google製のアプリで、HTMLレンダリングエンジンWebkitを使って展開し、それをPDFや画像ファイル化するためのものです。
対応OSもWindows・macOS・Linuxに対応しているので、私のようにあれこれ使う人にとってはとても助かります。

Raspbian stretch LITEにインストールするも動かず

インストール方法は、おなじみのaptを使います。

sudo apt-get install wkhtmltopdf
使い方は、下記のコマンドです。
wkhtmltoimage [URL] [画像ファイル]

Raspberry Pi Zero Wで使ってみたのですが、下記のようなエラーが出て動きません。

QXcbConnection: Could not connect to display
中止
フレームバッファが必要なのかも。

仮想フレームバッファ Xvfbで実行

前回画像化でも使った仮想フレームバッファ Xvfbを今回も使います。
まずは、インストール。

sudo apt-get install xvfb
次に、仮想フレームバッファをこのアプリで作成し、そこでwkhtmltopdfを実行します。
xvfb-run -a -s '-screen 0, 1024x768x24' wkhtmltoimage [URL] [画像ファイル]
これで、どんな環境でも使えると思います。

出来た画像ファイルをupload-gphotosGoogleフォトへアップロード

使わせていただいているブログサイト Bloggerの記事作成機能はとても便利で、Googleフォトの画像も直接貼り付けられます。
そこで、HTMLから作った画像を、upload-gphotosGoogleフォトへアップロードします。

upload-gphotosはTypeScriptで書かれていますが、JavaScriptへトランスパイルされたモノがnpmでインストール可能なので、今回はそれを使わせていただきます。

node.js、npm、nを最新に

Raspbianに標準インストールされているパッケージが古く、そのままでは動きません。
そこで、最新版のインストール作業から始めます。

まず、パッケージリストを更新します。

sudo apt-get update
次に、npmをインストール。
sudo apt-get install -y npm
次に、npmの最新版をインストール。
sudo npm install -g npm
npmをnpmでインストールってなんかだか不思議な感じですけどね😅
このブログを書いてる段階のRaspbian最新版September 2017では、下記のようなエラーがでます。
/usr/bin/env: ‘node’: No such file or directory
これは、node.jsのコマンドであるnodeがないために出るエラーです。nodejsという古い名前のままなので、nodeという名前でリンクを作ります。
sudo ln -s /usr/bin/nodejs /usr/bin/node
そして、再びnpmの最新版をインストールするコマンドを実行すると処理が始まります。 npm最新版になったら、次はnode.jsのバージョン管理パッケージnをインストール。
sudo npm install -g n
やっと、本題の最新版node.jsのインストールです。
sudo n stable
もし、最新バージョンがリリースされているにも関わらずError: invalid version~というエラーが出た場合、そのバージョンのターゲットCPUのパッケージがまだアップロードされていない可能性があります。
Raspberry Piは、現段階でARMv6/7/8の3種類のCPUアーキテクチャ商品があります。Raspberry Pi 2 V1.2と3(コンピュータモジュール含む)がARMv8、Raspberry Pi 2 V1.1がARMv7、その他はARMv6です。ARMv8は64bitアーキテクチャなのでARM64としてリリースされてる事が多いですが、Raspbianは互換性維持のために32bitで動かしているので実質ARMv7と思って良いと思います。
ご使用のRaspberry Pi用CPUアーキテクチャパッケージがアップロードされていない場合は、
sudo n [バージョン番号]
直接バージョン番号を指定してインストールしてください。
あるかどうかの確認は、ダウンロード先のnodejs.orgにブラウザでアクセスし、バージョン毎にディレクトリ(フォルダ)が分かれているので、その中にlinux-arm~l.tar.gzってのがあるか確認し、ある中から最新バージョンのモノを指定してください。
念のため、インストールしたnode.jsのバージョンを確認しておいてください。
node -v
もし、古いバージョンのままの時はリンク先が古いものを指してるはずですので、最新版に変更すればOKです。

upload-gphotosをインストール

ここまで来たらもう一息!
upload-gphotosのページの下のInstallationのnpm (Recommended)を参考にインストールします。
スーパーユーザーでインストールするので、sudoを付けて実行してください。

sudo npm install -g upload-gphotos
使い方はこんな感じ
upload-gphotos [画像ファイル名] -u [Googleフォトユーザー名] -p [パスワード] 
ログインエラーが出た場合、安全性の低いログイン技術を使ったアプリの利用許可がされていない可能性があります。 安全性の低いアプリによるアカウントの使用を許可するの説明を参考に、安全性の低いアプリの許可にアクセスして有効にしてください。

シェルスクリプトでサクッと実行

準備は整ったので、これを組み合わせて簡単に実行でいるシェルスクリプトを作ります。
便宜上スクリプト名はhtml2pngとします。

nano html2png
#!/bin/bash
# 一時ファイルを作る
tmpfile=$(mktemp --suffix=.png)

function rm_tmpfile {
  [[ -f "$tmpfile" ]] && rm -f "$tmpfile"
}
trap rm_tmpfile EXIT
trap 'trap - EXIT; rm_tmpfile; exit -1' INT PIPE TERM

xvfb-run -a -s "-screen 0, 1024x768x24" wkhtmltoimage --width $2 $1 "$tmpfile"
upload-gphotos "$tmpfile" -u [ユーザー名] -p [パスワード]
出来たら実行権限を追加します。
chmod +x html2png
使い方はこんな具合。
html2png [URL] [横幅px]

upload-gphotosでちょっと気になった不具合

例によって、Raspberry Pi Zero W+Raspbian Stretch Ver.September 2017ベースで試していたのですが、upload-gphotos実装直前までこぎつけて不具合を見つけました。それは、他のRaspberry PiからSSHコマンドを使ってリモート実行した時に起こりました。

1つ目は、アップロード時のプログレッシブバーが表示されません。
wkhtmltopdfのプログレッシブバーや他の文字は表示されているので、upload-gphotosで使われてるモジュール固有の問題のようです。
原因を調べてみると、TTYチェックしてる部分があってそうじゃない場合はそもそも表示しないようなのです。
なぜ除外してるかさらに調べた所、1行の長さを取得してる部分があってそれがTTYじゃないと取得できず、仮にチェックを無効にしてもその後の処理でエラーになってしまいました。
まぁ、仕様通りの動作ですし、進捗状況が未表示でも問題ないので、このまま使わせていただく事にしました。

問題だったのはもう1つの不具合でした。
アップロード終了後に諸々の情報を表示して終わるのですが、それがSSHコマンドからの直接実行だと終わらないのです。
通信処理を非同期で行っていて、それをメインモジュールで同期モードで動かしてるようなのですが、TypeScriptとJavaScriptのトランスコンパイルの部分で仕様差が吸収出来てないような気がしました。
そのままではいろいろ困るので、cli.ts(cli.js)の末行付近にある

console.info(JSON.stringify(photos, null, 2));
の次の行にプロセスの終了命令を追加しました。
process.exit();
これで、終了しない不具合は解消されました。

最後の最後でまさかの!?

やっとブログも書き終わる!と思った矢先に、まさかの不具合発覚。
曜日が表示されていないのです。なぜ?どうして??
原因は簡単でした。日本語フォントがインストールされてなかったんです😅
ということで、Google Noto Fontsをインストール。
sudo apt-get install -y fonts-noto

無事に曜日が表示されました。

お読み頂きありがとうございました。
次回もお楽しみに!