1

Today I discovered that when I added album artwork to iTunes, it had only actually added it to some of the tracks in each album. So I did what anyone would do and tried to write a script to rectify that.

This is the first try, after about 3 hours of tinkering. It assumes that you have selected all the songs in an album.

#! /usr/local/bin/macruby
framework 'Foundation'
framework 'ScriptingBridge'

itunes = SBApplication.applicationWithBundleIdentifier("com.apple.itunes")
tracks = itunes.selection.get

# Find some track with artwork
artwork = tracks.map { |track| track.artworks[0] }.find { |a| a }
raise "No selected song has artwork" if artwork.nil?

# I checked artwork.rawData and it is PNG wrapped in an NSConcreteData.
pngData = artwork.rawData

tracks.each do |track|
  if track.artworks[0]
    puts "[O] #{track.name}"
  else
    puts "[X] #{track.name}"
    # Adding the same artwork object is apparently NG so we get the data from it
    # and make a copy.
    # There is probably a more straight-forward way to clone an object but
    # artwork.copy raises an exception.
    # I have tried using the keys 'data' and 'raw data' instead - same results.
    dict = {rawData: pngData}
    artwork_copy = itunes.classForScriptingClass('artwork').alloc.initWithProperties(dict)

    track.artworks.addObject(artwork_copy)

    raise "Didn't actually add the artwork" if track.artworks.empty?
  end
end

The call to addObject does not raise an exception, but I noticed that it doesn't actually add the artwork to the track (hence the check on the next line to speed up testing the script.)

I have been working mostly from Objective-C examples of ScriptingBridge and can't find any where other people have done this either. Lots of examples of getting the artwork but suprisingly few for setting it...

I did find an interesting mailing list thread from four years ago where someone else had a similar issue, but they never came to a solution either (or found it and didn't post it to the thread, which is worse and if they did that, they're a bad person and should feel bad.)

Hakanai
  • 12,010
  • 10
  • 62
  • 132

1 Answers1

1

Here is an example of Objective-C code that works for me. I don't know for macruby

iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier: @"com.apple.iTunes"];
SBElementArray *tracks = [[iTunesApp selection] get];
NSArray *trackWithArt = [tracks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"artworks[SIZE] > 0"]]; 
if ([trackWithArt count] > 0) {
    NSImage *tData = (NSImage*)[[[[trackWithArt objectAtIndex:0] artworks] objectAtIndex:0] data];
    for (iTunesTrack *tObj in [tracks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"artworks[SIZE] = 0"]]) {
        [[[[tObj artworks] objectAtIndex:0] propertyWithCode:'pPCT'] setTo:tData];
    }
}

Here is another method that works:

Replace the line in the loop by the following:

iTunesArtwork *tArtw = [[tObj artworks] objectAtIndex:0];
tArtw.data = tData; // set the track's artwork

MacRuby equivalents of both of the above (I'm using the latter for obvious reasons):

    # 0x70504354 = 'pPCT'
    track.artworks.objectAtIndex(0).propertyWithCode(0x70504354).setTo(artwork.data)
    # or,
    track.artworks.objectAtIndex(0).data = artwork.data

-

An alternative: it's very simple to do with AppleScript.

jackjr300
  • 7,111
  • 2
  • 15
  • 25
  • Yeah, I ended up using AppleScript as the workaround once I finally gave up, but now I see the secret. :) – Hakanai Oct 29 '12 at 11:11
  • It seems that the secret is to use objectAtIndex(n) instead of [n] - the array is immutable and [n] will always return nil, but objectAtIndex seems to create the object automatically. (And in retrospect I probably shouldn't have said "I" in that edit just now, haha.) – Hakanai Oct 29 '12 at 11:18
  • as of the current iTunes (12) version this one does not work for track items that has no artwork, replacing an existing artwork works fine but seems any referencing to the non existing item 0 will not create a new artwork item (anymore), tried multiple methods to add a new item none of them seems to be working (neither from apple script nor from objectiveC) does any of you know a working solution ? – Hofi Nov 12 '18 at 15:44
  • to be more precise, it is not working for cloud stored tracks (like the ones you have e.g. in iTunes Match), for locally stored only items it is still working fine – Hofi Nov 13 '18 at 09:41