tagging an image


画像を保存する際のファイル名のテンプレートに[cci]$TEXT[/cci]というものを新設した。これはキタ━━━━━━(゚∀゚)━━━━━━ !!!!!を除く最初のコメントに展開される。

レス画像を貼れる板の場合、レス画像を貼ってありかつコメントはキタ━━━━━━(゚∀゚)━━━━━━ !!!!!であるレスの引用は、そのレス番号を用いるのだそうだ。というわけでレス番号をクリックした際はそういうふうに振る舞うようにした。

webm/mp4を選択した際、それらもプレビュー表示の対象になるようにした。

ところでファイル名にコメントが含まれることのメリットというのは検索性の多少の向上というものがあるのだと思うけど、完全ではない。上の画像で言うとラーメンならラーメン、つけ麺ならつけ麺、豚野郎と犬子ならそういった文言がファイル名に含まれていれば完璧だが、そうなっているのもあれば、なっていないのもある。

画像を渡すとそれをAIが解析して、適切なタグ群を返すwebサービスみたいなのがあればいいのに。

Attach an image from clipboard

ついで、クリップボードに画像が格納されている時、コメント欄で Ctrl+V を押すとそれを直接添付ファイルにする機能を実装してみよう。

基本的にはコメント欄の paste イベントで clipboardData.files を見てその中から画像っぽいものを取り出し、それを覚えておき、投稿時に upfile 要素の内容の代わりに使用する、だけ。

のだが、いくつかめんどくさい点がある。

  • 貼り付けた画像のプレビューを生成しなければならない。これ自体は普通に upfile 要素で選択された画像をプレビューする処理と同じで何か新しくコードを書くわけではない。ただし、基本的な流れは画像を示す file インスタンスの内容を img 要素に割り当て、読み込み完了後に canvas 要素に縮小描画するというもので、特に file→img を従来は data スキーム経由で行っていた。しかしこれはけっこう重い。そんなわけでそこを createObjectURL/revokeObjectURL で行うようにした。今までそうしなかったというのはつまり Presto Opera でも動かすためだ。でももう Presto Opera のことは忘れる
  • 貼り付けられた画像のサイズが、板に貼れる上限を超えている場合。クリップボードに格納されている画像は、おそらく確実にブラウザへは image/png 形式で渡ってくる。png なのでサイズ的には常に大きめだ。それが板の上限を超えていた場合は jpeg でエンコードし直す必要がある。

    その場合は file を canvas に描画した後 toDataURL(‘image/jpeg’) で jpeg エンコードされた data スキームの URL を得て、それを XHR で読み込んで arraybuffer として取得し、そこから blob を生成…となる。途中で生成した canvas はそのままプレビュー生成にも使う。この辺、なんか回りくどい。canvas に blob を返すエンコードメソッドを設けてくれれば、それを FileReader から好きに読み込めばいいので汎用的だと思うのだけど…。

    あと、真面目に作るなら、jpeg エンコードしてもなお上限を超えている場合を考慮して、段階的に jpeg のクオリティを下げていくようなループにするべきなのだが、そこまではしなかった。

    ちなみに板に貼れる上限というのは注意書きの中で例えば「2000KBまで」とか書いてあるのをパースして覚えておくのですが。2000KB というのは 2000*1024=2048000byte のことでいいんだろうか。それとも 2000*1000=2000000byte か? 試してみた所 2000000byte 超のファイルは受け入れられるようなので前者とみなすようにした

だいたいこんな感じ。

Saving an image via Akahukuplus

虹裏にはたまにいわゆる虹裏ブラウザを話題とするスレが立つのだが、赤福プラスが俎上に載ることはめったにない。そしてまれに載った際はたいてい、使いにくくて機能の少ないクソと烙印を押されるのがオチなのであった。不満の一つに画像の保存をローカルに対して行えないという点があるようなので、それに対応してみよう。

