How do I convert this Applescript code for talking to iTunes to (Objective)C to increase performance

StackOverflow https://stackoverflow.com/questions/21202353

  •  29-09-2022
  •  | 
  •  

Question

I have some applescript code for building a representation of an iTunes library on OSX, trouble is for large libraries it is too large. I expect it to be significantly quicker if written in (Objective)C but I dont know how to do this. I know its a bug ask but would anyone be willing to rewrite this as (Objective)C.

set thePath to (POSIX file "/tmp/songkong_itunes_model.txt")
set fileref to open for access (thePath) with write permission
tell application "iTunes"
    set eof fileref to 0
    set mainLibrary to library playlist 1
    repeat with nexttrack in (get every track of mainLibrary)
        if (class of nexttrack is file track) then
            try
                set trackname to name of nexttrack
                set loc to location of nexttrack
                set locpath to POSIX path of loc
                set persistid to persistent ID of nexttrack
                set nextline to trackname & "::" & locpath & "::" & persistid
                tell current application to write nextline & "\n" as «class utf8» to fileref
            end try
        end if
    end repeat
end tell
close access fileref
return ""
Was it helpful?

Solution

"I expect it to be significantly quicker if written in (Objective)C"

No, it's slow primarily because you're retrieving the data in the most inefficient way possible, one value at a time. The same algorithm will be dog slow in any language, since every single get involves building and sending an Apple event to the iTunes process, which has to interpret the event, look up the value being referred to, and send it back to your process in a second reply event.

A secondary problem is that AppleScript is notoriously inefficient at iterating across large lists - due to some shoddy implementation, the time it takes to look up a list item increases in direct relation to the list's length, so the time taken to iterate the entire list increases quadratically (i.e. O(n*n) efficiency in Big-O notation). However, there's a standard kludge for getting around that problem, so it shouldn't be a deal breaker.

You have several options:

  1. If targetting 10.9+, you could use the new iTunesLibrary.framework to retrieve the data.

  2. If you need to support 10.8 or earlier, you could parse the iTunes Music Library.xml file in the user's iTunes music folder.

  3. Rewrite your AS code to minimise the number of Apple events being sent and optimize the iteration process.

1 and 2 you can figure out yourself. For 3, here's how you retrieve all your data using just 3(!) get events:

tell application "iTunes"
    tell every file track of library playlist 1
        set tracknames to its name
        set locs to its location
        set persistids to its persistent ID
    end tell
end tell

That'll give you 3 separate lists which you can iterate over in parallel:

set thePath to (POSIX file "/Users/has/songkong_itunes_model3.txt")
set fileref to open for access (thePath) with write permission
set eof fileref to 0

repeat with i from 1 to length of tracknames
    set nextline to item i of tracknames ¬
        & "::" & POSIX path of item i of locs ¬
        & "::" & item i of persistids
    tell current application to write nextline & "
" as «class utf8» to fileref
end repeat

close access fileref

If the repeat loop is also sucking up performance, you can trick AppleScript into performing the lookup in a slightly different way that doesn't suffer the same inefficiency. Haven't actually tested this (don't use iTunes) but I think this should do it:

tell application "iTunes"
    tell every file track of library playlist 1
        script performancekludge
            property tracknames : its name
            property locs : its location
            property persistids : its persistent ID
        end script
    end tell
end tell

set thePath to (POSIX file "/tmp/songkong_itunes_model.txt")
set fileref to open for access (thePath) with write permission
set eof fileref to 0

tell performancekludge
    repeat with i from 1 to length of its tracknames
        set nextline to item i of its tracknames ¬
            & "::" & POSIX path of item i of its locs ¬
            & "::" & item i of its persistids
        write nextline & linefeed as «class utf8» to fileref
    end repeat
end tell

close access fileref

(Too lazy to explain the details, but there's a chapter on tweaking performance in the book if you want to read further.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top