ramen

ひところステマが話題になったが、この記事もまた徹頭徹尾ステマである。

NTT のフレッツ光を契約していた頃の話なのだが、この契約は月ごとにいくばくかのなんちゃらポイントが与えられるシステムになっていた。貯まったポイントに応じていろいろな景品と交換できるというまあありがちなものだ。そして、景品の中で上等なもの(例えば iPad とか)は、「まあ10万年くらいフレッツ光に契約し続ければそれくらいのポイントが貯まるかもね…」というような無茶な設定になっている点も非常にありがちであった。

もちろんそんなに待ってられないので、解約する直前で使っちゃったのだが:

ramen-2

ramen-1

使ったというのはつまりこのラーメンだ。これがとても美味しかった。1 杯分の単価で考えるとそのへんで売ってる袋麺の 2 倍くらいになると思うが、10 倍美味しいので全く問題ない。

このセットは web から注文できる:

喜多方ラーメン游泉三浦屋 醤油・味噌7食入り
http://shop.fm-kitakata.co.jp/shop/shopdetail.html?brandcode=000000000849

Ubuntu 16.04

Ubuntu 16.04.1 LTS (Xenial Xerus) がリリースされうちのマシン(Xubuntu)にもアップグレード通知が来たのだが、困ったことがある。

現状 AMD 製 GPU の Linux 向けドライバには 3つの選択肢があり:

  • Radeon ドライバ: OSS で、あんまりハードウェアの性能は引き出せない。多分安定はしている
  • fglrx ドライバ: AMD 製のプロプラ。まあまあ性能を引き出してるのだと思うけど(あと設定が Catalyst による GUI で行えるので試行錯誤しやすい)、あんまり安定してない
  • amdgpu ドライバ: 最新の Linux カーネルの管理下にある OSS ドライバだが、AMD からの技術公開を受けて安定さと性能を両立している(という触れ込み)だが超ナウい GPU しか今のところサポートしていない

うちのマシンは GPU が AMD Radon HD 6320 というやつなのだけど、困ったことに AMD は 16.04 向けの fglrx を作らない宣言を出しているのであった。なので、今 16.04 にアップグレードすると Radeon ドライバか amdgpu ドライバを選択せざるを得ないのだけど、amdgpu は今のところ HD 6320 をサポートしていないので対象外(最終的にはもっと古い GPU もサポートすると言っているのだがいつかは不明)。そういうわけで Radeon ドライバが最終候補になるのだが。前述のとおりこれは OSS 原理主義者のためのものであって性能はいまいちなのである。

というわけで、16.04 にアップグレードしたくてもできない。はやく amdgpu が進歩してほしい。

The TV program time table #2

CloudFlare 絡みでもうひとつ。オリジンサーバから送出される静的なコンテンツについて、CloudFlare は CDN として振る舞う。つまり CloudFlare 側でキャッシュし、クライアントのアクセスの際はそのキャッシュを利用すると同時にオリジンサーバの負荷を低減する。

この静的の定義とはなんぞやというと、このへんに詳しい。特定の拡張子を持つもの、Cache-Control ヘッダで public と指定されかつ max-age が 0 より大きいもの、PageRule で特別に指定されたもの…という感じ。意外なことに html ファイル自体はキャッシュされない(PageRule の設定でキャッシュさせるようにすることはできる)。

で。あべアニのうち最もファイルサイズがでかいのは、番組表のデータである json ファイルなのであるが(gzip して 200KB 弱くらい)、アクセス解析を見てみたらなんとこれが CloudFlare 側にキャッシュされていないのであった。これはいけません。

とりあえず適切に Cache-Control ヘッダを付加するようにしてみたところ、効果なし。そこで mod_rewrite で擬似的な js ファイルとして扱うようにして、かつクエリ文字列を使わないようにしてみたところ、キャッシュに乗るようになった。間抜けなことにそれらを同時に変更したので、乗るようになった要因が拡張子なのかクエリ文字列なのかはっきりしていないのだが、まあ結果オーライなのだ。

ちなみに PageRule で何とかする方法もあるのだが、フリープランだと何と登録できる PageRule は 3 つまでなのである。ダイヤモンドより貴重なのでホイホイ使うわけにはいかない。

