0

MIDIデバイスを演奏したらパスワードが入力されるChrome拡張を作った

最近のChromeはWeb MIDI APIといってMIDIキーボード等の楽器を接続し、その入力が取れる。

Chrome拡張でもそれは使える。
inputタグにフォーカスしている時に楽器からの入力をキャプチャして、パスワードを生成&入力するChrome拡張を作ってみた。

https://github.com/shokai/webmidi-password-generator

パスワードは覚えるのが難しいけど曲のフレーズとかなら覚えやすいだろ、多分・・という気持ちを形にした。

といっても見た目に派手な動きがあるわけじゃなくてUSBコネクタにMIDIデバイス刺して演奏したらいきなりinputタグにパスワードが入力されてる状態になるので、blogで説明は難しい。

こんな感じでMIDIメッセージを受信してパスワードっぽい文字列が生成される。2回演奏して、ちゃんと同じパスワードが出てる。

これの問題はあらかじめパスワードを記憶させておいて取り出せるわけじゃないので、28c813172158みたいな到底覚えられない文字列になってしまう事で、作りながらうすうす気づいてたけどまあ自分でも使う気になれない。ビミョーすぎる。
パスワード覚えなくていいソリューションを作ってもパスワード覚えること前提のサービスやデバイスが世の中にたくさんあるから利便性の面でどうしても無理がある。使うなら完全移行しないとならないのが面倒くさい。

MIDIのメッセージは標準化されているものだから、別のMIDIキーボードと別のPCで同じフレーズを演奏したらちゃんと同じパスワードが入力できる、という点は良いとは思う。


久しぶりにChrome拡張を作ったら簡単だった

なんだか以前作った時より簡単になった気がする。
あまりしっかり差を把握していないんだけど、以前はもっとゴチャゴチャ色々なファイルを作らなければならなかったような。

Chrome拡張が簡単になったというより、browserifyでnpmのライブラリやnode中でpure jsで書かれているライブラリをそのままChrome拡張に持っていけるのですっきりしたのかも。

MIDIデバイスを開いて入力読んで現在ブラウザで開いているHTMLを少し書きなおすぐらいなら、
簡単なmanifest.jsonを書いて、その中で指定したjavascript1つに全部やらせればいい。

ようするにgreasemonkeyでやってたような事はmanifest.jsonとmain.js(と必要であればアイコン画像)さえ配置すればさくっとできる。browserifyのおかげでグローバル汚染せずにjQuery等を持ってくる事もできる。

こんな感じで書いてる。
npmでインストールしたjqueryと、Web MIDI APIからパスワード作って”password”イベントを発火してくれるライブラリをbrowserifyで1ファイルに固める。

main.es6
"use strict";
import PasswordGenerator from "./password_generator";
import $ from "jquery";

var target = null;
$("input")
.on("focus", function(e){
target = e.target;
})
.on("focusout", function(e){
target = null;
});

var passgen = new PasswordGenerator(8);
passgen.on("password", function(pass){
console.log(pass.raw);
console.log(`password => ${pass.string}`);
if(target){
target.value = pass.string;
}
});

Web MIDI APIはインタフェースにPromise、iterator、TypedArrayなどのES6の機能が使われている。ChromeでもES6が動くのでES6でいきなり書けばいいかなと思ったけど、Arrow Function等いくつかのES6の機能が動いたり動かなかったりして混乱したのでbrowserify+babelifyでES5に変換した。


Chrome拡張のインストール

拡張は昔は1ファイルに固めないと動かなかった気がするけど、最近は[ウィンドウ]→[拡張機能]→[パッケージ化されていない拡張機能を読み込む]でディレクトリを指定して読み込んでくれるので手軽。

0

rbenvやめてhomebrewでRubyインストールする事にした

rbenvでRubyインストールするのって色々オプションつけたり下準備が面倒だけど、brew install rubyで最新版入るし、1.8〜2.0の頃と違って2.0以上で動くコードは2.2でも問題ない感じがするからRuby処理系のバージョン切り替え要らないじゃん、homebrewで最新版インストールすればいいじゃんという気持ち