もちろん Chrome のエクステンションからローカルのファイルシステムを直接は操作できないのだが、拙作のLFO – ローカルファイル操作ライブラリを通すと不思議なことにファイルの読み書きは自由にできちゃうので、これを使う。すでに wasavi と kokoni が LFO を使用している。これに赤福プラスも加わることになる。

LFO 側で適宜設定を行ったあと、赤福プラスの設定でファイル保存名テンプレートを[cci]ピクチャ/ふたば/$SERVER/$BOARD/$YEAR-$MONTH/$DAY-$SERIAL.$EXT[/cci]などと設定し、使用するストレージを[cci]local[/cci]にする。で、保存リンクを押せばそのとおりの場所に保存される。これだけ。あとは画像を開いた際に自動的に保存、みたいなオプションもあればいいかな。

技術的には、任意のパスに保存するには画像本体の保存に先駆けていわゆる [cci]mkdir -p[/cci] 的な動作が必要になる。現バージョンの LFO はこの機能を持っていないのでまずそこから実装する必要があった。LFO というか、Chrome Apps の FileSystem API 自体にそんな素敵な機能はない。従って、パス中の個々のディレクトリごとに一つずつ掘っていくという繰り返し処理を書くことになる。これを FileSystem API を直接呼びながら実現するとコードがものすごく汚くなりそうだったので、Promise を使うようにした。ついでなので、他の LFO のコマンドもすべて Promise ベースで動作するように書き直した。

ちなみにこの機能は Chrome 専用だ(Chrome Apps が Firefox や Opera で動くならその限りではないが)。

Prevent from executing an inline script

以前にも書いた気がするが、赤福プラスはふたばが返す html をまるごと変換し、上書きする。従って、元の html に記述されている画像や、スクリプトや、インラインフレームの読み込みはまったく不要で、ブロックする必要がある。ブロックした上で、変換後の html から改めて読み込まなければならない。

そんなわけで、Chrome では WebRequest API を用いてそれを実現していたのだが、ただ一つ html に直接記述された script 要素、つまりインラインスクリプトの実行は見逃していた。まあこれを見逃しても src 属性付きのスクリプトの読み込みはブロックしているので、大抵のインラインスクリプトはなんちゃらが定義されていませんエラーになって実害はないのだが、気にはなる。

しかし、Chrome のエクステンションの API を眺めてみてもインラインスクリプトの実行をブロックする機能は見つからない。いやあることはある。例えば WebRequest でレスポンスヘッダに CSP を忍ばせて、インラインスクリプトを除外するとか、あるいは ContentSettings でふたば上のみの javascript の実行を禁止するとか。が、リファレンスを読んでみるといずれも html を読み込んでから DOMContentLoaded までの短い期間だけスクリプトの実行を抑制するという要件にはちょっと合ってない。

いろいろ調べてみたところ、content script で MutationObserver を用いて script 要素が DOM ツリーに追加された瞬間をキャッチし、type を text/javascript とか application/javascript から、スクリプトとして実行されないものに書き換えるとそういう動作を実現できるようだ。で、用が済んだら、DOMContentLoaded ハンドラで disconnect() すればいい。完璧だ。

ただし Firefox ではこの type 書き換え法が効かないので、その代わり script 要素の beforescriptexecute イベントをリスンして適宜 preventDefault() する。つまり実行タイミングを start_at にした content script から

var observer = new MutationObserver(ms => {
. function handleBeforeScriptExecute (e) {
. . e.target.removeEventListener(
. . . 'beforescriptexecute', handleBeforeScriptExecute, false);
. . e.preventDefault();
. };
. ms.forEach(m => {
. . m.addedNodes.forEach(node => {
. . . if (node.nodeType != 1 || node.nodeName != 'SCRIPT') return;
. . . node.type = 'text/plain';
. . . node.addEventListener(
. . . . 'beforescriptexecute', handleBeforeScriptExecute, false);
. . });
. });
});
observer.observe(document.documentElement, {
. childList: true,
. subtree: true
});

