Scripting Quicktime

I’m trying to create reference movies (as Quicktime .MOV documents) for all of the movies on my Media server, and the most obvious method is to use AppleScript and Quicktime Player. Why am I doing this? Because iTunes requires Quicktime Movies, I’ve mainly got AVIs, and I don’t want to (a) convert all of them, and (b) have iTunes manage them. I’d rather have the files in the right spot, and allow iTunes to manage the reference movies. Quicktime will often open files of varying types, even though iTunes will not. It’s then possible to save the file, and choose not to create a stand-alone movie. This will create a reference movie, which is much smaller than the original, but requires that the original remains in the same place in order to work. However, using AppleScript, the following will not work if AVIs are set to play in another player, like VLC:

1     tell application "Finder"
2       set theMovies to selection
3     end tell
4     
5     repeat with theMovie in theMovies
6       tell application "QuickTime Player"
7           open theMovie
8       end tell
9     end repeat

That is, the movie opens in it’s default application. To get it to work requires simply the following:

1     tell application "Finder"
2       set theMovies to selection
3     end tell
4     
5     repeat with theMovie in theMovies
6       tell application "QuickTime Player"
7           open theMovie as alias
8       end tell
9     end repeat

That little as alias causes Quicktime to open it, instead of VLC. How bizarre! The next problem is that by default, it puts the document(s) into a list. I often make the mistake of operating on a list, rather than each item individually. Especially when there’s only one item in the list. It’s like my eyes don’t see the curly brackets. So, we can save the movie, and by default it will save it as a reference movie. Next problem, how do we tell it where to save the movie? It will default to the root directory of the boot disk. Not really where I want to save this file, as I want to do multiple copies. But, what I can do is save the file, import it into iTunes, (ensuring that iTunes creates a copy of this file in it’s library) and then delete the file, or create the next one over the top. What I’m up to so far (and this doesn’t deal with names or anything yet):

 1     tell application "Finder"
 2      set theMovies to selection
 3     end tell
 4     
 5     repeat with theMovie in theMovies
 6     
 7      tell application "QuickTime Player"
 8          set mov to first item of (open theMovie as alias)
 9          save mov in (file "movie.mov")
10          close mov
11      end tell
12         
13      tell application "iTunes"
14          add alias "Macintosh HD:movie.mov"
15      end tell
16         
17     end repeat

More later. Sleep time now.

Rounding Ratings

I like that iTunes displays half-star ratings, but I want my tracks rated 51-59 to display this way too. That doesn’t work, but if you use the following script, it will round the ratings of selected tracks to the nearest half-star:

1     tell application "iTunes"
2       repeat with trk in selection
3           set rating of trk to ((rating of trk) / 10 as integer) * 10
4       end repeat
5     end tell

I hope Apple don’t include the functionality of odd-rated tracks to play different amounts, otherwise I’ll need to re-rate all of my music. Again.

Bypassing iTunes Downloader

My biggest hate about iTunes is that if the download of a Podcast fails, it needs to restart the whole download, rather than just resume. This really sucks if you are 21Mb into a 22.5Mb download. Using the technique illustrated in Adding a Podcast to a Pre-Existing Subscription, it’s possible to download the files in another program, such as wget, which can handle resumes. It also tends not to drop out as much as iTunes does when the network is congested. Which it is automatically when three downloads are concurrently occurring. There are some changes that need to be made to the process, but these are really just simplifications. You need to know the URL of the file you want to get, which can be obtained using the following AppleScript:

1     tell application "iTunes"
2         set the clipboard to address of item 1 of selection
3     end tell

Then, set up your http server, and create the same directory structure from your server root. Go to the relevant directory, and download the files you want to grab. Finally, go offline, and point the domain(s) at your own computer. Go back into iTunes, and use the GET button to grab the files from your own ‘server’. Don’t use the Update Podcasts button, or an error may occur. This removes the need to recreate the XML file, which is the most labour intensive part of the process illustrated in the previous post. It works a treat.

iTunes Ratings

