Fetching iTunes Store Data

Or, quite possible the best AppleScript I have ever written.

So, I have a new iMac, and I was moving over a heap of ’legacy’ media from another machine, and some of it was DVD rips of Scrubs, Mad Men and Big Love. Now, these were mostly well tagged, but often I didn’t have all of the nice descriptions that were in iTunes.

Typing in all of this data is fairly annoying.

AppleScript, jQuery to the rescue:

tell application "iTunes"
	repeat with theTrack in selection
		tell theTrack
			set trackNo to episode number
		end tell
		tell application "Safari"
			set theDesc to do JavaScript "$('span.index span').filter(function() { return parseInt($(this).text(),10) == " & trackNo & "; }).closest('tr').find('.description .text').text();" in document 1
			set theName to do JavaScript "$('span.index span').filter(function() { return parseInt($(this).text(),10) == " & trackNo & "; }).closest('tr').find('.name .text').text();" in document 1
		end tell
		tell theTrack
			set description to theDesc
			set name to theName
			set episode ID to (my replace(show, " ", ".")) & ".S" & my zero_pad(season number, 2) & "E" & my zero_pad(episode number, 2)
		end tell
	end repeat
end tell

on zero_pad(value, string_length)
	set tmp_string to "000000000" & (value as string)
	set padded_value to characters ((length of tmp_string) - string_length + 1) thru -1 of tmp_string as string
	return padded_value
end zero_pad

on replace(content, sub, repl)
	set delims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to sub
	set content to every word of content
	set AppleScript's text item delimiters to repl
	set content to content as text
	set AppleScript's text item delimiters to delims
	return content
end replace

You just need to have the relevant TV Series Season page in the frontmost window in Safari, and select all of the episodes for that season in iTunes. Then, run the script, and watch the magic.

BBEdit - Strip Outer HTML tags

So, I monitor the BBEdit Google group, now that I’m a paid-up BBEdit user. One question piqued my interest today, and here is my solution:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tell application "BBEdit"
	tell front window
		set cursorPos to characterOffset of selection
		balance tags
		set startPos to characterOffset of selection
		set endPos to startPos + (length of selection)
		select (characters (startPos - 6) thru (endPos + 6))
		set selectedText to selection as text
		if characters 1 thru 6 of selectedText as text is equal to "<span>" then
			set replaceText to characters startPos thru (endPos - 1) as text
			set selection to replaceText
			select insertion point before character (cursorPos - 6)
		else
			select insertion point before character (cursorPos)
		end if
	end tell
end tell

In summary, it uses the BBEdit builtin command to select the contents of the current tag, and then extends that selection to grab the span tags that surround it. If indeed it was as span block, then it removes those tags.

This is just a simple one-off, but it might be useful as a basis for generating a script that has more features: like arbitrary tag types (rather than just span), or some other thing I haven’t thought of.

Note that it will only strip the outer tags. BBEdit has a Remove Markup feature, but that does not seem to be accessible using AppleScript.

Address Book: Family Relationships

I have a fairly large address book.488 contacts, who are shared between my various hobbies and pastimes. Including a large number of people who are related to one another. In some of these cases, I have populated the Related Names field, often where I just need to know a child’s parents names, but also in other cases where the related person is also in my address book.

I have several groups of people who are siblings, and I could go through each sibling and add in ‘father, mother, sister, brother’ items for each sibling, but it would be great if this could be done automatically.

The father/mother bit should be relatively straightforward, since the fields are the same:

Bob and Alice have two children, Carol and David. If I have put father:Bob and mother:Alice in for Carol, and I put brother:David in, then the system should be able to easily put father:Bob and mother:Alice in for David.

The tricky bit is with gender related to siblings. There is no default field for gender in Address Book, so how does the system know if David should have sister:Carol or brother:Carol? Especially since I don’t want to have to hand-code a list of names and which gender they are. I have many people with unusual names, and some with non-gender-specific names.

A short-term solution might be to (a) see if there are other sibling relationships (ie, if there is also brother:Eric, and Eric has sister:Carol, then clearly Carol is a female, and David should also have sister:Carol); or (b) have a temporary sibling:Carol field, which can be changed by the user.

You could then have a Smart Address Book Group which has all people with a sibling: field, which would make finding those people easier.

Conceivably, this should be possible with AppleScript. I have, however, butted heads with Address Book’s scripting in the past. It doesn’t always seem to be that easy to get data from this application.

