Pushing latest episodes

あべ☆アニに、任意の番組の最新話を自動的に通知予約する機能を追加している。

そのためにはまず最新話を検知する処理をどこに置くかということを考えないといけない。番組の通知予約機能自体に立ち返ってみると登場人物が3者いる: ここのサーバ、ブラウザの拡張機能(のバックグラウンドページ)、あべ☆アニのページである。

番組の開始時刻になったことを検知し、タブを開く処理を置くのにどこが相応しいかを考えると、あべ☆アニのページはそれを開いている時しか機能しない。サーバ側でやってPush通知をするとなると通知が番組開始1分前に確実に届くかは保証できないし、通知を受けるサービスワーカーから行えるタブ操作が微妙に痒いところに手が届かない。と言うわけで、ブラウザの拡張機能にアラームをしかける実装になっている。

では、最新話の検知機能はどうか。それを行うには番組表の全ての枠を走査しなければならないが、まずあべ☆アニのページにしても拡張機能にしても個々のクライアントがやるべき処理ではない。一方で元々ここのサーバが番組表をキャッシュするために定期的に番組表を取り込んでいるのだから、その一環として最新話かどうかを判断する処理を入れるのが最も収まりがいいだろう。

そんなわけで検知機能はサーバ側に置き、見つけたらPush通知により個々のクライアントに送りつける構成にしよう。Push通知に関してはWeb PushのAPIをそのまま使うと妙にやることが多くてめんどくさいので、Firebase SDKを使うことにする。

ということでだいたいできて今dog-foodingしているところである。今月中くらいにはリリースしたい。

chrome.input.ime

ブラウザの中にはたくさんの文字入力可能なオブジェクトがありさまざまな種別の文字が入力される。一方で IME には入力モードというものがある。そのため時々入力したいものと入力されるもののミスマッチが起こる。

そんなわけで、その辺をブラウザと IME が密接にやりとりして調整してくれると嬉しい。しかし実際には、ブラウザから IME を制御することはほとんどできないし、HTML の規格を見るにむしろできないようにしている感すらある。

ところで、タイトルのような chrome extension の API がある。これは本来は ChromeOS のためのもので、IME の機能のうちプリエディットや変換候補リストといった UI 部分を担当する。つまり変換エンジン自体は自分で書かないといけないのだが、これを活用すればブラウザから制御できる IME ができるかもしれない。

で、すでにこの API を用いて作成された SKK というものがあって:
http://blog.jmuk.org/2012/07/chromeosskk.html
インストールしてみたものの、ChromeOS には存在するのであろう入力メソッド選択インターフェースが ChromeOS 以外の OS で動くブラウザにはないので、つまりこの SKK をアクティブにする手段がない。おのれ…おのれ…!

finding time shifted programs

abema.tvでへやキャン△をやっているのだけど、配信が木曜の3:55〜という謎の時間帯で困る。

なので1週間有効の見逃し配信で見るわけなのだが、見逃し配信が有効な過去の番組枠をあべ☆アニから検索するのがめんどくさい。

ということで検索時にそういうものも対象とするようにした。

tab emphasis

アクティブでないタブがトラッカーによって更新された場合、文書のタイトルを点滅させるようにした。しかし単に点滅だと意外に目立たない。タブの背景色をいじれるといいんだけどな。あるいは音を出せばいいのかな?

数年前の赤福プラスは (Presto)Opera、Chrome、Firefox(Add-on SDK) で動いていた。これらのブラウザがサポートするエクステンションの API は微妙に、あるいはかなり異なっていたので、それらの差異を吸収する層が必要で、赤福プラスにおいてはそれは Kosian というライブラリだった。

さてそこから時が過ぎてブラウザのエクステンションは Chromium のそれで統一されてしまう勢いになったので、Kosian の存在意義が危うくなってしまった。なのでラッパの層を段階的に剥がしていきたい。同時に、個々のモジュールは ECMAScript Module で組み込むようにしたい。

ということで、そうした。文章で書くと簡潔だけどこれはなかなか大変でした。

