先日、foo_customdbコンポーネントを利用し、ネットで見つけたRubyスクリプトを使って、このコンポーネントが使うSQLite3のデータベースファイルをiTunesから作成して表示させた。使い始める前に最新にこのデータベースを作りなおせば万事OKだと思われた。
しかし、foo_customdbは開発終了になっているらしいのだ。開発者のブログでも確かにもうコンポーネント開発はしていないようだし、2ch(foobar2000の情報はググッても基本的に公式wiki、個人ブログと2ch程度しか日本語情報は存在しない)でも最近(去年、今年くらい)はそうなっていた。で、公式(本家といったほうがいいかもしれない)のfoo_playcountというのが最近のオススメらしいのだ。
が、これはSQLiteを使っているのではなく、専用バイナリを使ってデータを管理していて、XMLでのデータのインポート/エクスポートに対応している。しかし、iTunesからの正式なデータのインポート方法が確立されているわけではなく、諦めたという情報もあり、海外の掲示板でも過去のバージョンでは一部iTunesのXMLからデータを抜き出して、加工して、などという方法が書かれていたが、私の場合、iTunesには18000曲以上あるので、インポート用のXMLの作成に手作業はありえない。
で、わかったのは、次のことである。
- インポート用XMLファイルは一行に一アイテムを書く
- アイテムのキーはファイル名や曲名などのメタ情報ではなく、コンポーネントの内部情報である「ID」なる値である
- コンポーネントの開発者はその「ID」を積極的に公開する気はないし、今後も予定はない
なので、ハックするしかない。で、やってみた。一気にやると大変なので、一アイテム選んだ。一アイテム選んで、そのアイテムのPlayback Statisticsを右クリックメニューからXMLに書きだしてみた。それに書かれている「ID」は少なくともその選んだアイテムの「ID」なので、そのアイテムに対してインポートのテストができる。今日は、インポートしたいデータを「ID」がわかった時にどうつくるか?をテーマにした。全アイテムの「ID」を集める作業は別途考えることとした。仕事で言う「ペンディング」である。
私がインポートしたいのはiTunesからの、レート、再生回数、最後に再生した日時の3つである。これは、foo_playcountコンポーネントでは、Rating、Play Count、Last Playedに対応する。XMLインポートをメニューから選択した時にチェックする項目である。XMLファイルでのキーはそれぞれ、Rating、Count、LastPlayedになる。これはトライ&エラーで確認した。
でそれぞれ設定する値である。面倒なので簡単なものからいく。Countはずばり、iTunesから回数の数値を拾ってきてセットすればいいだけなので、何も考えなくていい。変換する必要はない。
Ratingは悩んだ。iTunesでは0~100の数値を内部で持っていて、星1つが20の重みを持っている。星3つつけた曲のRatingの数値は60の値として取得される。foo_customdbコンポーネントでは星の数がSQLiteデータベース内部での数値に対応していたので、0~5に変換すればよかった。iTunesでGUI設定している限りは端数は出ないので、20で割るだけだったので良かった。しかし、何を設定してXMLインポートしてもfoo_playcountコンポーネントでは反応しない。わからないときは逆をやればいい。レートなし(設定なし)から星1つ、2つ、3つ、4つ、5つのそれぞれの6つの状態を作って6回XMLエクスポートして数値を読んだ。すると驚くべき数値が出てきた。
- Rating 0:出力なし
- Rating 1:63
- Rating 2:106
- Rating 3:149
- Rating 4:191
- Rating 5:234
よくわからない数値の上に等差数列にすらなっていない(0と1の間は別としても3と4の間だけ差分が違う)。
ちなみにiTunesのつもりで80をXMLに書いてインポートしたら星がつかなかったので不正な値は無視される(空の時は出力なし、固定長星表示での状態で、星は並んだが全部白い星になった、レートをアンセットすると星が消える)。
これは、自動化するのであればプログラムの中で固定の値をswitch文などで当てはめるしかないだろうし、将来使用変更されたらスクリプトで追いかけるのが大変だろう。ただ、一度乗り換えたら逆をやろうと思わない限り必要ないものなので移行ツールをアップデートすることはないだろうが。
で、一番ハックに手間取ったのが日付である。ものすごく長い整数値が使われていた。それはエクスポートしたXMLにAddedとして追加日が出力されていたのでわかったことである。その数値が何の形式で日付を表しているのか?それを調べなくてはならなかった。私は数値で日付を表すものは恥ずかしながら元職業プログラマのくせしてUNIX Time(1970/1/1 0:00:00 UTCが0の秒単位)しか知らなかったので調べるしかなかった。
まず、iTunesから。XMLを見ると、Play Dateという整数値とPlay Date UTCという文字列形式があった。これをもとに解析した。かなり時間がかかった。ただ、数字の桁数から秒単位であることは想像できたし、オーダー的に100年程度なのもわかった。問題は起点だ。ググったらわかった。MacやExcelで標準的に使われているものだとわかった。1904/1/1 0:00:00 UTCからの秒数だった。わかるまで時間がかなりかかったけれども。1900年を起点にしないのはイレギュラーな閏年を挟まないようにという理由らしい。2000年が閏年なので、起点をココにしておくとシフト単位が自然でなおかつ桁あふれ範囲で閏年判定が要らないということらしい。まぁ、それはいい。iTunes側の数字はわかった。
次はfoobar2000のfoo_playcountがAddedに出力した妙に長い数値である。こんなに長い数値を使う日付、見たことがなかった。ちなみに、10進数で18桁ある。日本語で読めないorz
これもググった。NTFSのファイルシステムの日付の管理に使っているシリアル数らしい。起点は1601/1/1 0:00:00 UTCで64bit整数で、刻みは0.1μ秒...どんな単位だよ。で、手計算で一つやってみた。foobar2000で本利用時には追加日なんていう意味のない日付は表示するつもりはないのだが、仮に表示させた。そして、iTunesで対象トラックの最後に再生した日時を調べて、差分を手計算するのである。
foobar2000で見た追加日:2011/9/16 23:58:45 JST
iTunesでの最後に再生した日時:2011/9/14 7:35:12 JST
これを引き算してみると、追加日から2日16時間23分33秒前の値を作ればいいことになる。で、この差分を秒数にすると、231813秒になる。最初失敗したのだけども相手は0.1μs単位なので、後ろにゼロを7つつける。「2318130000000」これが差分。追加日のシリアル値は「129606587255302660」というブログに入力するのも嫌になる値。追加日から差分を引くと「129604269125302660」という値が得られる。厳密には違ってくるが、コンピュータでは数の切り捨ては面倒(仕様を考えたくない)し、foobar2000で一度再生すれば上書きされるデータだし、表示上は問題ないだろうから気にしないで値を求めた。そして、この値をXMLのLastPlayedにセットしてやる。
これでインポート用のデータが揃ったので、インポートする。
結論。IDがわかって、上記計算をして値を決めればデータは移行できる。
今回は追加日からの差分で最後に再生した日時を変換したが、基準日のシリアル値を求めておけば桁数を合わせて(10000000倍して)足し算すれば終わりである。手でやるよりコンピュータにやらせるほうが強い部分だろう。桁あふれさえ起きない作りにしておけばいいだけだ。64bit整数必須...
問題は各トラックごとのID値の列挙ができるのかどうか?そこにかかってきた。100曲ぐらいだったら手作業でデータベース化するんだけどなぁ。18000曲以上は無理。COM使うにしても相手はコンポーネントだし。どうにかならないか?
コメント