This script sifts through your iTunes DB and tells you what you really like to listen to. It told me that my mental list of my top N and my actual listening habits disagree. It also illustrates that you don't need to parse XML if it is well formed and it can be way faster than rexml and the other xml-overkill libraries.
- = (2306 tot, 327.33 adj): Everything! (Disc 2) by Tones On Tail
- = (2295 tot, 316.93 adj): Mezzanine by Massive Attack
- = (2026 tot, 293.42 adj): Earth Sun Moon by Love & Rockets
- = (1789 tot, 259.74 adj): Kakusei by DJ Krush
- = (1700 tot, 241.82 adj): Staring At The Sea: The Singles 1979-1985 by The Cure
- = (1753 tot, 240.61 adj): 100th Window by Massive Attack
- = (1307 tot, 224.80 adj): Nouvelle Vague by Nouvelle Vague
- = (1458 tot, 207.87 adj): Substance 1987 (Disc 1) by New Order
- = (1342 tot, 194.84 adj): Meiso by DJ Krush
- = (1351 tot, 192.62 adj): Express by Love & Rockets
See the code after the cut.
require 'time'
class Album < Array
def age
map { |track| track.age }.max
end
def score
total / Math.log(age)
end
def total
inject(0.0) do |total_score, track|
total_score + track.score
end
end
def self.parse(file)
today = Time.now
d = {}
library = Hash.new { |h,k| h[k] = Album.new }
IO.foreach(File.expand_path(file)) do |line|
if line =~ /<key>(Name|Artist|Album|Date Added|Play Count|Rating)<\/key><.*?>(.*)<\/.*?>/ then
key = $1.downcase.split.first
val = $2
d[key.intern] = val
if d.size == 6 then
date = d[:date].sub(/T.*/, '')
key = "#{d[:album]} by #{d[:artist]}"
age = ((today - Time.parse(date)) / 86400.0).to_i
library[key] << Track.new(age, d[:play].to_i, d[:rating].to_i / 20)
d.clear
end
end
end
library
end
end
Track = Struct.new(:age, :count, :rating)
class Track
def score
rating * count.to_f
end
end
max = (ARGV.shift || 10).to_i
file = ARGV.shift || "~/Music/iTunes/iTunes Music Library.xml"
library = Album.parse(file)
top = library.sort_by { |h,k| -k.score }[0...max]
top.each_with_index do |(artist_album, album), c|
puts "%-3d = (%4d tot, %5.2f adj): %s" % [c+1, album.total, album.score, artist_album,]
album.each do |t|
puts " #{t.age} days old, #{t.count} count, #{t.rating} rating = #{t.score}"
end if $DEBUG
end

i am new to ruby, and i really liked this piece. i am figuring it out slowly and learning a lot from it. Please keep posting STIW.
For someone moving over from Java, the ruby idiom or way is so hard to catch. any suggestions of projects i could study to understand the Ruby way would be appreciated. (I do have the pickaxe with me)
I love that this will show me what i actually like to listen to. I really disagree with itunes stats.
gordon http://www.cheapgreencar.com
I recently found myself wanting to quickly rearrange the files in my library. After cursing the Apple for such a horrible XML file I figured there had to be someone that already parsed the thing. Here you are.
Thanks for this.