Thursday, June 08, 2006

iPod-last.fm bridge

("How to get your iPod nano terribly scratched in just a few days" picture, from ArsTechnica. Don't try this at home.)
Woot, I wrote a script that takes info about what I played on an iPod and sends it to last.fm.

Now my last.fm charts can be more accurate. :-D Of course, Sylver is still on the top.

Some people were kind enough to reverse engineer iPod binary database format, that made the work much easier. Audioscrobbler (last.fm backend) protocol is also reasonably documented. Well, at least the general overview is documented, and AmaroK's scrobbler.cpp is not that bad for the details :-)

The script is available here. If you need help getting it running, just tell me :-)

42 comments:

  1. Anonymous00:07

    Oh... :)

    I've would be very grateful if you send me this script. I've tried Audioscrobbler, iScrobler, YumiPod... and nothing seems to work with my iPod.

    Please, send it to iwan.manjak@gmail.com

    ReplyDelete
  2. The latest version is for download here:
    http://taw.pl.eu.org/misc/lasfm_ipod_bridge-2.tar.gz

    Have fun :-) It was tested only on a few iPods, so if it doesn't work with yours, just tell me.

    ReplyDelete
  3. Anonymous23:31

    Hi,

    I downloaded your scripts and modified the user/password and the path to my ipod mounted.

    When I run get_play_counts.rb I get:


    ./get_play_counts.rb:40:in `initialize': No such file or directory - /mnt/usbhd/iPod_Cont/iTunes/Play Counts (Errno::ENOENT)
    from ./get_play_counts.rb:40:in `open'
    from ./get_play_counts.rb:40:in `play_counts_raw_data'
    from ./get_play_counts.rb:43:in `play_counts'
    from ./get_play_counts.rb:104


    I have looked in the /mnt/usbhd/iPod_Cont/iTunes directory and there are other files (iTunesDB, iTunesPrefs, etc.) but no sign of "Play Counts", any idea of what am i missing or how to fix it?.

    My system runs Debian stable with 2.6.18 kernel, my version of ruby is 1.8.2 and my iPod is a 5G.

    thanks in advance!

    ReplyDelete
  4. "Play Counts" file is normally in the same directory as iTunesDB, iTunesPrefs etc. As far as I know, it is deleted and recreated sometimes (when iTunesDB changes). Maybe for some reason it only got deleted and not recreated ? Did you try playing something or syncing with iTunes (or some other app) ?

    ReplyDelete
  5. Anonymous18:28

    Hi,

    thanks for the quick reply and sorry for my tardy one. Like you said, that file is recreated when you sync your ipod with itunes, and I seemengly did that before running the script, so there were no songs to be uploaded.
    I did run the get_play_counts.rb script when the file was accessible and it got all the songs played in the ipod since the last update:

    slandete@geri:~/drivers_etc/ipod_lastfm_bridge >:./get_play_counts.rb
    a[0]=The Organ&t[0]=A Sudden Death&b[0]=Grab That Gun&m[0]=&l[0]=174&i[0]=2006-11-14 12:47:35
    a[0]=The Organ&t[0]=There Is Nothing I Can Do&b[0]=Grab That Gun&m[0]=&l[0]=155&i[0]=2006-11-14 12:50:10
    a[0]=The Organ&t[0]=I Am Not Surprised&b[0]=Grab That Gun&m[0]=&l[0]=164&i[0]=2006-11-14 12:52:55
    a[0]=The Organ&t[0]=No One Has Ever Looked So Dead&b[0]=Grab That Gun&m[0]=&l[0]=117&i[0]=2006-11-14 12:54:52
    a[0]=The Organ&t[0]=Memorize The City&b[0]=Grab That Gun&m[0]=&l[0]=190&i[0]=2006-11-14 12:58:02
    a[0]=The Organ&t[0]=Hidden Track&b[0]=Grab That Gun&m[0]=&l[0]=37&i[0]=2006-11-14 12:58:39
    a[0]=The Organ&t[0]=Brother&b[0]=Grab That Gun&m[0]=&l[0]=241&i[0]=2006-11-14 16:47:57
    a[0]=The Organ&t[0]=Steven Smith&b[0]=Grab That Gun&m[0]=&l[0]=126&i[0]=2006-11-14 16:50:03
    a[0]=The Organ&t[0]=Love, Love, Love&b[0]=Grab That Gun&m[0]=&l[0]=211&i[0]=2006-11-14 16:53:35
    a[0]=The Organ&t[0]=Basement Band Song&b[0]=Grab That Gun&m[0]=&l[0]=252&i[0]=2006-11-14 16:57:47
    a[0]=The Organ&t[0]=Sinking hearts&b[0]=Grab That Gun&m[0]=&l[0]=129&i[0]=2006-11-14 16:59:57
    slandete@geri:~/drivers_etc/ipod_lastfm_bridge >:


    the problem now is that I don't know how to submit the results; I've tried using the script as_submit.rb without arguments, putting the results into a file and then using

    ./as_submit.rb < file

    and other ways, and what I get is

    ./as_submit.rb:24: Protocol not understood: UPTODATE (RuntimeError)
    5515c35a10cb6afc7e61029da4739f31
    http://87.117.229.205:80/protocol_1.1
    INTERVAL 1
    from ./as_submit.rb:15:in `each'
    from ./as_submit.rb:15

    What am I doing wrong?

    ReplyDelete
  6. Anonymous18:28

    Hi,

    thanks for the quick reply and sorry for my tardy one. Like you said, that file is recreated when you sync your ipod with itunes, and I seemengly did that before running the script, so there were no songs to be uploaded.
    I did run the get_play_counts.rb script when the file was accessible and it got all the songs played in the ipod since the last update:

    slandete@geri:~/drivers_etc/ipod_lastfm_bridge >:./get_play_counts.rb
    a[0]=The Organ&t[0]=A Sudden Death&b[0]=Grab That Gun&m[0]=&l[0]=174&i[0]=2006-11-14 12:47:35
    a[0]=The Organ&t[0]=There Is Nothing I Can Do&b[0]=Grab That Gun&m[0]=&l[0]=155&i[0]=2006-11-14 12:50:10
    a[0]=The Organ&t[0]=I Am Not Surprised&b[0]=Grab That Gun&m[0]=&l[0]=164&i[0]=2006-11-14 12:52:55
    a[0]=The Organ&t[0]=No One Has Ever Looked So Dead&b[0]=Grab That Gun&m[0]=&l[0]=117&i[0]=2006-11-14 12:54:52
    a[0]=The Organ&t[0]=Memorize The City&b[0]=Grab That Gun&m[0]=&l[0]=190&i[0]=2006-11-14 12:58:02
    a[0]=The Organ&t[0]=Hidden Track&b[0]=Grab That Gun&m[0]=&l[0]=37&i[0]=2006-11-14 12:58:39
    a[0]=The Organ&t[0]=Brother&b[0]=Grab That Gun&m[0]=&l[0]=241&i[0]=2006-11-14 16:47:57
    a[0]=The Organ&t[0]=Steven Smith&b[0]=Grab That Gun&m[0]=&l[0]=126&i[0]=2006-11-14 16:50:03
    a[0]=The Organ&t[0]=Love, Love, Love&b[0]=Grab That Gun&m[0]=&l[0]=211&i[0]=2006-11-14 16:53:35
    a[0]=The Organ&t[0]=Basement Band Song&b[0]=Grab That Gun&m[0]=&l[0]=252&i[0]=2006-11-14 16:57:47
    a[0]=The Organ&t[0]=Sinking hearts&b[0]=Grab That Gun&m[0]=&l[0]=129&i[0]=2006-11-14 16:59:57
    slandete@geri:~/drivers_etc/ipod_lastfm_bridge >:


    the problem now is that I don't know how to submit the results; I've tried using the script as_submit.rb without arguments, putting the results into a file and then using

    ./as_submit.rb < file

    and other ways, and what I get is

    ./as_submit.rb:24: Protocol not understood: UPTODATE (RuntimeError)
    5515c35a10cb6afc7e61029da4739f31
    http://87.117.229.205:80/protocol_1.1
    INTERVAL 1
    from ./as_submit.rb:15:in `each'
    from ./as_submit.rb:15

    What am I doing wrong?

    ReplyDelete
  7. S.: You're not doing anything wrong. last.fm recently updated their software, and my program got confused (it expected MD5s in upper case, but they changed to lower case).

    Just grab the updated version.

    And that's how you use it - you take output of get_play_counts.rb, remove all songs you don't want people to know you've listened ;-), and use it as input to as_submit.rb.

    And of course, you need to set your ipod path in get_play_counts.rb, and your username and password in as_submit.rb.

    Tell me if it worked.

    ReplyDelete
  8. Anonymous13:45

    Hi,

    sorry for asking again, but I grabbed the last version, modified username, password and path to my mounted ipod ans made sure it's the one I am using; no problem with extracting the songs to a file, but when i use the "./as_submit.rb < file" I get

    ./as_submit.rb:30: undefined method `post_form' for Net::HTTP:Class (NoMethodError)
    from ./as_submit.rb:12:in `each'
    from ./as_submit.rb:12

    Could it be that my version of ruby is missing a library or something like that?

    (thanks for your patience)

    ReplyDelete
  9. S.: According to this post on ruby-lang, it is in Ruby standard library since Ruby 1.8.3, which was released in September 2005.

    If you have 1.8.2 or older, it's probably easiest to upgrade your Ruby.

    ReplyDelete
  10. Anonymous02:08

    I have ruby 1.8.2, but will upgrade. I'll get back to you with news as soon as I have it done.

    ReplyDelete
  11. Anonymous03:00

    is there a complete noob guide to installing this?

    thanks.

    ReplyDelete
  12. Anonymous12:11

    being a total noob, i wondered how this works, how do i use these files?

    Thanks

    ReplyDelete
  13. Here's a step-by-step guide.

    If you don't have it installed already, install Ruby 1.8.3 or newer.

    Get the tarball.

    Unpack it (tar xvzf tarball.tar.gz).

    Edit config.rb and put your last.fm user name and password there (and any other settings you want to change)

    Make sure your iPod is mounted somewhere. Usually it's something like /media/sda2/ or /media/sdb2/. If it's somewhere else, just edit config.rb.

    Run ./get_play_counts.rb. It will read list of songs you played from your iPod and print it.

    Does it work ? Great.

    Send the songs to last.fm:
    ./get_play_counts.rb | ./as_submit.rb

    Did it work ?

    ReplyDelete
  14. Anonymous10:51

    If you like Python, there is another script here that works on Windows, Linux and MacOS:
    http://www.hoc.net/mike/source/iPodScrobbler

    Supports Unicode too. :-)

    Mike

    ReplyDelete
  15. I finally upgraded to debian testing, got the right version of ruby and got it working.
    Really great script, I found no other way of submitting songs played in an ipod in Linux, congratulations.

    ReplyDelete
  16. Anonymous18:42

    Hello,

    I just installed the latest version from http://taw.pl.eu.org/misc/lasfm_ipod_bridge-2.tar.gz and I'm having similar issues to Anonymous...

    get_play_counts.rb returns all the correct information but when I pass the data to as_submit.rb I get the following:

    ./as_submit.rb:15: Protocol not understood: UPTODATE (RuntimeError)
    ee27c9e0f8fe504fac431bfb5517a45f
    http://87.117.229.205:80/protocol_1.1
    INTERVAL 1
    from ./as_submit.rb:12:in `each'
    from ./as_submit.rb:12

    Thank you in advance.

    ReplyDelete
  17. Mark: Oops, taw.pl.eu.org is an incredibly old mirror I forgot about, and the version you downloaded is old and buggy.

    The right website is http://taw.chaosforge.org/ipod_lastfm_bridge/. I will update or remove taw.pl.eu.org soon.

    ReplyDelete
  18. Anonymous16:16

    Your scripts are great. See my blog that shows you where to find unlimited
    iPod downloads
    . Also read this article on iPod music downloads.

    ReplyDelete
  19. Anonymous18:33

    I have a dumb question.. How do I even run the script?

    ReplyDelete
  20. Anonymous19:01

    I get all kinds of syntax errors if I try to run it from Terminal, and only after changing a lot of the rb files to make it happy. I'm guessing that's not the way they're supposed to be run? :)

    ReplyDelete
  21. Anonymus: How are you running it ? Just unpack it, open some terminal, enter the right directory and try ./get_play_counts.rb Should work undex Linux and Mac OS X.

    What error messages are you getting instead ?

    ReplyDelete
  22. Anonymous14:56

    Here's what i get.. if i then remove the last_old part, it moves on to something else to say error to:
    I'm baffled :D

    robbie-dillons-computer:~ robbiedillon$ /Users/robbiedillon/Desktop/ipod_lastfm_bridge/get_play_counts.rb
    /Users/robbiedillon/Desktop/ipod_lastfm_bridge/get_play_counts.rb:7:in `require': /Users/robbiedillon/Desktop/ipod_lastfm_bridge/config.rb:33: syntax error (SyntaxError)
    # $last_old = "2006-12-08 08:06:14"
    ^
    /Users/robbiedillon/Desktop/ipod_lastfm_bridge/config.rb:33: Illegal octal digit
    # $last_old = "2006-12-08 08:06:14"
    ^
    /Users/robbiedillon/Desktop/ipod_lastfm_bridge/config.rb:33: Illegal octal digit
    # $last_old = "2006-12-08 08:06:14"
    ^
    /Users/robbiedillon/Desktop/ipod_lastfm_bridge/config.rb:33: syntax error
    # $last_old = "2006-12-08 08:06:14"
    ^ from /Users/robbiedillon/Desktop/ipod_lastfm_bridge/get_play_counts.rb:7

    The carrot is pointing to the -, :, and spaces

    ReplyDelete
  23. Anonymous: Maybe you edited config.rb in some text editor which automatically converted -, :, and spaces to some Unicode charcters which confuse Ruby ?

    Do you get the same error before editing config.rb ? Do tou get such error if you use a different editor ?

    ReplyDelete
  24. Anonymous01:28

    hmm, I've tried everything I can think of.. now it says it can't find the .db even when it's pointed in the right place. being on an intel mac wouldn't have anything to do with it would it?

    perhaps i just wasn't meant to scrobble all of my songs!
    :)
    Robbie

    ReplyDelete
  25. Anonymous: I just tried it on Intel Mac and I have a solution. Normally Macs don't treat iPods as storage devices, instead they treat them as magical iTunes things. That needs to be fixed by going to iTunes > your iPod > Summary > Options, enabling "Maually manage music" and disabling "open iTunes when this iPod is connected" (as iTunes seem to automatically erase Play Counts). Then disconnect and reconnect your iPod.

    After that ./get_play_counts.rb worked on Intel Mac. If ./as_submit.rb doesn't update your Ruby - some Macs have very old versions of it (1.8.2 or something).

    I just found out that in case of too old Ruby as_submit simply says "Network error, retrying" instead of giving a more informative error. I'll fix the error message later.

    ReplyDelete
  26. Just tried the script with my iPod Classic silver 80gb, and it seems to work flawlessly, thank you very much :)

    ReplyDelete
  27. Anonymous02:34

    this is pretty useful. do you know if this works on the nano 3g?

    ReplyDelete
  28. rockwallca: I don't see any reason why it shouldn't work on nano 3g.

    ReplyDelete
  29. heh, it used to work fine until today (ipod classic silver 80G), now as_submit gets:

    BADAUTH
    INTERVAL 1

    all the time; handshake goes fine, but then when trying to submit songs it goes boom :(
    the spec says this may be a sign of server overload, but then why would it give me 1 sec intervals? and why does audacious still work and successfully submit? beats me...

    While trying to figure it out I changed as-submit to only handshake when needed (the protocol 1 guidelines say plugins should only handshake on start and after repeated catastrophic events like network errors) and display some info using libnotify (nothing fancy, just system("notify-send blah blah")). If you are interested in these, I put up a patch.

    ReplyDelete
  30. m0n5t3r: Whatever the spec says, the interval is always 1 second.

    I'll take a look at the patch later.

    ReplyDelete
  31. in the end I went and implemented AS protocol 1.2 and reading the ipod play counts from scratch (in python; not much of a ruby guy), I need to write a decent command line tool, invent a name and get a client id from russ before I release it into the wild; your code was very useful for morale (specs for binary stuff are always scary at the first glance, code is much friendlier) :)

    One thing that made me bang my head against the wall for a while was that my timestamps were off by one hour; then I discovered that yours were the same: the number you substract from the mac timestamp (and which I initially stole from your script) is an hour too small; if you got it from something like "date 'Jan 1 1904' +%s", try "date -u 'Jan 1 1904' +%s", it appears that date shows local time by default ;)

    ReplyDelete
  32. m0n5t3r: IIRC get_play_counts script prints local dates, and only as_submit converts them to UTC. That might be the source of the problem.

    ReplyDelete
  33. Hi there... anychance that you're going to update the script for the BADAUTH problem?

    For two days now I've been getting the following:

    Failed:
    BADAUTH
    INTERVAL 1

    Thanks
    Mike

    ReplyDelete
  34. I fixed it and implemented a v1.2 protocol even, but eventhough it now submits fine, the submissions simply don't turn up on last.fm... :-(

    ReplyDelete
  35. I had a similar problem while I was developing gScrobbler, and it turned out that I had misread the protocol spec and used a yet unsupported value for the "o" parameter (only "P" and "L" are implemented, anything else won't show up); actually, in my case, it was worse: some plays did show up, because I am also setting the "love" flag for tracks with a rating over 3, and "love" implies a play for the AudioScrobbler thing, in spite of the invalid origin :))

    ReplyDelete
  36. Sadly I do submit the o parameter.. but thanks for the suggestion! :)

    I guess I'll have to check with Russ and get a client id first.
    Do submissions with tst as client id even get imported into the personal user history, btw?

    ReplyDelete
  37. Anonymous20:44

    Thanks so much for this script! I've wasted such a lot of times with programs like YamiPod, which frustated me no end at times, because it's so buggy and tends to freeze.

    Now I can at last send stuff easily to last.fm again, which means a lot to me.

    So, thanks alot! You made my day.

    Michael

    ReplyDelete
  38. Anonymous13:38

    how can i start the script under windows xp?

    ReplyDelete
  39. Anonymous: It's possible to use it under XP, but it's probably not worth the effort. It's really meant for Linux and OSX, it's trivial to use it on both.

    ReplyDelete
  40. Anonymous08:33

    good day!
    don't you know why the lastfm website doesn't accept submitted tracks? it shows only smth like "now listening using Ruby Scrobbler Script" and that's all.

    ReplyDelete
  41. newstufflover: I cannot really say, as I don't really use an iPod, but it might be spamfilter. If you use some other program to scrobble in addition to this one, you need to know that if track date (of something you listened to on your iPod a week ago) is older than track date of the most recent already submitted song (which you listened on your desktop computer today), such track will be thrown away silently.

    ReplyDelete
  42. hello. thanks for suggestions.

    tried to apply your help but still no luck. it submits tracks okay and i made sure theirs dates are correct and older than the already scrobbled (played on my computer and scrobbled) ones. but they don't appear in my lastfm page nevertheless.

    ReplyDelete