ぐぐたすに投稿された画像をまとめて保存するスクリプトを書いた

Google+でユーザが投稿した画像を全部一括で保存するコードを書いた。

Node.jsがインストールされていることが前提。手順は以下のようになる。

1. Gistからコードを取得

git clone https://gist.github.com/87ff2dd22687600efac4.git
mv 87ff2dd22687600efac4/ iggts/
cd iggts/
npm install 

2. Google Developer ConsoleGoogle+ APIを有効にしてAPI Keyを取得

3. iggts.coffeeを編集してAPI Keyを設定

4. 画像を取得
たとえば、松村香織 - Google+に投稿された画像をダウンロードする場合

mkdir -p images/108705263081706477178
./node_modules/coffee-script/bin/coffee iggts.coffee 108705263081706477178 ./images/108705263081706477178

これでしばらく待てば、ディレクトリには画像がたくさんできているはず。

OpenCVのサンプルを静的にAndroid Studioでビルドしたときのメモ

OpenCV for Android SDKに入っているsamples/face-detectionをStatic InitializationでAndroid Studioを使ってビルドするまでのメモ。
使ったものは、

プロジェクトを新規作成

Android Studioで空のプロジェクトを作成。
/app/src/main/に/samples/face-detection/*をコピー。
ただし、java/以下には既存のディレクトリを残して、src/からファイルだけをコピーする。
今回はパッケージ名をcom.sample.appとするので、java/com/sample/app/にsrc/org/opencv/samples/facedetect/*をコピー。
コピーした.javaファイルはそれぞれ

    package com.example.app;

と変更しておく。

また、AndroidManifest.xmlも該当する箇所を

    package="com.tanitter.face_detection.app"

と変更しておく。

(でもこんなことせず単にインポートしたらいいと思う。)

SDKを配置

/app/src/main/にjniLibsという名前のディレクトリを作る。
ここにはAPKに含める.soを入れる。
まず、/sdk/native/libs/*をコピーして入れておく。

/librariesを作って、その下に/sdk/java/をopencv/とリネームしてコピー。
/libraries/opencv/build.gradleを作って、中身を次のようにする。

apply plugin: 'android-library'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.9.+'
    }
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
        versionCode 2480
        versionName "2.4.8"
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            res.srcDirs = ['res']
            aidl.srcDirs = ['src']
        }
    }
}
<||

/settings.gradleを
>||
include ':app', ':libraries:opencv'

に変更。
/app/build.gradleに

dependencies {
    ...
    compile project(':libraries:opencv')
}

を追加。

また、/app/に/sdkをコピー。
こちらは、JNIをビルドするときのため。

JNIの設定

/app/src/main/jni/Android.mkを編集して

OPENCV_LIB_TYPE:=STATIC

を追加。

ただしデフォルトでは、Android Studio(gradle)はここで設定したAndroid.mkを見ないでJNIをビルドしてしまうので、build.gradleを編集して

android {
    ...
    sourceSets.main.jni.srcDirs = []
}

のようにして、gradleではJNIがビルドされないようにする。

JNIの関数名を編集

(パッケージ名をサンプルと同じにしていれば、この作業は必要ない。)
/app/src/main/jni/DectationBasedTracker_jni.(cpp|h)において、"org_opencv_samples_facedetect"を"com_example_app"で置換する。

初期化方法を変更

サンプルコードは、そのままだとOpenCV Managerを利用しようとするので、FdActivity.javaを編集して

    static {
        OpenCVLoader.initDebug();
    }

をFdActivityクラスに追加する。
また、onResume()を

    @Override
    public void onResume()
    {
        super.onResume();
        //OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
        mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
    }

のようにする。

ビルド

まずは、JNIを手動でビルドする。
そのために、/app/src/main/に cd して /ndk-build を実行する。
出来上がったlibs/*はjniLibs/にコピーする。

そして、/gradlew build すれば、動作するAPKができるはず。

単語帳を履歴から自動で作成して、語彙の学習ができるサービスを作った

Flashward

英語など外国語の学習において、単語の意味を辞書で調べて、それを記憶する。
この繰り返しのプロセスを、なるべく効率的にしようというのが、このサービスである。

このサービスは、単語帳を作成するChrome拡張機能と、それを復習するWebサイトから成る。
インストールされた拡張機能は、ユーザが辞書サイトで単語を調べるたびに、その単語を単語帳に自動的に追加する。
つまり、ユーザは自分自身で単語帳を作る必要がなく、その面倒な作業はこのサービスに任せることができる。

また、単語の復習には間隔反復と呼ばれるアプローチを採っている。
この方法は、単語を繰り返し間隔を開けながら表示することで、多くの単語の記憶を効果的にするものである。
このサービスでは、ユーザの入力から各々の忘却曲線を推定して、最適な繰り返しの間隔を導出するアルゴリズムを用いている(SM-15)。

自動で単語帳を作ってくれて、復習にちょうどいいタイミングで単語を表示してくれるサービス、それがFlashwardである。

ブラウザのショートカットキーを拡張するChrome拡張機能を作った

Chrome ウェブストア - Moly Keys

この拡張機能を使うと、画面のスクロールやフォームの選択などの操作をキーボードに割り当てることができる。
ブラウザのキーバインディングを追加する似たような拡張機能はストアにすでに存在するのになぜ?と思われるかもしれない。
他の似たような拡張機能には、次のような問題があった。

  • Webページによってはスクロールできないことがある。
  • フォームにフォーカスが当たっているとキーバインドが効かない。
  • 重い。

などなど。
これら快適さを損なう問題を解消して、手に馴染むキー操作を実現すべくこの拡張機能を作った。

この拡張機能は基本的な操作を提供するが、それだけではキーボードのみでブラウジングを行うために十分ではない。
そのため、キー操作でハイパーリンクをクリックするMoly HaHや、タブの選択を快適にするMoly Tab Menuを併せて利用するとよい。

ここからは、先に挙げた問題点について、もうすこし詳しく述べていく。

スクロール問題

たとえばGmailInoReaderなどのWebサイトにおいて、他の多くの拡張機能ではスクロールが効かない。
これらのサイトでは、スクロールすべき対象がページ全体ではなくその内部のHTML要素であるのがその理由である。
他の拡張機能ではページ全体のみをスクロールするのに対して、Moly KeysはまずフォーカスのあるHTML要素に対してスクロールを試みる。
そのため、より多くのWebサイトや組込みPDFビューアなどにおいてキーバインドによるスクロールを実現できる。

フォーカス問題

他の多くの拡張機能では、たとえばinputにフォーカスが当たっていて編集可能な状態のとき、入力されたキーを横取りしてキーバインド操作を発火することはない。
これはCtrlやAltなどの修飾キーを伴わないキーアサイメントに対しては望ましい動作であるが、たとえばCtrl-Iなど文字入力にならないようなキーバインドではショートカットとして機能してほしいと思われる。
そのため、Moly Keysでは修飾キーの有無によって、編集可能要素にフォーカスがある場合に作動するかどうかを判断する。
また、修飾キー付きの各キーバインドに対して、発火しない例外となる要素の種類をオプションのRaw Settingsから設定することもできる。

Twitterで友達の友達をまとめてリストに入れるWebサービスを作った

Friends2 Timeline

このWebサービスでは、自分がフォーローしてるユーザがフォローしてるユーザをメンバーとしたリストを作ることができる。自分がフォローしてるユーザを辿って、新しいユーザを見つけられないかな?と思って作ってみた。

Twitterアカウントでサインインすると、フォローしてるユーザの一覧が表示されるので、まずユーザを選択する。そしてリスト名を入力して"Update"ボタンを押す。すると、選択されたユーザのフォローしてるユーザがリストに追加される。処理の完了までには時間がかかる場合があるので、ローディングのアイコンが出ている間はページを閉じてはいけない。また、追加できるユーザ数は、ひとつのリストにつき5000までという制限があるので、それを超えないようにする。

ちなみにこのサービスでは、先日書いたCORSプロキシサーバのCorsètを利用した。

ブラウザからTwitter APIを呼ぶためのCORSプロキシサーバを書いた

GitHub - slaypni/Corset: Twitter CORS proxy server

Twitter APICORSに対応しておらず、Access-Control-Allow-OriginなどのヘッダがHTTPレスポンスに付与されない。そのため、ブラウザはXHRを使って直接Twitter APIを叩くことができない。この問題を解決するひとつの方法が、Twitter APIを中継してクロスドメインを許可するレスポンスを返すサーバを用意することである。この記事で紹介するCorsètは、この中継を行うサーバ側プログラムである。

このコードはGithubからcloneして、すぐに利用できる。以下ではCorsètの特徴と使いかたを紹介する。

Heroku対応

CorsètはHerokuにデプロイして使うことができる。Herokuに関する設定は次のようになる。

heroku create
heroku addons:add redistogo:nano
heroku config:set URL=http://your-server-name.herokuapp.com

セッション管理で用いるRedisを有効にするのと、認証のコールバックで必要となる、自身のURLを環境変数に設定する。

OAuth認証

CorsètからAPIを呼ぶためには、まずCorsètを通じてTwitterの認証を行う。そのために必要な情報はsrc/config.coffeeに記述する。

module.exports =
    sessionSecret: 'Random string'
    oauthConsumerKey: 'Twitter OAuth Consumer key'
    oauthConsumerSecret: 'Twitter OAuth Consumer secret'
    firebaseSecret: 'Firebase Secret'
    originUrl: 'http://example.com'
    callbackUrl: 'http://example.com'

oauthConsumerKeyとoauthConsumerSecretにTwitterのConsumer keyとConsumer Secretを入力する。
callbackUrlには、認証の成功時のリダイレクト先を入力する。
またoriginUrlにAccess-Control-Allow-Originヘッダの値も設定しておく。この値は'*'ではだめで、呼び出し元のURLを入力する。そうしなければクレデンシャルの送信ができない(セッションを持てない)ためである。

src/config.coffeeを書き換えたらcake buildとしてコンパイルしてから、Herokuにpushする。これで認証を行うことができる。URLは"/auth"。

API Call

Corsètは、Consumer KeyやAccess Tokenなどをすべてサーバ側で持ち、ブラウザとはセッションを張ることでAPIの呼び出しを代行する。そのためブラウザはAPIを呼ぶとき、XHRでwithCredentials=trueとしてリクエストを送る必要がある。呼び出しURLは"https://api.twitter.com"を"http://your-server-name.herokuapp.com/api"と置き換えるだけでよい。

Firebase Auth Token

オプション機能。src/config.coffeeのfirebaseSecretを設定することで、FirebaseのCustom Loginで利用するTokenを取得できるようになる。URLは"/auth/firebase"。

HTMLのウェブページの内容をJSONで取得するWeb APIを作った

html2api

アプリなんかを作ってたりすると、ある普通のウェブページに書かれてる情報をJSONみたいな形式で取得したいと思うことがよくある。
しかしながら、そういうWeb APIを提供しているサービスはWebサイト全体のごく一部で、ほとんどのページはHTMLを返すだけ。
そこで、HTMLのウェブページをあたかもJSONを返すWeb APIのように利用するためのサービスを作った。

このWebサービスでは、"対象のサイトのURL"と"結果に含めるデータを指定するテンプレート"とを渡すと、そのページをJSONにして返してくれる。
"結果に含めるデータを指定するテンプレート"は、JSONCSSセレクタを合わせたような記法で書くことができる。