KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

Java

とある日本の楽曲目録(JASRAC)

umjammer です、 お久しぶりです、すっかり寒い季節になってしまっています。 私は音楽を聞くのが趣味の一つです。iPod と iTunes を使っています。自分の持っているすべての音楽を一元管理できるってのは世界観が変わるよと言われてやってみました。たしかに世界観は変わったのですが、デジタル潔癖症 [1] を罹患している私は思わぬところでハマってしまいました。楽曲の情報をすべて埋めてやらないと気が済まないんです。 楽曲のタイトルとアルバムはたいてい iTunes が勝手にデータベースから取ってきてくれるので問題ありません。まずハマったのはすべてのアルバムのジャケットをそろえることです。600x600 pixel のジャケットを揃えるのにものすごい労力を費やしたものです。
ジャケ写取得参考ソフト iGCover なければアマゾンのユーザーがアップロードしたジャケ写 それでもなければ discogs.com
iTunes Artworks


ジャケットをすべて揃えたあとリストを眺めてると、入ってるのと入ってないものがある項目として作曲者が目についてしまいました。うまっていない項目をうめないとデジタル潔癖症の私の心は癒えません。 ググッてみたのですがジャケットみたいにフリーなソフトは見つかりません。 自作するしかありません。 アーティスト、曲名から作曲者を検索するプログラム さて作曲者情報はどこから取ってきましょうか? 実はいいデータベースがあるんですよ。それも国内に。そう JASRAC楽曲データベースです。ネットでは何かと言われています JASRAC ですが、さすが音楽の世界を牛耳っているだけあって持っているデータベースはメジャーどころでは洋邦問わずほとんど網羅されています。そしてそのデータベースはネットで公開されています。
http://www2.jasrac.or.jp/eJwid/
技術者としては文句をいう暇があるのならその代わりにとことん利用させていただきます。 普通の Web Form インターフェースで API などなさそうなのでスクレイピングすることにします。iTunes から作曲者情報の無い楽曲のアーティストとタイトルを取ってきてそれを引数にしてJASRAC データベースを検索します。 JASRAC のページは最初のページのボタンを押さないとフォーム検索してくれないので Web コントロールを行うソフトとして htmlunit を使います。 iTunes データベースは XML ファイルとして存在するので XPath で読み込もうとしたのですが、容量がでかいため(2万曲近くあるので) JDK 付属の DOM で扱うとメモリが溢れてしまうので Saxon を使います。 流れは以下のようになります。
iTunes Music Library.xml -> xpath (artist, title) -> htmlunit -> JASRAC DB -> xpath (composer) -> TSV file
JASRAC のデータベース内の人名は変な規則で正規化されてるので普通に読みやすくしてやる必要があります。あと alias も同時に登録されているのでダブリを除かないといけないのですが面倒なのでそのままです。 今までの流れを記述した実際のコードは以下になります。
iTunes.java


実行結果の TSV は以下の様になります。
JASRAC Database Scraping
見つかった場合は先頭が RESULT、見つからなかった場合は NONE、もしかしての候補がある場合は MAYBE になっています。 さすがに JASRAC データベースも 100% 網羅しているわけではないので、見つからなかった場合は、
discogs.com allmusic.com
等のサイトを利用してください。 allmusic.com はとても面倒なのでスクレイピングしたいところですが力尽きました...orz iTunes に作曲者を登録するプログラム 先のプログラムは作曲者情報を TSV ファイルに落とすだけですので、実際に iTunes に登録する必要があります。Mac では Apple Script で iTunes をコントロールすることができますので、こんなプログラムを書いてやります。

$ cat composer.scpt
on run argv
  tell application "iTunes"
    set results to (every file track of library playlist 1 whose artist equals item 1 of argv and name equals item 2 of argv)
    -- display dialog item 1 of argv & ", " & item 2 of argv & ", " & count of results
    repeat with t in results
      try
        tell t to set composer to item 3 of argv
      end try
    end repeat
  end tell
end run

TSV ファイルから先頭が RESULT なものだけを抽出して上のプログラムに食わせます。

