ニコ動は楽しいコメントが流れてくるのが良いところだが、 不快なコメントも流れてくるのが悪いところである。 楽しもうとして不快になるのは本末転倒だ。 これを回避するためにNG機能というものがあって、 登録したワードやアカウントに該当するコメントを非表示にすることができる。
しかしこのNG機能、クライアントサイドで処理するくせに、 何故か登録数の上限が40件しかないので使っているとすぐ上限に達してしまう。 それから、登録したワードが含まれているコメントは全て非表示になるので、 例えば「←○○○○○」みたいな形のコメント(コメントでバトルする人がよく使う)をNGしようとすると左端以外の場所に「←」がある無実のコメントも消えてしまう。
そこでコメントを正規表現でNGするスクリプトを書いた。
正規表現なので|
で区切ればいくらでも追加できるし、
さっきの例はパターン^←.+
を使えば冤罪がすこし減る。
雑に作ったので使い勝手に少々難があるのだが、 スクリプトの解説も書いておくので適当に改良して使ってもらえると嬉しい。
説明
ニコニコ動画のコメントを正規表現でNGするスクリプト · GitHub
↑コードをGistに載せた。Tampermonkeyとかに登録すれば使える。 Google Chromeでしか動作確認をしていないが、変な機能は使ってないのでFirefoxでも動くと思う。
動画再生ページでEscキーを押すとNGパターン設定のプロンプトが開く。 この設定はLocal Storageに保存されるので永続する。
なお、このスクリプトが非表示にするのは動画に流れるコメントだけで、 動画の右側に出るコメントリストは非表示にしない。
そういえば、スクリプトではなく素のNGに引っ掛かって「字幕が視聴者の総意」というコメントが消えてしまっているが、これは楽しいコメントであって冤罪だな。まあ正規表現であっても文脈を読むことはできない……
解説
ニコ動のコメントは次の手順で描画される。
document.createElement
でCanvasを生成する。- 生成されたCanvasにコメントをひとつ描画する。
drawImage
でコメント表示用のCanvas (.CommentRenderer > canvas
) にコピーする。
これを踏まえて以下のようなスクリプトを実行することで正規表現によるNGを実現した。
まずdocument.createElement
を書きかえて、
Canvasが生成された場合にfillText
を監視するようにする。
改造されたfillText
はCanvasと文字列の対応をWeakMapに保存する。
// createElementで生成されたcanvasのfillTextを監視 const canvas_to_text = new WeakMap(); const createElement = document.createElement; document.createElement = function(tagName, ...args) { const elem = createElement.call(this, tagName, ...args); if (tagName === 'canvas') { const ctx = elem.getContext('2d'); const fillText = ctx.fillText; ctx.fillText = function(text, ...args) { canvas_to_text.set(elem, text); return fillText.call(this, text, ...args); }; } return elem; };
つぎにコメント表示用のCanvasのdrawImage
を書きかえる。
改造されたdrawImage
は、第一引数がCanvasであるとき、
対応する文字列をWeakMapから取り出す。
これがNGのパターンとマッチする場合に書き込みをキャンセルすることで、
正規表現によるNG機能が実現する。
// コメント表示用canvasのdrawImageを監視 let image_to_is_safe = new WeakMap(); const commentCanvas = document.querySelector('.CommentRenderer > canvas'); const commentCtx = commentCanvas.getContext('2d'); const drawImage = commentCtx.drawImage; commentCtx.drawImage = function(image, ...args) { let is_safe = image_to_is_safe.get(image); if (is_safe === undefined) { const text = canvas_to_text.get(image); if (text === undefined) { is_safe = true; } else { const m = text.match(regexp); if (m) { console.log('NG', m[0]); is_safe = false; } else { is_safe = true; } } image_to_is_safe.set(image, is_safe); } if (is_safe) { drawImage.call(this, image, ...args); } };
確認済みのCanvasをimage_to_is_safe
というWeakMapに格納することで同じ文字列に何度も同じ正規表現のマッチが行われることを避けている
(パフォーマンスにどれくらい影響するのかよくわからないが)。
NGパターンが変更された際にはimage_to_is_safe
がリセットされるようになっていて、新しいNGパターンが即座に反映される。
ところで
実はニコ動のLocal Storageには公式NG機能の設定がJSONで保存されている。
これを適当に編集してページをリロードすると……驚くべきことに上限を突破してNG設定を登録できてしまう。しかもちゃんと動く。
わざわざCanvasをいじるコードを書くよりLocal Storageの書きかえを支援するコードを書いた方が良かったかもしれない。 正直なところ正規表現が使えてもそんなに嬉しくないからなぁ。
一応デメリットもある。
- 設定の反映にリロードが要るので、NG設定の追加が即座に反映されない。
- NG設定が上限を超えているとき、NGの追加ボタンや削除ボタンが機能しなくなる。
- 上限を超えたNG設定はサーバーに反映されない。