dougscripts.com

Spotlight Scripting for iTunes

Tiger's Spotlight technology allows you to do some pretty thorough searching. Here's how you can create your own iTunes Spotlight search routines with AppleScript.

Metadata

Spotlight uses the metadata in the files on your computer to quickly determine matches for search terms. Metadata, as the saying goes, is data about data. Every file contains metadata and usually this is a list of various file properties. Different kinds of files can contain many different types of metadata. For instance, audio files contain information about bit rate, artist, album, and so on; whereas a graphics file will contain information about pixel width, or camera information, and so on.

Apple has introduced three new UNIX commands specifically for working with metadata and Spotlight (mdls, mdfind, and mdutil -- do a man in Terminal for the specifics). We can use these commands in do shell script routines within an AppleScript.

What metadata do we want?

I'm only concerned with audio files in this article, but you can use the same techniques with other types of files. The first thing you'll want to know is the metadata a typical audio file contains. To find out, use the mdls command. This command takes a POSIX-type file path as a parameter and returns its metadata. I've been using it like this:

set the_meta_data to (do shell script "mdls " & quoted form of POSIX path of ((choose file) as string))

I just have the Event Log selected in the Data Window of Script Editor, run the script, choose a file, and the result is something like this:

  • kMDItemAlbum = "39 Minutes of Bliss (In an Otherwise Meaningless World)"
  • kMDItemAttributeChangeDate = 2005-05-06 11:06:49 -0400
  • kMDItemAudioBitRate = 127944
  • kMDItemAudioChannelCount = 2
  • kMDItemAudioTrackNumber = 4
  • kMDItemAuthors = (Caesars)
  • kMDItemCodecs = ("")
  • kMDItemContentCreationDate = 2005-02-23 20:29:07 -0500
  • kMDItemContentModificationDate = 2005-02-23 21:58:19 -0500
  • kMDItemContentType = "com.apple.protected-mpeg-4-audio"
  • kMDItemContentTypeTree = ( "com.apple.protected-mpeg-4-audio", "public.audio", "public.audiovisual-content", "public.data", "public.item", "public.content" )
  • kMDItemDisplayName = "Jerk It Out.m4p"
  • kMDItemDurationSeconds = 196.1383333333333
  • kMDItemFSContentChangeDate = 2005-02-23 21:58:19 -0500
  • kMDItemFSCreationDate = 2005-02-23 20:29:07 -0500
  • kMDItemFSCreatorCode = 1752133483
  • kMDItemFSFinderFlags = 0
  • kMDItemFSInvisible = 0
  • kMDItemFSLabel = 0
  • kMDItemFSName = "Jerk It Out.m4p"
  • kMDItemFSNodeCount = 0
  • kMDItemFSOwnerGroupID = 80
  • kMDItemFSOwnerUserID = 501
  • kMDItemFSSize = 3474926
  • kMDItemFSTypeCode = 0
  • kMDItemID = 44192
  • kMDItemKind = "MPEG-4 Audio File (Protected)"
  • kMDItemLastUsedDate = 2005-05-06 11:06:49 -0400
  • kMDItemMediaTypes = (Sound)
  • kMDItemMusicalGenre = "Pop"
  • kMDItemStreamable = 0
  • kMDItemTitle = "Jerk It Out"
  • kMDItemTotalBitRate = 127944
  • kMDItemUsedDates = (2005-05-05 20:00:00 -0400)

Lotta good stuff in there! You may notice that some of the info contained in a file's metadata is accessible by an info for via Finder scripting. But what's really important here is not so much the values but the labels, those "kMDItem-" things. These are the parameters for the other new UNIX command we will want to use, mdfind.

Making the search script

The mdfind command works very simply. Its syntax, using Terminal, looks something like this:

mdfind "some kMDlabel == 'some string' another kMDlabel == 'some other string'"

This command returns a string containing the filepaths of any result separated by returns.

Here's a basic AppleScript you can try yourself

set the_command to ("mdfind " & "\"kMDItemTitle == 'Jerk It Out'\" ") as string
set returned_list to (do shell script the_command)

Now, if you have an audio file on your Spotlight-indexed system that is titled "Jerk It Out" (not the filename, the actual name of the track which is embedded in the metadata), the result will be the filepath (or filepaths) to the found files.

Of course, we can perform much more interesting searches by using select metadata labels. This script will grab the filepaths of all the files whose artist is "Caesars" and album is "39 Minutes of Bliss (In an Otherwise Meaningless World)". Notice how I have used the particular labels in the search query:

set the_command to ("mdfind " & "\"kMDItemAuthors == 'Caesars' kMDItemAlbum == '39 Minutes of Bliss (In an Otherwise Meaningless World)'\"") as string
set returned_list to (do shell script the_command)

It's fast, too!