nginxアップデートしたらopensslも一緒に上がってRubyがぶっ壊れた

Yosemiteにアップデートしたので
MacをYosemiteにアップデートした

homebrewで入れてたnginxもアップデートした。

% brew tap marcqualie/nginx
% brew install nginx-full --with-mp4 --with-mp4-h264-module

最近のはデフォルトでwebsocket proxyが有効になっていて便利!とか思っていたら、homebrewのopensslもアップデートされて、rbenvでインストールしてたRubyからSSLが使えなくなった。Ruby全滅した。

そこでrbenv+ruby-buildでRuby 2.2.2をインストールしたのだが、SSLを使う処理を動かすと「digest/sha1.bundle, 9): Symbol not found: _rb_Digest_SHA1_Finish (LoadError)」というエラーで死んでしまう。

というかrbenvでRubyインストールしてopensslやらreadlineやらが動かなくて色々コンパイルしなおすの、OS入れ替える毎にやってていい加減アホくさいのでやめたい。
ここ4年ぐらい一度もストレートに成功したこと無い。


homebrewでインストールする


よく見たらhomebrewにrubyっていうパッケージがあって、ちゃんと2.2.2がインストールできる。
node.jsもhomebrewで最新版入れて使ってるし、特別Ruby 1.8.7で動かさないとならないアプリとかが無い限り別に言語処理系のバージョン管理いらないんじゃないのという気になって

% brew uninstall --force rbenv ruby-build
% brew install ruby
した。ruby 2.2.2とgem 2.4.5が入った。


jrubyも入れた。
% brew install Caskroom/cask/java
% brew install jruby

全く問題なく使えてる。


LaunchAgentからのRuby起動

PATHが消えてるので自分でEnvironmentVariablesを設定するのと最初のbundleを/usr/local/bin/bundleとフルパス指定さえすればhomebrewで入れたruby 2.2.2が使えた。

    <dict>
<key>RACK_ENV</key>
<string>production</string>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/bundle</string>
<string>exec</string>
<string>rackup</string>
<string>config.ru</string>
<string>-p</string>
<string>5000</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>sho</string>
<key>WorkingDirectory</key>
<string>/Users/sho/my/app/path</string>
<key>StandardOutPath</key>
<string>/var/log/myapp/myapp.log</string>
<key>StandardErrorPath</key>
<string>/var/log/myapp/myapp.log</string>
<key>UserName</key>
<string>sho</string>

0

chokidar-cliのファイル更新監視が速い

1年ぐらいgulpを使おうとがんばったけど理解できなかったので、先月からこちらを参考にpackage.jsonのnpm run scriptにshell script書いてタスク実行する方式をやっている

参考:Grunt/Gulpで憔悴したおっさんの話 – MOL

あらためて最近使ってるコマンドの周辺ツールを調べると、ファイル更新検知して差分だけタスク実行してくれる機能があるのが多い。


あとは上の記事にも書いてあるんだけどparallelshellで複数のwatchしてくれる系コマンドを同時に実行できるので

