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 ""
"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:
If targetting 10.9+, you could use the new iTunesLibrary.framework to retrieve the data.
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.
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.)