Now, all this is well and good if you want to hard-code your strings. But we can use iTunes scripting to grab specific tags and dump them into our query. First, select a song, get the tag data you want, and make it usable in the do shell script. Let's search for some duplicate files using the Song Name and the size of a selected track:

tell application "iTunes"
	-- select one track:
	if selection of front browser window is not {} then
		set selected_track to item 1 of selection of front browser window
		-- get the tag data
		tell selected_track to set {nom, siz} to {name, size}
	end if
end tell

set the_command to ("mdfind " & "\"kMDItemTitle == '" & nom & "' kMDItemFSSize == '" & siz & "'\"") as string
set returned_list to (do shell script the_command)

OK, that's fine, but there's a coupla things:

First of all, the filepaths are still only listed in Script Editor. I'll insert a routine that "reveals" them in the Finder in just a sec'.

Second of all, and more of a concern to me, is my problem with single-quotes in strings. Now, I'm pretty good at figuring out quote-escaping, but for some reason, Tiger either does it differently or I'm a Monkey's Uncle. Anyway, notice how your search terms have to be surrounded in single-quotes. If a search string contains a single-quote, like an apostrophe, the script will fail because the do shell script interprets the next occurrence of a single-quote as the end of the string. Yes, yes, I tried "quoted form of" but that made things even worse! Dunno why it is.

So, we will include a handler that escapes the single-quotes for us. I don't like doing it that way, but, afterall, I am a Monkey's Uncle.

So back to "revealing" the found files. The mdfind command returns a string of results, not an AppleScript list. To convert the string to a list, which will be handy, I'll use my trusty "text_to_list()" handler. Next, to escape any single-quotes, I will use the equally trusty "replace_chars()" handler. Once I have my results, I will repeat through them and have the Finder reveal each file:

tell application "iTunes"
	-- select one track:
	if selection of front browser window is not {} then
		set selected_track to item 1 of selection of front browser window
		-- get the tag data
		tell selected_track to set {nom, siz} to {name, size}
	end if
end tell

-- escape any single-quotes in text strings,
-- in this case, any that may be in the nom variable
set nom to replace_chars(nom, "'", "\\'")

set the_command to ("mdfind " & "\"kMDItemTitle == '" & nom & "' kMDItemFSSize == '" & siz & "'\"") as string
set returned_list to (do shell script the_command)

if returned_list is not "" then -- we got some results
	-- make a list from the string; items are separated by carriage returns
	set new_list to text_to_list(returned_list, return)
	-- show each file in the Finder
	repeat with this_file in new_list
		set this_file to POSIX file this_file
		tell application "Finder"
			reveal this_file
			activate
		end tell
	end repeat
end if

on replace_chars(txt, srch, repl)
	set AppleScript's text item delimiters to the srch
	set the item_list to every text item of txt
	set AppleScript's text item delimiters to the repl
	set txt to the item_list as string
	set AppleScript's text item delimiters to ""
	return txt
end replace_chars

on text_to_list(txt, delim)
	set saveD to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to {delim}
		set theList to every text item of txt
	on error errStr number errNum
		set AppleScript's text item delimiters to saveD
		error errStr number errNum
	end try
	set AppleScript's text item delimiters to saveD
	return (theList)
end text_to_list

Fun, huh?

More UNIX Commands

You can also use UNIX commands in your Spotlight query. The script below uses logical AND operator (&&) to get any file that has an Album metadata tag, using the "*" wildcard", AND whose label color is blue, which is designated with the parameter value "4". (The count of these files is logged in the script, but with Event Log turned on when running from Script Editor you will see the filepath results.):

set the_command to ("mdfind " & "\"kMDItemAlbum == '*' && kMDItemFSLabel == 4\"") as string
set returned_list to (do shell script the_command)

log (count of text_to_list(returned_list, return))

on text_to_list(txt, delim)
	set saveD to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to {delim}
		set theList to every text item of txt
	on error errStr number errNum
		set AppleScript's text item delimiters to saveD
		error errStr number errNum
	end try
	set AppleScript's text item delimiters to saveD
	return (theList)
end text_to_list

Get another script

I have posted a script called Bring Out Yer Dead that searches for possibly-existing files of "dead tracks", which uses the same routines here. It uses the Song Name, Artist, and Album of a "dead track" and does a Spotlight search for potentially matching files. Then you can decide if you want to re-add them to iTunes, deleting the "dead track" and copying the original "dead track's" tags over to the new track. Just the tip of the ice berg as far as I'm concerned. Let me know what you come up with.

You can find out more about Spotlight metadata at this ADC page and more power-user tips using Spotlight in general on this page.

Site contents © 2001 - 2024 (that's right: 2001) Doug Adams and weblished by Doug Adams. Contact support AT dougscripts DOT com. About.
All rights reserved. Privacy.
AppleScript, iTunes, iPod, iPad, and iPhone are registered trademarks of Apple Inc. This site has no direct affiliation with Apple, Inc.
The one who says "it cannot be done" should not be interrupting the one who is doing it.