akahukuReplyUSBのテレビチューナーを付けているのでXubuntu上で地上波テレビを見られるわけなのだが、困ったことに割と不安定なのである。VLCでストリームを開くと、ノイズまみれになったり、何も映らなかったり、Xごと固まったりする、ときがある。再起動直後はそうなる可能性が高く、数日起動しっぱなしの後はうまく映る可能性の方が高くなるというのも妙にアナログ的で何が悪いのか目星を付けにくい。Xごと固まると復旧するのも大変だ。どんな入力も受け付けなくなる。lightdmとかするととりあえず入力を受け付ける所までは戻る。これでおかしなテレビが直ったりするわけではない。ワンセグを見る分には特に問題はなさそうなので、つまるところB-CASカードからのデータ読み込みと復号を担当するlibarib25.soに問題があるのかもしれない。すると、これを解決する手段としてはアレしかないわけで、あちこちからソースをかき集め、アレをビルドしてアレと差し替えたところ、安定するようになった。いやー...すごいなアレは。akahukuReply次にアプリケーションサーバとなるここに関して。SDKと呼ばれるライブラリを利用できる。サポートされるのはNode、Java、Python、C#、Goであり、phpはない。そんなわけで非公式のphp向けライブラリがあるのでそれを使う。してphp版を公式に出さないんですか...どうして...。それはそれとして、php向けライブラリはドキュメントも充実しているのでありがたい。アプリケーションサーバでやることは、クライアントから渡されたトークン、番組のシリーズIDをデータベースに保存し、番組表の取り込み時に該当するシリーズの最新話を見つけたらPush通知を行う...といった感じである。やること自体は割と簡単。このシリーズIDとは何かというと。番組表は番組枠が列挙してある。番組枠とは時間で区切られた区間のことだ。この枠に番組が収まっている。この仕様から見れば、番組枠の中に複数の番組を入れることも不可能ではないので、データ上も番組枠(slot)にぶら下がっている番組(programs)は配列になっている。シリーズIDはこのprogramsの各要素に生えているseriesオブジェクトのidプロパティであり、定義がないので断言はできないが、これが配信される作品全体に対して振られたユニークなIDのようである。同様にprogramsの各要素に生えているepisodeオブジェクトのsequenceプロパティが該当シリーズにおける放送順を示している、ようである。したがって、データベースにシリーズIDとその最新シーケンスナンバーのセットを保持しておき、番組表を取り込んだ際に照合して追跡中のシリーズでありかつ新しいシーケンスナンバーを持っている番組枠があれば、それが最新話ということになる(実際には、さらにmarkフラグなども見る)。ところがここでひとつ問題がある。上記のシリーズIDの正規形は\d+-\d+らしいのだが、ときおりこのフォーマットから外れたものがある。シリーズIDが175-1rthzhecdmeといったものになったり、シーケンスナンバーが20とか80とか突拍子もないものになったりする。これはどうも「仮のシリーズID」的なもので、未来の番組表に初めて現れた最新話とかがこの状態になったりするようだ。実際の配信日になると本来のシリーズIDが割り振られる。この仕様がどういう意味を持っているのか分からないが、とにかく不完全なシリーズIDは最新話を検知するための足掛かりにならない。そこで、シリーズIDを元にした検知と平行して、タイトルによるヒューリスティックな検知も行うことにする。これは、例えば検知を開始した番組のタイトルを覚えておいて、それと似た感じなら同一シリーズと見なすものだ。似た感じというのはリーベンシュタイン距離とかそんな感じのアレである。ところでここで使用する番組のタイトルというのは、正確には番組枠のタイトルである。様々な枕詞が付いたりする。これはヒューリスティックな検知にはノイズなので困る。seriesオブジェクトに作品自体のタイトルを示すプロパティが格納されていると嬉しいのだけど。いうわけでAbema.tvの番組表データはいろいろと不思議な点が多い。SDKを使用する構成にする。この組み合わせは、一応Firebaseのドキュメントでも公式に動作が保証されている。いうわけか知らないが拡張機能内でFirebaseを使用する際のサンプルやガイダンスはドキュメントに一切ない。そのため備忘録として気付いたことをメモしておく。ちなみに、上記の保証というのはSDKがchrome-extension:スキームを認識するということである。これは今のところSDKはFirefox上のWebExtensionsでは動作しないことを意味する。したがって、あべ☆アニの最新話の追跡機能もまた、Firefoxでは動作しない。拡張にSDKを組み込む。src="https://www.gstatic.com/firebasejs/8.2.1/firebase-app.js"></script>てな感じの要素をバックグラウンドページに仕込んでおくことである。'self'"といったCSPの記述も必要。この拡張をストアにアップする際にリモートコードを使用している旨の告知とその理由が必要になる。SDKを使う分には明示的にそれをする必要はない。firebase-messaging-sw.jsという名前のファイルをサイトのルート(つまり拡張のアーカイブのルート)に置き、必要な処理をそこに書いとけばいい。ワーカーの登録はFirebaseが勝手にやる。登録済みのregistrationを元にFirebaseアプリケーションオブジェクトを構築することもできるが、Firebase側でよしなにしてくれることをわざわざやることもないだろう。aFunctionDefinedSomewhereElse);とかはしておく。Push通知を受けるためのトークンをfirebase.messaging().getToken()によって取得することができる。これによって得られる文字列は永続的なものであり、一度取得したら永続的なストレージに入れて使い回していい。この手のトークンにはたいていrefreshイベントをリスンして期限切れに備えましょう的なノウハウがあったりするが、必要ない。onTokenRefreshイベントは用意されているがすでにdeprecateである。このトークンは通知の宛先となるものなので、通知を出す側であるアプリケーションサーバへ送り付ける。これが漏れると勝手に通知を出されるので厳重に暗号化した上で適切に送信しなければならない...はずなのだが、ドキュメントでは単に「トークンを取得したら、それをアプリサーバーに送信して、適切な方法で保管します。」としか記述されていない。送信のためのベストプラクティスとかはないのである。postすればいいらしい。あべ☆アニextensionではblowfishで暗号化した上でここのサーバに送信している。拡張のソース見ればすぐバレちゃうけど。SDKのコードをimportScript()して、必要な情報を元にFirebaseアプリケーションオブジェクトを生成し、onBackgroundMessageイベントをリスンする...とドキュメントには書いてあり、それが正道なんだろうが、実は単に通知を受けるだけならワーカー自身のpushイベントをリスンするだけで事足りる。FirebaseはPush通知以外にも様々な機能が統合されているので、通知を受けるとともにそれらの機能を利用する場合はドキュメント通りのやりかたが正しいのだろう。あべ☆アニextensionでは通知を単に受けるだけで良いので、直接pushイベントを見ている。importScript()云々もしていない。サービスワーカーで通知を受けたらデータペイロードを拡張のバックグラウンドページへ送信する。ここで困るのはバックグラウンドページがイベントページ、つまり用が済んだら勝手にアンロードされるモードの場合だ。この場合、サービスワーカーのClients#matchAll()ではバックグラウンドページを取得できないし、起こすこともできない。実はサービスワーカーのグローバルオブジェクトにchrome.runtime.getBackgroundClient()というものが生えており、それを呼ぶと必要ならバックグラウンドページを起こした上でそれに対応するClientオブジェクトで解決されるPromiseを返すようになっている。このメソッドはChromeの拡張APIドキュメントのどこにも載ってない、この記事だけのかなりのオトク情報である。