| Comments: |
I don't know if you're using 10.4, but I would expect that Automator actions would be faster because they're compiled Objective-C objects. This assumes that an Automator action exists for iTunes that does what you want.
i assume you've never actually used automator.
my god it's slow.
I think you're better off parsing the xml yourself and if you want updates, checking every N units of time to see if it's changed.
Here, this bit takes a lot less than hours for me. I left in a couple of debug lines, they add a listing of the time when it starts, and the number of songs, then the time when it ends. Strip those out if you're happy with the script. Also, it doesn't add things to the output variable, it adds them to a list then returns the list. This gives the output as comma separated. Oh, and I set it to output POSIX paths rather than Applescript's default Mac style paths.
tell application "iTunes" set returnList to {} copy ((current date) as string) to the end of returnList set thePlaylist to playlist "Library" copy (count of every track in thePlaylist) to the end of returnList set allTracks to every track of thePlaylist repeat with theTrack in allTracks set theLocation to theTrack's location copy the POSIX path of theLocation to the end of returnList end repeat copy ((current date) as string) to the end of returnList end tell
return returnList
For me, it gives this output (I used the -ss option to osascript to output the list with quotes around the strings):
{"Wednesday, December 07, 2005 9:45:06 AM", 2055, "/Users/poetman/Music/iTunes/iTunes Music/Compilations/Alice In Chains_ Greatest Hits/Would_.m4p", (lots of others deleted for brevity), "/Users/poetman/Music/iTunes/iTunes Music/Echolaylia/The 27th Letter/Pennsylvania.mp3", "Wednesday, December 07, 2005 9:45:42 AM"}
2055 is the number of songs in my music library (OK, so I'm not big on collecting songs). More will add to the time, but it only took 36 seconds to go through all 2055, and I wouldn't think it'd be much more than a minute for double that.
![[User Picture]](http://l-userpic.livejournal.com/5887295/515656) | From: jwz Thu, 8-Dec-2005 12:51 AM (UTC)
-ss | (Link)
|
Apparently "-ss" is the fastlane to notworkingland. It slows it down a lot, and tends to do this: osascript: script had a result, but couldn't display it.: Cannot allocate memory I imagine they implemented -ss in applescript, sigh.
Is there a reason it needs to be a script?
If it's a one-time thing, or an occasional one, what I do is go to the playlist, make it show me only the fields I'm looking for (usually just track name and artist), then select all, copy, and paste into a text file.
Even simpler:
tell application "iTunes" to get the location of every file track of playlist "Foo"
Does it all in one go, as opposed to thousands of individual AppleEvents.
--Quentin
![[User Picture]](http://l-userpic.livejournal.com/1938/4354) | From: spike Wed, 7-Dec-2005 4:07 PM (UTC)
| (Link)
|
Exactly. I've been working with AppleScript since it was knee high to a grasshopper, and one of the things I've learned is that you want to minimize the number of operations that you perform. The two biggest ways to do that, IMNSHO, are: (1) When you want to get the same property from every object in a set, use an approach like coolerq's above: set valuelistvar to the property of every element of container(2) When you want to select certain items from a group, use a "whose" clause instead of a loop..test..accumulate setup: set objectlistvar to every element of container whose propertyname is valueYou can actually use any expression for the whose clause, not just "is". You can combine these two approaches to get selected information from selected objects all in one go: tell application "iTunes" get the location of every file track of playlist "Dark Pulse 4" whose name contains "remix" end tellMany applications have native implementation of whose-clause resolution, which lets them use their own internal indicies, data structures, caches, hacks, and so on to return the answer as fast as possible. (FileMaker is a classic example: if you say 'get every record of database "DB" whose field "area code" is "212"', the result is nearly instant since FileMaker actually uses it's 'database' data structures to find the matching records all at once.) Not all applications support native whose-clause resolution, and applications that do provide it don't have to provide it for every possible query type. If the app doesn't provide a native whose-clause resolver, the AppleScript engine runs the loop for you and accumulates the results, which is still faster than writing the loop in AppleScript yourself. Now, just so that you don't think I'm some kind of AppleScript apologist, here's the absolute suckiest implementation issue in the whole language at it exists today: the AppleScript "list" object does not support whose clauses. So you can't do the obvious thing you're going to want to: get every item of myobjectlist whose property operator valueThe omission of this single feature leads directly to the need to iterate over lists with repeat and accumulate the results "by hand", all of which slows everything down and causes most AppleScript programs to revert to their most base, vile, and slothful nature.
Well, that beats my script. Gah, why didn't I think of that?
jwz, if you like the idea of getting back the POSIX paths, then this script'll do it. Same setup on my end as before, but this only takes 6 seconds rather than 36. I'd say that's a win.
tell application "iTunes" set returnList to {} set locationList to the location of every file track of playlist "Library" repeat with theLocation in locationList copy the POSIX path of theLocation to the end of returnList end repeat end tell
return returnList
Maybe you'd better use some more civilized (Ruby, Python) or less, but still civilized languages (Perl)?
![[User Picture]](http://l-userpic.livejournal.com/67455339/3059589) | From: crimethnk Wed, 7-Dec-2005 5:05 PM (UTC)
civilization-- | (Link)
|
Mac::Glue is a perl module I wrote some years ago, which is now included in Tiger. It uses AppleScript vocabulary with Perl syntax, combining the "best" of both worlds ... The slight downside is you need to create a "glue" for each app prior to using it. Docs are included with the module, and I can be asked for more information. use Mac::Glue ':glue';
my $itunes = new Mac::Glue 'iTunes';
my $tracks = $itunes->obj(file_tracks => library_playlist => 1);
my $locations = $tracks->prop('location');
for my $location ($locations->get) {
print "$location\n";
}"whose" clauses are also possible: my $tracks = $itunes->obj(
file_track => whose(NOT =>
[ artist => contains => 'Kenny G' ]
),
library_playlist => 1
);
And so on.
That's what's beautiful about Appscript. For example, to get the value of the first paragraph of the topmost document in TextEdit:
app('TextEdit').documents[1].paragraphs[1].get()
This is equivalent to the AppleScript statement:
get paragraph 1 of document 1 of application "TextEdit"
![[User Picture]](http://l-userpic.livejournal.com/7209669/1340367) | From: marapfhile Wed, 7-Dec-2005 5:05 PM (UTC)
AppleScript as Functional Language? | (Link)
|
Interesting approach--sounds almost like treating AppleScript as a functional language with techniques like map and filter.
![[User Picture]](http://l-userpic.livejournal.com/12072952/2346955) | From: ckd Wed, 7-Dec-2005 7:19 PM (UTC)
| (Link)
|
You could use Python with PyObjC. Bob Ippolito's posted some sample code on his blog. Using the native XML parser to turn it into a Python dict is far, far faster than anything using AppleScript/osascript/the Python appscript bindings/etc.
From: effbot Sat, 10-Dec-2005 11:31 AM (UTC)
| (Link)
|
You don't really need PyObjC to do this; any XML toolkit can deal with the iTunes file. With this PLIST parsing recipe (scroll down a bit) and few lines of dictionary drilling and ugly search loops, I can pull out the files for a playlist in just over a second for a 5 megabyte iTunes file. Most of the time is spent on PLIST processing, so you could probably speed this up 2-4 times by operating directly on the XML structure. However, I suspect it will take more than j"ust over a second" to persuade JWZ to start tinkering with Python, so I'm not sure it would be a net win ;-)
From the above code, except using a string (as you originally wanted to), taking the playlist as a commandline argument. 134 seconds for a smartplaylist with 2000 songs in it.
#!/bin/sh
osascript \
-e 'tell application "iTunes"' \
-e ' set returnString to ""' \
-e " set locationlist to the location of every file track of playlist \"$1\"" \
-e ' repeat with theLocation in locationList' \
-e ' set returnString to returnString & the POSIX path of theLocation & "\n"' \
-e ' end repeat' \
-e 'end tell' \
-e 'return returnString'
Same, except using the original list from the above code and sed. It's a little more than twice as fast on my craptastic iBook2k. 63 seconds.
#!/bin/sh
osascript \
-e 'tell application "iTunes"' \
-e ' set returnList to {}' \
-e " set locationlist to the location of every file track of playlist \"$1\"" \
-e ' repeat with theLocation in locationList' \
-e ' copy the POSIX path of theLocation to the end of returnList' \
-e ' end repeat' \
-e 'end tell' \
-e 'return returnList' | \
sed 's/,\ \//\
\//g'
Rather than use applescript's "quoted form" or other such yuck, I would continue piping through sed to transform the paths if need be. Too bad iTunes doesn't natively dump .m3u playlists. Or, too bad I'm too dumb to figure out how to get iTunes to dump .m3u playlists. Maybe this is why you wanted to write this script in the first place?
![[User Picture]](http://l-userpic.livejournal.com/5887295/515656) | From: jwz Thu, 8-Dec-2005 12:56 AM (UTC)
#! | (Link)
|
BTW, the trick to writing shell scripts directly in AppleScript is: #!/bin/sh
exec osascript <<\EOF
tell application "iTunes"
... The backslash prevents sh expansion of $. You don't actually need to end the file with "EOF".
the more applescript I see, the more I'm convinced that the design doc said nothing more than the following:
1. Watch all 80s movies that feature computers. 2. Create a syntax parser that will run every program or command line featured in those movies. 3. profit.
![[User Picture]](http://l-userpic.livejournal.com/5887295/515656) | From: jwz Thu, 8-Dec-2005 12:57 AM (UTC)
| (Link)
|
It's like some demented chimera of COBOL and SQL.
![[User Picture]](http://l-userpic.livejournal.com/66868633/33052) | From: ch Thu, 8-Dec-2005 4:25 AM (UTC)
| (Link)
|
ding! the (bacon)monkey wins the prize.
![[User Picture]](http://l-userpic.livejournal.com/52383449/1168646) | From: avirr Sun, 11-Dec-2005 2:00 AM (UTC)
| (Link)
|
Close ;-) The folks who wrote it were designing an English-like scripting language based on... LISP!
The other main issue is that they didn't like multiple verbs, so everything is just set the foo to bar, and you have to guess about foo and bar. ARGHHHHH!
![[User Picture]](http://l-userpic.livejournal.com/1665848/555896) | From: kitten_moon Thu, 8-Dec-2005 3:21 AM (UTC)
XSLT is also horrible | (Link)
|
... but in different ways. Behold, a stylesheet: <?xml version="1.0"?>
<xsl:stylesheet version="1.0" id="stylesheet"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="xsl:stylesheet"/>
<xsl:param name="playlist" select="'Bootylicious'"/>
<xsl:variable name="plarray"
select="/plist/dict/array[preceding-sibling::key[1]='Playlists']"/>
<xsl:key name="tracks"
match="/plist/dict/dict[preceding-sibling::key[1]='Tracks']/key"
use="."/>
<xsl:template match="/">
<xsl:apply-templates
select="$plarray/dict[key[.='Name' and following-sibling::string[1]=$playlist]]"
mode="playlist"/>
</xsl:template>
<xsl:template match="dict" mode="playlist">
<xsl:for-each select="array/dict">
<xsl:variable name="trackid" select="integer"/>
<xsl:variable name="track"
select="key('tracks',$trackid)/following-sibling::dict[1]"/>
<xsl:value-of select="$track/string[preceding-sibling::key[1]='Name']"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>Save this to your iTunes directory, then apply it to your XML file like so: xsltproc --novalid --stringparam playlist "Name of your playlist here" \
makeplaylist.xsl "iTunes Music Library.xml" Output will be a list of file://localhost/ URLs, which approximates what you want I think? (This script adapted from some other stuff I had lying around, hence the wierd mode="playlist" stuff. Whatever, it works for me)
![[User Picture]](http://l-userpic.livejournal.com/1665848/555896) | From: kitten_moon Thu, 8-Dec-2005 3:36 AM (UTC)
Re: XSLT is also horrible | (Link)
|
xsltproc available hereOr use another XSLT engine.
Oh, and you can do, like... queries on it. And stuff. SELECT SUBSTRING(artist FROM 1 FOR 30) AS artist,
CAST(CAST(AVG(CASE WHEN rating = 0 THEN
(SELECT AVG(rating)
FROM tracks r
WHERE r.rating <> 0 AND
r.archive_id = ot.archive_id)
ElSE
rating
END) AS INTEGER)
AS CHAR(4)) || '%' as atr,
COUNT(*) AS considered,
(SELECT COUNT(u.*)
FROM tracks u
WHERE (u.archive_id, u.artist, rating) =
(ot.archive_id, ot.artist, 0)) AS unrated
FROM tracks ot
WHERE archive_id = (SELECT archive_id
FROM archives
JOIN users USING (user_id)
WHERE (users.name,
host,
source_name,
source_kind) =
('brianfeldman',
'macintosh.green.homeunix.org',
'Brian Feldman',
'iPod'))
GROUP by ot.artist, ot.archive_id
HAVING (SELECT COUNT(*) FROM (SELECT a.album, COUNT(*)
FROM tracks a
WHERE a.archive_id = ot.archive_id AND
a.artist = ot.artist
GROUP BY a.artist, a.album) AS real_albums) > 1
ORDER BY atr DESC, artist
LIMIT 25;
itunes: File > Export Song list Save as Type "text file" filename: "poop.txt"
command line: > cut -f 25 poop.txt
From: jjminer Tue, 13-Dec-2005 12:07 AM (UTC)
Presentation on XSL using iTunes Library | (Link)
|
| |