CloudFlare のキャッシュに乗っているかどうかは、レスポンスヘッダに [cci]cf-cache-status: HIT[/cci] が含まれているかどうかで判断できる。また、CloudFlare に限らずコンテンツが CDN によって適切に分散されているかどうかは https://www.webpagetest.org/ でテストすることができる。

The TV program time table

Abema.tv というサービスがある。これはつまりインターネット上の無料 TV サービスである。様々なチャンネルを持っているのだが、その中に 5 つくらいアニメ専門チャンネルがあり、そのせいで(あるいはそのおかげで)虹裏 img はここのところ半分 Abema 実況板と化している状態だ。

というわけで見てみたところ、Abema のサービス自体は非常によく出来ているのだが、ただひとつ番組表が使いにくいと感じた。全体的に重いのと、1 ページに 5 チャンネル分の番組表が表示されるのだがこれが単純にチャンネル番号順で、カテゴリ分けされるわけではない。そのため例えばアニメだけで一覧、ということができない。また、ページを非アクティブ状態からアクティブにした瞬間、どうも現在時刻に合わせた番組枠を表示しようとスクロール位置を変えるっぽいのだが、それがバグってるっぽい上になかなか直らない。……などなど、いろいろと不満があるのである。

そういうわけで、代替物を作った:

あべ☆アニ
https://appsweets.net/abeani/

これを作る過程での技術的なポイントはなくもないのだが、本質的にはただの html と javascript だけのシンプルなページであり、特に書くほどのものではない。

しかしこの番組表自体には直接関係ない分野から強いて 1 つ書いておくとすると、CloudFlare のキャッシュとの連携が挙げられる。知ってのとおり CloudFlare は静的ファイルをキャッシュする。従って web ページを開発して頻繁にサーバにアップロードして動作確認をする際はそのキャッシュ機構が邪魔になる。そんなわけで CloudFlare のサイトにログインすると現れるダッシュボードには個別のファイルのキャッシュをパージする機能が用意されているのだが、もちろん手作業になるのでめんどくさい。

と、だれでもそう思うわけで、API が用意されている。で結局、rsync した結果を元に API を呼び出すスクリプトを書いて解決。

Talk about news #2

PhoneticNews は一種の RSS リーダーと考えることができるが(ただし NHK ニュースに関しては公式サイトが内部的に利用している、限りなく RSS に近い独自形式の XML を利用している)、任意のアドレスのフィードを利用できるわけではないので用途はかなり限定的だ。

なぜフィードを絞っているのかというと、発話の際のトラフィックの問題だ。PhoneticNews は発話データを得るのに VoiceTextAPI を利用している。この API のエンドポイントに文字列を POST すると、それを読み上げた発話の aac データが返ってくる。

この VoiceText へのネットワークアクセスは PhoneticNews をインストールした Chrome がそれぞれ個別に行うため、任意のフィードを登録できるようにすると VoiceText 側の負荷が結構なものになるかもしれないのである。まあ、勝手な想像なのですが。

API のリファレンスには呼び出しに関する回数や頻度の制限などは記述されていないので、どこまで許されるのか実際わからないのであった。うーんお問合せしたほうがいいのかなー。

いずれにしてもできうることを考えると、

  • 各 Chrome から個別に VoiceText にアクセスさせることをやめて、ここのサーバを経由させる。ここのサーバが代理的に VoiceText へのやり取りを行う: ここのサーバへの負荷はどうするのかという話になるが、現状でここのサーバは CloudFlare のお世話になっているのでたぶん負荷の面では大丈夫
  • 発話データを Chrome 自身の tts で生成する: ただし、日本語の発話データは OSX か、Windows10 以降じゃないと取得できない、らしい

あたりになると思う。はてさて。

Talk about news

PhoneticNews という Chrome エクステンションを前書いたのだが、いくつか既知のバグがあり、特に Chrome の起動時に二重に読み上げが行われる現象がある。この機会に探ってみた。

このエクステンションは定期的に NHK のニュースを読み込み、それを読み上げる。そのためにバックグラウンドで定期的に読み込み処理、読み上げ処理を走らせることが動作の核になる。一般的にそれを実現するには setTimeout や setInterval を使うのだが、Chrome のエクステンションの場合はバックグラウンドページの構造について Event Pages という別のアプローチがあり、PhoneticNews もそれを使っている。しかしこれがなかなかに癖があり、正しくそれに対応していなかったのがバグの原因になっている。

