Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

42 Astoundingly Useful Scripts and Automations for the Macintosh

Work faster and more reliably. Use Perl, Python, AppleScript, Swift, and Automator to automate the drudgery of computer use. Add actions to the services menu and the menu bar, and create drag-and-drop apps.

Use simple scripts and make your Macintosh play music, roll dice, and talk to you. Create ASCII art from your photos. There’s a script for all of that in my new book, 42 Astoundingly Useful Scripts and Automations for the Macintosh.

Cleaning iTunes track information

Jerry Stratton, June 28, 2007

I recently downloaded a bunch of old music from the Internet Archive audio archive. It’s a great resource, but on importing the tracks into iTunes, I noticed that a lot of the informational fields in the tracks had a lot of spaces on the back end. This causes weirdness in the iTunes display as it tries to center the text in its marquee.

Since I downloaded over 60 tunes, it wasn’t worth it to go through each one just to fix a minor problem like that. AppleScript seemed the obvious solution, but what I really needed was a “trim” function, and AppleScript doesn’t have one. Python does, however. It’s called “strip” and it is on every string object.

I could, of course, write a trim function in AppleScript, but why, when we have appscript? Especially when ASTranslate is so useful as an appscript AppleScript translator?

The AppleScript to get all tracks in a playlist:

[toggle code]

  • tell application "iTunes"
    • tell playlist "Recently Added"
      • get every track
    • end tell
  • end tell

ASTranslate converts this to:

  • app(u'/Applications/iTunes.app').playlists['Recently Added'].tracks.get()

We can thus loop through an arbitrary playlist using:

[toggle code]

  • iTunes = appscript.app("iTunes")
  • for track in iTunes.playlists[playlist].tracks.get():
    • trim(track.name)

Note that our (as yet unspecified) trim function is being sent the method without parentheses. In Python, this means we’re sending the actual method, not the results of the method (in this case, it’s probably an object, not a method). Because appscript information methods have both a get and a set, this allows our trim function to get the information, trim the spaces off the ends, and then set the correction information:

Here is a trim function that will use Python’s strip method to fix any field that needs to be stripped of white space:

[toggle code]

  • def trim(trackmethod):
    • value = trackmethod.get()
    • trimmedvalue = value.strip()
    • if value != trimmedvalue:
      • trackmethod.set(trimmedvalue)

We can send this function track.name, track.artist, track.album, or any text information field of an iTunes track that appscript has access to. If “trackmethod” is track.artist, then “trackmethod.get()” is equivalent to “track.artist.get()”, and “trackmethod.set()” is equivalent to “track.artist.set()”.

Here’s the full script:

[toggle code]

  • #!/usr/bin/pythonw
  • import appscript
  • import optparse
  • def message(*args):
    • if options.verbose:
      • print " ".join(args).encode('utf_8', 'ignore')
  • def trim(trackmethod):
    • value = trackmethod.get()
    • trimmedvalue = value.strip()
    • if value != trimmedvalue:
      • message("Trimming", trimmedvalue)
      • if not options.dryrun:
        • trackmethod.set(trimmedvalue)
  • #set up command-line options and help
  • usage="%prog [options] <playlist>"
  • description="Cleans text fields in iTunes tracks in the specified play list."
  • parser = optparse.OptionParser(usage=usage, description=description)
  • parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False)
  • parser.add_option("-d", "--dry-run", dest="dryrun", action="store_true", default=False)
  • (options, args) = parser.parse_args()
  • #the play list--and only one playlist--is a required argument
  • if len(args)== 1:
    • playlist = args[0]
    • iTunes = appscript.app("iTunes")
    • for track in iTunes.playlists[playlist].tracks.get():
      • trim(track.name)
      • trim(track.artist)
      • trim(track.album)
      • trim(track.comment)
  • else:
    • parser.print_help()

I added a few options using optparse: a --verbose option and a --dry-run option. A dry run goes through the playlist but doesn’t make any changes to it.

For example, to clean any recently added tracks (tracks in my “Recently Added” playlist), I might do a verbose dry-run first, and then do the actual clean:

  • itunesclean --verbose --dry-run "Recently Added"
  • itunesclean "Recently Added"

Now that I have it, I’m sure I’ll end up modifying this script to do all sorts of other batch changes to my playlists.

“There were times when I’d laugh at you. But now I’m crying, no use denying, there’s no other gal will do.”

  1. <- BASIC Alternatives
  2. Default Groups ->