とこんな感じのコードを走らせる。

ちなみに Presto Opera だとこんな長いコードを書かなくても、

window.opera.addEventListener('BeforeScript', function(e){e.source=''}, false);

だけで実現できる。オーパーツすぎる…。

そういえば忘れていたけど、次のリリースから赤福プラスは Presto Opera をもうサポートしない。

Ubuntu broke

Ubuntu Server を入れている機械も 18.04.1 にあげようと do-release-upgrade を走らせたところ、見事に失敗して何やら dpkg の依存関係がめちゃくちゃになってしまった。あばばばば。

せっかくなので、新規インストールした。もともと 32bit 版だったのでいつかは入れ直さないといけなかった。従ってちょうどいい機会と言えばそうだ。

ネットワーク周りが netplan というもので管理するようになってたり、[cci]/etc/apt/sources.list[/cci] に universe が入ってなかったりが若干のつまづきポイントだったが大体滞りなく移行。

From crying 16yo to grieving 18yo

Xubuntu を 16.04 から 18.04.1 に上げたので諸々をメモ。

  • ターミナルで bdf/pcf フォントを使えなくなった。これはアップグレードの際はいつものことで、[cci]/etc/fonts/conf.d/[/cci] から no-bitmap.conf 的なものを削除し、yes-bitmap.conf だけを残す(なければ [cci]../conf.avail/[/cci] からシンボリックリンクを貼る)
  • ログインできない。正確にはパスワードをキーボードから打ち込んでも正しいパスワードと認識されない。スクリーンキーボードを使用すると通る。まだ原因は調べてない
  • ログイン直後、マウスの右ボタンを連続して押している状態になっているらしく、右クリックからのメニューがまともに使用できない。数分で勝手に治る。まだ原因は調べてない
  • xfce のパネルを縦置きしている。従来はテキストが縦方向に描写されていたのが横方向になってしまう。これは単に見た目の問題なので気にしないことにする。多分きっとおそらくそのうち修正される
  • xkb の設定がリセットされた。うちの機械では過去の記事の通りに [cci]/usr/share/X11/xkb/[/cci] 以下のファイルへ直接必要な設定を埋め込んでいるという若干行儀の悪いことをしているので当然だ。ホームディレクトリに設定を置くようにしてもいいのだが、そうするとたまにやらかしてリカバリーモードで起動せざるを得なかったときにぎょえーとなる。そういうわけで再び [cci]/usr/share/X11/xkb/[/cci] 以下のファイルを弄る
  • cifs のマウントの際 smb のプロトコル ver3 がデフォルトになったそうなのだが、そうすると Windows10 機との接続に失敗するようなので fstab で vers=1 を追記。対 Win10 なのになんで ver1 じゃないとだめなの? 暇があったらあとで調べる

それから、これはもしかしたら 16.04 の頃からだったのかもしれないのだが、Thinkpad USB Keyboard with trackpoint …のトラックポイントの加速度の設定が、どうも標準の設定画面からはできてない気がする。この辺を参考に設定。


$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint id=10 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Power Button id=7 [slave keyboard (3)]
↳ Integrated Camera: Integrated C id=8 [slave keyboard (3)]
↳ Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint id=9 [slave keyboard (3)]
↳ C-Media Electronics Inc. USB PnP Sound Device id=11 [slave keyboard (3)]
↳ Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint id=12 [slave keyboard (3)]

製品名は “Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint” とのことだ。