エクステンションのバックグラウンドが Event Pages のルールに従うか否かで変わる最大の物は、つまりバックグラウンドページが勝手にアンロードされるか否かということだ。バックグラウンドがアイドル状態になると即アンロードされる。アンロードというのは Chrome ブラウザの環境下においては、エクステンションに割り当てられたプロセスを終了するという意味だ。つまり Event Pages に対応すると、タスクマネージャを開いた時ずらっと Chrome が並ぶのを幾ばくか減らすことができるというわけだ。これはもちろんその分のメモリを開放するという意味もあるが、特にモバイル機器において不要な負荷を減らしバッテリーの寿命を伸ばすという効果があるのだと思う。

さて Event Pages では setTimeout/setInterval の代わりに chrome.alarms を使う。これにアラームを登録すると指定したタイミングにバックグラウンドがロードされ、次に chrome.runtime.onStartup に登録したイベントハンドラが呼ばれ、最後にアラームに登録したイベントハンドラが呼ばれる。

このアラームが特徴的なのは、登録が Chrome ブラウザに対して永続的であるということだ。定期的に発生するアラームは、エクステンションのインストール時にただ 1 度登録すればいいのである。PhoneticNews ではそれを知らず、アラームの登録をエクステンションのスタートアップ時に毎回行っていた。これが起動時に二重に読み上げが行われていた原因だ。

その他、従来は NHK ニュースだけに限っていたのを他のニュース配信社もいくつか含めるようにした。
phoneticnews-options
これ以外に、例えば共同通信や時事通信あたりも入れようかなと思ったのだが、なぜか日本の配信社は RSS フィードを公開していない、あるいはこっそりとしか公開していないところが多い。

アイコン押下時のポップアップでは最後に読み込んだ 1 件のニュースだけを表示していたのだが、最大 10 件にまで拡大した。
phoneticnews-popup

fixed: GRADIUS

gradius-on-edge

手持ちのプロジェクトをいろいろ整理しているのだが、ずいぶん前に作ったグラディウスのソースを読んでたら RequestAnimationFrame の使い方を勘違いしたまま数年経過してたようなのでとりあえず直してみた。若干スクロールがスムーズになった気のせいがするかもしれない以外は特に動作は変わっていない。というよりも、各種データをどこからどうやって生成したのかきれいさっぱり忘れているのでソースを近視眼的にいじるくらいしかもうできないのだが。

その他、Windows 10 の edge で動かないという話があったので


edge でも動作するよう確認。

edge 自身は…将来に期待という感じ。今夏の Windows 10 のアップデートで拡張機能の実装を含めて良くなるそうなので、そのときになったら wasavi のテストもしてみたい。

Hello, Windows 10

期限が近づいてきたので、Windows 7 マシンを 10 にアップグレードさせた。

アップグレードにはいくつかの方法があるが、最近の Windows 10 のビルドは新規インストール時に 7 のプロダクトキーを受容してくれるそうなので、そうした。つまり新しいハードディスクを取り付け、10 の ISO イメージを書き込んだ USB メモリからブートし、インストールし、7 のプロダクトキーを入力してデジタル権利付与なるものを得た。

今のところ未解決のトラブルは 1 つのみ: スリープさせると、その直後(大体 1 分以内)に勝手にスリープを解除しようとする。

その他は元の環境にあったソフトウェアを再インストールするのが面倒なくらいで、特に問題はない。というのも Windows 上の作業はだいたい VirtualBox の仮想マシンでやるので VirtualBox を入れて構成ファイルを元に戻せばそれで済んでしまう。強いて言えば、元の環境で地デジを見るのは DY-UD200 + TVTest という「ああ、アレね…」感あふれる構成だったのだが、それを Windows 10 上に再現するのがとても面倒だった。

Optimize a loading of assets

赤福プラスの話。

赤福プラス 3.x はふたばの画像掲示板を開いた時に DOMContentLoaded のタイミングで一旦ドキュメントをすべて書き換える。これはなかなかアグレッシブな動作で、3.x 未満の赤福プラスや、おそらくは他の、ブラウザ上で動作するふたば閲覧エクステンションはあくまでふたばがネイティブで送出するコンテンツに何かを「付け足す」ようになっていると思う。赤福プラス 3.x はそうではなく、完全に書き換える。元のドキュメントからメタな中間 XML ドキュメントを生成し、それを XSLT によって HTML に変換・再構築し、元のドキュメントに上書きする。

