Wednesday, November 15, 2006

iTunes Automatic Rating, or Why Your Gnarls Barkley Album Mysteriously Has Five Stars

For a long time, I struggled with how to rate my songs in iTunes. I wanted the rating I assigned to a song to sum up my long history of feelings for that song with but a single integer. I wanted to know that I could go to my five-star list for a surefire hit, or prune out some one-star songs when I was getting low on disk space.



But when I was a child, I thought like a child. The problem is, my tastes change. I constantly surprise myself when I repeat a song 3 or 4 times in a row, only to find out it's a mediocre two-star. I also look sheepishly on some of the garbage I rated 5 stars when I was in a weird mood.



It dawned on me that maybe the most accurate, mood-agnostic way to determine my opinion of a song is something I call the play ratio. Specifically, it is the ratio of the number of times I've "played" a song to the number of times I've either "played" or "skipped" it. If I've listened to a song 17 times and never skipped it, the ratio is 1; if I skip it every time it comes up in random mode, it's a 0. If I've listened 6 and skipped 4, it's a 0.6, and so on.



Background: How iTunes interprets your feelings


(The beauty of the integration between iTunes and iPod never ceases to amaze me. The cool part about this system is how it works identically between the two. For ease of discussion, I'll keep things specific to iTunes, but the logic crosses over to the iPod as well.)



When you start playing a song in iTunes, one of three things is destined to happen:


  • The Play Count will increment by one.

  • The Skip Count will increment by one.

  • Neither count will increment.



Luckily, as users we have complete control over which one of these happens, which is why this system works. The ONLY WAY to increment the Play Count by one (other than Applescript, which we'll get to) is to let the song play through to the end. You can skip to almost the end, but iTunes HAS to finish playing the song. Likewise, the ONLY WAY to increment the Skip Count is to use the "Next Track" button with the Fast-Forward icon to skip to the next song. ANY OTHER activity, like pressing "Pause" or double-clicking a different track, results in no change to either number.



Another aside: I consider this the best possible way of implementing the idea of play and skip counts, and at the risk of bowing down at the crowded altar of Apple Fanboy Mecca, it's obvious some thought went into this. Kudos, Apple team.



So even though those counts are somewhat read-only, we now that we know how to manipulate them. When listening to your music, simply listen to the stuff you like, and skip the stuff you don't. The trick then becomes converting the Play Ratio into a rating, and that's where a bit of AppleScript comes in handy.



Implementation


This is a script I run every night or so to translate Play Ratio into star rating. It utilizes the capability of iTunes to display half-star ratings, about which more info is available around the internet. It also ignores tracks with a total play count (played + skipped) less than 5. You may want to lower that number; I admit I selected it somewhat arbitrarily. It processes every track in a playlist I have called MusicOnly, the idea for which came from Merlin Mann. It also dumps all of those tracks with play counts less than or equal to 5 into a playlist called "To Rate." If you use this script as-is, make sure to add that playlist first. Here it is in all its super-simple glory:



tell application "iTunes"
    set maxTotalPlays to 1
    set theTracks to every track of user playlist "MusicOnly"
    set ratePlaylist to "To Rate"
    delete every file track of playlist ratePlaylist
    repeat with theTrack in theTracks
        set totalPlays to (played count of theTrack) + (skipped count of theTrack)
        if totalPlays > 5 then
            if skipped count of theTrack = 0 then
                set rating of theTrack to 100
            else
                set rating of theTrack to 10 * (((played count of theTrack) / totalPlays * 10 + 0.5) as integer)
            end if
        else
            add (get location of theTrack) to user playlist ratePlaylist
        end if
    end repeat
end tell



Save that somewhere and run it every night, or whenver you sync, or when you think about it. As you can see, the math is pretty simple, and it's based on the fact that iTunes stores the ratings on a 0-100 scale: 0 = 0 stars, 20 = 1 star, 40 = 2 stars, 50 = 2.5 stars, and so on. That random-looking "+0.5" is in there to bump everything up a bit--I had songs with 17 plays and 1 skip getting rated 4.5 when they definitely needed to be 5! The algorithm could probably use some improvement, so your suggestions are welcome.



The only other thing I felt I needed to make the system complete was a way to skip a song without 'penalizing' it. Sometimes I really like a song but I'm just not in the mood for it when it comes up in shuffle, so I needed a quick way to scrub almost to the end and let it finish playing before going to the next song. Turns out it's just a single line of AppleScript:



tell application "iTunes" to set player position to (duration of current track) - 3



I have Quicksilver triggers set up to skip to the next song (a built-in script) with the keystroke cmd-alt-ctrl-right arrow and to scrub to the end of the current song, using the above one-line script, with cmd-alt-ctrl-up arrow.



In case of emergencies, you can reduce the Skip Count of a song by 1 using the following one-liner script:



tell application "iTunes" to set skipped count of current track to ((skipped count of current track) - 1)



If you know how to read, you can probably figure out how to manipulate that script to edit the Play Count instead, or add or subtract more than 1 from the number. I haven't needed to use that a whole lot, but if you're picky, make up a few permutations, save them in your Scripts folder, and use quicksilver to launch them with a few keystrokes.



While it will obviously take longer to rate an entire music library this way, I think it provides a more accurate way to determine just how much you like a particular song. Instead of making up some sort of semi-rational mapping for the 5-star scheme ("well, 5 stars for the songs I really like, 4 stars for the songs I 'kind of' like... but do I REALLY like this song today, or just 'kind of?'"), it's simply a binary decision: do I want to listen to this song right now or not?



Give it a try, and let me know what you think. And by the way, I totally suck at AppleScript, but it took me about three times as long to write this blog post as it did to write the actual script.

1 comment:

  1. great idea.
    the script kept hanging on me doing something with the To Rate playlist, so I took that stuff out of the script. And then I realized you'd probably save a lot of time by using a smart playlist that says Playcount is less than 5 instead of using applescript to build a playlist.
    cool

    ReplyDelete