From OmniNerd: Playing Favourites, I decided to run my own version of the experiment that compares how often songs with different ratings get played by iTunes. The difference with my experiment is that it has 101 tracks, each with a different rating (0, 1, …, 99, 100). To start with, I created a new user, so I wouldn’t have to muck around with my own iTunes library. I recorded a short empty track with Sound Studio. That is, I started the application, pressed Record, then Stop, and saved this. Then, I imported this track into iTunes, and made it so it was only 0.01 seconds in length (use Info, Options, Stop Time). This will make the experiment much faster than the previous one, which had 1 second tracks. I’m not that concerned it’s an AIFF, as I made this script to create the 101 empty tracks, as AAC or MP3:

 1     tell application "iTunes"
 2      set i to 0
 3      set base to first item of selection
 4      repeat 101 times
 5          set trk to first item of (convert base)
 6          set name of trk to i
 7          set rating of trk to i
 8          set i to i + 1
 9      end repeat
10     end tell

This will duplicate the selected track (or the first track of the selection, if you have more than one) 101 times. It will then give a name to each of these, 0-100, and assign the same rating as the name. Since doing this also seemed to play the tracks once, I used the following script to reset the play count. The bonus is that it can be used to reset the play counts and run the experiment again, with different parameters.

1     tell application "iTunes"
2       repeat with trk in selection
3           set played count of trk to 0
4       end repeat
5     end tell

It’s now time to run the experiment. I set the Party Shuffle to use the Library, and to play higher rated songs more often. Then I clicked play, and went away for about 12 hours. • When I came back, I could see what the data was starting to look like, but I grabbed the data using this script anyway:

 1     tell application "iTunes"
 2      set theData to {}
 3      repeat with trk in selection
 4          set theRating to name of trk
 5          set theCount to played count of trk
 6          set thisData to {theRating, theCount}
 7          copy thisData to the end of theData
 8      end repeat
 9     end tell
10     
11     get theData
12     
13     tell application "TextEdit"
14      set theDoc to make new document
15      repeat with thisData in theData
16          set text of theDoc to text of theDoc & (first item of thisData) & "," & (second item of thisData) & return
17      end repeat
18     end tell

Then, I saved this file as a CSV file, and imported it into Excel, which when plotted, gives the following graph: To me, the results look pretty clear. iTunes does not take into account the actual rating, only the star value. Even the new half-stars aren’t notably different. I’ll continue to run the experiment, and put up another post later. I may change some things, and rerun the original experiment, just to confirm it, although my results seem to do this already.

Address Book Scripting

I’m trying to save time by creating some simple scripts to add values to Address Book entries. For instance, I have a whole lot of people in a group who are living in the same state, and I want to add this to all addresses. However, it doesn’t seem to be possible to get or set the state of an address:

1     tell application "Address Book"
2       tell my card
3           set the_state to state of address 1
4       end tell
5     end tell

This fails. With the very helpful:

Address Book got an error: Can’t make state of address 1 of my card into type reference.

Which means absolutely nothing, as far as I can tell. Googling this string brings no joy. Now the weird thing is that if I switch over to another user, it works fine. Update: I quit all running Apps (with the intention of restarting, but System Update stopped me) and retried it. It worked. Something I was running was interfering with it, but I have no idea what.

Profiling AppleScript code

This looks promising. Shark is a tool that can be used to profile code. I haven’t done any serious profiling since some python script I was working on years ago on BeOS. Daniel Jalkut has a nice post, Shark Bites Script up on his blog, on how to use Shark to profile AppleScript code. Since I’ve been doing a bit of AppleScripting lately, and I have some code that is in serious need of optimisation, this might be worth a second look.

Script to XHTML

