I like iPod
曲数の遷移
以下のグラフはiTunesのライブラリのxmlから生成した曲数の遷移です。

'03-11から始まっているのは、そのときにディスククラッシュがあったから。 過去半年の時速は 130曲/月らしい。
plistのplist.rbを使ってこんなスクリプトで抽出。 元のデータはiTunesからライブラリをXML出力したファイルです。5MBくらいのファイルを処理させたら iBookG4で6分かかった。
お。[RAA:xmlscan]を使うバージョンscan_plist.rbでは、1分くらいだ。速い。
require 'plist'
def main
plist = Plist.file_to_plist(ARGV.shift)
bin = Hash.new(0)
plist['Tracks'].each do |track, dict|
key = dict['Date Added'].strftime('%Y-%m')
bin[key] += 1
end
bin.keys.sort.inject(0) do |sum, key|
sum += bin[key]
puts "#{key} #{sum}"
sum
end
end
main
ある期間でたくさん聴いたやつを調べたいなあ。
4/24 - 5/24
Play Time (song) (total: 111.3H)
1.0H The Right Time / Monday満ちる 0.8H Be Who You Are / Monday満ちる 0.8H Watertight (Napt Remix) / MJ Cole Feat. Laura Vane 0.7H Yikes! Peach Cut 5' 24"!! (by HANDSOMEBOY TECHNIQUE) / ピチカート・ファイヴ 0.7H Bodyswerve (Grant Nelson Remix) / M-Gee Feat. Mica Paris 0.7H Dig Deep / Monday満ちる 0.7H Jill / Paris Match 0.6H Saturday Night / Paris Match 0.6H Lovin' You More (That Big Track) / Steve Mac Vs. Mosquito Feat. Steve Smith 0.6H ボーイ フレンド / AYUSE KOZUE
Play Time (artist) (total: 111.3H)
7.6H Paris Match 7.1H Monday満ちる 3.0H Halfby 2.8H Pizzicato Five 2.6H スキマスイッチ 2.6H Infracom! 2.5H AYUSE KOZUE 2.4H [Re:Jazz] 2.4H Bonnie Pink 2.3H フジ子・ヘミング
Play Time (album) (total: 111.3H)
7.1H ROUTES 5.3H After Six 4.2H MOTIVATION3 compiled by TOWA TEI 3.1H Jazflora 3.0H Green Hours 2.6H [re:jazz] 2.4H [Re:Jazz] (Re:Mix) 2.2H Pizzicato Five CUT UP - Single 2.0H Modern Sounds From Italy 2 1.9H 憂愁のノクターン
集計スクリプト
何度も実行するとXMLを読み込む時間がバカにならないので、Marshalでキャッシュする。 iTunes Music Library.xmlを扱うクラスはどんなインターフェイスが良いんだろう。 もっとたくさんアプリを書かないと想像できないな。
以前(?)Marshalで保存した結果との再生回数の差分をとれるようにしてみた。(2005-06-04)
# itml.rb
require 'scan_plist'
class ITunesMusicLibrary
def initialize
setup_fname
end
def setup_fname
@xml_fname = File.expand_path('~/Music/iTunes/iTunes Music Library.xml')
@dump_fname = File.expand_path('~/iTML.dump')
end
def load_plist
modified_xml = File.mtime(@xml_fname)
modified_dump = File.mtime(@dump_fname) rescue nil
if modified_dump && modified_xml < modified_dump
return load_dump(@dump_fname)
else
if modified_dump
suffix = modified_dump.strftime("-%y%m%d")
File.rename(@dump_fname, @dump_fname + suffix)
end
plist = load_xml(@xml_fname)
File.open(@dump_fname, 'w') { |fp| fp.write(Marshal.dump(plist)) }
return plist
end
end
def load_xml(fname)
Plist.file_to_plist(fname)
end
def load_dump(fname)
File.open(fname) { |fp| Marshal.load(fp) }
end
end
class TopRateReporter
def initialize(plist)
@plist = plist
end
attr_reader :plist
def subtract_playcount(old_plist)
rev = {}
old_plist['Tracks'].each do |k, v|
pid = v['Persistent ID']
rev[pid] = v
end
@plist['Tracks'].each do |k, v|
track = rev[v['Persistent ID']]
next unless track
count = v['Play Count'].to_i
p(v) unless v['Location'] == track['Location']
old_count = track['Play Count'].to_i
v['Play Count'] = count - old_count
end
end
def report_play_time_by_song
report_time(play_time(nil), 'song')
end
def report_play_count_by_song
report_count(play_count(nil), 'song')
end
def report_play_time_by_artist
report_time(play_time('Artist'), 'artist')
end
def report_play_time_by_album
report_time(play_time('Album'), 'album')
end
def report_play_count_by_artist
report_count(play_count('Artist'), 'artist')
end
def report_play_count_by_album
report_count(play_count('Album'), 'album')
end
def play_time(key_name = 'Artist')
make_bin(key_name) { |v| v['Total Time'].to_i * v['Play Count'].to_i }
end
def play_count(key_name = 'Artist')
make_bin(key_name) { |v| v['Play Count'].to_i }
end
private
class SongKey
def initialize(track_id, value)
@track_id = track_id
@value = value
end
def hash
@track_id.hash
end
def to_s
[@value['Name'], @value['Artist']].join(' / ')
end
end
def make_bin(key_name = 'Artist')
@plist['Tracks'].inject({:all => 0}) { |bin, kv|
v = kv[1]
if key_name
key = v[key_name]
else
key = SongKey.new(kv[0], kv[1])
end
bin[key] ||= 0
value = yield(v)
bin[key] += value
bin[:all] += value
bin
}.sort_by { |kv| -1 * kv[1] }
end
def report_time(result, caption)
printf("=== Play Time (%s) (total: %6.1fH)", caption, result[0][1] / 3600000.0)
puts
result[1, 20].each { |v|
hour = v[1] / (3600000.0)
printf("%6.1fH %s", hour, v[0])
puts
}
puts
end
def report_count(result, caption)
printf("=== Play Count (%s) (total: %d)", caption, result[0][1])
puts
result[1, 20].each { |v|
printf(" %6d %s", v[1], v[0])
puts
}
puts
end
end
def main(diff = nil)
iml = ITunesMusicLibrary.new
app = TopRateReporter.new(iml.load_plist)
app.report_play_time_by_song
app.report_play_time_by_artist
app.report_play_time_by_album
app.report_play_count_by_song
app.report_play_count_by_artist
app.report_play_count_by_album
return unless diff
a = iml.load_dump(File.expand_path(diff))
app.subtract_playcount(a)
app.report_play_time_by_song
app.report_play_time_by_artist
app.report_play_time_by_album
app.report_play_count_by_song
app.report_play_count_by_artist
app.report_play_count_by_album
end
main(ARGV.shift)