$ cat tsv2sh
#!/bin/bash

OIFS=$IFS

IFS=' '

exec < "composers.tsv"
while read LINE
do
  if [[ "$LINE" =~ ^RESULT* ]]; then
    # 注意! -F"<tab>"
    echo $LINE | awk -F"    " '{ printf "osascript composer.scpt \"%s\" \"%s\" \"%s\"\n", $2, $3, $4 }'
  fi
done

IFS=$OIFS

$ sh tsv2sh > tmp.sh ; sh -v tmp.sh
:

作曲者の項目のほとんど (17914/17979=99.6%) を埋めることができました!ぱちぱち〜
iTunes Composers


あとがき 最近ネット配信の音源が増えてきました。海外からの直ダウンロード等ですと JASRAC が 管理していないので作曲者がわからない曲がでてきました。困ったものです。TAG はちゃんと埋めてくださいね。 スクレイピングの際に利用サイトに負荷をかけないようにご注意願います。また岡崎事件みたいなことになる可能性もあるということを十分ご理解いただいた上で実行してください。 そういえば歌詞の項目がすべてうまっていない気がするのですが、さらに無駄な時間を浪費しそうなので気づかなかったことにしています(笑)

[1] リアルの部屋は汚いくせにPCの中のデータやフォルダはきれいに整理されてなければ許されない性格

Twitterで140文字の制限を超えてみせる???

umjammer です、 先日 Twitter にて 140 文字以上投稿されるという事件が起きました。もうそのバグは塞がれたみたいですが、人間制限を設けられるとやはり超えてみたいと思うものです。じゃあ超えてみせましょう(笑) 140文字以上tweetできるTwitter利用サイト、その名も「Too言ったー」。
toowitter

http://umjammer09.appspot.com/ まず Twitter のアカウントは取得しておいてください。初めて使用する場合には「signin」のリンクをクリックしてください。OAuth を用いた例の認証が行われるので、許可してあげてください。 signin 後、サイトのテキストボックスに長文を入れて「Too言っとく」ボタンを押せば140文字の制限を超えて投稿できます。投稿後しばらくすると「成功しました」のダイアログが出ます。「Preview」ボタンは投稿する前にイメージを把握することができます。投稿後は中程の「View Your Post」リンクをクリックすれば今投稿したものを見ることができます。 Twitter ユーザなら何をしているか一目瞭然ですね。 改行とかエラー処理を真面目に行っていないので何か問題があってもご容赦ください。 まとめ 冗談みたいなサービスですが、結構新しめの技術を使っています。 HTML5 の canvas を使用しています。canvas にテキストエリアに入力した文字をレンダリングして toDataURL メソッドで画像にして GAE に送ってそこから API を使って投稿しています。ブラウザによっては動かない可能性があります。 JavaScript は苦手なので GWT と gwt-g2d を使用しました。本家の canvas を使用せずに gwt-g2d を使用したのは本家には文字を描くメソッドがなかった(ハズ)からです。GWT は楽ですね。もう少し言うなら canvas 周りのグラフィックス API を早く統一して欲しいです、出来れば Java 2D 準拠で。Google さん主導なんだから最低でも android API 準拠にしてくれればと思います。 ソースはこちら参考サイト 補足 私も Twitter ユーザですし、140 文字だからこそ Twitter の文化があるということは十分理解しています。あくまで技術検証を目的とした洒落のサービスだということをご理解願います。

とあるOAuthのtwitterクライアント

umjammer です、 今月末で Twitter API は BASIC 認証を廃止して OAuth オンリーに移行するみたいですね。以前ソーシャルアプリ関連で OAuth を調べていたときに Twitter も試していたのでその時の成果物を公開します。ずいぶん前のことなので忘れていました。危うく旬を逃すところでした。 では、順を追って OAuth を使用した Twitter デスクトップクライアントの作り方を説明します。 Twitter クライアント用のコンシューマーキー、シークレットの取得
  1. この URL で Twitter クライアントアプリ申請を行う
  2. 今回はデスクトップアプリなのでブラウザではない方のチェックボックスをチェックしておく
    twi1


  3. 設定を保存すると画面が変わり、コンシューマーキー、シークレットを取得できる
  4. 同時にアクセストークンリクエストや認可画面URLも取得
    twi2