I wrote my own script to convert the selection (or whole front document) in Script Editor to XHTML. It uses the same CSS tags as Jonathon’s program, but does not add the style data in, unless a property is set. I’ve also got the Source Code for his program, so it will be interesting to see similarities.

  1     (*
  2     Script to XHTML v1.0
  3     ©2005 Matthew Schinckel
  4     http://schinckel.net/
  5     
  6     Converts selection (or whole document) into XHTML code.
  7     
  8     Insert the StyleSheet into your CSS if you want.  Else set externalCSS to false.
  9     
 10     Put it in your Scripts Menu, and it will work a treat for you.  
 11     Even copies the data to the clipboard.
 12     
 13     Bugs/Issues:
 14     
 15     • Script Editor reports the start of comments [--] as being black, not grey.
 16     • Doesn't handle references.
 17     • Operators and values are treated all as values.
 18     
 19     *)
 20     
 21     property StyleSheet : "
 22     .AppleScript { background-color:#ffffff; border: solid black 1px; padding:0.5em 1em 1em 1em; text-align:left; font-family: Verdana,Sans-Serif; overflow:auto; font-size:0.9em; white-space:pre; line-height:1.2em; margin-bottom:9px;}
 23     .as_new_text  { font-family:Courier; color: purple; }
 24     .as_operators  { color: black; }
 25     .as_language  { font-weight: bold; color: blue; }
 26     .as_application  { color: blue; }
 27     .as_comments, .as_comment  { font-style:italic; color: gray; }
 28     .as_values  { color: black; }
 29     .as_variables ,.as_variable { color: green; }
 30     .as_references  { color: purple; }
 31     "
 32     property externalCSS : true
 33     
 34     -- The HTML code either side of the block.
 35     property htmlStart : "<pre class='AppleScript'>" & return
 36     property htmlEnd : return & "</pre>" & return
 37     
 38     property tagStart : "<span class='"
 39     property tagMiddle : "'>"
 40     property tagEnd : "</span>"
 41     
 42     -- Apparently, this one is different to 'return'
 43     property enter : "
 44     "
 45     -- For testing, set this to 2, for use via Script Menu, set to 1.
 46     property win : 1
 47     
 48     if externalCSS is not true then set htmlStart to htmlStart & "<style>" & return & StyleSheet & return & "</style>" & return
 49     
 50     tell application "Script Editor"
 51         set theData to contents of selection of document of window win
 52         if (theData is "") then
 53             -- No selection, let's do the whole document.
 54             set textList to attribute run of contents of document of window win
 55             set fontList to font of attribute run of contents of document of window win
 56             set colorList to color of attribute run of contents of document of window win
 57         else
 58             set textList to attribute run of contents of selection of document of window win
 59             set fontList to font of attribute run of contents of selection of document of window win
 60             set colorList to color of attribute run of contents of selection of document of window win
 61         end if
 62     end tell
 63     
 64     set html to ""
 65     
 66     repeat with i from 1 to the count of textList
 67         set theClass to whichClass(item i of fontList, item i of colorList)
 68         set theText to my HTMLify(item i of textList)
 69         set html to html & tagStart & theClass & tagMiddle & theText & tagEnd
 70     end repeat
 71     
 72     set the clipboard to htmlStart & html & htmlEnd
 73     beep
 74     
 75     -- Utility Functions
 76     
 77     on whichClass(theFont, theColor)
 78         -- Set the class according to the font or colour.
 79         if theFont is "Verdana-Bold" then return "as_language"
 80         if theColor is {16384, 32768, 0} then return "as_variables"
 81         if theColor is {19660, 19960, 19960} then return "as_comments"
 82         if theColor is {0, 0, 65535} then return "as_application"
 83         if theColor is {32768, 0, 32768} then return "as_new_text"
 84         return "as_values"
 85     end whichClass
 86     
 87     on replace(theText, find, replace)
 88         -- Nice replace function
 89         set OldDelims to AppleScript's text item delimiters
 90         set AppleScript's text item delimiters to find
 91         set newText to text items of theText
 92         set AppleScript's text item delimiters to replace
 93         set theResult to newText as text
 94         set AppleScript's text item delimiters to OldDelims
 95         return theResult
 96     end replace
 97     
 98     on HTMLify(someText)
 99         -- This might need some more entries.
100         -- Perhaps a better way of doing it...?
101         set someText to replace(someText, "&", "&amp;")
102         set someText to replace(someText, "\"", "&quot;")
103         set someText to replace(someText, "<", "&lt;")
104         set someText to replace(someText, ">", "&gt;")
105         --set someText to replace(someText, tab, "&nbsp;&nbsp;&nbsp;&nbsp;")
106         --set someText to replace(someText, enter, "<br />" & enter)
107         --set someText to replace(someText, return, "<br />" & return)
108         return someText
109     end HTMLify

Insert iTunes Data into Post

I used to use ecto to paste all of my entries on my blog, but with Blogsome’s XMLRPC issue, I have to use a browser. But it would be nice to automatically get the name and information about my currently playing iTunes track, just like ecto used to do. I wrote an AppleScript that does this, and, inserts the data in at the insertion point for you. Because it’s intended to be run from the Script Menu, and everything run from there runs as “System Events”, I had to hard code in the browser name. If you use another browser, just replace the second line with whatever your browser is called.

 1 property star : «data utxt2605» as Unicode text
 2 property browser : "Camino"
 3 on run {}
 4     
 5     tell application "System Events"
 6         try
 7             get process "iTunes"
 8         on error
 9             return "No Track"
10         end try
11     end tell
12     
13     tell application "iTunes"
14         set theTrack to current track
15         set theArtist to artist of theTrack
16         set theAlbum to album of theTrack
17         set theRating to rating of theTrack
18         set theTrack to name of theTrack
19     end tell
20     
21     set theString to "<p class=’itunes’> " & theTrack & " • <a href='http://www.google.com/search?q=%22" & theArtist & "%22>" & theArtist & "</a> • <a href=’http://www.google.com/search?q=%22" & theAlbum & "%22>" & theAlbum & "</a> " & myRating(theRating)
22     set the clipboard to theString
23     
24     tell application browser
25         activate
26         tell application "System Events" to keystroke "v" using {command down}
27     end tell
28 end run
29 
30 on myRating(theRating)
31     set theResult to ""
32     set theTimes to (theRating - 9) / 20 as integer
33     repeat theTimes times
34         set theResult to theResult & star
35     end repeat
36     return theResult
37 end myRating

'Fixed' XHTML Export

Since I need to replace all “ in the generated XHTML with ‘, I use the replace function from the previous post.

 1     (* 
 2     Bugs:
 3     
 4     Does not like no selection: no real way to get the selection from SEE anyway.
 5     Sometimes does not execute if called from Script Menu.  Intermittant.
 6     *)
 7     
 8     -- If called from Script Menu, need to do this.
 9     
10     tell application "SubEthaEdit"
11      activate
12      tell application "System Events" to keystroke "C" using {command down}
13     end tell
14     
15     set theStart to (the clipboard)
16     
17     set the clipboard to (my replace(theStart, "\"", "'"))
18     
19     beep
20     
21     -- Another way of the Replace Function being called:
22     
23     -- set the clipboard to (replaceText from theStart to "'" instead of "\"")
24     
25     on replace(theText, find, replace)
26      set OldDelims to AppleScript's text item delimiters
27      set AppleScript's text item delimiters to find
28      set newText to text items of theText
29      set AppleScript's text item delimiters to replace
30      set theResult to newText as text
31      set AppleScript's text item delimiters to OldDelims
32      return theResult
33     end replace
34     
35     -- Alternate version of replace()
36     
37     to replaceText from theText to replace instead of find
38      set OldDelims to AppleScript's text item delimiters
39      set AppleScript's text item delimiters to find
40      set theText to text items of theText
41      set AppleScript's text item delimiters to replace
42      set theText to theText as text
43      set AppleScript's text item delimiters to OldDelims
44      theText
45     end replaceText

Code Markup

I like to present styled code for my readers, but I’m not totally happy with my methods of getting it: SubEthaEdit’s Export as XHTML is very cool, but I still have to tweak it so it looks nice. And I’ve got a great program for getting the XHTML version of the code Script Editor (AppleScript) is currently displaying. But, I’d like more flexibility. I want the code to be tagged with classes, like it’s possible to do with the Script Editor add-on, rather than using inline styles. I’ve already done this for the Script Editor code, but I just need to remember to remove the inline stylesheet from the start of the generated code. Unfortunately the Script has been saved as a Run Only. I’d love to know a way to decompile one of these Scripts…apparently there isn’t. I’ll have to write my own. Or see if Jon is nice enough to give the Source Code. As for the SubEthaEdit part: I should be able to do it. I’ve learned a couple of tricks that will help.