skk is … #3

・カタカナを入力する簡便なショートカットとして、見出し語を入力して Q を押すとカナに変換されて確定する仕様がある。これを「ゔぁいおりん」という入力について適用すると「ゔァイオリン」になってしまう。

これは元をたどればもともと skk が euc-jp エンコーディングで動いていたためで、Unicode にしかない濁点つきの「ゔ」を知らないからなのだろう。しかし今動いてる skk の本体は vala で書かれた libskk であり、基本となるエンコーディングもたぶん utf-8 だろうからそういうしがらみは実はないはずだ。

ということで追ってみたが https://github.com/ueno/libskk/blob/master/libskk/util.vala#L43 以下に定義されている変換テーブルに「ゔ」のエントリがないからだと思う。「う゛」はある。このテーブルもキーマッピングやローマ字同様に json ファイルから読み込むようにしてほしい…。

・libskk 内のいじった設定を反映させるために fcitx を再起動するのだが、その直後に高い確率で thunar がクラッシュする。ふふ、お前マジぶっころがすぞ。

・fcitx-skk の設定で初期入力モードを「直接入力」にしても反映されない。常にひらがな入力で開始する。

・現在の入力モードを通知領域に表示してもらえるととっても嬉しいのだけど、そうなってくれない。そのかわりに状態パネルなるものを常に表示するようにすると、そこに現れる。まあ…それでもいいんだけど…。いや良くない。邪魔だ。

・xkb で右 alt を F20 に交換している。そのキーにひらがな入力に入るコマンドを割り当てたいが、なぜか動作しない。どういうキーを受けてどういうマッピングをしたのかをレポートするログ機能が libskk にほしい。

skk is … #2

絵文字の変換を行うために
https://github.com/uasi/skk-emoji-jisyo
の SKK 辞書を使いたい。ダウンロードして、dictionary_list に追記する。

file=$FCITX_CONFIG_DIR/SKK-JISYO.emoji.utf8,mode=readonly,encoding=UTF-8,type=file

が、認識してくれない。なんで…? と思ってソースを見てみたら
https://gitlab.com/fcitx/fcitx-skk/blob/master/src/skk.c#L196
なるほど readonly の場合は $FCITX_CONFIG_DIR の展開をしていない。というか、この関数自体の文字列処理とメモリ管理の書き方がなんとなく危うい感じがする…。

そういうわけで仕方ないのでベタに書いて認識。

file=/home/akahuku/devel/ref/skk/skk-emoji-jisyo/SKK-JISYO.emoji.utf8,mode=readonly,encoding=UTF-8,type=file

その他の環境変数や “~” はどうなんだろうか。このパス名は libskk を経由して GLib の File_for_path() という立派な関数に渡されるのでそこでなんかうまいことしてくれるのかもしれない。試してない。

しかし認識したのはよいものの、入力中に “/” で入る見出し語をアルファベットで編集するモードでこの辞書内の見出しを補完できない。

うーむ。まあこの補完って先頭一致なのでできたとしてもあんまり嬉しくはないのだけど。例えば heart だけでも

black_heart
blue_heart
broken_heart
couple_with_heart
couple_with_heart_man_man
couple_with_heart_woman_man
couple_with_heart_woman_woman
gift_heart
green_heart
heart
heart_decoration
heart_eyes
heart_eyes_cat
heartbeat
heartpulse
hearts
heavy_heart_exclamation
kissing_heart
purple_heart
revolving_hearts
sparkling_heart
two_hearts
yellow_heart

こんな感じで先頭だったり真ん中だったり末尾だったり単語中にあったりごちゃごちゃなので。

skk is …

mecab-skkserv のテスト中、キー押下から反応が返ってくるまでやたら時間がかかる現象に陥ったことが一度あると前の記事に書いた。

調べてみると、この原因はまさに mecab-skkserv だった。発生する条件は、見出し語の編集中に Ctrl+I もしくは Tab を押す、つまり補完を開始するとそういう状態になる。