例えばこんな感じでpackage.jsonのscriptsを書いて、npm run watchで全部watchしてビルドしたりlintしたりしている。
{
"name": "es6-study",
"private": true,
"version": "1.0.0",
"scripts": {
"watch": "parallelshell 'npm run build -- --watch' 'esw src/*.es6 server/*.es6 --watch'",
"build": "babel src/ --out-dir dist/ --source-maps inline",
"test": "eslint src/*.es6 server/*.es6",
"server": "babel-node server/app.es6",
"start": "npm run build && npm run server"
},


で、よく見たらbabel/eslint-watch/watchifyでみんなchokidarというnpmを使っていた。
chokidarはNode組み込みのfs.watchとMac用のfseventsを適切に使い分けてくれるクロスプラットフォームなファイル監視ライブラリで、これを使えばファイルの更新を監視してタスク実行する系のツールは簡単に作れそう。

さらにchokidar-cli npmというのがあってこれをグローバルインストールすると使えるchokidarコマンドがファイル監視してからの指定コマンド実行をやってくれる。

例えばこんな感じで実行するとwatchしてテストを実行できる
% chokidar "**/*.es6" -c 'npm run test'

watchというnpmもファイル更新監視してコマンド実行してくれるコマンドが付いているんだけど、こっちはpollingなので1秒ぐらいラグがあってつらい。chokidarだと一瞬で更新検知して実行されててすごい。

0

Enterキー押す毎にJXAでiTerm2の背景を変える

指定ディレクトリの下の写真をランダムにiTermの背景設定するコマンドができた。
https://gist.github.com/shokai/872e375c0b0636b0c2ae


Enterキーを押す毎に画像を切替えるようにzshを設定してみたらすごい疲れる感じになった。これでアニメgifが再生できたら最高だったんだけどただの静止画になってしまう。

写真.appの中身をランダムに表示している。写真.appの顔認識機能が作ったサムネイルも読み込まれるせいで、たまたま撮った写真に写っている知らない人の顔ドアップサムネイル画像もたまに表示されて誰コイツ感がある。
定期的にtumblrとかから画像を取ってきても楽しそう。


ふつうにプログラムを書いたりしているところ


JXA

AppleScriptの代わりにJavaScriptで書けるというJavaScript for Automationを使ってみた。Yosemiteから使える。公式名称ではないがなぜかJXAと呼ばれているらしい。

JavaScript for Automation Release Notes

ES6も微妙にサポートされていて、分割代入とspread operatorが動いた。
var [a,b] = ["foo","bar"]; // a="foo" b="bar"とそれぞれ代入される
[1,2,3,...[4,5,6]]; // [1,2,3,4,5,6]になる
Promiseオブジェクトはあるけど「Event loop not supported.」というエラーが出て使えない。
setTimeoutとsetIntervalは無い。ブロックするdelay(秒)がある


iTerm2

iTerm2 – Mac OS Terminal Replacement

デフォルトのTerminal.appと違ってMacのウニョーンエフェクトなしでフルスクリーンモードにできるので気に入ってる。あと背景を半透明にできるので裏のドキュメントを透かしながら見れるのが便利。

AppleScriptからの操作のドキュメントがある。
https://iterm2.com/applescript.html


AppleScriptでiTermの背景を変える

引数に渡した画像ファイルが背景画像になる。
iterm-bg-image.applescript
#!/usr/bin/osascript
on run argv
set img to item 1 of argv

tell application "iTerm"
tell the current terminal
tell the current session
set background image path to img
end tell
end tell
end tell
end run


JavaScript for Automation (JXA) でiTermの背景を変える

上のAppleScriptをJXAで書き直すとこうなる。
tell applicationのかわりにApplicationという関数があったりする。AppleScriptの文法は8割方理解してないんだけど、とりあえずキャメルケースで接続していけば良いっぽい。

iterm-bg-image.js
#!/usr/bin/osascript -l JavaScript

function run(argv){
var img = argv[0];
var app = Application("iTerm");
app.currentTerminal.currentSession.backgroundImagePath = img || null;
}
backgroundImagePathにnullを入れると背景をクリアできる。JSだとこういうのも書きやすい。

ディレクトリ内からランダムに背景指定する


指定パスがディレクトリかの判定はNSFileManagerを使った。ディレクトリの下のファイルの列挙はdoShellScriptでlsやfindを呼んだ。

-rオプションを付けるとサブディレクトリの下の画像も探すようにした。
iterm-bg-image.js
#!/usr/bin/osascript -l JavaScript

var fm = $.NSFileManager.defaultManager;

function run(argv){
var [path, opts] = parseArgv(argv);
var img = getImagePath(path, opts.r);
console.log(img);
setImage(img);
}

function parseArgv(argv){
var path = null;
var opts = {};
argv.forEach(function(arg){
var match = arg.match(/^\-+(.+)$/);
if(match){
opts[match[1]] = true;
}
else{
path = arg;
}
});
return [path, opts];
}

function getImagePath(path, recursive){
if(!path) return;
var ref = Ref();
var exists = fm.fileExistsAtPathIsDirectory(path, ref);
var isDirecotry = ref[0];
if(!exists) return;
if(!isDirecotry) return path;
var files = getImagesInDirectory(path, recursive);
return files[Math.floor(Math.random()*files.length)];
}

function getImagesInDirectory(path, recursive){
path = path.replace(/\/$/, "");
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var files = [];
if(recursive){
files = app.doShellScript("find '"+path+"'").split(/\r/);
}
else{
files = app.doShellScript("ls '"+path+"'").split(/\r/)
.map(function(f){
return path+"/"+f;
});
}
return files.filter(function(f){
return /\.(jpe?g|gif|png|bmp)$/i.test(f);
});
}

function setImage(img){
var app = Application("iTerm");
app.currentTerminal.currentSession.backgroundImagePath = img || null;
}


Enterキーを押す毎に背景を変える


参考:zshでコマンドが空の状態でenter押したときに任意のコマンドを実行する方法 – kei_q


写真.appに入っている写真を適当に表示する

.zshrc
function iterm-bg-image-random(){
zle accept-line
if [[ -z "$BUFFER" ]]; then
$(iterm-bg-image ~/Pictures/写真\ Library.photoslibrary -r > /dev/null 2>&1 &)
fi
}
zle -N iterm-bg-image-random
bindkey '^m' iterm-bg-image-random

0

MacをYosemiteにアップデートした

ようやくYosemiteにアップデートした。

homebrewをよけておく

/usr/local があるとアップデートに6時間ぐらいかかるらしいので、どこか別の場所によけておいてOSアップデートが終わったら元に戻す

% mv /usr/local ~/usr_local

そのまま戻して、今のところコンパイルしなおしたりしないでも動いてる。


Yosemiteインストール

App Store.appからアップデートした。
ダウンロード終わってからはわりとすぐだったと思う。


ファンが爆音で回り続け、SSD容量がファイル消しても消しても減り続ける

アップデート後、起動した瞬間からファンが回りまくって凄く重い。SSDの容量も10GB空いてたのにいきなり「残り1GBです」という警告がでておどろく。ファイル削除してもまたみるみる減って1GBになる。
Yosemiteにアップデートしなきゃよかったと思った


iPhotoのライブラリを消す

iPhotoから写真.appに移行したら、写真が入ってるライブラリが~/Picturesに2種類できて容量2倍になってたのでiPhotoのを削除した。


ウィンドウの半透明処理をoff

なんかウィンドウがところどころビミョーに透けていて読みにくい。
システム環境設定→アクセシビリティ→ディスプレイ→透明度を下げる にチェック入れる。
文字も読みやすくなる。

セーフブート

OS起動時にshift押しっぱなし
色々生成された余計な物が消えたっぽい

ここまででファンは静かになった。


safe sleepを普通のsleepに

スリープ時にRAMの内容をSSDに全部保存しにいって、/private/var/vm/にゴミがどんどん溜まるやつ
Macのスリープモード

これは以前設定した値はそのまま引き継がれていたので問題なかった


Spotlight

システム環境設定→Spotlight でbingへの送信を止める

Spotlightの検索候補を全部外すと軽くなるとか書いてある記事が色々見つかるけどその必要は無かった。



LaunchAgentからhomebrewのコマンドが呼べない

ログにnodeが見つからないというエラーがたくさん出ていた

env: node: No such file or directory


/usr/local/binにPATHが通っていないらしい。とりあえずシンボリックリンクで解決

% ln -s /usr/local/bin/node /usr/bin/node

ちゃんとLaunchAgentにPATHを追加する方法もあるみたいだけど、PATHを追加するshellscriptを書いて専用のplistから呼び出すとかなんかわけわからないのでやってない
Yosemiteアップデートでlaunchdが死亡した – retlet.net


sayに男の声が増えた

Macの最大の目玉機能のsay
% say -v Otoya こんにちは
事前にシステム環境設定→音声入力と読み上げ で追加する


AppleScriptのかわりにJavaScriptで書ける


あとでやる
JavaScript for Automation Release Notes