Perhaps this type of feature, as well as my other hope for Address Book (notice if two contacts have the same number, including just the last X digits (for +614 vs 04 numbers in Australia, which are the same), and allow manipulation of this data, might appear in Leopard.

iCal and Invitations

I’ve used Gmail/Calendar, and have just migrated my data back to iCal, since now I have a laptop it makes a bit more sense to have the data stored locally. There is one thing I’d like to see possible in both systems is to mark a meeting attendee as attending. It only appears to be possible to do this from the person themselves. Sometimes I speak to an attendee of a function, and they are coming, but the email or calendar system they are using does not integrate well with iCal/gCal, so the email is sent back, and I need to modify stuff manually. Google Calendar at least allows you to add “Guests”, something I haven’t yet figured out how to do with iCal. However, with iCal, it appears to be possible to script iCal, so I may be able to add this functionality.

Getting Finder Comments in different languages

bash: mdls filename | grep FinderComment AppleScript: tell application "Finder"     comment of file end tell python: #!/usr/bin/pythonw from appscript import * path = '/Users/NAME/your/path/here' comment = app('Finder').items[path.replace('/', ':')].comment.get()

Waiting for an Application to launch

I use an AppleScript to implement “Open Terminal Here” functionality on my Macs, and just now I noticed on the Intel machine it had stopped working properly if Terminal wasn’t already running. For some reason, on PPC it still sends the message through, but on x86 it doesn’t. So, I had to use the following code to get it to work if Terminal wasn’t already running, which can be generalised to any application: repeat while "Terminal" is not in name of processes     delay 0.5 end repeat The whole script, which can be saved as an application bundle, set to not show in dock, and then placed in the Toolbar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    -- when the toolbar script icon is clicked
    --
    on run
    	tell application "Finder"
    		activate
    		try
    			set this_folder to (the target of the front window) as alias
    			--display dialog POSIX path of this_folder
    		on error
    			set this_folder to startup disk as alias
    		end try
    		my process_item(this_folder)
    	end tell
    end run
    
    
    
    -- This handler processes folders dropped onto the toolbar script icon
    --
    on open these_items
    	repeat with i from 1 to the count of these_items
    		set this_item to item i of these_items
    		my process_item(this_item)
    	end repeat
    end open
    
    
    -- this subroutine processes does the actual work
    --
    on process_item(this_item)
    	tell application "System Events"
    		try
    			get process "Terminal"
    			
    			tell application "Terminal"
    				activate
    				do script "cd " & (quoted form of POSIX path of this_item)
    			end tell
    		on error -- Terminal Not running, launch and run in first window.
    			launch application "Terminal"
    			-- May need to wait until Terminal finishes launching
    			repeat while "Terminal" is not in name of processes
    				delay 0.5
    			end repeat
    			tell application "Terminal"
    				activate
    				-- So we don't create a new window: in window 1
    				do script "cd " & (quoted form of POSIX path of this_item) in window 1
    			end tell
    		end try
    	end tell
    end process_item
    

Scripting iTunes 7

There are a few new things in AppleScript support for iTunes 7.

artwork:

downloaded (boolean, r/o) : was this artwork downloaded by iTunes?

playlist:

special kind (none/Audiobooks/folder/Movies/Music/Party Shuffle/Podcasts/Purchased Music/TV Shows/Videos, r/o) : special playlist kind

track:
episode ID (Unicode text) : the episode ID of the track
episode number (integer) : the episode number of the track
gapless (boolean) : is this track from a gapless album?
season number (integer) : the season number of the track
skipped count (integer) : number of times this track has been skipped
skipped date (date) : the date and time this track was last skipped
show (Unicode text) : the show name of the track
video kind (none/movie/music video/TV show) : kind of video track

Most of these are expected with the addition of the new features: gapless playback and TV/Movie support. At least now we can programmatically change the tags for TV shows. There are two there that are quite interesting: skipped count/date.

They are available as columns in the list views too, I just hadn’t noticed them yet. Now it will be possible to have smart playlist additions like: skipped count < 2.

Better iTunes Data Insert

I’ve updated my Insert iTunes Data (HTML) script for generating a block of text that describes the current playing iTunes track. This version links to the iTunes Music Store, rather than Google. It is relatively smart, in that the track name link also has the artist name, and the album name does too, as long as the album isn’t a compilation. The only (slight) bug is that the track name seems to need to be just the way iTMS displays it, so having (Live) might stuff up a link. This might be only if there isn’t a Live version in the iTMS.

I Can’t Tell You Why (Live) • Eagles • Hell Freezes Over ★★★★★

Code after the jump.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    -- Insert some data about the currently playing iTunes track into browser.
    
    property star : «data utxt2605» as Unicode text
    property half : «data utxt00BD» as Unicode text
    property quarter : «data utxt00BC» as Unicode text
    property threeq : «data utxt00BE» as Unicode text
    property browser : "Firefox"
    property URLprefix : "itms://phobos.apple.com/WebObjects/MZSearch.woa/wa/advancedSearchResults?"
    
    on run {}
    	
    	tell application "System Events"
    		try
    			get process "iTunes"
    		on error
    			return "No Track"
    		end try
    	end tell
    	
    	tell application "iTunes"
    		set theTrack to current track
    		set theArtist to artist of theTrack
    		set theAlbum to album of theTrack
    		set theRating to rating of theTrack
    		set theComp to compilation of theTrack
    		set theTrack to name of theTrack
    		--set trackInfo to {theTrack, theArtist, theAlbum, theRating}
    	end tell
    	
    	set trackLink to "<a href='" & URLprefix & "songTerm=" & HTMLify(theTrack) & "&amp;artistTerm=" & HTMLify(theArtist) & "'>" & theTrack & "</a>"
    	set artistLink to "<a href='" & URLprefix & "artistTerm=" & HTMLify(theArtist) & "'>" & theArtist & "</a>"
    	if theComp is true then
    		set theArtist to "" -- No Artist if album is compilation!
    	end if
    	set albumLink to "<a href='" & URLprefix & "albumTerm=" & HTMLify(theAlbum) & "&amp;artistTerm=" & HTMLify(theArtist) & "'>" & theAlbum & "</a>"
    	
    	set theString to "<p class='itunes'> " & trackLink & " • " & artistLink & " • " & albumLink & " " & myRating(theRating) & "</p>"
    	
    	set the clipboard to theString
    	
    	tell application browser
    		activate
    		tell application "System Events" to keystroke "v" using {command down}
    	end tell
    end run
    
    on HTMLify(someText)
    	set someText to replace(someText, "&", "&amp;")
    	set someText to replace(someText, "\"", "&quot;")
    	set someText to replace(someText, "<", "&lt;")
    	set someText to replace(someText, ">", "&gt;")
    	set someText to replace(someText, " ", "+")
    	set someText to replace(someText, "'", "&apos;")
    	return someText
    end HTMLify
    
    on replace(theText, find, replace)
    	-- Nice replace function
    	set OldDelims to AppleScript's text item delimiters
    	set AppleScript's text item delimiters to find
    	set newText to text items of theText
    	set AppleScript's text item delimiters to replace
    	set theResult to newText as text
    	set AppleScript's text item delimiters to OldDelims
    	return theResult
    end replace
    
    on myRating(theRating)
    	set theResult to ""
    	repeat 5 times
    		if theRating ≥ 20 then
    			set theResult to theResult & star
    		else if theRating ≥ 10 then
    			set theResult to theResult & half
    		end if
    		set theRating to theRating - 20
    	end repeat
    	return theResult
    end myRating

One More Time • Daft Punk • Triple J Hottest 100 - Volume 9 ★★★

Searching Zen

I googled for HTMLify Applescript, wanting to find out some simple code for how to escape the text in an AppleScript string, so I can pass it as arguments to a URL. My site came up with the top link. I was hoping for a better solution to the one I had come up with!

Resizing Images in AppleScript/Automator

I’ve already written a small Automator application that will resize an image to 640x480, but what about if I want to change it to a different size. There’s no way to set the resize amount on the fly in Automator, so I thought, it should be easy to do it in AppleScript. Except, for some reason, Preview.app isn’t scriptable. I do have Image Events.app floating around (not sure if it’s a standard part of the system), which can do what I want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    tell application "Finder"
    	set sels to selection
    end tell
    
    
    repeat with sel in sels
    	tell application "Image Events"
    		set img to open sel as alias
    		set dims to dimensions of img
    	end tell
    	
    	set cursize to "Current Size of Image: " & (first item of dims as integer as text) & " x " & (last item of dims as integer as text)
    	
    	display dialog cursize
    end repeat

This gets the current size, but I’m yet to find an easy way to get input from the user, and resize it according to this. I’m sure it can’t be that tricky!