さて DOMContentLoaded というのは定義としては DOM の構造を構築し終わった段階で発火するイベントで、load イベントに比べてかなり早い段階で発生する。しかし、これはドキュメントに付随するスタイルシートやスクリプトや画像や iframe で指定されるサブフレームのドキュメントをまだ読んでいない段階というわけでは *ない*。もしかしたら並行して読んでいる途中かもしれないし、そうでないかもしれない。特に最新のブラウザほど、並行動作をしている割合が高い。

このブラウザの動作は、赤福プラスの仕様とは相容れない。もしその手のアセットを読み込むとしても、それは赤福プラスが管理する文書の下で読み込まれるべきで、その前段階でネットワークアクセスが発生するのは全て無駄になるため、好ましくない。

そういうわけで、その辺りを最適化するために以前から Presto Opera では外部の script 要素の動作をブロックしたりしていた。似たようなことは Chrome の WebRequest API でもできるわけで、やってみた。

やることは、赤福プラスがページを再構築している間、そのページから発生するふたば外へのリクエストをすべてブロックすることである。そのために、

  • バックエンドの main.js にタブ ID をキーとするハッシュ initializingTabIds を用意する
  • WebRequest API によりすべてのリクエストをリスンする
  • ふたばの画像掲示板の URL がアクセスされたら、そのタブ ID を initializingTabIds に記録する
  • その他の何かがアクセスされた場合、それが属するタブ ID が initializingTabIds に含まれていれば、リクエストをキャンセルする
  • 赤福プラスのフロントエンドがページを再構築を完了したら、バックエンドへ initialized メッセージを投げる
  • バックエンドが initialized メッセージを受け取ったら、そのタブ ID を intializingTabIds から削除する

という処理を加えた。

ところで WebRequest を使うには manifest.json の permissions に “webrequest” および対象となる URL パターンを含める必要がある。今回の場合は URL は http://*/* および https://*/*/ というかなり広範囲に及ぶ強いパーミッションを必要とする。その場合 webstore での審査もけっこう時間がかかるようだ。昨日の午後提出して今審査が通った。

ちなみに Firefox 版はそういう最適化をしていないので読み込みが一番遅い。気が向いたらがんばる。

Assign to a register

エディタを開いた時に特定のレジスタに常に任意の内容が入っているようにするには、vim では vimrc に

:let &z="foobar"

みたいにすると思う。この手のことは、wasavi ではまだできない(レジスタの内容自体は wasavi を閉じるときにエクステンションのバックエンドへ永続化されるので結果的に件のツイートの要件はすでに満たしている)。

できないというのは、vim の [cci]let[/cci] 文は、ex コマンドというよりは vimscript のいわゆる assignment statement で(ただしヘルプ上では expression command ということになっている)、wasavi にはまだスクリプティングの機能がないということだ。

もしその手の機能を設けるとしたら、ex コマンドに [cci]script[/cci] を新設して

:script wasavi.registers["z"] = "foobar";


:script << END wasavi.registers["z"] = "foobar"; END

な感じになると思う(スクリプトの文法がこうなるかどうかは未定だ)。vim のようにスクリプトの構造が ex コマンドのスーパーセットになっているような設計には、多分しない。

実は以前に script コマンドを実装しかけたことはあったのだが

  • wasavi の動作のうち、バックエンドにメッセージを送信して返事を待つ箇所は非同期だ。したがって、スクリプトも単にシーケンシャルに実行するわけにはいかない。つまり

    a = wasavi.registers["*"];
    print a;

    というようにはいかない。

    wasav.registers.get("*").then(function (content) {
    print a;
    });

    のような形式にせざるを得ない。すべてこの形式だとお気楽なスクリプトの範疇を超えている。同期か非同期かで文法を使い分けるとしてもそれも煩雑すぎる
  • スクリプトが例えば javascript であると定義するとそれを実行するのは eval 文になるが、それだとできることが多すぎる。何でもできすぎると動作の保証もセキュリティ面での安全性の保証もできない
  • 一方で完全に動作を制御可能なスクリプトインタープリタを実装し、その管理下でスクリプトを動かすとなるとコードサイズと実行速度の危険が危ない

などなど問題がありすぎてこりゃ時期尚早だということで棚上げになっている。どうしたものか。