アクセストークンリクエストや認可画面URLは後述の TwitterOAuthProvider.java で使用しています。 Twitter クライアント用のアクセストークン、シークレットの取得 さきほど取得したコンシューマーキー、シークレットを使用してアクセストークンとシークレットを取得します。Twitter の OAuth のアクセストークンとシークレットは期限が設けられていないみたいですので一回取得すれば今のところずっと使用可能です。 流れとしては、
  1. コンシューマーキー、シークレットを引数にして認可画面をリクエスト
  2. 認可画面のURLが帰ってくる
    tw3


  3. URLの画面をブラウザ上で表示し認可を行なう
  4. ブラウザ画面上にベリファイアが表示される
    tw4


  5. ベリファイアを引数にアクセストークン、シークレットをリクエスト
  6. アクセストークン、シークレットを取得
な感じになります。コードにすると以下になります。

        String consumerKey = "xxxxxxxxxxxxxxxxxxxx";
        String consumerSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
        
        // コンシューマーを生成
        OAuthUtil oauth = new OAuthUtil(new OAuthConsumer("oob", consumerKey, consumerSecret, new TwitterOAuthProvider()));
        // リクエストトークンを取得
        oauth.doGettingRequestToken();

        // 認可のURLを取得
        System.out.println("以下のURLをブラウザで開いてください。");
        System.out.println(oauth.getAuthorizationURL());

        // アクセストークンを取得
        System.out.print("ベリファイアを入力: ");
        oauth.doGettingAccessToken(oauth.readLineFromStdin());

        // 
        System.out.println("accessToken: " + oauth.getAccessToken());
        System.out.println("tokenSecret: " + oauth.getAccessTokenSecret());

Twitter クライアントの作成 アクセストークン、シークレットを取得したところでようやくクライアント作成にかかれます。今回は便利なライブラリ twitter4j を使用しました。今まで取得したキーを引数に API を呼んでやるとサクっと投稿アプリが作れます。あとは GUI 頑張るだけです。


        String consumerKey = "xxxxxxxxxxxxxxxxxxxxx";
        String consumerSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";

        String accessTokenString = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        String tokenSecretString = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";

        TwitterFactory factory = new TwitterFactory();
        Twitter twitter = factory.getInstance();
        twitter.setOAuthConsumer(consumerKey, consumerSecret);
        AccessToken accessToken = new AccessToken(accessTokenString, tokenSecretString);
        twitter.setOAuthAccessToken(accessToken);

        Status status = twitter.updateStatus(statusString);
        System.out.println("Successfully updated the status to [" + status.getText() + "].");

結果です、クライアント名が今回作成したアプリケーション名になっているでしょう?
tw5


