[Pythonmac-SIG] appscript: selecting many songs in iTunes

has hengist.podd at virgin.net
Mon Jan 16 17:56:27 CET 2006

Niko Matsakis wrote:

>I have a program which identifies duplicates in iTunes and attempts  to delete them automatically.  It seems to work like a charm, except  for one problem: once I have identified the set of songs to delete, I  have to go through them one-by-one and delete them.  So, if I have  accumulated the database_IDs for a set of songs into a list  'songdbids', I do something like:
>	allsongs = app ('iTunes').sources['Library'].playlists ['Library'].tracks
>	for songdbid in songdbids:
>		allsongs.filter (its.database_ID == songdbid).get()[0].delete ()
>This works fine when I test on small music collections, but when I  try to run it across my full set of music it takes forever (as you  might imagine).

Running a filter test across all tracks is expensive. On a large playlist, doing it repeatedly is going to be murder. So this is the operation you want to minimize/eliminate.

>Now, the number one rule of appscript performance which has been  drilled into my head is to avoid AppleEvents like the plague.

Yes, although dispatching the events themselves are relatively cheap these days (context switching in OS X doesn't suck like it did in OS 9); it's the cost of packing and unpacking their payloads, evaluating queries against the application's object model, etc. that sucks up most of the cycles. Of course, the fewer events you send, the less packing/unpacking/querying/etc. is going to be done as well, so it all pans out in the end. :)

>To  that end, it seems like what I want to do is something like:
>		allsongs.filter (its.database_ID in songdbids]).delete ()
>except I don't think that the 'in' operator works,

See ch.8 of the appscript manual for a list of supported comparison forms. Not all Python operators were amenable to overloading; in this case you need to use 'its.database_ID.isin(songdbids)'.

>and it looks like  I'm missing a get() in there in any case.

Depends what you're trying to get. iTunes scripting interface is pretty extensive, but a lot of the implementation is rather crude and many commands won't work on more than one object at a time. So the above might work as-is, but it probably won't, in which case you need to get a list of track references and tell iTunes to delete each individually:

    for song in allsongs.filter (its.database_ID.isin(songdbids)): song.delete()

>Can anyone think of a better way to do this?  Perhaps I am on the  wrong track altogether in working with database_IDs?

They seem to be the obvious way to check for dupes. You just don't want to perform multiple filter tests across your entire playlist to locate tracks by DB ID. But a single test shouldn't be too punishing (assuming iTunes can manage it ok).

>I can't select  by album or artist, however, as that would delete both copies of the  duplicated album (naturally).

And you'd still be running tons of filter tests to do it.

Anyway, try the fixes shown above. If it still doesn't work out well for you, another option would be to try an alternative approach like:

tracksRef = app('iTunes').playlists['Library'].tracks
d = {}
for id, track in zip(tracksRef.database_ID(), tracksRef()):
    d[id] = d.get(id, []) + [track]
for dupes in [i for i in d.values() if len(i) > 1]:
    for dupe in dupes[1:]:

No idea how that'll compare; getting a list of references to every single track won't be cheap due to packing/unpacking costs. You just have to experiment.



More information about the Pythonmac-SIG mailing list