この補完機能は skkserv のプロトコルで言うと 4 に当たるのだけど、mecab-skkserv.cpp のメインループでそれに対応する応答を行っていないため、fcitx 側(正確には libskk)が応答を延々待ち続けることになるのだろう。知らないコマンドを無視する mecab-skkserv の問題とも言えるし、レスポンスを読む際にタイムアウトを考慮しない呼び出し側の問題とも言える。

--- mecab-skkserv-original.cpp	2020-01-17 17:37:19.897237779 +0900
+++ mecab-skkserv.cpp	2020-01-17 17:38:26.822432515 +0900
@@ -33,20 +33,10 @@
 #include <stdexcept>
 #include <set>
 
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#if defined HAVE_GETOPT_H && defined HAVE_GETOPT_LONG
-#include <getopt.h>
-#else
-#include "getopt.h"
-#endif
-
 #define STDIN (fileno(stdin))
 #define BUFSIZE 8192
 #define DEFAULT_CAND_SIZE 20
@@ -155,6 +145,10 @@
       case '3':
 	std::cout << "localhost:127.0.0.1: " << std::flush;
 	break;
+       
+      case '4':
+	std::cout << '4' << std::endl;
+	break;
       }
     }
 

というわけで修正自体は数行付け足すだけでいいのだけど、このパッチを誰にどう投げればいいのかよく分からない。助けて偉い人。

mozc is … #4

ということで mecab-skkserv をビルドする。

mecab 本体のビルド

まず mecab 本体を落としてきて普通にビルドする。特に記すことはない。このページで提供されている辞書は mecab-skkserv には不要なので作っても作らなくてもいい。
https://taku910.github.io/mecab/

mecab-skkserv のビルド

http://chasen.org/~taku/software/mecab-skkserv/
おそらく幾つかの点でコンパイルエラーになるので

  • mecab-skkserv.cpp で getopt.h を #include している箇所を削除
  • dicrc に cost-factor = 700 とかを追記する(値は700〜800で任意とのこと)

等々を施す必要があるだろう。ちなみにビルドの際に辞書も構築されるが、特に ./configure で指定しなければ euc-jp エンコーディングになる。

systemd ユニットの作成

mecab-skkserv 本体のインストールまで済んだら次は xinetd か tcpserver に登録することになっているが、2020年の現在はそれらではなく systemd の話になる。以下のファイルを新規作成する。

/etc/systemd/system/mecab-skkserv.socket ファイル

[Unit]
Description=mecab-skkserv socket

[Socket]
ListenStream=1178
Accept=yes

[Install]
WantedBy=sockets.target

/etc/systemd/system/mecab-skkserv@.service ファイル

[Unit]
Description=mecab-skkserv service
Requires=mecab-skkserv.socket

[Service]
Type=simple
ExecStart=/usr/local/bin/mecab-skkserv
StandardInput=socket
StandardError=journal

[Install]
WantedBy=multi-user.target

systemd に対して有効にする。この有効化はマシン再起動をまたいでも永続的である。

$ systemctl enable mecab-skkserv.socket
$ systemctl start mecab-skkserv.socket
$ systemctl status mecab-skkserv.socket

この設定だと外のマシンからの接続も受け付けるので、拒否する必要があるなら iptables とかのレベルでうまいことする。

この状態でちゃんと動いてるかどうかは例えば

$ echo "2 " | nc localhost 1178

などとして確認できる。mecab-skkserv のバージョン情報が返ってくればとりあえず接続の受付と起動は正常。

fcitx-skk に対して mecab-skkserv を辞書として登録する

~/.config/fcitx/skk/dictionary_list に以下の行を追加

type=server,host=localhost,port=1178

このファイルでは辞書の指定の順番が重要な気がするが、SKK_JISYO.* の上に置く場合と下に置く場合でどう違うのかは試してない。

また、上記の通り普通に mecab-skkserv をビルドすると euc-jp エンコーディングの辞書が構築される。skkserv プロトコル自体が euc-jp で動くことになっているのでそれで正しいのだが、絵文字など unicode でしか表現できない文字を辞書に含めることができなくなる。その場合辞書を utf-8 で構築し、dictionary_list には

