Using iTunes Match to Fix Your Music Metadata

Ever since Apple launched iTunes Match late last year, it’s been great for being able to have a copy of your music available everywhere, on your iPhone, computer and your Apple TV. One of the best features for me is being able to ‘match’ music I’ve ripped from elsewhere. This is great for lots of older stuff I ripped from CDs at a much lower bitrate (128kbps MP3).

What Match doesn’t do however, is ‘fix’ metadata. When you import a CD, usually you’re able to match it with CDDB so you get the track titles and artists for that CD without manually adding them. But this (usually) doesn’t work with custom/obscure CDs, such as a mixtape from a friend. And you could’ve been lazy and went easy on the tags, not filling out the artist correctly.

It’d be great if Match could offer to fix track metadata, afterall, it knows the music is available in the iTunes Store!

Fortunately, there’s a few ways to fix that, and it doesn’t take too long… (If you’re on a PC, head towards the bottom of the post)

For this blog post, I’ll be using a ruby script created by @tapbot_paul, which I’ve tweaked myself to favour song results from the AU iTunes Store first (being myself based in AU).
You can download it here.

Before continuing, please make a backup of your music library – I’d hate for it to ruin your library, nuke your computer… and I’m not responsible!

Every song in iTunes has its own unique ID – this allows you to create a direct link to the song, and can be used by Ping etc to make sure you ‘like’ the right song from the right album. Usually only songs purchased from iTunes have this ID inside them; luckily, music downloaded from iTunes Match (that is, those with a kind of ‘Matched AAC audio file’) actually do have this ID inside them.

If your music isn’t a Matched AAC audio file, ensure that the song has an iCloud status of ‘Matched’, then proceed to delete the song from your computer (not from iCloud), then redownload it again.

Apple also provide an API for developers to look up song metadata using the iTunes song ID – perfect!

To sum up, this will only work for music matched with iTunes, and has a ‘kind’ of ‘Matched AAC audio file’.

For this blog post, I’m going to be looking at a small collection of music ripped from a CD and incorrectly tagged. (yes, laugh at my taste all you want)

First up, we’re going to require a few things to get this script working.

Fire up Terminal and paste in the following (you will require XCode (free) from the App Store if you haven’t already installed it)
(As Appleman has commented, you might also need to install the command line tools: Go to Xcode -> Preferences -> Downloads tab then install the “Command Line Tools.” )

sudo gem install json
sudo gem install rb-appscript

You also need to put tunes.rb into your iTunes folder. Open up Finder, open the Go menu, and choose ‘Go to Folder‘.

Type in

~/Library/iTunes

And put tunes.rb in here. (this will only install it for your user account, so any other user account on your computer will need to do this too)

Now you’re all ready to start. Put the iTunesMatchMetadata.rb script on your desktop – this will make it easy to launch and run.

Open up iTunes and select the songs you wish to match. (make sure that their kind is ‘Matched AAC audio file’ – if they’re not, ensure that their iCloud status is ‘Matched’ and then delete and redownload the matched version)

If you haven’t selected any songs and run the script, it will scan all songs in your library

Back in Terminal, swap to your desktop;

cd Desktop

And now we can launch the metadata script!

ruby iTunesMatchMetadata.rb

Assuming you did select songs in iTunes, the script will look for songs with a kind of ‘Matched AAC’ and look for IDs, before querying a whole range of iTunes Stores. When songs are found, the metadata is simply overwritten into the songs.

Occasionally you will get songs, that, even though they have been matched, simply don’t exist in any of the iTunes Stores. Unfortunately this is a known issue. However it will try its best to query many different iTunes Stores looking for a ‘match’. (ha)

Once it’s finished and you’re back to your

desktop$

you’re done! Check iTunes for your freshly ‘fixed’ metadata.

If you’re not on OS X, or the thought of Ruby and the Terminal frightens you, there are other tools available. I haven’t tested and therefore cannot vouch for their success/failure, but you’re welcome to try and comment here!

Mac OS X: iTunes Match Helper
Windows: itunes-match-tagger

Let me know how you get on and leave a comment with your 2¢!

