iTunesで指定したプレイリストの曲だけを取り出したい

iTunesで指定したプレイリストの曲だけを取り出したい! と思ったので、やってみる。お気に入りプレイリストに入っている曲だけおでかけようマシンにコピーしたりするのだ。

iTunesの曲情報その他は、Macだと"~/Music/iTunes/iTunes Music Library.xml"に記録されているので、そこからプレイリストの情報を読み込めば良いはず。このファイルが壊れると、いろいろと悲しいのでバックアップしてチャレンジ。

まず、なにはともあれXMLといえばXML::Simpleだろうということで、

use strict;
use warnings;
use utf8;
use XML::Simple;

my $itunes_lib = XMLin(shift);
# 以下略

とかやって見るものの、なぜかXML::Simpleが正確にXMLを読んでくれない。これは、ちょっとわからんのでパス。

Mac::PropertyListというCPANモジュールがあるので次はこれを試してみる。

use strict;
use warnings;
use utf8;
use Mac::PropertyList;
use Perl6::Say;

my $itunes_lib = Mac::PropertyList::parse_plist_file(shift);
# 以下略

よさそうに思えるも、このモジュール、それなりにサイズの大きい"iTunes Music Library.xml"を読み込むと遅くて使い物にならない。棄て。

しかたがないので、適当にCPANサーチしてXML::MyXMLというのを見つけたのでこれを試してみると、なかなか良さそう。がんばって"iTunes Music Library.xml"をごりごり読み込んで、処理するスクリプトを書いた。

use warnings;
use utf8;use XML::MyXML qw(xml_to_object);
use Perl6::Say;use Perl6::Slurp;
use YAML;
use Encode;use List::MoreUtils qw(zip);
use URI::Escape;

my ($filename, $keyword) = @ARGV;

# xmlを読み込んでパース
# XXX 時間がかかる!
my $xml = slurp $filename;
my $itunes_lib = xml_to_object($xml);

# key と ファイルパスのハッシュを作る
my @song_keys  = map {
    $_->value
} $itunes_lib->path('dict/dict/key');

my @song_files = map { 
    my @strings = $_->path('string');
    my $path = 'no file';
    for my $string (@strings) {
        if ($string->value =~ m/^file:/) {
            $path = $string->value;
            $path = uri_unescape($path);
            $path =~ s!^file://localhost!!;
        }
    }
    $path;
} $itunes_lib->path('dict/dict/dict');

my %itunes_key_file_map = zip @song_keys, @song_files;

# キーワードにマッチするプレイリストから曲一覧をkeyで取得して
# itunes_key_file_mapを使ってファイルパスを表示
my @playlist_dicts = $itunes_lib->path('dict/array/dict');
for my $playlist_dict (@playlist_dicts) {
    my $playlist_name = decode_utf8($playlist_dict->path('string')->value);
    if ($playlist_name =~ decode_utf8($keyword)){
        my @track_dicts = $playlist_dict->path('array/dict');
        for my $track_dict (@track_dicts) {
            my $key =  $track_dict->path('integer')->value;
            say $itunes_key_file_map{$key};
        }
    }
}

プロンプトで次のようにすると、指定したプレイリストに含まれる曲の、実体のファイルのフルパスが表示される。

$ perl playlist_file.pl "iTunes Music Library.xml" お気に入り
/Users/yohei/Music/iTunes/iTunes Music/FictionJu ...

決め打ちしまくりのあやしげなコードだけれど、なんとか動作するものが手に入った。

ただ、この"iTunes Music Library.xml"というファイルは巨大になりがちで*1このXMLをパースするのにすごく*2時間がかかってしまう。もう少し軽量なやり方はないかなぁ。

と、考えたところで、これはと言える軽量な方法を思いついた。やっとこのスクリプトが完成したところなんだけどなー。

何となく長くなったので、次のエントリーに書こう。

*1:手元のファイルは9MB

*2:90秒くらい