皆さん、Advent Calendar書いてますか! 今回は計算機系Advent Calendar向け記事を書く上で、「端末上の操作」を動画として保存・公開できるasciinemaコマンドについて紹介します。
端末を「録画」する
Advent Calendar(アドベントカレンダー)の本来の由来はともかくとして、日本の技術系界隈では12月1日あたりから計算機系記事が充実するイベントとして定着しつつあります。
さて、計算機系の記事を書く上で頻出するのが、コマンドライン上での操作です。大抵の場合はpreタグのように整形済みテキスト(PREformatted text)として、文書の中に埋め込むことでしょう。しかしながらそれでは静的なテキストであって、「実行した結果がどのようなタイミングで表示されるか」はわかりません。もちろんアニメーションGIFやAPNGにしたり、YouTubeにアップロードすれば動画にはなるものの、今度は実行内容を手元にコピーアンドペーストできません。
そこで今回紹介するのが、Python製の端末録画ツールである「asciinema」です。
「端末の操作を保存する」ツールはasciinemaに限らず古くからたくさん存在します。たとえばUbuntuであれば最初からインストールされているscript
コマンドはその代表格ですし、ttyrecも古くから使われています。ttystudioは直接GIFやAPNGに生成できますし、Ruby製のShelrやPython製のTermRecordなど同様のツールは枚挙にいとまがありません。
その中でも今回紹介するasciinemaには、次のような特徴があげられます。
- 録画データの再生結果からテキストをコピーアンドペーストできる
- 録画データを専用のWebサイトへにアップロード・公開・再生・共有できる
- YouTubeのように自前のWebサイトで上記アップロード結果を埋め込み表示できる
- 自前のアップロードサイトを構築可能
- ローカルプレイヤーでも早送り・コマ送り・一時停止が可能
- JSONファイル(newline-delimited JSON)として保存される
- ホスト間のストリーミング再生も可能
- パッケージリポジトリからかんたんにインストールできる
- キー入力を保存するかどうか選択可能(v2フォーマットから)
一部は他の録画ツールでもサポートしていますし、他のソフトウェアと組み合わせたら大抵の機能は実現可能ではあります。ただこれらすべてをひとつのツールで実現するとなると、asciinemaが有力な選択肢となってくるのです。
ちなみに「保存したキー入力」を再生時に表示する仕組みは、まだ存在しません。今のところ、録画時にオプションを指定すればJSONデータにキー入力の情報が保存されるようにはなっているものの、プレイヤー側の対応が進んでいない状況です。
基本的な録画と再生の方法
まずはasciinemaをインストールしましょう。Python製のツールなので様々なインストール方法が存在しますが、Ubuntu 18.04 LTS以降ならリポジトリからパッケージをインストールする方法が一番かんたんで確実でしょう。
asciinemaは「asciinema サブコマンド」形式で実行します。録画は「asciinema rec ファイル名」です。ファイル名を省いた場合、録画終了時に録画した結果が共有サイトにアップロードされます(アカウントは不要。共有サイトについては後述を参照)。あとからuploadサブコマンドでもアップロード可能ですので、基本的にはファイル名を指定してローカルで保存しておきましょう。
試しに日本語入力やキャリッジリターンのようなカーソル位置がジャンプする操作、さらにはVimのようなスクリーンエディタも動かしてみましょう。録画が終了したら「Ctrl-D」もしくは「exit」で終了してください。
recサブコマンドは、実行したシェルの表示結果をすべて記録します。しかしながら実行時に「-c コマンド
」を指定すると、そのコマンドの出力結果だけ記録されるのです。たとえば「asciinema -c "man asciinema" asciinema.cast
」のように使います。
録画データは「asciinema play ファイル名」で再生できます。
実際にWebプレイヤー上で再生した結果は次のとおりです。
ポイントは再生中の文字列を選択・コピーできる点です。適当なタイミングで一時停止し、Webブラウザー上の他の文字列と同じように、マウスで文字列を選択してみましょう。
ちなみにgihyo.jpは本文の横幅が狭いため、横80桁幅の録画データを再生しようとすると右端が切れてしまいます。画面全体を確認したいならプレイヤー右下のボタンから全画面表示するか、asciinema上で再生してください。
ローカルでもWebプレイヤーでもスペースキーで一時停止・一時停止解除できます。ローカルプレイヤーの場合は一時停止中に「.」と入力することで、コマ送り(asciinema上の1フレームごとの再生)が可能です。playサブコマンドに「-s 数字
」を指定すると数字の数だけ倍速で再生します。
実際に中身を見ていると、ただのJSONファイルになっていることがわかります。
JSONファイルの先頭には、フォーマットバージョンや端末のプロパティが記録されています。「newline-delimited JSON」なため、いずれの行もvalidなJSONです。言い方を変えると、個々の行を削除しても再生時に表示される内容が変わるだけで、フォーマットとしては問題ありません。よって不要な出力を手作業であとから整理できるのです。
さらに録画時に「--stdin
」オプションを付けることで、標準入力のキー操作も記録可能です。たとえばsudoコマンド時のパスワードもそのまま残ります。前述のように今のところまだプレイヤーが対応していないものの、将来的には端末の操作に合わせてキー入力をプレイヤーが表示するようになるかもしれません。
なお、「asciinema cat ファイル名」を使うとレンダリングした結果をダンプできます。記事を書くにあたって、実行結果のみをコピーアンドペーストしたい際に便利でしょう。
録画データのアップロード
録画したデータを他の人と共有したいなら、データをそのまま渡す以外に、asciinema.orgにアップロードする手段もあります。ちなみにアップロードだけならアカウントは不要です。
このようにアップロード先のURLが表示されます。アクセスしてみると、実際に再生できることがわかるでしょう。アップロード直後の状態は「Private」扱いになっています。これはURLを知っている人のみが再生できるモードです。ただし、URLは比較的推測可能である(上記のような英数字の文字列以外にも連番の数字が割り当てられる)ため、本当にプライベートなものはアップロードしてはいけません。
サイトにプレイヤーを埋め込みたい場合は、「Share」ボタンに表示される情報を参照してください。「Download」を押すとデータそのものをダウンロードできます。ちなみにローカルのasciinemaコマンドでも直接再生可能です。
さらにasciinema.orgにアカウントを作成し、アップロードしたデータをアカウントに紐付けると主に次のような設定が可能になります。
- タイトルの変更
- 公開・非公開設定の変更
- サムネイルとして表示するフレームの指定
- 録画データの削除
- Webプレイヤー上の端末の外観の変更
アカウントに必要な情報はメールアドレスだけです。まずasciinema.orgにアクセスし、右上の「Log in/Sign up」をクリックします。次に表示される入力フォームにメールアドレスを入力し「Log in」を押します。「Welcome to asciinema.org」というタイトルの15分だけ有効なリンク付きのメールが届きますので、それをクリックします。最後にユーザー名を設定します。次回以降ログインが必要になったら、ユーザー名を入力するとワンタイムリンクメールが届きますので、そこにアクセスしてください。
asciinemaコマンドを実行するPCとアカウントとの紐付けは、「asciinema auth」コマンドを使用します。実はasciinemaコマンドを実行すると、「~/.config/asciinema/install-id
」にそのマシンを一意に示すIDが生成されます。このIDをasciinema.org上のアカウントと紐付けることで、「そのPCからアップロードしたデータ」と「asciinema.org上のアカウント」がリンクします。
authサブコマンドを実行しましょう。
URLが表示されるので、asciinema.orgにログインした状態でそのURLにアクセスしてください。これでこのPCからアップロードしたファイルやこれからアップロードするファイルは、すべて同じアカウントに紐付きます。
ちなみに別のPCでasciinemaを実行したり、「~/.config/asciinema/install-id
」を削除した場合は、そのたびにauthサブコマンドの実行が必要になります。
ストリーミング再生
asciinemaのJSONファイルのフォーマットは「newline-delimited JSON」であるため、比較的容易に追記できます。実際「--append
」オプションを使うと、既存の録画データの後ろに新規データを追記できるのです。
このようなフォーマットの特性から、asciinemaはライブストリーミングにも対応しています。たとえばnetcatを使って、ある端末の操作を、ネットワーク越しの別の端末から見られるようにしてみましょう。
まず、ストリーミングを受信する側(ホスト名:maki)でポート12345を待受状態にしておきます。もしIPv6でアクセスする場合は「-6
」オプションも付けてください。
次に配信する側(端末を操作する側)は、データの出力先を受信側のアドレスとポート番号にしてasciinemaを実行します。
これで送信側での端末の操作が、受信側の端末にも表示されるはずです。これにより、気になるあの人の端末上の生活をネットワーク越しにずっと眺めていられます。もちろんパスワードのようなエコーバックしないキー入力は見えないままなので、セキュリティ面もばっちりです。
追記可能性を用いたもうひとつの特性が「録画中に録画データを再生できる」ことです。
sl.castを録画中に、sl.castを再生すると、その再生コマンドの中身と再生結果がsl.castに追記録画され、それが再生され、追記録画され……とひたすら無限に続いていきます。役に立つかどうかは二の次です。なお、中断するにはCtrl-Cで再生を止めてからexitして録画を止めてください。
asciinemaのデータを映像ファイル化する
asciinema.orgにアップロードすれば再生できる状態で録画データを共有できます。また、asciinema-playerを使えば、任意のデータをWebサイトやブログに埋め込めます。もちろん閲覧者は、再生中にコマンドをコピーできます。
しかしながら動画化してしまったほうが共有しやすいケースもあるでしょう。たとえばSNSやチャットに埋め込むなら、GIFやAPNGのほうが確実です。そこで最後に、asciicast2gifを用いてasciinemaの録画データを映像ファイル化する方法を紹介します。
asciicast2gifはNode.jsを利用したアプリケーションです。つまりPhantomJSを用いてJavaScript版のWebプレイヤーを実行し、そのレンダリング結果をフレームごとにPNGファイルにし、最後にGIFに合成しています。そう、圧倒的な力技です。このためNode.jsやImageMagickなどが必要です。UbuntuにおけるNode.jsの実行環境の構築にはいくつかの方法が存在します。今回はお手軽なsnapパッケージのインストール方法を利用しましょう。
snapパッケージ版のnpmの場合、「-g
」オプションを使うとPhantomJSのインストールに失敗したため、今回はローカルにインストールしています。
asciicast2gifには録画データのJSONファイルと出力するGIFファイル名を渡します。JSONファイルはネットワーク越しでも取得可能です。特にasciinema.orgにアップロード済みのデータについては、URL末尾に「.json
」と付けることでJSONファイルを取得できます。
力技なのでかなりPCのスペックが必要です。少なくとも400フレーム弱の録画データの場合は、それなりに最近のPCを使ったものの30分待っても生成できませんでした。上記程度のフレーム数だと数秒で完了します。また、フォントが合っていないと崩れる場合もあるようです。
ついでに4Kディスプレイでslコマンドを実行した様子をAPNGに変換したものもこちらにアップロードしています。サイズが1.6MB程度ですので、そこそこのスペックと大きなディスプレイでお楽しみください。
ちなみにrectermを使うと、asciinemaを再生しながら音声を録音し、その録音データとasciicast2gifで生成したGIFを合わせてMP4動画ファイルの作成してくれます。CLI専門YouTuberとして、音声解説付きの端末操作動画を作りたい際に使えることでしょう。
えっ、アドベントカレンダーって25日までなんですか?
端末操作を説明するにあたって、動画である必要性はあまりありません。しかしながら計算機系の記事は誠実に書こうとすればするほど文字過多になりがちです。そこに端末操作の内容まで文字で提供するとなると、一般的な読者は離れていくことでしょう[1]。
さりとて段落の合間合間に、関連性があるかどうかよくわからない画像素材を挟むのは倫理的に憚られます。お願いですから憚ってください。
そこで端末の操作をasciinemaで映像化してしまうのです。「動く何か」は子どもから猫に至るまで、みんな大好きです。実用的かどうかはともかく、一手間かけている感じも醸し出せます。もしこれからアドベントカレンダーを書かれるなら、視覚的に動くものが映る可能性がある端末操作を、asciinemaで録画した動画を提供してはいかがでしょうか。
ちなみに筆者はこれまで一度もアドベントカレンダーを書けたことがありません。