38 comments
  1. On Lion running the sudo gem commands gives me an error;

    ERROR: Error installing json:
    ERROR: Failed to build gem native extension.

    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb
    mkmf.rb can’t find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/ruby.h

    Any idea? I saw somewhere suggested to install Xcode; but I don’t feel like installing a 1,3GB program to run a short script…

    1. Hey Elrik – you can try the other application mentioned at the end of the post – else unfortunately you may be required to download XCode.

    2. Probably because you have to install the Command Line Tools with XCode. With OSX Lion they disabled a lot of commands in terminal unless you install the Command Line Tools for developers.

  2. This didn´t work on my Mac. I installed XCode but this didn´t help. I got that message:

    Building native extensions. This could take a while…
    ERROR: Error installing json:
    ERROR: Failed to build gem native extension.

    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb
    mkmf.rb can’t find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/ruby.h

    Gem files will remain installed in /Library/Ruby/Gems/1.8/gems/json-1.7.3 for inspection.
    Results logged to /Library/Ruby/Gems/1.8/gems/json-1.7.3/ext/json/ext/parser/gem_make.out

  3. Hmm, terminal throws me this error:

    method_missing’: Unknown property, element or command: ‘selection’ (RuntimeError)
    from iTunesMatchMetadata.rb:54

    1. I also get the error: `method missing’. The complete error that was returned is:

      /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:676:in `method_missing’: Unknown property, element or command: ‘selection’ (RuntimeError)
      from iTunesMatchMetadata.rb:54

      Any suggestions? I would love to update my metadata!

      Please. And thank you.

    1. Can’t edit posts, disregard the previous post. What I meant to ask is why I am getting this error after installing ruby and running the script:

      /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/_aem/connect.rb:89:in `launch_application’: The Classic emulation environment was required but is not available. (-10828) (Connect::CantLaunchApplicationError)
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/_aem/connect.rb:182:in `local_app’
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/aem.rb:146:in `by_path’
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:57:in `send’
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:57:in `connect’
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:159:in `reference_by_name’
      from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:633:in `method_missing’
      from meta.rb:55

  4. You need to install the command line tools for Xcode too…

    You can get Xcode from the Mac App Store. You’ll need at least version 4.4 of Xcode for it to work with OS X Mountain Lion. After the installation, open up Xcode in your /Applications folder. You’d want to go to Xcode -> Preferences -> Downloads tab then install the “Command Line Tools.” After you’re done, quit Xcode and fire up Terminal and follow the steps mentioned prior.

    Good Luck!

  5. For italian users, you can change code on line 63 with this:

    if iTunes_track.kind.get == ‘File audio AAC corrispondente’

    It will works fine!

  6. Worked fine (also in iTunes 11). However I needed to change the language of my system preferences to english.

  7. Thank you for the post!
    I’m getting this error:

    Found 1793 matched tracks
    Querying AU store for 1793 tracks */Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:542:in `_send_command’: CommandError (Appscript::CommandError)
    OSERROR: -1700
    MESSAGE: Can’t make some data into the expected type.
    COMMAND: app(“/Applications/iTunes.app”).sources.ID(73).user_playlists.ID(18643).file_tracks.ID(18652).album.set(nil)
    from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:642:in `method_missing’
    from iTunesMatchMetadata.rb:38:in `update_track’
    from iTunesMatchMetadata.rb:92
    from iTunesMatchMetadata.rb:90:in `each’
    from iTunesMatchMetadata.rb:90
    from iTunesMatchMetadata.rb:88:in `each’
    from iTunesMatchMetadata.rb:88
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/delegate.rb:137:in `each_slice’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/delegate.rb:137:in `each’
    from iTunesMatchMetadata.rb:84:in `each_slice’
    from iTunesMatchMetadata.rb:84
    from iTunesMatchMetadata.rb:81:in `each’
    from iTunesMatchMetadata.rb:81

    Any idea?

  8. Hello,

    I’m getting the same error as George. What goes wrong?

    MacBook-Pro-van-Mark-Ramaekers:desktop Mark$ ruby iTunesMatchMetadata.rb
    Reading 8077 tracks ********************************************************************************
    Found 8077 matched tracks
    Querying AU store for 8077 tracks */Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:542:in `_send_command’: CommandError (Appscript::CommandError)
    OSERROR: -1700
    MESSAGE: Can’t make some data into the expected type.
    COMMAND: app(“/Applications/iTunes.app”).sources.ID(72).user_playlists.ID(100410).file_tracks.ID(100430).album.set(nil)
    from /Library/Ruby/Gems/1.8/gems/rb-appscript-0.6.1/lib/appscript.rb:642:in `method_missing’
    from iTunesMatchMetadata.rb:38:in `update_track’
    from iTunesMatchMetadata.rb:92
    from iTunesMatchMetadata.rb:90:in `each’
    from iTunesMatchMetadata.rb:90
    from iTunesMatchMetadata.rb:88:in `each’
    from iTunesMatchMetadata.rb:88
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/delegate.rb:137:in `each_slice’
    from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/delegate.rb:137:in `each’
    from iTunesMatchMetadata.rb:84:in `each_slice’
    from iTunesMatchMetadata.rb:84
    from iTunesMatchMetadata.rb:81:in `each’
    from iTunesMatchMetadata.rb:81

  9. Hi Mark and George,
    Issue seems to be when iTunes store doesn’t have an album associated with the song. The code can’t handle an empty album name.

    Just place a little if statement in the “update_track” method.
    It should look like this now (minus the line numbers of course, also line 36 was only for debugging):

    35 def update_track(result)
    36 puts “Track Id= #{result[‘trackId’]} | Artist Name= #{result[‘artistName’]} | Album Name= #{result[‘collectionName’]} | Track Name= #{result[‘trackName’]}”
    37 @iTunes_track.name.set(result[‘trackName’])
    38 if !result[‘collectionName’].nil?
    39 @iTunes_track.album.set(result[‘collectionName’])
    40 end
    41 @iTunes_track.artist.set(result[‘artistName’])
    42 @iTunes_track.track_count.set(result[‘trackCount’])
    43 @iTunes_track.track_number.set(result[‘trackNumber’])
    44 @iTunes_track.genre.set(result[‘primaryGenreName’])
    45 @iTunes_track.disc_count.set(result[‘discCount’])
    46 @iTunes_track.disc_number.set(result[‘discNumber’])
    47 end

    1. Hi Bo,

      I’m a total nitwit. My script now looks like below.
      I’m getting the message:

      MacBook-Pro-van-Mark-Ramaekers:desktop Mark$ ruby iTunesMatchMetadata.rb
      iTunesMatchMetadata.rb:37: Invalid char `\342′ in expression
      iTunesMatchMetadata.rb:37: Invalid char `\200′ in expression
      iTunesMatchMetadata.rb:37: Invalid char `\234′ in expression
      iTunesMatchMetadata.rb:37: dynamic constant assignment
      puts “Track Id= #{result[‘trackId’]} | Artist…

      What am I doing wrong?

      #!/usr/bin/env ruby
      # by @tapbot_paul
      # Don’t blame me if this nukes your metadata, formats your drive, kills your kids
      # This script goes through any iCloud Matched songs in your iTunes library and tries to update the
      # metadata from the iTunes Store
      # Will run against selected tracks or if nothing selected entire library
      # install the required gems with the following commands
      # sudo gem install json
      # sudo gem install rb-appscript
      # then run the script with “ruby meta.rb”
      require ‘rubygems’
      require ‘appscript’
      require ‘json’
      require ‘open-uri’
      require(“~/Library/iTunes/tunes.rb”)

      class Track
      attr_reader :iTunes_id
      attr_reader :iTunes_track
      def initialize(iTunes_track)
      @iTunes_track = iTunes_track
      path = @iTunes_track.location.get.path
      file_string = File.open(path, ‘r’).read(1024) # data always seems to be around 600-700 bytes in
      index = file_string.index(‘song’)
      if index
      @iTunes_id = file_string[index+4,4].unpack(‘N’)[0]
      else
      puts “Couldn’t find @iTunes_track id #{track.name.get}”
      end
      end

      def valid?
      @iTunes_id.nil?
      end

      def update_track(result)
      puts “Track Id= #{result[‘trackId’]} | Artist Name= #{result[‘artistName’]} | Album Name= #{result[‘collectionName’]} | Track Name= #{result[‘trackName’]}”
      @iTunes_track.name.set(result[‘trackName’])
      if !result[‘collectionName’].nil?
      @iTunes_track.album.set(result[‘collectionName’])
      end
      @iTunes_track.artist.set(result[‘artistName’])
      @iTunes_track.track_count.set(result[‘trackCount’])
      @iTunes_track.track_number.set(result[‘trackNumber’])
      @iTunes_track.genre.set(result[‘primaryGenreName’])
      @iTunes_track.disc_count.set(result[‘discCount’])
      @iTunes_track.disc_number.set(result[‘discNumber’])
      end

      end

      WORK_SIZE = 100
      STDOUT.sync = true
      tracks = []

      app = Appscript.app.by_name(“iTunes”, Tunes)

      iTunes_tracks = app.selection.get
      if iTunes_tracks.count == 0
      iTunes_tracks = app.library_playlists[1].tracks.get
      end

      print “Reading #{iTunes_tracks.count} tracks ”
      iTunes_tracks.each do | iTunes_track |
      begin
      if iTunes_track.kind.get == ‘Gematcht AAC-audiobestand’
      track = Track.new(iTunes_track)
      unless track.valid?
      tracks < e
      puts e
      end
      end
      puts ”

      # all 2 char countries codes lots currently not valid %w(AF AX AL DZ AS AD AO AI AQ AG AR AM AW AU AT AZ BS BH BD BB BY BE BZ BJ BM BT BO BQ BA BW BV BR IO BN BG BF BI KH CM CA CV KY CF TD CL CN CX CC CO KM CG CD CK CR CI HR CU CW CY CZ DK DJ DM DO EC EG SV GQ ER EE ET FK FO FJ FI FR GF PF TF GA GM GE DE GH GI GR GL GD GP GU GT GG GN GW GY HT HM VA HN HK HU IS IN ID IR IQ IE IM IL IT JM JP JE JO KZ KE KI KP KR KW KG LA LV LB LS LR LY LI LT LU MO MK MG MW MY MV ML MT MH MQ MR MU YT MX FM MD MC MN ME MS MA MZ MM NA NR NP NL NC NZ NI NE NG NU NF MP NO OM PK PW PS PA PG PY PE PH PN PL PT PR QA RE RO RU RW BL SH KN LC MF PM VC WS SM ST SA SN RS SC SL SG SX SK SI SB SO ZA GS SS ES LK SD SR SJ SZ SE CH SY TW TJ TZ TH TL TG TK TO TT TN TR TM TC TV UG UA AE GB US UM UY UZ VU VE VN VG VI WF EH YE ZM ZW)

      countries = %w(NL US GB FR DE CA IT JP DZ AO AI AG AR AM AT AZ BS BH BD BB BY BE BZ BM BO BW BR BN BG CM KY CL CN CO CR CI HR CY CZ DK DM DO EC EG SV EE ET FI GH GR GD GT GY HN HK HU IS IN ID IE IL JM JO KZ KE KR KW LV LB LY LI LT LU MO MK MG MY MV ML MT MU MX MD MS MM NP AU NZ NI NE NG NO OM PK PA PY PE PH PL PT QA RO RU KN LC VC SA SN RS SG SK SI ZA ES LK SR SE CH TW TZ TH TT TN TR TC UG UA AE UY UZ VE VN VG YE)

      puts “Found #{tracks.count} matched tracks”
      countries.each do | country |
      print “Querying #{country} store for #{tracks.count} tracks ”
      counter = 0
      tracks.dup.each.each_slice(WORK_SIZE) do | subtracks |
      ids = subtracks.map { | track | track.iTunes_id }
      iTunesHash = JSON.parse(open(“http://itunes.apple.com/lookup?id=#{ids.join(‘,’)}&country=#{country}”).read)
      print ‘*’
      iTunesHash[‘results’].each do | result |
      result_id = result[‘trackId’]
      subtracks.each do | track |
      if result_id == track.iTunes_id
      track.update_track(result)
      tracks.delete(track)
      counter += 1
      break
      end
      end
      end
      end
      puts ” #{counter} updated”
      break if tracks.empty?
      end

      puts “Couldn’t find meatadata for #{tracks.count} tracks” if tracks.count != 0

    1. Hey Lala,
      Could you please explain what you mean by ‘copyright’ tag?
      This script should only change the title, album and artist fields, without touching anything else.

      1. By copyright tag I mean the record label field that appears under the title, album and artist tags when clicking on “Get Info” on iTunes. Thank you for the reply in advance.

  10. I absolutely loved this post, and I had no problems running the script. Thanks so much!

    The only question I have is if there is any way to make this script gather and replace any other information, like genre, year, explicit/clean, etc? Or is that related to a limitation of the API?

  11. Hey sorry for my bad english i’m french, I don’t know why :

    new-host:Desktop ludovicsalmeron$ ruby iTunesMatchMetadata.rb
    Reading 1003 tracks
    Found 0 matched tracks
    Querying AU store for 0 tracks 0 updated

    Only AU store and no others. Can U help me ?

  12. Have you found a workaround for tracks that are not found in any of the stores? Seems odd to me that ANY would be skipped if they are still available on iTunes.

    Awesome script! I can’t thank you enough.

  13. Thanks! This works like a charm. I fixed up a couple hundred tunes tonight that had been resisting everything else.

  14. This was working fine on my iMac with OS 10.8 today I updated to OS 10.9 and now it’s not working.

    what do you think changed on 10.9 ?

  15. Hey Guys,
    I got some problems with getting updated tracks, this is what termanl said:

    Reading 1119 tracks
    Found 0 matched tracks
    Querying AU store for 0 tracks 0 updated
    MacBook-Pro-van-Gert-Jan:Desktop GertJan$
    MacBook-Pro-van-Gert-Jan:Desktop GertJan$
    MacBook-Pro-van-Gert-Jan:Desktop GertJan$
    MacBook-Pro-van-Gert-Jan:Desktop GertJan$ ruby iTunesMatchMetadata.rb

    Can anyone see whats the problem of not getting any updated tracks?

    Thanks in advance!

    Gert-Jan

  16. This is working quite well, however I have the same question as some of the guys above, i.e. Lala and Anthony. Is there a way to edit the script so that it adds other metadata, e.g. Copyright information and explicit/clean tags? I have tried adding to the script myself (although I really am no expert) but I can never get it to work.

    Replies would be much appreciated (either from the author or someone who knows how to do this).

    Thanks

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.