今回使用したその他のコードとライブラリ OAuthUtil.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import net.oauth.OAuth; import net.oauth.OAuthAccessor; import net.oauth.OAuthConsumer; import net.oauth.OAuthException; import net.oauth.OAuthMessage; import net.oauth.client.OAuthClient; import net.oauth.client.httpclient4.HttpClient4; /** * OAuthUtil. */ public class OAuthUtil { /** */ protected OAuthConsumer consumer; /** */ protected OAuthClient client; /** */ public OAuthUtil(OAuthConsumer consumer) { this.consumer = consumer; this.client = new OAuthClient(new HttpClient4()); } /** * リクエストトークンを取得する。 */ public void doGettingRequestToken() throws IOException, OAuthException, URISyntaxException { doGettingRequestToken(null); } /** * @before {@link #doGettingRequestToken(Map)} */ private OAuthAccessor requestAccessor; /** * リクエストトークンを取得する。 * * http://.../oauth/request_token */ public void doGettingRequestToken(Map
TwitterOAuthProvider.java
import net.oauth.OAuthServiceProvider; /** * TwitterOAuthProvider. */ public class TwitterOAuthProvider extends OAuthServiceProvider { public TwitterOAuthProvider() { super("http://twitter.com/oauth/request_token", "http://twitter.com/oauth/authorize", "http://twitter.com/oauth/access_token"); } }
その他のライブラリ
  • net.oauth.core/oauth-20090617
  • net.oauth.core/oauth-consumer-20090617
  • net.oauth.core/oauth-httpclient4-20090617
  • net.homeip.yusuke/twitter4j-core-2.1.*
まとめ OAuth や Twitter API にはライブラリが揃っていますので簡単に OAuth アプリが作れました。 ただ OAuth を使用したデスクトップクライアントソフトにはここに書かれているような問題があります。悪用すればクライアントなりすましも可能でしょう。 以前紹介した OpenFeint API も OAuth を使用しています。iPhone や iPad のアプリは .ipa ファイルとして iTunes ライブラリに保存されていますが、単なる .zip ファイルなので解凍して中を覗くことができます。皆さん暗号化とかされてるみたいですのでまだシークレットキーを発見したことはないのですが、リバースエンジニアリングされればもしかすると発見できるかもしれません。そうなるとハイスコア改ざんやトロフィーコンプリートなども簡単にできちゃいます。 私もいろいろ考えたのですがいい回避策が思いつきません。皆さんなにか良い知恵があればお教えください。あとは OAuth WRAP に期待ですか。 訂正 2010-06-18 一枚目の画像が説明と異なっていたので差し替えました。

オレオレ Kindle Development Kit (KDK) で NES エミュレータ

umjammer です、 新しいプラットフォームが出たら、まずしなければいけない一番大事なことを忘れていました...orz そうです、 NES エミュレータを移植することです。androidBD-J も先にやられてしまったのでこんどこそ一番乗りになってやります。せっかく オレオレ KDK 作ったんだしね。


オリジナルは NESCafe。 ソースコード http://code.google.com/p/umjammer/source/browse/trunk/vavi-apps-nes-kdk

ソーシャルアプリの作り方 第三回 - OpenFeint

umjammer です、 ソーシャルアプリの作り方の第三回になります。 ソーシャルアプリとして Mixi アプリを作成しましたが、単なるゲームを Mixi に持ってきただけではソーシャルな要素が全くなく、ソーシャルなプラットフォームを使う意味がありません。 このシリーズでは前回前々回にすでに出てきているようにハイスコア管理をソーシャル要素として使用します。アプリの内容がゲームですからソーシャルグラフ上でスコアを競ってもらいます。またできるだけ簡単に作ることを方針としていますので、一から API を実装するつもりはありません。Mixi はゲームに特化した API は公開していませんので、すでに世の中にあるソーシャルプラットフォームを探し出してきて使用します。 今回はゲームに特化したソーシャルプラットフォームとして今 iPhone で盛り上がりつつある OpenFeint を使用することにします。SPACE INVADERS INFINITY GENE も採用してますよ。
OpenFeint Logo


ここからがこのシリーズの一番の目玉になります! OpenFeint は iPhone 用に SDK をオープンソースで提供しています。今のところ Objective-C で書かれた iPhone 用しか存在しません。盛り上がっているソーシャルプラットフォームなのでぜひ使用したいものです。幸い SDK はオープンソースなのでプロトコルを解析してやれば他のプラットフォームでも動かすことが出来そうです。

OpenFeint プロトコル解析

iPhone 用の SDK にコンパイル時オプションとして -DDEBUG を設定してやります。あとソースを追っかけていくと最終的に NSURLConnection クラスを使用しているので通信メソッドに渡すパラメータなどを NSLog で出力させるようにします。そして iPhone 用に OpenFeint を使用したサンプルゲームを作成して、実際操作しながら機能に対する通信ログを取っていきます。OpenFeint SDK は UI も含めた膨大な機能を持っていますので、今回は OpenFeint に対するログインと、ユーザー操作、ハイスコア、アチーブメント機能あたりに絞って解析しました。 解析して分かったことは、OpenFeint API は REST を用いて API 認可には HMAC-SHA1 シグネチャの OAuth を使用しています。そしてデータとして XML を返します。JSON じゃないんですね。

REST & OAuth

OAuth はライブラリが公開されていますのでそれを使用します。簡単に使用するために軽くラップします。 http://code.google.com/p/umjammer/source/browse/trunk/vavi-apps-gae03/src/main/java/vavi/util/openfeint/OpenfeintOAuthUtil.java GAE/J 上ではホワイトリストの関係上 HTTPClient4 が使用できなかったのでその辺りを自作します。通信は OpenFeint API が https を使用しているため、頻繁にタイムアウトが起こります。GAE/J の API の URLFetch にはソケットのタイムアウト指定ができないため URLConnection を使用します。 http://code.google.com/p/umjammer/source/browse/trunk/vavi-apps-gae03/src/main/java/vavi/util/openfeint/GaeHttpClient.java

XML Binding

戻り値である XML の定義 XSD を OpenFeint は用意してくれていませんので、さきほど解析した実データの XML インスタンスを素に XSD を作ります。Castor というソフトウェアにその機能が備わっていますので使用します。ただしパッチが必要なのですが。 XML インスタンスから XSD を作成する ant のマクロ定義例

  <macrodef name="xml2xsd"
            description="Generate XML Schema from XML Instance.">
    <attribute name="in" />
    <attribute name="out" />
    <sequential>
      <java classname="org.exolab.castor.xml.schema.util.XMLInstance2Schema"
            fork="true">
        <classpath>
          <pathelement location="${dir.build}" />
          <path refid="run.class.path" />
          <fileset dir="${ant.home}/lib">
            <include name="*.jar" />
          </fileset>
        </classpath>
        <arg value="@{in}"/>
        <arg value="@{out}"/>
      </java>
    </sequential>
  </macrodef>
XSD ができれば JAXB を使用して Java XML バインディングを行います。これで OpenFeint API の XML を気にすることなくコーディング出来ます。 JAXB で XML Binding を行う ant のマクロ定義例

  <macrodef name="xsd2jaxb" description="works fine">
    <attribute name="package" />
    <attribute name="url" />
    <attribute name="dir" />
    <sequential>
      <delete>
    	<fileset dir="@{dir}">
    	  <include name="*.java" />
    	</fileset>
      </delete>
      <exec executable="${xjc.bin}">
        <arg line="-p @{package}"/>
        <arg line="-d @{dir}"/>
        <arg line="@{url}"/>
      </exec>
    </sequential>
  </macrodef>
OAuth API をラップしたコードと自動生成された XML Binding コードを使用して OpenFeint API for GAE/J を作成していきます。


完成したところで、外部 API であるハイスコアリストを呼び出してみます。
OpenFeint Ranking on GAE/J


まとめ

ハイスコア管理を外部 API として GAE 越えで OpenFeint API にアクセスするソーシャルアプリがこれで完成しました。通信が二段階で行われるため実用スピードにするにはもう少し工夫する必要がありますね。 OpenFeint API は GAE/J に移植できたわけですから Android にも簡単に移植できますね。これで iPhone, Android, Mixi 共通でハイスコア管理できたりできるようになりますよ。 もう一つ、XML のインスタンスから XSD を作成する方法は、●天 WEB SERVICE をはじめ国内の XML を返すウェブサービスがほとんど XSD なり DTD を公開していないためよく活用させてもらっています。

シリーズのまとめ

三回にわたりソーシャルアプリをお手軽に作成する方法を紹介しました。アプリ、ホスティング、ソーシャル API すべて無料で作成することができます。そして将来的に Mixi 等で公開してトラフィックが増えたとしても僅かな課金で行えそうだと言うレポートも存在します。みなさんもチャレンジしてみてはいかがでしょうか? OpenFeint for Java も独立したプロジェクトとして公開したいと思います。API が膨大なため実装が大変です。協力してくれる人いないかな?OpenFeint for Android を本家より先に出せたらなぁ?と妄想中です。
 KLab若手エンジニアブログのフッター