$ xinput list-props "pointer:Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"
Device 'Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint':
Device Enabled (140): 1
Coordinate Transformation Matrix (142): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
libinput Natural Scrolling Enabled (276): 0
libinput Natural Scrolling Enabled Default (277): 0
libinput Scroll Methods Available (278): 0, 0, 1
libinput Scroll Method Enabled (279): 0, 0, 1
libinput Scroll Method Enabled Default (280): 0, 0, 1
libinput Button Scrolling Button (281): 2
libinput Button Scrolling Button Default (282): 2
libinput Middle Emulation Enabled (283): 0
libinput Middle Emulation Enabled Default (284): 0
libinput Accel Speed (285): 0.500000
libinput Accel Speed Default (286): 0.000000
libinput Accel Profiles Available (287): 1, 1
libinput Accel Profile Enabled (288): 1, 0
libinput Accel Profile Enabled Default (289): 1, 0
libinput Left Handed Enabled (290): 0
libinput Left Handed Enabled Default (291): 0
libinput Send Events Modes Available (261): 1, 0
libinput Send Events Mode Enabled (262): 0, 0
libinput Send Events Mode Enabled Default (263): 0, 0
Device Node (264): "/dev/input/event3"
Device Product ID (265): 6127, 24585
libinput Drag Lock Buttons (292):
libinput Horizontal Scroll Enabled (293): 1

このデバイスに設定できるプロパティ群。個別の詳細は [cci]man xinput[/cci] で知るべし。で、[cci]/usr/share/X11/xorg.conf.d/[/cci] あたりに適当にファイルを作って中身を

Section "InputClass"
Identifier "Trackpoint tweaks"
MatchProduct "Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint"
MatchDevicePath "/dev/input/event*"

# The default driver now, so optional to put in this line.
# But does not hurt mentioning where the options will go.
Driver "libinput"

Option "Accel Speed" "0.5"
EndSection

こんな感じにする。

Installing Digital TV into Linux

ふだん家では Linux マシンがメインで、サブの Windows マシンはたまにテレビを見るときにつけるという感じだったのだが。ここに来てそれに接続していた15年選手のディスプレイがついに壊れてしまった。なんということでしょう。ブラタモリと鉄腕 DASH が見れなくなってしまうじゃないか。

Windows マシンへは Chrome Remote Desktop を通してリモートで操作する選択肢もあり、10fps くらいになるが一応メインマシンからテレビを見ることはできる…のだが、これが恐ろしく負荷が高い。大体30分もすると熱のために勝手に電源が落とされてしまう。

さてどうするか。ディスプレイが壊れたのだからディスプレイを新調すればいいのだが、実際のところ Windows マシンはほとんど使っていないのであった。ほとんど使ってないマシンに素敵なナウいディスプレイくっつけて何が楽しいのか。何も楽しくない。

それよりも、メインマシンで直接テレビが見られれば遥かに楽なのだが。でも Linux で地デジなんてできるの…? と思いつつググってみたら、なんと今では録画サーバーなるものを Linux マシンで組むことはごくごくあったりまえの行為らしい。そ、そうなんだ…。すごい。

ということでこのあたりを参考にチューナーとカードリーダーを入手してみたところ、実に簡単に VLC でテレビを視聴するところまでいけた。すごい。

このままでも視聴自体はできるのだが、チャンネルを変えたりするのが若干面倒なのでここから Chinachu の諸々を入れるといいようだ。これはまあ、そのうちにやりたい(やらないフラグ)。

CPU 負荷はだいたい10%くらい。すごい。ところでサブチャンネルを選択したい場合とかはどうするのかな。

Pikoyan