type=server,host=localhost,port=1178,encoding=UTF-8

などと指定すれば良い。

あとは fcitx を再起動すれば普通に使えるようになる。素の SKK だと Watashi<spc>noNamae<spc>haNakano<spc>desu. などと打っていたものが Watashino<spc>Namaeha<spc>Nakanodesu. くらいまでシンプルになる。いやこれはあんまり良い例じゃないな。とにかく送り仮名を気にする必要がほとんどの場合なくなる。

困った点としては

  • skkserv の向う側の辞書は一切変換の学習をしない。学習を skkserv 辞書にフィードバックする仕様自体がプロトコルにない?
  • たまにキー押下からレスポンスが返ってくるまで数十秒かかる状態に陥ることがある。たまにというか今まで1回だけそうなった。原因不明
  • 素の SKK に比べると、変換のレスポンスはほんのわずかに遅れる感じはする。ほんの0.1〜0.2秒ほど。慣れの範囲だろう

とはいえ普通に快適なので、当分 mozc を使うことはないだろう。

mozc is … #3

もう少し SKK で遊んでみよう。SKK といえば辞書をネットワーク越しに持てるのが特徴のひとつである。そのへんを追いかけてみる。skkserv なんて15年くらい前の話題なので2周くらい遅れてるわけだが気にしてはいけない。

さてコンピュータ上で日本語を入力する現代的かつ一般的な方式というのは、文字入力できるフィールドにおける挿入ポイントに対してフロントエンドとなるインターフェースが用意されていて、そのプリエディットバッファ内での入力と文節単位の(漢字)変換結果の選択をおこなったのち、最終的な変換結果全体をフィールドに送出するというものだ。

しかし、この方式が本当に日本語入力の完成された到達点なのか? と言うとそうは思えない。なんか日本語変換してるときって言い様のないストレスを感じません? この原因はプリエディットというものの存在にあると思う。正確に言えばプリエディットという非常にモーダル性の高いインターフェースに役割を持たせすぎて、そのライフタイムを長くさせる設計が良くない。

思うに、プリエディットがアクティブで、そこにいろんなものが溜まってる状態というのは、いわば便秘に苛まれている状態そのものなんだろう。その状態のストレスがどんなものか、説明するまでもない。食べたものも入力したものも体内での役目を終えたら即出力されるのがストレスのない生き方なのは自明の理なのに、プリエディットに頼りすぎるという全く正反対の食生活を押し付けてくる現代の日本語入力インターフェースは健康的にも邪悪というより他はない。

そういう観点で SKK を見てみると、SKK の本質からは若干ずれている気がするが、漢字変換の必要がない文字種についてはプリエディットを経ずに直接入力できるという仕様が、SKK を通して入力する際の妙な気楽さ、ストレスの低さに貢献している要素のひとつであるように思える。これは非常に重要。

一方で、SKK の本質である漢字変換の際に送り仮名の開始位置を明示しなければならない仕様は、それが手書きに通じて良いと肯定する意見もあるが、やはりさすがに使いやすさをスポイルしている要素だと言わざるを得ないと思う。ここがなんとかなるとかなり嬉しい。

そういうことで最初に戻るのだが、SKK の場合辞書の部分が分離されていて後から追加できたり、見出し語から対応する語を返す仕様さえ満たしていれば他はなんでもありだったりするので、それを利用してもう少し楽に変換したい。具体的には mecab-skkserv を入れたい。mecab-skkserv はその名の通り mecab をバックエンドに持つ辞書で、これにより送り仮名を意識することなく変換することができるはずだ。ただし、mecab-skkserv の説明で「疑似的に連文節変換ができる」と言われることがあるが、SKK にも mecab-skkserv にも連文節を編集する機能はないのでそれはあまり正しくはない。素の SKK が漢字列とそれに付随する送り仮名の頭という風変わりな単位で変換するのに比較してより自然に文節単位で変換できるようにはなる。