再放送中のけものフレンズには字幕が付いているのだが:

  • サーバルがジャパリバスの運転席を持ってジャンプする最後のシーン、「ぴこやん!」としか聞こえない。たぶん「とりゃー!」と言ってるのだと思うのだがぴこやんとしか聞こえない。自信がないので書き起こしでは地の文でごまかしてある。どうしよう
  • タイトルで○○ちほーとよくあるが、字幕ではカタカナのチホーである。これはアプリ版でもそうなので、そうなのかなと迷ったのだが、とりあえず書き起こしではひらがなで統一してある。しかしカタカナが正しいとなると、じゃあ「へいげんちほー」はセリフとしては(ライオンが 1 度セリフとして発音している)へいげんチホー? 平原チホー? ヘイゲンチホー? 判断する材料がない。どうしよう
  • 再放送のソースは最初に放送されたそれに比べて若干の修正が施してある。これは intro や outro のテロップも含まれる。甚だしいところでは、声優名が全然違うものになってたりする。これはどれを底とすべきだろうか
  • 再放送とはあまり関係ないが、サウンドトラックによって各 BGM の題名が明らかになったので、書き起こしにおいて BGM が奏でられるタイミングでその題名を追加したい。が、困ったことにサウンドトラックに収録されているのはサウンドトラック用に編集されたものであって、放送されたものに含まれるものとは若干違うのであった。また、常に BGM の最初から奏でられるわけではなく特徴的な最後の締めが多用されたりするパターンもある。どうしよう

Integration #2

そういうわけで、設定のうち例外リストとなっていたものを site overrides とした。こんな感じで記述する:

http://example.org/* * block
http://example.net/*.html * writeas=p

ディレクティブとして有効な行は、3つの要素からなる。左から順に URL パターン、CSS セレクタ、アクション。まず URL パターンと CSS セレクタで要素を特定する。例外リストの段階では CSS セレクタは省略可能で、省略した場合ユニバーサルセレクタを指定したことになっていたが、省略不可能になった。サイトすべての編集可能要素を対象にしたい場合はユニバーサルセレクタを明示的に記述する必要がある。

最後の要素がアクションで、これが [cci]block[/cci] であった場合は、そのパターンに合致する要素上では wasavi の起動が抑制される。それ以外であった場合、set コマンドの引数として wasavi 起動時に評価される。つまり、exrc の最後に付加される。

 * * *

これとは直接は関係ないのだけど、バックグラウンド側での設定の持ち方を変えた。従来は各ブラウザの差異を吸収するような抽象的なクラスを経由していたが、単に chrome の API を直接呼ぶようにした。

Integration

wasavi のオプションに、writeas というものがあり、こういう仕様になっている。つまり、contentEditable な要素に対して wasavi のバッファを書き戻す際、どのような DOM の構造にするかを指定する。たとえば [cci]p[/cci] なら、各行を [cci]p[/cci] 要素として書き戻す。

しかし、当然ながら contentEditable な要素の内部構造をどのように保持しているかはサイトごとにまちまちであり、ひとつに決められるものではない。そこで、writeas には 連想配列を表す json 文字列を代入することも許している。こんな感じで:

set writeas='{ \
"http://example.com/*": "div", \
"http://example.net/*": [ \
{ \
"selector": "#any-css-selector", \
"writeas": "textAndBreak" \
} \
] \
}'

連想配列のキーが URL パターンで、値が writeas という形式だ。または、さらに連想配列の配列を入れ子にして、CSS セレクタを指定することもできる。これにより、つまりサイトごとに writeas の定義を分けることができる。

ただ……見てのとおり exrc 中に改行のエスケープしまくりで書かないといけないので、とても面倒くさい。また [cci]set all[/cci] した時の見栄えもとてもよくない。もっと洗練された形式で保持する必要がある。

ところで、サイトごとに定義を振り分けているものは wasavi はもうひとつ持っている。サイト自身が提供するスクリプトとコンフリクトするか何かでうまく動かない場合に、wasavi の起動をしないという例外リストである。これは普通のプレインテキストで

http://example.com/
http://exmaple.org/ #some-id

のように行ごとに URL を書き連ねる。URL のあとに任意の個数の空白を挟んで CSS セレクタを記述することもできる。これと writeas の定義を統合できないだろうか。つまり例外リストではなく per-site overrides という扱いにしたい。

override := URL-pattern (CSS-selector)? action
action := allow-actions
"block"
allow-actions := allow-action ("," allow-action)*
allow-action := "writeas" "=" writeas-value
writeas-value: "div" | "p" | "textAndBreak" | "plaintext" | "html"

という感じだ。